Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/dom/events/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 27 kB image not shown  

Quelle  Clipboard.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "mozilla/dom/Clipboard.h"

#include <algorithm>

#include "mozilla/AbstractThread.h"
#include "mozilla/BasePrincipal.h"
#include "mozilla/RefPtr.h"
#include "mozilla/Result.h"
#include "mozilla/ResultVariant.h"
#include "mozilla/dom/BlobBinding.h"
#include "mozilla/dom/ClipboardItem.h"
#include "mozilla/dom/ClipboardBinding.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/PromiseNativeHandler.h"
#include "mozilla/dom/DataTransfer.h"
#include "mozilla/dom/DataTransferItemList.h"
#include "mozilla/dom/DataTransferItem.h"
#include "mozilla/dom/Document.h"
#include "mozilla/StaticPrefs_dom.h"
#include "imgIContainer.h"
#include "imgITools.h"
#include "nsArrayUtils.h"
#include "nsComponentManagerUtils.h"
#include "nsContentUtils.h"
#include "nsGlobalWindowInner.h"
#include "nsIClipboard.h"
#include "nsIInputStream.h"
#include "nsIParserUtils.h"
#include "nsISupportsPrimitives.h"
#include "nsITransferable.h"
#include "nsNetUtil.h"
#include "nsServiceManagerUtils.h"
#include "nsStringStream.h"
#include "nsTArray.h"
#include "nsThreadUtils.h"
#include "nsVariant.h"

static mozilla::LazyLogModule gClipboardLog("Clipboard");

namespace mozilla::dom {

Clipboard::Clipboard(nsPIDOMWindowInner* aWindow)
    : DOMEventTargetHelper(aWindow) {}

Clipboard::~Clipboard() = default;

// static
bool Clipboard::IsTestingPrefEnabledOrHasReadPermission(
    nsIPrincipal& aSubjectPrincipal) {
  return IsTestingPrefEnabled() ||
         nsContentUtils::PrincipalHasPermission(aSubjectPrincipal,
                                                nsGkAtoms::clipboardRead);
}

// Mandatory data types defined in
// https://w3c.github.io/clipboard-apis/#mandatory-data-types-x. The types
// should be in the same order as kNonPlainTextExternalFormats in
// DataTransfer.
static const nsLiteralCString kMandatoryDataTypes[] = {
    nsLiteralCString(kHTMLMime), nsLiteralCString(kTextMime),
    nsLiteralCString(kPNGImageMime)};

// static
Span<const nsLiteralCString> Clipboard::MandatoryDataTypes() {
  return Span<const nsLiteralCString>(kMandatoryDataTypes);
}

namespace {

/**
 * This is a base class for ClipboardGetCallbackForRead and
 * ClipboardGetCallbackForReadText.
 */

class ClipboardGetCallback : public nsIClipboardGetDataSnapshotCallback {
 public:
  explicit ClipboardGetCallback(RefPtr<Promise>&& aPromise)
      : mPromise(std::move(aPromise)) {}

  // nsIClipboardGetDataSnapshotCallback
  NS_IMETHOD OnError(nsresult aResult) override final {
    MOZ_ASSERT(mPromise);
    RefPtr<Promise> p(std::move(mPromise));
    p->MaybeRejectWithNotAllowedError(
        "Clipboard read operation is not allowed.");
    return NS_OK;
  }

 protected:
  virtual ~ClipboardGetCallback() { MOZ_ASSERT(!mPromise); };

  // Not cycle-collected, because it should be nulled when the request is
  // answered, rejected or aborted.
  RefPtr<Promise> mPromise;
};

class ClipboardGetCallbackForRead final : public ClipboardGetCallback {
 public:
  explicit ClipboardGetCallbackForRead(nsIGlobalObject* aGlobal,
                                       RefPtr<Promise>&& aPromise)
      : ClipboardGetCallback(std::move(aPromise)), mGlobal(aGlobal) {}

  // This object will never be held by a cycle-collected object, so it doesn't
  // need to be cycle-collected despite holding alive cycle-collected objects.
  NS_DECL_ISUPPORTS

  // nsIClipboardGetDataSnapshotCallback
  NS_IMETHOD OnSuccess(
      nsIClipboardDataSnapshot* aClipboardDataSnapshot) override {
    MOZ_ASSERT(mPromise);
    MOZ_ASSERT(aClipboardDataSnapshot);

    nsTArray<nsCString> flavorList;
    nsresult rv = aClipboardDataSnapshot->GetFlavorList(flavorList);
    if (NS_FAILED(rv)) {
      return OnError(rv);
    }

    AutoTArray<RefPtr<ClipboardItem::ItemEntry>, 3> entries;
    // We might reuse the request from DataTransfer created for paste event,
    // which could contain more types that are not in the mandatory list.
    for (const auto& format : kMandatoryDataTypes) {
      if (flavorList.Contains(format)) {
        auto entry = MakeRefPtr<ClipboardItem::ItemEntry>(
            mGlobal, NS_ConvertUTF8toUTF16(format));
        entry->LoadDataFromSystemClipboard(aClipboardDataSnapshot);
        entries.AppendElement(std::move(entry));
      }
    }

    RefPtr<Promise> p(std::move(mPromise));
    if (entries.IsEmpty()) {
      p->MaybeResolve(nsTArray<RefPtr<ClipboardItem>>{});
      return NS_OK;
    }

    // We currently only support one clipboard item.
    p->MaybeResolve(
        AutoTArray<RefPtr<ClipboardItem>, 1>{MakeRefPtr<ClipboardItem>(
            mGlobal, PresentationStyle::Unspecified, std::move(entries))});

    return NS_OK;
  }

 protected:
  ~ClipboardGetCallbackForRead() = default;

  nsCOMPtr<nsIGlobalObject> mGlobal;
};

NS_IMPL_ISUPPORTS(ClipboardGetCallbackForRead,
                  nsIClipboardGetDataSnapshotCallback)

class ClipboardGetCallbackForReadText final
    : public ClipboardGetCallback,
      public nsIAsyncClipboardRequestCallback {
 public:
  explicit ClipboardGetCallbackForReadText(RefPtr<Promise>&& aPromise)
      : ClipboardGetCallback(std::move(aPromise)) {}

  // This object will never be held by a cycle-collected object, so it doesn't
  // need to be cycle-collected despite holding alive cycle-collected objects.
  NS_DECL_ISUPPORTS

  // nsIClipboardGetDataSnapshotCallback
  NS_IMETHOD OnSuccess(
      nsIClipboardDataSnapshot* aClipboardDataSnapshot) override {
    MOZ_ASSERT(mPromise);
    MOZ_ASSERT(!mTransferable);
    MOZ_ASSERT(aClipboardDataSnapshot);

    AutoTArray<nsCString, 3> flavors;
    nsresult rv = aClipboardDataSnapshot->GetFlavorList(flavors);
    if (NS_FAILED(rv)) {
      return OnError(rv);
    }

    mTransferable = do_CreateInstance("@mozilla.org/widget/transferable;1");
    if (NS_WARN_IF(!mTransferable)) {
      return OnError(NS_ERROR_UNEXPECTED);
    }

    mTransferable->Init(nullptr);
    mTransferable->AddDataFlavor(kTextMime);
    if (!flavors.Contains(kTextMime)) {
      return OnComplete(NS_OK);
    }

    rv = aClipboardDataSnapshot->GetData(mTransferable, this);
    if (NS_FAILED(rv)) {
      return OnError(rv);
    }

    return NS_OK;
  }

  // nsIAsyncClipboardRequestCallback
  NS_IMETHOD OnComplete(nsresult aResult) override {
    MOZ_ASSERT(mPromise);
    MOZ_ASSERT(mTransferable);

    if (NS_FAILED(aResult)) {
      return OnError(aResult);
    }

    nsAutoString str;
    nsCOMPtr<nsISupports> data;
    nsresult rv =
        mTransferable->GetTransferData(kTextMime, getter_AddRefs(data));
    if (!NS_WARN_IF(NS_FAILED(rv))) {
      nsCOMPtr<nsISupportsString> supportsstr = do_QueryInterface(data);
      MOZ_ASSERT(supportsstr);
      if (supportsstr) {
        supportsstr->GetData(str);
      }
    }

    RefPtr<Promise> p(std::move(mPromise));
    p->MaybeResolve(str);

    return NS_OK;
  }

 protected:
  ~ClipboardGetCallbackForReadText() = default;

  nsCOMPtr<nsITransferable> mTransferable;
};

NS_IMPL_ISUPPORTS(ClipboardGetCallbackForReadText,
                  nsIClipboardGetDataSnapshotCallback,
                  nsIAsyncClipboardRequestCallback)

}  // namespace

void Clipboard::RequestRead(Promise& aPromise, const ReadRequestType& aType,
                            nsPIDOMWindowInner& aOwner,
                            nsIPrincipal& aSubjectPrincipal,
                            nsIClipboardDataSnapshot& aRequest) {
#ifdef DEBUG
  bool isValid = false;
  MOZ_ASSERT(NS_SUCCEEDED(aRequest.GetValid(&isValid)) && isValid);
#endif

  RefPtr<ClipboardGetCallback> callback;
  switch (aType) {
    case ReadRequestType::eRead: {
      callback =
          MakeRefPtr<ClipboardGetCallbackForRead>(aOwner.AsGlobal(), &aPromise);
      break;
    }
    case ReadRequestType::eReadText: {
      callback = MakeRefPtr<ClipboardGetCallbackForReadText>(&aPromise);
      break;
    }
    default: {
      MOZ_ASSERT_UNREACHABLE("Unknown read type");
      return;
    }
  }

  MOZ_ASSERT(callback);
  callback->OnSuccess(&aRequest);
}

void Clipboard::RequestRead(Promise* aPromise, ReadRequestType aType,
                            nsPIDOMWindowInner* aOwner,
                            nsIPrincipal& aPrincipal) {
  RefPtr<Promise> p(aPromise);
  nsCOMPtr<nsPIDOMWindowInner> owner(aOwner);

  nsresult rv;
  nsCOMPtr<nsIClipboard> clipboardService(
      do_GetService("@mozilla.org/widget/clipboard;1", &rv));
  if (NS_FAILED(rv)) {
    p->MaybeReject(NS_ERROR_UNEXPECTED);
    return;
  }

  RefPtr<ClipboardGetCallback> callback;
  switch (aType) {
    case ReadRequestType::eRead: {
      nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(owner);
      if (NS_WARN_IF(!global)) {
        p->MaybeReject(NS_ERROR_UNEXPECTED);
        return;
      }

      AutoTArray<nsCString, std::size(kMandatoryDataTypes)> types;
      types.AppendElements(Span<const nsLiteralCString>(kMandatoryDataTypes));

      callback = MakeRefPtr<ClipboardGetCallbackForRead>(global, std::move(p));
      rv = clipboardService->GetDataSnapshot(
          types, nsIClipboard::kGlobalClipboard, owner->GetWindowContext(),
          &aPrincipal, callback);
      break;
    }
    case ReadRequestType::eReadText: {
      callback = MakeRefPtr<ClipboardGetCallbackForReadText>(std::move(p));
      rv = clipboardService->GetDataSnapshot(
          AutoTArray<nsCString, 1>{nsLiteralCString(kTextMime)},
          nsIClipboard::kGlobalClipboard, owner->GetWindowContext(),
          &aPrincipal, callback);
      break;
    }
    default: {
      MOZ_ASSERT_UNREACHABLE("Unknown read type");
      break;
    }
  }

  if (NS_FAILED(rv)) {
    MOZ_ASSERT(callback);
    callback->OnError(rv);
    return;
  }
}

static bool IsReadTextExposedToContent() {
  return StaticPrefs::dom_events_asyncClipboard_readText_DoNotUseDirectly();
}

already_AddRefed<Promise> Clipboard::ReadHelper(nsIPrincipal& aSubjectPrincipal,
                                                ReadRequestType aType,
                                                ErrorResult& aRv) {
  // Create a new promise
  nsGlobalWindowInner* owner = GetOwnerWindow();
  RefPtr<Promise> p = dom::Promise::Create(owner, aRv);
  if (aRv.Failed()) {
    return nullptr;
  }

  // If a "paste" clipboard event is actively being processed, we're
  // intentionally skipping permission/user-activation checks and giving the
  // webpage access to the clipboard.
  if (RefPtr<DataTransfer> dataTransfer =
          owner->GetCurrentPasteDataTransfer()) {
    // If there is valid nsIClipboardDataSnapshot, use it directly.
    if (nsCOMPtr<nsIClipboardDataSnapshot> clipboardDataSnapshot =
            dataTransfer->GetClipboardDataSnapshot()) {
      bool isValid = false;
      clipboardDataSnapshot->GetValid(&isValid);
      if (isValid) {
        RequestRead(*p, aType, *owner, aSubjectPrincipal,
                    *clipboardDataSnapshot);
        return p.forget();
      }
    }
  }

  if (IsTestingPrefEnabledOrHasReadPermission(aSubjectPrincipal)) {
    MOZ_LOG(GetClipboardLog(), LogLevel::Debug,
            ("%s: testing pref enabled or has read permission", __FUNCTION__));
  } else {
    // Testing pref is not enabled and no read permission (for extension), so
    // need to check user activation.
    WindowContext* windowContext = owner->GetWindowContext();
    if (!windowContext) {
      MOZ_ASSERT_UNREACHABLE("There should be a WindowContext.");
      p->MaybeRejectWithUndefined();
      return p.forget();
    }

    // If no transient user activation, reject the promise and return.
    if (!windowContext->HasValidTransientUserGestureActivation()) {
      p->MaybeRejectWithNotAllowedError(
          "Clipboard read request was blocked due to lack of "
          "user activation.");
      return p.forget();
    }
  }

  RequestRead(p, aType, owner, aSubjectPrincipal);
  return p.forget();
}

already_AddRefed<Promise> Clipboard::Read(nsIPrincipal& aSubjectPrincipal,
                                          ErrorResult& aRv) {
  return ReadHelper(aSubjectPrincipal, ReadRequestType::eRead, aRv);
}

already_AddRefed<Promise> Clipboard::ReadText(nsIPrincipal& aSubjectPrincipal,
                                              ErrorResult& aRv) {
  return ReadHelper(aSubjectPrincipal, ReadRequestType::eReadText, aRv);
}

namespace {

struct NativeEntry {
  nsString mType;
  nsCOMPtr<nsIVariant> mData;

  NativeEntry(const nsAString& aType, nsIVariant* aData)
      : mType(aType), mData(aData) {}
};
using NativeEntryPromise = MozPromise<NativeEntry, CopyableErrorResult, false>;

class BlobTextHandler final : public PromiseNativeHandler {
 public:
  NS_DECL_THREADSAFE_ISUPPORTS

  explicit BlobTextHandler(const nsAString& aType) : mType(aType) {}

  RefPtr<NativeEntryPromise> Promise() { return mHolder.Ensure(__func__); }

  void Reject() {
    CopyableErrorResult rv;
    rv.ThrowUnknownError("Unable to read blob for '"_ns +
                         NS_ConvertUTF16toUTF8(mType) + "' as text."_ns);
    mHolder.Reject(rv, __func__);
  }

  void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
                        ErrorResult& aRv) override {
    AssertIsOnMainThread();

    nsString text;
    if (!ConvertJSValueToUSVString(aCx, aValue, "ClipboardItem text", text)) {
      Reject();
      return;
    }

    RefPtr<nsVariantCC> variant = new nsVariantCC();
    variant->SetAsAString(text);

    NativeEntry native(mType, variant);
    mHolder.Resolve(std::move(native), __func__);
  }

  void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
                        ErrorResult& aRv) override {
    Reject();
  }

 private:
  ~BlobTextHandler() = default;

  nsString mType;
  MozPromiseHolder<NativeEntryPromise> mHolder;
};

NS_IMPL_ISUPPORTS0(BlobTextHandler)

static RefPtr<NativeEntryPromise> GetStringNativeEntry(
    const nsAString& aType, const OwningStringOrBlob& aData) {
  if (aData.IsString()) {
    RefPtr<nsVariantCC> variant = new nsVariantCC();
    variant->SetAsAString(aData.GetAsString());
    NativeEntry native(aType, variant);
    return NativeEntryPromise::CreateAndResolve(native, __func__);
  }

  RefPtr<BlobTextHandler> handler = new BlobTextHandler(aType);
  IgnoredErrorResult ignored;
  RefPtr<Promise> promise = aData.GetAsBlob()->Text(ignored);
  if (ignored.Failed()) {
    CopyableErrorResult rv;
    rv.ThrowUnknownError("Unable to read blob for '"_ns +
                         NS_ConvertUTF16toUTF8(aType) + "' as text."_ns);
    return NativeEntryPromise::CreateAndReject(rv, __func__);
  }
  promise->AppendNativeHandler(handler);
  return handler->Promise();
}

class ImageDecodeCallback final : public imgIContainerCallback {
 public:
  NS_DECL_ISUPPORTS

  explicit ImageDecodeCallback(const nsAString& aType) : mType(aType) {}

  RefPtr<NativeEntryPromise> Promise() { return mHolder.Ensure(__func__); }

  NS_IMETHOD OnImageReady(imgIContainer* aImage, nsresult aStatus) override {
    // Request the image's width to force decoding the image header.
    int32_t ignored;
    if (NS_FAILED(aStatus) || NS_FAILED(aImage->GetWidth(&ignored))) {
      CopyableErrorResult rv;
      rv.ThrowDataError("Unable to decode blob for '"_ns +
                        NS_ConvertUTF16toUTF8(mType) + "' as image."_ns);
      mHolder.Reject(rv, __func__);
      return NS_OK;
    }

    RefPtr<nsVariantCC> variant = new nsVariantCC();
    variant->SetAsISupports(aImage);

    // Note: We always put the image as "native" on the clipboard.
    NativeEntry native(NS_LITERAL_STRING_FROM_CSTRING(kNativeImageMime),
                       variant);
    mHolder.Resolve(std::move(native), __func__);
    return NS_OK;
  };

 private:
  ~ImageDecodeCallback() = default;

  nsString mType;
  MozPromiseHolder<NativeEntryPromise> mHolder;
};

NS_IMPL_ISUPPORTS(ImageDecodeCallback, imgIContainerCallback)

static RefPtr<NativeEntryPromise> GetImageNativeEntry(
    const nsAString& aType, const OwningStringOrBlob& aData) {
  if (aData.IsString()) {
    CopyableErrorResult rv;
    rv.ThrowTypeError("DOMString not supported for '"_ns +
                      NS_ConvertUTF16toUTF8(aType) + "' as image data."_ns);
    return NativeEntryPromise::CreateAndReject(rv, __func__);
  }

  IgnoredErrorResult ignored;
  nsCOMPtr<nsIInputStream> stream;
  aData.GetAsBlob()->CreateInputStream(getter_AddRefs(stream), ignored);
  if (ignored.Failed()) {
    CopyableErrorResult rv;
    rv.ThrowUnknownError("Unable to read blob for '"_ns +
                         NS_ConvertUTF16toUTF8(aType) + "' as image."_ns);
    return NativeEntryPromise::CreateAndReject(rv, __func__);
  }

  RefPtr<ImageDecodeCallback> callback = new ImageDecodeCallback(aType);
  nsCOMPtr<imgITools> imgtool = do_CreateInstance("@mozilla.org/image/tools;1");
  imgtool->DecodeImageAsync(stream, NS_ConvertUTF16toUTF8(aType), callback,
                            GetMainThreadSerialEventTarget());
  return callback->Promise();
}

static Result<NativeEntry, ErrorResult> SanitizeNativeEntry(
    const NativeEntry& aEntry) {
  MOZ_ASSERT(aEntry.mType.EqualsLiteral(kHTMLMime));

  nsAutoString string;
  aEntry.mData->GetAsAString(string);

  nsCOMPtr<nsIParserUtils> parserUtils =
      do_GetService(NS_PARSERUTILS_CONTRACTID);
  if (!parserUtils) {
    ErrorResult rv;
    rv.ThrowUnknownError("Error while processing '"_ns +
                         NS_ConvertUTF16toUTF8(aEntry.mType) + "'."_ns);
    return Err(std::move(rv));
  }

  uint32_t flags = nsIParserUtils::SanitizerAllowStyle |
                   nsIParserUtils::SanitizerAllowComments;
  nsAutoString sanitized;
  if (NS_FAILED(parserUtils->Sanitize(string, flags, sanitized))) {
    ErrorResult rv;
    rv.ThrowUnknownError("Error while processing '"_ns +
                         NS_ConvertUTF16toUTF8(aEntry.mType) + "'."_ns);
    return Err(std::move(rv));
  }

  RefPtr<nsVariantCC> variant = new nsVariantCC();
  variant->SetAsAString(sanitized);
  return NativeEntry(aEntry.mType, variant);
}

static RefPtr<NativeEntryPromise> GetNativeEntry(
    const nsAString& aType, const OwningStringOrBlob& aData) {
  if (aType.EqualsLiteral(kPNGImageMime)) {
    return GetImageNativeEntry(aType, aData);
  }

  RefPtr<NativeEntryPromise> promise = GetStringNativeEntry(aType, aData);
  if (aType.EqualsLiteral(kHTMLMime)) {
    promise = promise->Then(
        GetMainThreadSerialEventTarget(), __func__,
        [](const NativeEntryPromise::ResolveOrRejectValue& aValue)
            -> RefPtr<NativeEntryPromise> {
          if (aValue.IsReject()) {
            return NativeEntryPromise::CreateAndReject(aValue.RejectValue(),
                                                       __func__);
          }

          auto sanitized = SanitizeNativeEntry(aValue.ResolveValue());
          if (sanitized.isErr()) {
            return NativeEntryPromise::CreateAndReject(
                CopyableErrorResult(sanitized.unwrapErr()), __func__);
          }
          return NativeEntryPromise::CreateAndResolve(sanitized.unwrap(),
                                                      __func__);
        });
  }
  return promise;
}

// Restrict to types allowed by Chrome
// SVG is still disabled by default in Chrome.
static bool IsValidType(const nsAString& aType) {
  return aType.EqualsLiteral(kPNGImageMime) || aType.EqualsLiteral(kTextMime) ||
         aType.EqualsLiteral(kHTMLMime);
}

using NativeItemPromise = NativeEntryPromise::AllPromiseType;
static RefPtr<NativeItemPromise> GetClipboardNativeItem(
    const ClipboardItem& aItem) {
  nsTArray<RefPtr<NativeEntryPromise>> promises;
  for (const auto& entry : aItem.Entries()) {
    const nsAString& type = entry->Type();
    if (!IsValidType(type)) {
      CopyableErrorResult rv;
      rv.ThrowNotAllowedError("Type '"_ns + NS_ConvertUTF16toUTF8(type) +
                              "' not supported for write"_ns);
      return NativeItemPromise::CreateAndReject(rv, __func__);
    }

    using GetDataPromise = ClipboardItem::ItemEntry::GetDataPromise;
    promises.AppendElement(entry->GetData()->Then(
        GetMainThreadSerialEventTarget(), __func__,
        [t = nsString(type)](const GetDataPromise::ResolveOrRejectValue& aValue)
            -> RefPtr<NativeEntryPromise> {
          if (aValue.IsReject()) {
            return NativeEntryPromise::CreateAndReject(
                CopyableErrorResult(aValue.RejectValue()), __func__);
          }

          return GetNativeEntry(t, aValue.ResolveValue());
        }));
  }
  return NativeEntryPromise::All(GetCurrentSerialEventTarget(), promises);
}

class ClipboardWriteCallback final : public nsIAsyncClipboardRequestCallback {
 public:
  // This object will never be held by a cycle-collected object, so it doesn't
  // need to be cycle-collected despite holding alive cycle-collected objects.
  NS_DECL_ISUPPORTS

  explicit ClipboardWriteCallback(Promise* aPromise,
                                  ClipboardItem* aClipboardItem)
      : mPromise(aPromise), mClipboardItem(aClipboardItem) {}

  // nsIAsyncClipboardRequestCallback
  NS_IMETHOD OnComplete(nsresult aResult) override {
    MOZ_ASSERT(mPromise);

    RefPtr<Promise> promise = std::move(mPromise);
    // XXX We need to check state here is because the promise might be rejected
    // before the callback is called, we probably could wrap the promise into a
    // structure to make it less confused.
    if (promise->State() == Promise::PromiseState::Pending) {
      if (NS_FAILED(aResult)) {
        promise->MaybeRejectWithNotAllowedError(
            "Clipboard write is not allowed.");
        return NS_OK;
      }

      promise->MaybeResolveWithUndefined();
    }

    return NS_OK;
  }

 protected:
  ~ClipboardWriteCallback() {
    // Callback should be notified.
    MOZ_ASSERT(!mPromise);
  };

  // It will be reset to nullptr once callback is notified.
  RefPtr<Promise> mPromise;
  // Keep ClipboardItem alive until clipboard write is done.
  RefPtr<ClipboardItem> mClipboardItem;
};

NS_IMPL_ISUPPORTS(ClipboardWriteCallback, nsIAsyncClipboardRequestCallback)

}  // namespace

already_AddRefed<Promise> Clipboard::Write(
    const Sequence<OwningNonNull<ClipboardItem>>& aData,
    nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
  // Create a promise
  RefPtr<nsGlobalWindowInner> owner = GetOwnerWindow();
  RefPtr<Promise> p = dom::Promise::Create(owner, aRv);
  if (aRv.Failed()) {
    return nullptr;
  }

  Document* doc = owner->GetDoc();
  if (!doc) {
    p->MaybeRejectWithUndefined();
    return p.forget();
  }

  // We want to disable security check for automated tests that have the pref
  //  dom.events.testing.asyncClipboard set to true
  if (!IsTestingPrefEnabled() &&
      !nsContentUtils::IsCutCopyAllowed(doc, aSubjectPrincipal)) {
    MOZ_LOG(GetClipboardLog(), LogLevel::Debug,
            ("Clipboard, Write, Not allowed to write to clipboard\n"));
    p->MaybeRejectWithNotAllowedError(
        "Clipboard write was blocked due to lack of user activation.");
    return p.forget();
  }

  // Get the clipboard service
  nsCOMPtr<nsIClipboard> clipboard(
      do_GetService("@mozilla.org/widget/clipboard;1"));
  if (!clipboard) {
    p->MaybeRejectWithUndefined();
    return p.forget();
  }

  nsCOMPtr<nsILoadContext> context = doc->GetLoadContext();
  if (!context) {
    p->MaybeRejectWithUndefined();
    return p.forget();
  }

  if (aData.Length() > 1) {
    p->MaybeRejectWithNotAllowedError(
        "Clipboard write is only supported with one ClipboardItem at the "
        "moment");
    return p.forget();
  }

  if (aData.Length() == 0) {
    // Nothing needs to be written to the clipboard.
    p->MaybeResolveWithUndefined();
    return p.forget();
  }

  nsCOMPtr<nsIAsyncSetClipboardData> request;
  RefPtr<ClipboardWriteCallback> callback =
      MakeRefPtr<ClipboardWriteCallback>(p, aData[0]);
  nsresult rv = clipboard->AsyncSetData(nsIClipboard::kGlobalClipboard,
                                        owner->GetWindowContext(), callback,
                                        getter_AddRefs(request));
  if (NS_FAILED(rv)) {
    p->MaybeReject(rv);
    return p.forget();
  }

  GetClipboardNativeItem(aData[0])->Then(
      GetMainThreadSerialEventTarget(), __func__,
      [owner, request, context, principal = RefPtr{&aSubjectPrincipal}](
          const nsTArray<NativeEntry>& aEntries) {
        RefPtr<DataTransfer> dataTransfer =
            new DataTransfer(ToSupports(owner), eCopy,
                             /* is external */ true,
                             /* clipboard type */ Nothing());

        for (const auto& entry : aEntries) {
          nsresult rv = dataTransfer->SetDataWithPrincipal(
              entry.mType, entry.mData, 0, principal);

          if (NS_FAILED(rv)) {
            request->Abort(rv);
            return;
          }
        }

        // Get the transferable
        RefPtr<nsITransferable> transferable =
            dataTransfer->GetTransferable(0, context);
        if (!transferable) {
          request->Abort(NS_ERROR_FAILURE);
          return;
        }

        // Finally write data to clipboard
        request->SetData(transferable, /* clipboard owner */ nullptr);
      },
      [p, request](const CopyableErrorResult& aErrorResult) {
        p->MaybeReject(CopyableErrorResult(aErrorResult));
        request->Abort(NS_ERROR_ABORT);
      });

  return p.forget();
}

already_AddRefed<Promise> Clipboard::WriteText(const nsAString& aData,
                                               nsIPrincipal& aSubjectPrincipal,
                                               ErrorResult& aRv) {
  nsCOMPtr<nsIGlobalObject> global = GetOwnerGlobal();
  if (!global) {
    aRv.ThrowInvalidStateError("Unable to get global.");
    return nullptr;
  }

  // Create a single-element Sequence to reuse Clipboard::Write.
  nsTArray<RefPtr<ClipboardItem::ItemEntry>> items;
  items.AppendElement(MakeRefPtr<ClipboardItem::ItemEntry>(
      global, NS_LITERAL_STRING_FROM_CSTRING(kTextMime), aData));

  nsTArray<OwningNonNull<ClipboardItem>> sequence;
  RefPtr<ClipboardItem> item = MakeRefPtr<ClipboardItem>(
      ToSupports(GetOwnerWindow()), PresentationStyle::Unspecified,
      std::move(items));
  sequence.AppendElement(*item);

  return Write(std::move(sequence), aSubjectPrincipal, aRv);
}

JSObject* Clipboard::WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aGivenProto) {
  return Clipboard_Binding::Wrap(aCx, this, aGivenProto);
}

/* static */
LogModule* Clipboard::GetClipboardLog() { return gClipboardLog; }

/* static */
bool Clipboard::ReadTextEnabled(JSContext* aCx, JSObject* aGlobal) {
  nsIPrincipal* prin = nsContentUtils::SubjectPrincipal(aCx);
  return IsReadTextExposedToContent() ||
         prin->GetIsAddonOrExpandedAddonPrincipal() ||
         prin->IsSystemPrincipal();
}

/* static */
bool Clipboard::IsTestingPrefEnabled() {
  bool clipboardTestingEnabled =
      StaticPrefs::dom_events_testing_asyncClipboard_DoNotUseDirectly();
  MOZ_LOG(GetClipboardLog(), LogLevel::Debug,
          ("Clipboard, Is testing enabled? %d\n", clipboardTestingEnabled));
  return clipboardTestingEnabled;
}

NS_IMPL_CYCLE_COLLECTION_CLASS(Clipboard)

NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(Clipboard,
                                                  DOMEventTargetHelper)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END

NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(Clipboard, DOMEventTargetHelper)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END

NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Clipboard)
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)

NS_IMPL_ADDREF_INHERITED(Clipboard, DOMEventTargetHelper)
NS_IMPL_RELEASE_INHERITED(Clipboard, DOMEventTargetHelper)

}  // namespace mozilla::dom

Messung V0.5
C=90 H=100 G=95

¤ Dauer der Verarbeitung: 0.17 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.