tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

DataTransfer.cpp (61723B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "DataTransfer.h"
      8 
      9 #include "mozilla/BasePrincipal.h"
     10 #include "mozilla/BasicEvents.h"
     11 #include "mozilla/CheckedInt.h"
     12 #include "mozilla/ClipboardContentAnalysisChild.h"
     13 #include "mozilla/ClipboardReadRequestChild.h"
     14 #include "mozilla/Span.h"
     15 #include "mozilla/SpinEventLoopUntil.h"
     16 #include "mozilla/StaticPrefs_dom.h"
     17 #include "mozilla/dom/BindingUtils.h"
     18 #include "mozilla/dom/ContentChild.h"
     19 #include "mozilla/dom/DOMStringList.h"
     20 #include "mozilla/dom/DataTransferBinding.h"
     21 #include "mozilla/dom/DataTransferItemList.h"
     22 #include "mozilla/dom/Directory.h"
     23 #include "mozilla/dom/Document.h"
     24 #include "mozilla/dom/DocumentInlines.h"
     25 #include "mozilla/dom/Element.h"
     26 #include "mozilla/dom/Event.h"
     27 #include "mozilla/dom/FileList.h"
     28 #include "mozilla/dom/IPCBlobUtils.h"
     29 #include "mozilla/dom/OSFileSystem.h"
     30 #include "mozilla/dom/Promise.h"
     31 #include "mozilla/dom/WindowContext.h"
     32 #include "nsArray.h"
     33 #include "nsBaseClipboard.h"
     34 #include "nsCRT.h"
     35 #include "nsComponentManagerUtils.h"
     36 #include "nsContentUtils.h"
     37 #include "nsError.h"
     38 #include "nsIClipboard.h"
     39 #include "nsIContent.h"
     40 #include "nsIContentAnalysis.h"
     41 #include "nsIDragService.h"
     42 #include "nsIObjectInputStream.h"
     43 #include "nsIObjectOutputStream.h"
     44 #include "nsIScriptContext.h"
     45 #include "nsIScriptGlobalObject.h"
     46 #include "nsIScriptObjectPrincipal.h"
     47 #include "nsIScriptSecurityManager.h"
     48 #include "nsIStorageStream.h"
     49 #include "nsISupportsPrimitives.h"
     50 #include "nsIXPConnect.h"
     51 #include "nsNetUtil.h"
     52 #include "nsPresContext.h"
     53 #include "nsQueryObject.h"
     54 #include "nsReadableUtils.h"
     55 #include "nsStringStream.h"
     56 #include "nsVariant.h"
     57 
     58 namespace mozilla::dom {
     59 
     60 // The order of the types matters. `kFileMime` needs to be one of the first
     61 // two types. And the order should be the same as the types order defined in
     62 // MandatoryDataTypesAsCStrings() for Clipboard API.
     63 static constexpr nsLiteralCString kNonPlainTextExternalFormats[] = {
     64    nsLiteralCString(kCustomTypesMime), nsLiteralCString(kFileMime),
     65    nsLiteralCString(kHTMLMime),        nsLiteralCString(kRTFMime),
     66    nsLiteralCString(kURLMime),         nsLiteralCString(kURLDataMime),
     67    nsLiteralCString(kTextMime),        nsLiteralCString(kPNGImageMime),
     68    nsLiteralCString(kPDFJSMime)};
     69 
     70 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(DataTransfer)
     71 
     72 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DataTransfer)
     73  NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
     74  NS_IMPL_CYCLE_COLLECTION_UNLINK(mItems)
     75  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDragTarget)
     76  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDragImage)
     77  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
     78 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     79 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DataTransfer)
     80  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
     81  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mItems)
     82  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDragTarget)
     83  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDragImage)
     84 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     85 
     86 NS_IMPL_CYCLE_COLLECTING_ADDREF(DataTransfer)
     87 NS_IMPL_CYCLE_COLLECTING_RELEASE(DataTransfer)
     88 
     89 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DataTransfer)
     90  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
     91  NS_INTERFACE_MAP_ENTRY(mozilla::dom::DataTransfer)
     92  NS_INTERFACE_MAP_ENTRY(nsISupports)
     93 NS_INTERFACE_MAP_END
     94 
     95 // the size of the array
     96 const char DataTransfer::sEffects[8][9] = {
     97    "none", "copy", "move", "copyMove", "link", "copyLink", "linkMove", "all"};
     98 
     99 // Used for custom clipboard types.
    100 enum CustomClipboardTypeId {
    101  eCustomClipboardTypeId_None,
    102  eCustomClipboardTypeId_String
    103 };
    104 
    105 static DataTransfer::Mode ModeForEvent(EventMessage aEventMessage) {
    106  switch (aEventMessage) {
    107    case eCut:
    108    case eCopy:
    109    case eDragStart:
    110      // For these events, we want to be able to add data to the data transfer,
    111      // Otherwise, the data is already present.
    112      return DataTransfer::Mode::ReadWrite;
    113    case eDrop:
    114    case ePaste:
    115    case ePasteNoFormatting:
    116    case eEditorInput:
    117      // For these events we want to be able to read the data which is stored in
    118      // the DataTransfer, rather than just the type information.
    119      return DataTransfer::Mode::ReadOnly;
    120    default:
    121      return StaticPrefs::dom_events_dataTransfer_protected_enabled()
    122                 ? DataTransfer::Mode::Protected
    123                 : DataTransfer::Mode::ReadOnly;
    124  }
    125 }
    126 
    127 DataTransfer::DataTransfer(
    128    nsISupports* aParent, EventMessage aEventMessage, bool aIsExternal,
    129    mozilla::Maybe<nsIClipboard::ClipboardType> aClipboardType)
    130    : mParent(aParent),
    131      mEventMessage(aEventMessage),
    132      mMode(ModeForEvent(aEventMessage)),
    133      mIsExternal(aIsExternal),
    134      mClipboardType(aClipboardType) {
    135  mItems = new DataTransferItemList(this);
    136 
    137  // For external usage, cache the data from the native clipboard or drag.
    138  if (mIsExternal && mMode != Mode::ReadWrite) {
    139    if (aEventMessage == ePasteNoFormatting) {
    140      mEventMessage = ePaste;
    141      CacheExternalClipboardFormats(true);
    142    } else if (aEventMessage == ePaste) {
    143      CacheExternalClipboardFormats(false);
    144    } else if (aEventMessage >= eDragDropEventFirst &&
    145               aEventMessage <= eDragDropEventLast) {
    146      CacheExternalDragFormats();
    147    }
    148  }
    149 }
    150 
    151 DataTransfer::DataTransfer(nsISupports* aParent, EventMessage aEventMessage,
    152                           nsITransferable* aTransferable)
    153    : mParent(aParent),
    154      mTransferable(aTransferable),
    155      mEventMessage(aEventMessage),
    156      mMode(ModeForEvent(aEventMessage)),
    157      mIsExternal(true) {
    158  mItems = new DataTransferItemList(this);
    159 
    160  // XXX Currently, we cannot make DataTransfer grabs mTransferable for long
    161  //     time because nsITransferable is not cycle collectable but this may
    162  //     be grabbed by JS.  Additionally, the data initializing path is too
    163  //     complicated (too optimized) for D&D and clipboard.  They are cached
    164  //     only formats first, then, data of all items will be filled by the
    165  //     items later and by themselves.  However, we shouldn't duplicate such
    166  //     path for saving the maintenance cost.  Therefore, we need to treat
    167  //     that DataTransfer and its items are in external mode.  Finally,
    168  //     release mTransferable and make them in internal mode.
    169  CacheTransferableFormats();
    170  FillAllExternalData();
    171  // Now, we have all necessary data of mTransferable.  So, we can work as
    172  // internal mode.
    173  mIsExternal = false;
    174  // Release mTransferable because it won't be referred anymore.
    175  mTransferable = nullptr;
    176 }
    177 
    178 DataTransfer::DataTransfer(nsISupports* aParent, EventMessage aEventMessage,
    179                           const nsAString& aString)
    180    : mParent(aParent),
    181      mEventMessage(aEventMessage),
    182      mMode(ModeForEvent(aEventMessage)) {
    183  mItems = new DataTransferItemList(this);
    184 
    185  nsCOMPtr<nsIPrincipal> sysPrincipal = nsContentUtils::GetSystemPrincipal();
    186 
    187  RefPtr<nsVariantCC> variant = new nsVariantCC();
    188  variant->SetAsAString(aString);
    189  DebugOnly<nsresult> rvIgnored =
    190      SetDataWithPrincipal(u"text/plain"_ns, variant, 0, sysPrincipal, false);
    191  NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
    192                       "Failed to set given string to the DataTransfer object");
    193 }
    194 
    195 DataTransfer::DataTransfer(nsISupports* aParent,
    196                           nsIClipboard::ClipboardType aClipboardType,
    197                           nsIClipboardDataSnapshot* aClipboardDataSnapshot)
    198    : mParent(aParent),
    199      mEventMessage(ePaste),
    200      mMode(ModeForEvent(ePaste)),
    201      mClipboardType(Some(aClipboardType)) {
    202  MOZ_ASSERT(aClipboardDataSnapshot);
    203 
    204  mClipboardDataSnapshot = aClipboardDataSnapshot;
    205  mItems = new DataTransferItemList(this);
    206 
    207  AutoTArray<nsCString, std::size(kNonPlainTextExternalFormats)> flavors;
    208  if (NS_FAILED(aClipboardDataSnapshot->GetFlavorList(flavors))) {
    209    NS_WARNING("nsIClipboardDataSnapshot::GetFlavorList() failed");
    210    return;
    211  }
    212 
    213  // Order is important for DataTransfer; ensure the returned list items follow
    214  // the sequence specified in kNonPlainTextExternalFormats.
    215  AutoTArray<nsCString, std::size(kNonPlainTextExternalFormats)> typesArray;
    216  for (const auto& format : kNonPlainTextExternalFormats) {
    217    if (flavors.Contains(format)) {
    218      typesArray.AppendElement(format);
    219    }
    220  }
    221 
    222  CacheExternalData(typesArray, nsContentUtils::GetSystemPrincipal());
    223 }
    224 
    225 DataTransfer::DataTransfer(
    226    nsISupports* aParent, EventMessage aEventMessage,
    227    const uint32_t aEffectAllowed, bool aCursorState, bool aIsExternal,
    228    bool aUserCancelled, bool aIsCrossDomainSubFrameDrop,
    229    mozilla::Maybe<nsIClipboard::ClipboardType> aClipboardType,
    230    nsCOMPtr<nsIClipboardDataSnapshot> aClipboardDataSnapshot,
    231    DataTransferItemList* aItems, Element* aDragImage, uint32_t aDragImageX,
    232    uint32_t aDragImageY, bool aShowFailAnimation)
    233    : mParent(aParent),
    234      mEffectAllowed(aEffectAllowed),
    235      mEventMessage(aEventMessage),
    236      mCursorState(aCursorState),
    237      mMode(ModeForEvent(aEventMessage)),
    238      mIsExternal(aIsExternal),
    239      mUserCancelled(aUserCancelled),
    240      mIsCrossDomainSubFrameDrop(aIsCrossDomainSubFrameDrop),
    241      mClipboardType(aClipboardType),
    242      mClipboardDataSnapshot(std::move(aClipboardDataSnapshot)),
    243      mDragImage(aDragImage),
    244      mDragImageX(aDragImageX),
    245      mDragImageY(aDragImageY),
    246      mShowFailAnimation(aShowFailAnimation) {
    247  MOZ_ASSERT(mParent);
    248  MOZ_ASSERT(aItems);
    249 
    250  // We clone the items array after everything else, so that it has a valid
    251  // mParent value
    252  mItems = aItems->Clone(this);
    253  // The items are copied from aItems into mItems. There is no need to copy
    254  // the actual data in the items as the data transfer will be read only. The
    255  // dragstart event is the only time when items are
    256  // modifiable, but those events should have been using the first constructor
    257  // above.
    258  NS_ASSERTION(aEventMessage != eDragStart,
    259               "invalid event type for DataTransfer constructor");
    260 }
    261 
    262 DataTransfer::~DataTransfer() = default;
    263 
    264 // static
    265 already_AddRefed<DataTransfer> DataTransfer::Constructor(
    266    const GlobalObject& aGlobal) {
    267  RefPtr<DataTransfer> transfer =
    268      new DataTransfer(aGlobal.GetAsSupports(), eCopy, /* is external */ false,
    269                       /* clipboard type */ Nothing());
    270  transfer->mEffectAllowed = nsIDragService::DRAGDROP_ACTION_NONE;
    271  return transfer.forget();
    272 }
    273 
    274 JSObject* DataTransfer::WrapObject(JSContext* aCx,
    275                                   JS::Handle<JSObject*> aGivenProto) {
    276  return DataTransfer_Binding::Wrap(aCx, this, aGivenProto);
    277 }
    278 
    279 namespace {
    280 
    281 class ClipboardGetDataSnapshotCallback final
    282    : public nsIClipboardGetDataSnapshotCallback {
    283 public:
    284  ClipboardGetDataSnapshotCallback(nsIGlobalObject* aGlobal,
    285                                   nsIClipboard::ClipboardType aClipboardType)
    286      : mGlobal(aGlobal), mClipboardType(aClipboardType) {}
    287 
    288  // This object will never be held by a cycle-collected object, so it doesn't
    289  // need to be cycle-collected despite holding alive cycle-collected objects.
    290  NS_DECL_ISUPPORTS
    291 
    292  // nsIClipboardGetDataSnapshotCallback
    293  NS_IMETHOD OnSuccess(
    294      nsIClipboardDataSnapshot* aClipboardDataSnapshot) override {
    295    MOZ_ASSERT(aClipboardDataSnapshot);
    296    mDataTransfer = MakeRefPtr<DataTransfer>(
    297        ToSupports(mGlobal), mClipboardType, aClipboardDataSnapshot);
    298    mComplete = true;
    299    return NS_OK;
    300  }
    301 
    302  NS_IMETHOD OnError(nsresult aResult) override {
    303    mComplete = true;
    304    return NS_OK;
    305  }
    306 
    307  already_AddRefed<DataTransfer> TakeDataTransfer() {
    308    MOZ_ASSERT(mComplete);
    309    return mDataTransfer.forget();
    310  }
    311 
    312  bool IsComplete() const { return mComplete; }
    313 
    314 protected:
    315  ~ClipboardGetDataSnapshotCallback() {
    316    MOZ_ASSERT(!mDataTransfer);
    317    MOZ_ASSERT(mComplete);
    318  };
    319 
    320  nsCOMPtr<nsIGlobalObject> mGlobal;
    321  RefPtr<DataTransfer> mDataTransfer;
    322  nsIClipboard::ClipboardType mClipboardType;
    323  bool mComplete = false;
    324 };
    325 
    326 NS_IMPL_ISUPPORTS(ClipboardGetDataSnapshotCallback,
    327                  nsIClipboardGetDataSnapshotCallback)
    328 
    329 }  // namespace
    330 
    331 // static
    332 already_AddRefed<DataTransfer>
    333 DataTransfer::WaitForClipboardDataSnapshotAndCreate(
    334    nsPIDOMWindowOuter* aWindow, nsIPrincipal* aSubjectPrincipal) {
    335  MOZ_ASSERT(aWindow);
    336  MOZ_ASSERT(aSubjectPrincipal);
    337 
    338  nsCOMPtr<nsIClipboard> clipboardService =
    339      do_GetService("@mozilla.org/widget/clipboard;1");
    340  if (!clipboardService) {
    341    return nullptr;
    342  }
    343 
    344  BrowsingContext* bc = aWindow->GetBrowsingContext();
    345  if (!bc) {
    346    return nullptr;
    347  }
    348 
    349  WindowContext* wc = bc->GetCurrentWindowContext();
    350  if (!wc) {
    351    return nullptr;
    352  }
    353 
    354  Document* doc = wc->GetExtantDoc();
    355  if (!doc) {
    356    return nullptr;
    357  }
    358 
    359  RefPtr<ClipboardGetDataSnapshotCallback> callback =
    360      MakeRefPtr<ClipboardGetDataSnapshotCallback>(
    361          doc->GetScopeObject(), nsIClipboard::kGlobalClipboard);
    362 
    363  AutoTArray<nsCString, std::size(kNonPlainTextExternalFormats)> types;
    364  types.AppendElements(
    365      Span<const nsLiteralCString>(kNonPlainTextExternalFormats));
    366 
    367  nsresult rv = clipboardService->GetDataSnapshot(
    368      types, nsIClipboard::kGlobalClipboard, wc, aSubjectPrincipal, callback);
    369  if (NS_FAILED(rv)) {
    370    return nullptr;
    371  }
    372 
    373  if (!SpinEventLoopUntil(
    374          "DataTransfer::WaitForClipboardDataSnapshotAndCreate"_ns,
    375          [&]() { return callback->IsComplete(); })) {
    376    return nullptr;
    377  }
    378 
    379  return callback->TakeDataTransfer();
    380 }
    381 
    382 void DataTransfer::SetDropEffect(const nsAString& aDropEffect) {
    383  // the drop effect can only be 'none', 'copy', 'move' or 'link'.
    384  for (uint32_t e = 0; e <= nsIDragService::DRAGDROP_ACTION_LINK; e++) {
    385    if (aDropEffect.EqualsASCII(sEffects[e])) {
    386      // don't allow copyMove
    387      if (e != (nsIDragService::DRAGDROP_ACTION_COPY |
    388                nsIDragService::DRAGDROP_ACTION_MOVE)) {
    389        mDropEffect = e;
    390      }
    391      break;
    392    }
    393  }
    394 }
    395 
    396 void DataTransfer::SetEffectAllowed(const nsAString& aEffectAllowed) {
    397  if (aEffectAllowed.EqualsLiteral("uninitialized")) {
    398    mEffectAllowed = nsIDragService::DRAGDROP_ACTION_UNINITIALIZED;
    399    return;
    400  }
    401 
    402  static_assert(nsIDragService::DRAGDROP_ACTION_NONE == 0,
    403                "DRAGDROP_ACTION_NONE constant is wrong");
    404  static_assert(nsIDragService::DRAGDROP_ACTION_COPY == 1,
    405                "DRAGDROP_ACTION_COPY constant is wrong");
    406  static_assert(nsIDragService::DRAGDROP_ACTION_MOVE == 2,
    407                "DRAGDROP_ACTION_MOVE constant is wrong");
    408  static_assert(nsIDragService::DRAGDROP_ACTION_LINK == 4,
    409                "DRAGDROP_ACTION_LINK constant is wrong");
    410 
    411  for (uint32_t e = 0; e < std::size(sEffects); e++) {
    412    if (aEffectAllowed.EqualsASCII(sEffects[e])) {
    413      mEffectAllowed = e;
    414      break;
    415    }
    416  }
    417 }
    418 
    419 void DataTransfer::GetMozTriggeringPrincipalURISpec(
    420    nsAString& aPrincipalURISpec) {
    421  auto* dragSession = GetOwnerDragSession();
    422  if (!dragSession) {
    423    aPrincipalURISpec.Truncate(0);
    424    return;
    425  }
    426 
    427  nsCOMPtr<nsIPrincipal> principal;
    428  dragSession->GetTriggeringPrincipal(getter_AddRefs(principal));
    429  if (!principal) {
    430    aPrincipalURISpec.Truncate(0);
    431    return;
    432  }
    433 
    434  nsAutoCString spec;
    435  principal->GetAsciiSpec(spec);
    436  CopyUTF8toUTF16(spec, aPrincipalURISpec);
    437 }
    438 
    439 nsIPolicyContainer* DataTransfer::GetPolicyContainer() {
    440  auto* dragSession = GetOwnerDragSession();
    441  if (!dragSession) {
    442    return nullptr;
    443  }
    444  nsCOMPtr<nsIPolicyContainer> policyContainer;
    445  dragSession->GetPolicyContainer(getter_AddRefs(policyContainer));
    446  return policyContainer;
    447 }
    448 
    449 already_AddRefed<FileList> DataTransfer::GetFiles(
    450    nsIPrincipal& aSubjectPrincipal) {
    451  return mItems->Files(&aSubjectPrincipal);
    452 }
    453 
    454 void DataTransfer::GetTypes(nsTArray<nsString>& aTypes,
    455                            CallerType aCallerType) const {
    456  // When called from bindings, aTypes will be empty, but since we might have
    457  // Gecko-internal callers too, clear it to be safe.
    458  aTypes.Clear();
    459 
    460  return mItems->GetTypes(aTypes, aCallerType);
    461 }
    462 
    463 bool DataTransfer::HasType(const nsAString& aType) const {
    464  return mItems->HasType(aType);
    465 }
    466 
    467 bool DataTransfer::HasFile() const { return mItems->HasFile(); }
    468 
    469 void DataTransfer::GetData(const nsAString& aFormat, nsAString& aData,
    470                           nsIPrincipal& aSubjectPrincipal,
    471                           ErrorResult& aRv) const {
    472  // return an empty string if data for the format was not found
    473  aData.Truncate();
    474 
    475  nsCOMPtr<nsIVariant> data;
    476  nsresult rv =
    477      GetDataAtInternal(aFormat, 0, &aSubjectPrincipal, getter_AddRefs(data));
    478  if (NS_FAILED(rv)) {
    479    if (rv != NS_ERROR_DOM_INDEX_SIZE_ERR) {
    480      aRv.Throw(rv);
    481    }
    482    return;
    483  }
    484 
    485  if (data) {
    486    nsAutoString stringdata;
    487    data->GetAsAString(stringdata);
    488 
    489    // for the URL type, parse out the first URI from the list. The URIs are
    490    // separated by newlines
    491    nsAutoString lowercaseFormat;
    492    nsContentUtils::ASCIIToLower(aFormat, lowercaseFormat);
    493 
    494    if (lowercaseFormat.EqualsLiteral("url")) {
    495      int32_t lastidx = 0, idx;
    496      int32_t length = stringdata.Length();
    497      while (lastidx < length) {
    498        idx = stringdata.FindChar('\n', lastidx);
    499        // lines beginning with # are comments
    500        if (stringdata[lastidx] == '#') {
    501          if (idx == -1) {
    502            break;
    503          }
    504        } else {
    505          if (idx == -1) {
    506            aData.Assign(Substring(stringdata, lastidx));
    507          } else {
    508            aData.Assign(Substring(stringdata, lastidx, idx - lastidx));
    509          }
    510          aData =
    511              nsContentUtils::TrimWhitespace<nsCRT::IsAsciiSpace>(aData, true);
    512          return;
    513        }
    514        lastidx = idx + 1;
    515      }
    516    } else {
    517      aData = stringdata;
    518    }
    519  }
    520 }
    521 
    522 void DataTransfer::SetData(const nsAString& aFormat, const nsAString& aData,
    523                           nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
    524  RefPtr<nsVariantCC> variant = new nsVariantCC();
    525  variant->SetAsAString(aData);
    526 
    527  aRv = SetDataAtInternal(aFormat, variant, 0, &aSubjectPrincipal);
    528 }
    529 
    530 void DataTransfer::ClearData(const Optional<nsAString>& aFormat,
    531                             nsIPrincipal& aSubjectPrincipal,
    532                             ErrorResult& aRv) {
    533  if (IsReadOnly()) {
    534    aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
    535    return;
    536  }
    537 
    538  if (MozItemCount() == 0) {
    539    return;
    540  }
    541 
    542  if (aFormat.WasPassed()) {
    543    MozClearDataAtHelper(aFormat.Value(), 0, aSubjectPrincipal, aRv);
    544  } else {
    545    MozClearDataAtHelper(u""_ns, 0, aSubjectPrincipal, aRv);
    546  }
    547 }
    548 
    549 void DataTransfer::SetMozCursor(const nsAString& aCursorState) {
    550  // Lock the cursor to an arrow during the drag.
    551  mCursorState = aCursorState.EqualsLiteral("default");
    552 }
    553 
    554 already_AddRefed<nsINode> DataTransfer::GetMozSourceNode() {
    555  auto* dragSession = GetOwnerDragSession();
    556  if (!dragSession) {
    557    return nullptr;
    558  }
    559 
    560  nsCOMPtr<nsINode> sourceNode;
    561  dragSession->GetSourceNode(getter_AddRefs(sourceNode));
    562  if (sourceNode && !nsContentUtils::LegacyIsCallerNativeCode() &&
    563      !nsContentUtils::CanCallerAccess(sourceNode)) {
    564    return nullptr;
    565  }
    566 
    567  return sourceNode.forget();
    568 }
    569 
    570 already_AddRefed<WindowContext> DataTransfer::GetSourceTopWindowContext() {
    571  auto* dragSession = GetOwnerDragSession();
    572  if (!dragSession) {
    573    return nullptr;
    574  }
    575 
    576  RefPtr<WindowContext> sourceTopWindowContext;
    577  dragSession->GetSourceTopWindowContext(
    578      getter_AddRefs(sourceTopWindowContext));
    579  return sourceTopWindowContext.forget();
    580 }
    581 
    582 already_AddRefed<DOMStringList> DataTransfer::MozTypesAt(
    583    uint32_t aIndex, ErrorResult& aRv) const {
    584  // Only the first item is valid for clipboard events
    585  if (aIndex > 0 && (mEventMessage == eCut || mEventMessage == eCopy ||
    586                     mEventMessage == ePaste)) {
    587    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
    588    return nullptr;
    589  }
    590 
    591  RefPtr<DOMStringList> types = new DOMStringList();
    592  if (aIndex < MozItemCount()) {
    593    // note that you can retrieve the types regardless of their principal
    594    const nsTArray<RefPtr<DataTransferItem>>& items =
    595        *mItems->MozItemsAt(aIndex);
    596 
    597    bool addFile = false;
    598    for (uint32_t i = 0; i < items.Length(); i++) {
    599      // NOTE: The reason why we get the internal type here is because we want
    600      // kFileMime to appear in the types list for backwards compatibility
    601      // reasons.
    602      nsAutoString type;
    603      items[i]->GetInternalType(type);
    604      if (NS_WARN_IF(!types->Add(type))) {
    605        aRv.Throw(NS_ERROR_FAILURE);
    606        return nullptr;
    607      }
    608 
    609      if (items[i]->Kind() == DataTransferItem::KIND_FILE) {
    610        addFile = true;
    611      }
    612    }
    613 
    614    if (addFile) {
    615      types->Add(u"Files"_ns);
    616    }
    617  }
    618 
    619  return types.forget();
    620 }
    621 
    622 nsresult DataTransfer::GetDataAtNoSecurityCheck(const nsAString& aFormat,
    623                                                uint32_t aIndex,
    624                                                nsIVariant** aData) const {
    625  return GetDataAtInternal(aFormat, aIndex,
    626                           nsContentUtils::GetSystemPrincipal(), aData);
    627 }
    628 
    629 nsresult DataTransfer::GetDataAtInternal(const nsAString& aFormat,
    630                                         uint32_t aIndex,
    631                                         nsIPrincipal* aSubjectPrincipal,
    632                                         nsIVariant** aData) const {
    633  *aData = nullptr;
    634 
    635  if (aFormat.IsEmpty()) {
    636    return NS_OK;
    637  }
    638 
    639  if (aIndex >= MozItemCount()) {
    640    return NS_ERROR_DOM_INDEX_SIZE_ERR;
    641  }
    642 
    643  // Only the first item is valid for clipboard events
    644  if (aIndex > 0 && (mEventMessage == eCut || mEventMessage == eCopy ||
    645                     mEventMessage == ePaste)) {
    646    return NS_ERROR_DOM_INDEX_SIZE_ERR;
    647  }
    648 
    649  nsAutoString format;
    650  GetRealFormat(aFormat, format);
    651 
    652  MOZ_ASSERT(aSubjectPrincipal);
    653 
    654  RefPtr<DataTransferItem> item = mItems->MozItemByTypeAt(format, aIndex);
    655  if (!item) {
    656    // The index exists but there's no data for the specified format, in this
    657    // case we just return undefined
    658    return NS_OK;
    659  }
    660 
    661  // If we have chrome only content, and we aren't chrome, don't allow access
    662  if (!aSubjectPrincipal->IsSystemPrincipal() && item->ChromeOnly()) {
    663    return NS_OK;
    664  }
    665 
    666  // DataTransferItem::Data() handles the principal checks
    667  ErrorResult result;
    668  nsCOMPtr<nsIVariant> data = item->Data(aSubjectPrincipal, result);
    669  if (NS_WARN_IF(!data || result.Failed())) {
    670    return result.StealNSResult();
    671  }
    672 
    673  data.forget(aData);
    674  return NS_OK;
    675 }
    676 
    677 void DataTransfer::MozGetDataAt(JSContext* aCx, const nsAString& aFormat,
    678                                uint32_t aIndex,
    679                                JS::MutableHandle<JS::Value> aRetval,
    680                                mozilla::ErrorResult& aRv) {
    681  nsCOMPtr<nsIVariant> data;
    682  aRv = GetDataAtInternal(aFormat, aIndex, nsContentUtils::GetSystemPrincipal(),
    683                          getter_AddRefs(data));
    684  if (aRv.Failed()) {
    685    return;
    686  }
    687 
    688  if (!data) {
    689    aRetval.setNull();
    690    return;
    691  }
    692 
    693  JS::Rooted<JS::Value> result(aCx);
    694  if (!VariantToJsval(aCx, data, aRetval)) {
    695    aRv = NS_ERROR_FAILURE;
    696    return;
    697  }
    698 }
    699 
    700 /* static */
    701 bool DataTransfer::PrincipalMaySetData(const nsAString& aType,
    702                                       nsIVariant* aData,
    703                                       nsIPrincipal* aPrincipal) {
    704  if (!aPrincipal->IsSystemPrincipal()) {
    705    DataTransferItem::eKind kind = DataTransferItem::KindFromData(aData);
    706    if (kind == DataTransferItem::KIND_OTHER) {
    707      NS_WARNING("Disallowing adding non string/file types to DataTransfer");
    708      return false;
    709    }
    710 
    711    // Don't allow adding internal types of the form */x-moz-*, but
    712    // special-case the url types as they are simple variations of urls.
    713    // In addition, allow x-moz-place flavors to be added by WebExtensions.
    714    if (FindInReadable(kInternal_Mimetype_Prefix, aType) &&
    715        !StringBeginsWith(aType, u"text/x-moz-url"_ns)) {
    716      auto principal = BasePrincipal::Cast(aPrincipal);
    717      if (!principal->AddonPolicy() ||
    718          !StringBeginsWith(aType, u"text/x-moz-place"_ns)) {
    719        NS_WARNING("Disallowing adding this type to DataTransfer");
    720        return false;
    721      }
    722    }
    723  }
    724 
    725  return true;
    726 }
    727 
    728 void DataTransfer::TypesListMayHaveChanged() {
    729  DataTransfer_Binding::ClearCachedTypesValue(this);
    730 }
    731 
    732 already_AddRefed<DataTransfer> DataTransfer::MozCloneForEvent(
    733    const nsAString& aEvent, ErrorResult& aRv) {
    734  RefPtr<nsAtom> atomEvt = NS_Atomize(aEvent);
    735  if (!atomEvt) {
    736    aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
    737    return nullptr;
    738  }
    739  EventMessage eventMessage = nsContentUtils::GetEventMessage(atomEvt);
    740 
    741  RefPtr<DataTransfer> dt;
    742  nsresult rv = Clone(mParent, eventMessage, false, false, getter_AddRefs(dt));
    743  if (NS_FAILED(rv)) {
    744    aRv.Throw(rv);
    745    return nullptr;
    746  }
    747  return dt.forget();
    748 }
    749 
    750 namespace {
    751 nsresult GetClipboardDataSnapshotWithContentAnalysisSync(
    752    const nsTArray<nsCString>& aFormats,
    753    const nsIClipboard::ClipboardType& aClipboardType,
    754    WindowContext* aWindowContext,
    755    nsIClipboardDataSnapshot** aClipboardDataSnapshot) {
    756  MOZ_ASSERT(aWindowContext);
    757  MOZ_ASSERT(nsIContentAnalysis::MightBeActive());
    758  nsresult rv;
    759  nsCOMPtr<nsITransferable> trans =
    760      do_CreateInstance("@mozilla.org/widget/transferable;1", &rv);
    761  NS_ENSURE_SUCCESS(rv, rv);
    762  trans->Init(nullptr);
    763  // Before anything reads the clipboard contents, do a full
    764  // content analysis on the clipboard contents (and cache it). This
    765  // prevents multiple content analysis dialogs from appearing
    766  // when multiple formats are read (see bug 1915351)
    767  RefPtr<ClipboardContentAnalysisChild> contentAnalysis =
    768      ClipboardContentAnalysisChild::GetOrCreate();
    769  IPCTransferableDataOrError ipcTransferableDataOrError;
    770  bool result = contentAnalysis->SendGetAllClipboardDataSync(
    771      aFormats, aClipboardType, aWindowContext->InnerWindowId(),
    772      &ipcTransferableDataOrError);
    773  NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
    774  if (ipcTransferableDataOrError.type() ==
    775      IPCTransferableDataOrError::Tnsresult) {
    776    rv = ipcTransferableDataOrError.get_nsresult();
    777    // This class expects clipboardDataSnapshot to be non-null, so
    778    // return an empty one
    779    if (rv == NS_ERROR_CONTENT_BLOCKED) {
    780      auto emptySnapshot =
    781          mozilla::MakeRefPtr<nsBaseClipboard::ClipboardPopulatedDataSnapshot>(
    782              trans);
    783      emptySnapshot.forget(aClipboardDataSnapshot);
    784    }
    785    return rv;
    786  }
    787  rv = nsContentUtils::IPCTransferableDataToTransferable(
    788      ipcTransferableDataOrError.get_IPCTransferableData(),
    789      true /* aAddDataFlavor */, trans, false /* aFilterUnknownFlavors */);
    790  NS_ENSURE_SUCCESS(rv, rv);
    791  auto snapshot =
    792      mozilla::MakeRefPtr<nsBaseClipboard::ClipboardPopulatedDataSnapshot>(
    793          trans);
    794  snapshot.forget(aClipboardDataSnapshot);
    795  return rv;
    796 }
    797 }  // namespace
    798 
    799 void DataTransfer::GetExternalClipboardFormats(const bool& aPlainTextOnly,
    800                                               nsTArray<nsCString>& aResult) {
    801  // NOTE: When you change this method, you may need to change
    802  //       GetExternalTransferableFormats() too since those methods should
    803  //       work similarly.
    804 
    805  MOZ_ASSERT(!mClipboardDataSnapshot);
    806 
    807  if (mClipboardType.isNothing()) {
    808    return;
    809  }
    810 
    811  RefPtr<WindowContext> wc = GetWindowContext();
    812  if (NS_WARN_IF(!wc)) {
    813    MOZ_ASSERT_UNREACHABLE(
    814        "How could this DataTransfer be created with a non-window global?");
    815    return;
    816  }
    817 
    818  nsCOMPtr<nsIClipboard> clipboard =
    819      do_GetService("@mozilla.org/widget/clipboard;1");
    820  if (!clipboard) {
    821    return;
    822  }
    823 
    824  nsresult rv = NS_ERROR_FAILURE;
    825  // If we're in the parent process already this content is exempt from
    826  // content analysis (i.e. pasting into the URL bar)
    827  bool doContentAnalysis = MOZ_UNLIKELY(nsIContentAnalysis::MightBeActive()) &&
    828                           XRE_IsContentProcess();
    829 
    830  nsCOMPtr<nsIClipboardDataSnapshot> clipboardDataSnapshot;
    831  if (aPlainTextOnly) {
    832    AutoTArray<nsCString, 1> formats{nsLiteralCString(kTextMime)};
    833    if (doContentAnalysis) {
    834      rv = GetClipboardDataSnapshotWithContentAnalysisSync(
    835          formats, *mClipboardType, wc, getter_AddRefs(clipboardDataSnapshot));
    836    } else {
    837      rv = clipboard->GetDataSnapshotSync(
    838          formats, *mClipboardType, wc, getter_AddRefs(clipboardDataSnapshot));
    839    }
    840  } else {
    841    AutoTArray<nsCString, std::size(kNonPlainTextExternalFormats) + 4> formats;
    842    formats.AppendElements(
    843        Span<const nsLiteralCString>(kNonPlainTextExternalFormats));
    844    // We will be using this snapshot to provide the data to paste in
    845    // EditorBase, so add a few extra formats here to make sure we have
    846    // everything. Note that these extra formats will not be returned in aResult
    847    // because of the checks below.
    848    formats.AppendElement(kNativeHTMLMime);
    849    formats.AppendElement(kJPEGImageMime);
    850    formats.AppendElement(kGIFImageMime);
    851    formats.AppendElement(kMozTextInternal);
    852 
    853    if (doContentAnalysis) {
    854      rv = GetClipboardDataSnapshotWithContentAnalysisSync(
    855          formats, *mClipboardType, wc, getter_AddRefs(clipboardDataSnapshot));
    856    } else {
    857      rv = clipboard->GetDataSnapshotSync(
    858          formats, *mClipboardType, wc, getter_AddRefs(clipboardDataSnapshot));
    859    }
    860  }
    861 
    862  if (NS_FAILED(rv) || !clipboardDataSnapshot) {
    863    if (rv == NS_ERROR_CONTENT_BLOCKED) {
    864      // Use the empty snapshot created in
    865      // GetClipboardDataSnapshotWithContentAnalysisSync()
    866      mClipboardDataSnapshot = clipboardDataSnapshot;
    867    }
    868    return;
    869  }
    870 
    871  // Order is important for DataTransfer; ensure the returned list items follow
    872  // the sequence specified in kNonPlainTextExternalFormats.
    873  AutoTArray<nsCString, std::size(kNonPlainTextExternalFormats)> flavors;
    874  clipboardDataSnapshot->GetFlavorList(flavors);
    875  for (const auto& format : kNonPlainTextExternalFormats) {
    876    if (flavors.Contains(format)) {
    877      aResult.AppendElement(format);
    878    }
    879  }
    880 
    881  mClipboardDataSnapshot = clipboardDataSnapshot;
    882 }
    883 
    884 /* static */
    885 void DataTransfer::GetExternalTransferableFormats(
    886    nsITransferable* aTransferable, bool aPlainTextOnly,
    887    nsTArray<nsCString>* aResult) {
    888  MOZ_ASSERT(aTransferable);
    889  MOZ_ASSERT(aResult);
    890 
    891  aResult->Clear();
    892 
    893  // NOTE: When you change this method, you may need to change
    894  //       GetExternalClipboardFormats() too since those methods should
    895  //       work similarly.
    896 
    897  AutoTArray<nsCString, 10> flavors;
    898  aTransferable->FlavorsTransferableCanExport(flavors);
    899 
    900  if (aPlainTextOnly) {
    901    auto index = flavors.IndexOf(nsLiteralCString(kTextMime));
    902    if (index != flavors.NoIndex) {
    903      aResult->AppendElement(nsLiteralCString(kTextMime));
    904    }
    905    return;
    906  }
    907 
    908  // If not plain text only, then instead check all the other types
    909  for (const auto& format : kNonPlainTextExternalFormats) {
    910    auto index = flavors.IndexOf(format);
    911    if (index != flavors.NoIndex) {
    912      aResult->AppendElement(format);
    913    }
    914  }
    915 }
    916 
    917 nsresult DataTransfer::SetDataAtInternal(const nsAString& aFormat,
    918                                         nsIVariant* aData, uint32_t aIndex,
    919                                         nsIPrincipal* aSubjectPrincipal) {
    920  if (aFormat.IsEmpty()) {
    921    return NS_OK;
    922  }
    923 
    924  if (IsReadOnly()) {
    925    return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
    926  }
    927 
    928  // Specifying an index less than the current length will replace an existing
    929  // item. Specifying an index equal to the current length will add a new item.
    930  if (aIndex > MozItemCount()) {
    931    return NS_ERROR_DOM_INDEX_SIZE_ERR;
    932  }
    933 
    934  // Only the first item is valid for clipboard events
    935  if (aIndex > 0 && (mEventMessage == eCut || mEventMessage == eCopy ||
    936                     mEventMessage == ePaste)) {
    937    return NS_ERROR_DOM_INDEX_SIZE_ERR;
    938  }
    939 
    940  // Don't allow the custom type to be assigned.
    941  if (aFormat.EqualsLiteral(kCustomTypesMime)) {
    942    return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
    943  }
    944 
    945  if (!PrincipalMaySetData(aFormat, aData, aSubjectPrincipal)) {
    946    return NS_ERROR_DOM_SECURITY_ERR;
    947  }
    948 
    949  return SetDataWithPrincipal(aFormat, aData, aIndex, aSubjectPrincipal);
    950 }
    951 
    952 void DataTransfer::MozSetDataAt(JSContext* aCx, const nsAString& aFormat,
    953                                JS::Handle<JS::Value> aData, uint32_t aIndex,
    954                                ErrorResult& aRv) {
    955  nsCOMPtr<nsIVariant> data;
    956  aRv = nsContentUtils::XPConnect()->JSValToVariant(aCx, aData,
    957                                                    getter_AddRefs(data));
    958  if (!aRv.Failed()) {
    959    aRv = SetDataAtInternal(aFormat, data, aIndex,
    960                            nsContentUtils::GetSystemPrincipal());
    961  }
    962 }
    963 
    964 void DataTransfer::MozClearDataAt(const nsAString& aFormat, uint32_t aIndex,
    965                                  ErrorResult& aRv) {
    966  if (IsReadOnly()) {
    967    aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
    968    return;
    969  }
    970 
    971  if (aIndex >= MozItemCount()) {
    972    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
    973    return;
    974  }
    975 
    976  // Only the first item is valid for clipboard events
    977  if (aIndex > 0 && (mEventMessage == eCut || mEventMessage == eCopy ||
    978                     mEventMessage == ePaste)) {
    979    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
    980    return;
    981  }
    982 
    983  MozClearDataAtHelper(aFormat, aIndex, *nsContentUtils::GetSystemPrincipal(),
    984                       aRv);
    985 
    986  // If we just cleared the 0-th index, and there are still more than 1 indexes
    987  // remaining, MozClearDataAt should cause the 1st index to become the 0th
    988  // index. This should _only_ happen when the MozClearDataAt function is
    989  // explicitly called by script, as this behavior is inconsistent with spec.
    990  // (however, so is the MozClearDataAt API)
    991 
    992  if (aIndex == 0 && mItems->MozItemCount() > 1 &&
    993      mItems->MozItemsAt(0)->Length() == 0) {
    994    mItems->PopIndexZero();
    995  }
    996 }
    997 
    998 void DataTransfer::MozClearDataAtHelper(const nsAString& aFormat,
    999                                        uint32_t aIndex,
   1000                                        nsIPrincipal& aSubjectPrincipal,
   1001                                        ErrorResult& aRv) {
   1002  MOZ_ASSERT(!IsReadOnly());
   1003  MOZ_ASSERT(aIndex < MozItemCount());
   1004  MOZ_ASSERT(aIndex == 0 || (mEventMessage != eCut && mEventMessage != eCopy &&
   1005                             mEventMessage != ePaste));
   1006 
   1007  nsAutoString format;
   1008  GetRealFormat(aFormat, format);
   1009 
   1010  mItems->MozRemoveByTypeAt(format, aIndex, aSubjectPrincipal, aRv);
   1011 }
   1012 
   1013 void DataTransfer::SetDragImage(Element& aImage, int32_t aX, int32_t aY) {
   1014  if (!IsReadOnly()) {
   1015    mDragImage = &aImage;
   1016    mDragImageX = aX;
   1017    mDragImageY = aY;
   1018  }
   1019 }
   1020 
   1021 void DataTransfer::UpdateDragImage(Element& aImage, int32_t aX, int32_t aY) {
   1022  if (mEventMessage < eDragDropEventFirst ||
   1023      mEventMessage > eDragDropEventLast) {
   1024    return;
   1025  }
   1026 
   1027  auto* dragSession = GetOwnerDragSession();
   1028  if (dragSession) {
   1029    dragSession->UpdateDragImage(&aImage, aX, aY);
   1030  }
   1031 }
   1032 
   1033 void DataTransfer::AddElement(Element& aElement, ErrorResult& aRv) {
   1034  if (IsReadOnly()) {
   1035    aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
   1036    return;
   1037  }
   1038 
   1039  mDragTarget = &aElement;
   1040 }
   1041 
   1042 nsresult DataTransfer::Clone(nsISupports* aParent, EventMessage aEventMessage,
   1043                             bool aUserCancelled,
   1044                             bool aIsCrossDomainSubFrameDrop,
   1045                             DataTransfer** aNewDataTransfer) {
   1046  RefPtr<DataTransfer> newDataTransfer = new DataTransfer(
   1047      aParent, aEventMessage, mEffectAllowed, mCursorState, mIsExternal,
   1048      aUserCancelled, aIsCrossDomainSubFrameDrop, mClipboardType,
   1049      mClipboardDataSnapshot, mItems, mDragImage, mDragImageX, mDragImageY,
   1050      mShowFailAnimation);
   1051 
   1052  newDataTransfer.forget(aNewDataTransfer);
   1053  return NS_OK;
   1054 }
   1055 
   1056 already_AddRefed<nsIArray> DataTransfer::GetTransferables(
   1057    nsINode* aDragTarget) {
   1058  MOZ_ASSERT(aDragTarget);
   1059 
   1060  Document* doc = aDragTarget->GetComposedDoc();
   1061  if (!doc) {
   1062    return nullptr;
   1063  }
   1064 
   1065  return GetTransferables(doc->GetLoadContext());
   1066 }
   1067 
   1068 already_AddRefed<nsIArray> DataTransfer::GetTransferables(
   1069    nsILoadContext* aLoadContext) {
   1070  nsCOMPtr<nsIMutableArray> transArray = nsArray::Create();
   1071  if (!transArray) {
   1072    return nullptr;
   1073  }
   1074 
   1075  uint32_t count = MozItemCount();
   1076  for (uint32_t i = 0; i < count; i++) {
   1077    nsCOMPtr<nsITransferable> transferable = GetTransferable(i, aLoadContext);
   1078    if (transferable) {
   1079      transArray->AppendElement(transferable);
   1080    }
   1081  }
   1082 
   1083  return transArray.forget();
   1084 }
   1085 
   1086 already_AddRefed<nsITransferable> DataTransfer::GetTransferable(
   1087    uint32_t aIndex, nsILoadContext* aLoadContext) {
   1088  if (aIndex >= MozItemCount()) {
   1089    return nullptr;
   1090  }
   1091 
   1092  const nsTArray<RefPtr<DataTransferItem>>& item = *mItems->MozItemsAt(aIndex);
   1093  uint32_t count = item.Length();
   1094  if (!count) {
   1095    return nullptr;
   1096  }
   1097 
   1098  nsCOMPtr<nsITransferable> transferable =
   1099      do_CreateInstance("@mozilla.org/widget/transferable;1");
   1100  if (!transferable) {
   1101    return nullptr;
   1102  }
   1103  transferable->Init(aLoadContext);
   1104 
   1105  // Set the principal of the global this DataTransfer was created for
   1106  // on the transferable for ReadWrite events (copy, cut, or dragstart).
   1107  //
   1108  // For other events, the data inside the transferable may originate
   1109  // from another origin or from the OS.
   1110  if (mMode == Mode::ReadWrite) {
   1111    if (nsCOMPtr<nsIGlobalObject> global = GetGlobal()) {
   1112      transferable->SetDataPrincipal(global->PrincipalOrNull());
   1113    }
   1114  }
   1115 
   1116  nsCOMPtr<nsIStorageStream> storageStream;
   1117  nsCOMPtr<nsIObjectOutputStream> stream;
   1118 
   1119  bool added = false;
   1120  bool handlingCustomFormats = true;
   1121 
   1122  // When writing the custom data, we need to ensure that there is sufficient
   1123  // space for a (uint32_t) data ending type, and the null byte character at
   1124  // the end of the nsCString. We claim that space upfront and store it in
   1125  // baseLength. This value will be set to zero if a write error occurs
   1126  // indicating that the data and length are no longer valid.
   1127  const uint32_t baseLength = sizeof(uint32_t) + 1;
   1128  uint32_t totalCustomLength = baseLength;
   1129 
   1130  /*
   1131   * Two passes are made here to iterate over all of the types. First, look for
   1132   * any types that are not in the list of known types. For this pass,
   1133   * handlingCustomFormats will be true. Data that corresponds to unknown types
   1134   * will be pulled out and inserted into a single type (kCustomTypesMime) by
   1135   * writing the data into a stream.
   1136   *
   1137   * The second pass will iterate over the formats looking for known types.
   1138   * These are added as is. The unknown types are all then inserted as a single
   1139   * type (kCustomTypesMime) in the same position of the first custom type. This
   1140   * model is used to maintain the format order as best as possible.
   1141   *
   1142   * The format of the kCustomTypesMime type is one or more of the following
   1143   * stored sequentially:
   1144   *   <32-bit> type (only none or string is supported)
   1145   *   <32-bit> length of format
   1146   *   <wide string> format
   1147   *   <32-bit> length of data
   1148   *   <wide string> data
   1149   * A type of eCustomClipboardTypeId_None ends the list, without any following
   1150   * data.
   1151   */
   1152  do {
   1153    for (uint32_t f = 0; f < count; f++) {
   1154      RefPtr<DataTransferItem> formatitem = item[f];
   1155      nsCOMPtr<nsIVariant> variant = formatitem->DataNoSecurityCheck();
   1156      if (!variant) {  // skip empty items
   1157        continue;
   1158      }
   1159 
   1160      nsAutoString type;
   1161      formatitem->GetInternalType(type);
   1162 
   1163      // If the data is of one of the well-known formats, use it directly.
   1164      bool isCustomFormat = true;
   1165      for (const char* format : kKnownFormats) {
   1166        if (type.EqualsASCII(format)) {
   1167          isCustomFormat = false;
   1168          break;
   1169        }
   1170      }
   1171 
   1172      uint32_t lengthInBytes;
   1173      nsCOMPtr<nsISupports> convertedData;
   1174 
   1175      if (handlingCustomFormats) {
   1176        if (!ConvertFromVariant(variant, getter_AddRefs(convertedData),
   1177                                &lengthInBytes)) {
   1178          continue;
   1179        }
   1180 
   1181        // When handling custom types, add the data to the stream if this is a
   1182        // custom type. If totalCustomLength is 0, then a write error occurred
   1183        // on a previous item, so ignore any others.
   1184        if (isCustomFormat && totalCustomLength > 0) {
   1185          // If it isn't a string, just ignore it. The dataTransfer is cached in
   1186          // the drag sesion during drag-and-drop, so non-strings will be
   1187          // available when dragging locally.
   1188          nsCOMPtr<nsISupportsString> str(do_QueryInterface(convertedData));
   1189          if (str) {
   1190            nsAutoString data;
   1191            str->GetData(data);
   1192 
   1193            if (!stream) {
   1194              // Create a storage stream to write to.
   1195              NS_NewStorageStream(1024, UINT32_MAX,
   1196                                  getter_AddRefs(storageStream));
   1197 
   1198              nsCOMPtr<nsIOutputStream> outputStream;
   1199              storageStream->GetOutputStream(0, getter_AddRefs(outputStream));
   1200 
   1201              stream = NS_NewObjectOutputStream(outputStream);
   1202            }
   1203 
   1204            CheckedInt<uint32_t> formatLength =
   1205                CheckedInt<uint32_t>(type.Length()) *
   1206                sizeof(nsString::char_type);
   1207 
   1208            // The total size of the stream is the format length, the data
   1209            // length, two integers to hold the lengths and one integer for
   1210            // the string flag. Guard against large data by ignoring any that
   1211            // don't fit.
   1212            CheckedInt<uint32_t> newSize = formatLength + totalCustomLength +
   1213                                           lengthInBytes +
   1214                                           (sizeof(uint32_t) * 3);
   1215            if (newSize.isValid()) {
   1216              // If a write error occurs, set totalCustomLength to 0 so that
   1217              // further processing gets ignored.
   1218              nsresult rv = stream->Write32(eCustomClipboardTypeId_String);
   1219              if (NS_WARN_IF(NS_FAILED(rv))) {
   1220                totalCustomLength = 0;
   1221                continue;
   1222              }
   1223              rv = stream->Write32(formatLength.value());
   1224              if (NS_WARN_IF(NS_FAILED(rv))) {
   1225                totalCustomLength = 0;
   1226                continue;
   1227              }
   1228              MOZ_ASSERT(formatLength.isValid() &&
   1229                             formatLength.value() ==
   1230                                 type.Length() * sizeof(nsString::char_type),
   1231                         "Why is formatLength off?");
   1232              rv = stream->WriteBytes(
   1233                  AsBytes(Span(type.BeginReading(), type.Length())));
   1234              if (NS_WARN_IF(NS_FAILED(rv))) {
   1235                totalCustomLength = 0;
   1236                continue;
   1237              }
   1238              rv = stream->Write32(lengthInBytes);
   1239              if (NS_WARN_IF(NS_FAILED(rv))) {
   1240                totalCustomLength = 0;
   1241                continue;
   1242              }
   1243              // XXXbz it's not obvious to me that lengthInBytes is the actual
   1244              // length of "data" if the variant contained an nsISupportsString
   1245              // as VTYPE_INTERFACE, say.  We used lengthInBytes above for
   1246              // sizing, so just keep doing that.
   1247              rv = stream->WriteBytes(
   1248                  Span(reinterpret_cast<const uint8_t*>(data.BeginReading()),
   1249                       lengthInBytes));
   1250              if (NS_WARN_IF(NS_FAILED(rv))) {
   1251                totalCustomLength = 0;
   1252                continue;
   1253              }
   1254 
   1255              totalCustomLength = newSize.value();
   1256            }
   1257          }
   1258        }
   1259      } else if (isCustomFormat && stream) {
   1260        // This is the second pass of the loop (handlingCustomFormats is false).
   1261        // When encountering the first custom format, append all of the stream
   1262        // at this position. If totalCustomLength is 0 indicating a write error
   1263        // occurred, or no data has been added to it, don't output anything,
   1264        if (totalCustomLength > baseLength) {
   1265          // Write out an end of data terminator.
   1266          nsresult rv = stream->Write32(eCustomClipboardTypeId_None);
   1267          if (NS_SUCCEEDED(rv)) {
   1268            nsCOMPtr<nsIInputStream> inputStream;
   1269            storageStream->NewInputStream(0, getter_AddRefs(inputStream));
   1270 
   1271            RefPtr<StringBuffer> stringBuffer =
   1272                StringBuffer::Alloc(totalCustomLength);
   1273 
   1274            // Subtract off the null terminator when reading.
   1275            totalCustomLength--;
   1276 
   1277            // Read the data from the stream and add a null-terminator as
   1278            // ToString needs it.
   1279            uint32_t amountRead;
   1280            rv = inputStream->Read(static_cast<char*>(stringBuffer->Data()),
   1281                                   totalCustomLength, &amountRead);
   1282            if (NS_SUCCEEDED(rv)) {
   1283              static_cast<char*>(stringBuffer->Data())[amountRead] = 0;
   1284 
   1285              nsCString str;
   1286              str.Assign(stringBuffer, totalCustomLength);
   1287              nsCOMPtr<nsISupportsCString> strSupports(
   1288                  do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID));
   1289              strSupports->SetData(str);
   1290 
   1291              nsresult rv =
   1292                  transferable->SetTransferData(kCustomTypesMime, strSupports);
   1293              if (NS_FAILED(rv)) {
   1294                return nullptr;
   1295              }
   1296 
   1297              added = true;
   1298            }
   1299          }
   1300        }
   1301 
   1302        // Clear the stream so it doesn't get used again.
   1303        stream = nullptr;
   1304      } else {
   1305        // This is the second pass of the loop and a known type is encountered.
   1306        // Add it as is.
   1307        if (!ConvertFromVariant(variant, getter_AddRefs(convertedData),
   1308                                &lengthInBytes)) {
   1309          continue;
   1310        }
   1311 
   1312        NS_ConvertUTF16toUTF8 format(type);
   1313 
   1314        // If a converter is set for a format, set the converter for the
   1315        // transferable and don't add the item
   1316        nsCOMPtr<nsIFormatConverter> converter =
   1317            do_QueryInterface(convertedData);
   1318        if (converter) {
   1319          transferable->AddDataFlavor(format.get());
   1320          transferable->SetConverter(converter);
   1321          continue;
   1322        }
   1323 
   1324        nsresult rv =
   1325            transferable->SetTransferData(format.get(), convertedData);
   1326        if (NS_FAILED(rv)) {
   1327          return nullptr;
   1328        }
   1329 
   1330        added = true;
   1331      }
   1332    }
   1333 
   1334    handlingCustomFormats = !handlingCustomFormats;
   1335  } while (!handlingCustomFormats);
   1336 
   1337  // only return the transferable if data was successfully added to it
   1338  if (added) {
   1339    return transferable.forget();
   1340  }
   1341 
   1342  return nullptr;
   1343 }
   1344 
   1345 bool DataTransfer::ConvertFromVariant(nsIVariant* aVariant,
   1346                                      nsISupports** aSupports,
   1347                                      uint32_t* aLength) const {
   1348  *aSupports = nullptr;
   1349  *aLength = 0;
   1350 
   1351  uint16_t type = aVariant->GetDataType();
   1352  if (type == nsIDataType::VTYPE_INTERFACE ||
   1353      type == nsIDataType::VTYPE_INTERFACE_IS) {
   1354    nsCOMPtr<nsISupports> data;
   1355    if (NS_FAILED(aVariant->GetAsISupports(getter_AddRefs(data)))) {
   1356      return false;
   1357    }
   1358 
   1359    // For flavour data providers, use 0 as the length.
   1360    if (nsCOMPtr<nsIFlavorDataProvider> fdp = do_QueryInterface(data)) {
   1361      fdp.forget(aSupports);
   1362      *aLength = 0;
   1363      return true;
   1364    }
   1365 
   1366    // Only use the underlying BlobImpl for transferables.
   1367    if (RefPtr<Blob> blob = do_QueryObject(data)) {
   1368      RefPtr<BlobImpl> blobImpl = blob->Impl();
   1369      blobImpl.forget(aSupports);
   1370    } else {
   1371      data.forget(aSupports);
   1372    }
   1373 
   1374    *aLength = sizeof(nsISupports*);
   1375    return true;
   1376  }
   1377 
   1378  nsAutoString str;
   1379  nsresult rv = aVariant->GetAsAString(str);
   1380  if (NS_FAILED(rv)) {
   1381    return false;
   1382  }
   1383 
   1384  nsCOMPtr<nsISupportsString> strSupports(
   1385      do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
   1386  if (!strSupports) {
   1387    return false;
   1388  }
   1389 
   1390  strSupports->SetData(str);
   1391 
   1392  strSupports.forget(aSupports);
   1393 
   1394  // each character is two bytes
   1395  *aLength = str.Length() * 2;
   1396 
   1397  return true;
   1398 }
   1399 
   1400 void DataTransfer::Disconnect() {
   1401  SetMode(Mode::Protected);
   1402  if (StaticPrefs::dom_events_dataTransfer_protected_enabled()) {
   1403    ClearAll();
   1404  }
   1405 }
   1406 
   1407 void DataTransfer::ClearAll() {
   1408  mItems->ClearAllItems();
   1409  mClipboardDataSnapshot = nullptr;
   1410 }
   1411 
   1412 uint32_t DataTransfer::MozItemCount() const { return mItems->MozItemCount(); }
   1413 
   1414 nsresult DataTransfer::SetDataWithPrincipal(const nsAString& aFormat,
   1415                                            nsIVariant* aData, uint32_t aIndex,
   1416                                            nsIPrincipal* aPrincipal,
   1417                                            bool aHidden) {
   1418  nsAutoString format;
   1419  GetRealFormat(aFormat, format);
   1420 
   1421  ErrorResult rv;
   1422  RefPtr<DataTransferItem> item =
   1423      mItems->SetDataWithPrincipal(format, aData, aIndex, aPrincipal,
   1424                                   /* aInsertOnly = */ false, aHidden, rv);
   1425  return rv.StealNSResult();
   1426 }
   1427 
   1428 void DataTransfer::SetDataWithPrincipalFromOtherProcess(
   1429    const nsAString& aFormat, nsIVariant* aData, uint32_t aIndex,
   1430    nsIPrincipal* aPrincipal, bool aHidden) {
   1431  if (aFormat.EqualsLiteral(kCustomTypesMime)) {
   1432    FillInExternalCustomTypes(aData, aIndex, aPrincipal);
   1433  } else {
   1434    nsAutoString format;
   1435    GetRealFormat(aFormat, format);
   1436 
   1437    ErrorResult rv;
   1438    RefPtr<DataTransferItem> item =
   1439        mItems->SetDataWithPrincipal(format, aData, aIndex, aPrincipal,
   1440                                     /* aInsertOnly = */ false, aHidden, rv);
   1441    if (NS_WARN_IF(rv.Failed())) {
   1442      rv.SuppressException();
   1443    }
   1444  }
   1445 }
   1446 
   1447 void DataTransfer::GetRealFormat(const nsAString& aInFormat,
   1448                                 nsAString& aOutFormat) const {
   1449  // For compatibility, treat text/unicode as equivalent to text/plain
   1450  nsAutoString lowercaseFormat;
   1451  nsContentUtils::ASCIIToLower(aInFormat, lowercaseFormat);
   1452  if (lowercaseFormat.EqualsLiteral("text") ||
   1453      lowercaseFormat.EqualsLiteral("text/unicode")) {
   1454    aOutFormat.AssignLiteral("text/plain");
   1455    return;
   1456  }
   1457 
   1458  if (lowercaseFormat.EqualsLiteral("url")) {
   1459    aOutFormat.AssignLiteral("text/uri-list");
   1460    return;
   1461  }
   1462 
   1463  aOutFormat.Assign(lowercaseFormat);
   1464 }
   1465 
   1466 already_AddRefed<nsIGlobalObject> DataTransfer::GetGlobal() const {
   1467  nsCOMPtr<nsIGlobalObject> global;
   1468  // This is annoying, but DataTransfer may have various things as parent.
   1469  if (nsCOMPtr<EventTarget> target = do_QueryInterface(mParent)) {
   1470    global = target->GetOwnerGlobal();
   1471  } else if (RefPtr<Event> event = do_QueryObject(mParent)) {
   1472    global = event->GetParentObject();
   1473  }
   1474 
   1475  return global.forget();
   1476 }
   1477 
   1478 already_AddRefed<WindowContext> DataTransfer::GetWindowContext() const {
   1479  nsCOMPtr<nsIGlobalObject> global = GetGlobal();
   1480  if (!global) {
   1481    return nullptr;
   1482  }
   1483 
   1484  const auto* innerWindow = global->GetAsInnerWindow();
   1485  if (!innerWindow) {
   1486    return nullptr;
   1487  }
   1488 
   1489  return do_AddRef(innerWindow->GetWindowContext());
   1490 }
   1491 
   1492 nsIClipboardDataSnapshot* DataTransfer::GetClipboardDataSnapshot() const {
   1493  return mClipboardDataSnapshot;
   1494 }
   1495 
   1496 nsresult DataTransfer::CacheExternalData(const char* aFormat, uint32_t aIndex,
   1497                                         nsIPrincipal* aPrincipal,
   1498                                         bool aHidden) {
   1499  ErrorResult rv;
   1500  RefPtr<DataTransferItem> item;
   1501 
   1502  if (strcmp(aFormat, kTextMime) == 0) {
   1503    item = mItems->SetDataWithPrincipal(u"text/plain"_ns, nullptr, aIndex,
   1504                                        aPrincipal, false, aHidden, rv);
   1505    if (NS_WARN_IF(rv.Failed())) {
   1506      return rv.StealNSResult();
   1507    }
   1508    return NS_OK;
   1509  }
   1510 
   1511  if (strcmp(aFormat, kURLDataMime) == 0) {
   1512    item = mItems->SetDataWithPrincipal(u"text/uri-list"_ns, nullptr, aIndex,
   1513                                        aPrincipal, false, aHidden, rv);
   1514    if (NS_WARN_IF(rv.Failed())) {
   1515      return rv.StealNSResult();
   1516    }
   1517    return NS_OK;
   1518  }
   1519 
   1520  nsAutoString format;
   1521  GetRealFormat(NS_ConvertUTF8toUTF16(aFormat), format);
   1522  item = mItems->SetDataWithPrincipal(format, nullptr, aIndex, aPrincipal,
   1523                                      false, aHidden, rv);
   1524  if (NS_WARN_IF(rv.Failed())) {
   1525    return rv.StealNSResult();
   1526  }
   1527  return NS_OK;
   1528 }
   1529 
   1530 void DataTransfer::CacheExternalDragFormats() {
   1531  // Called during the constructor to cache the formats available from an
   1532  // external drag. The data associated with each format will be set to null.
   1533  // This data will instead only be retrieved in FillInExternalDragData when
   1534  // asked for, as it may be time consuming for the source application to
   1535  // generate it.
   1536  auto* dragSession = GetOwnerDragSession();
   1537  if (!dragSession) {
   1538    return;
   1539  }
   1540 
   1541  // make sure that the system principal is used for external drags
   1542  nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
   1543  nsCOMPtr<nsIPrincipal> sysPrincipal;
   1544  ssm->GetSystemPrincipal(getter_AddRefs(sysPrincipal));
   1545 
   1546  // there isn't a way to get a list of the formats that might be available on
   1547  // all platforms, so just check for the types that can actually be imported
   1548  // XXXndeakin there are some other formats but those are platform specific.
   1549  // NOTE: kFileMime must have index 0
   1550  // TODO: should this be `kNonPlainTextExternalFormats` instead?
   1551  static const char* formats[] = {kFileMime,    kHTMLMime, kURLMime,
   1552                                  kURLDataMime, kTextMime, kPNGImageMime};
   1553 
   1554  uint32_t count;
   1555  dragSession->GetNumDropItems(&count);
   1556  for (uint32_t c = 0; c < count; c++) {
   1557    bool hasFileData = false;
   1558    dragSession->IsDataFlavorSupported(kFileMime, &hasFileData);
   1559 
   1560    // First, check for the special format that holds custom types.
   1561    bool supported;
   1562    dragSession->IsDataFlavorSupported(kCustomTypesMime, &supported);
   1563    if (supported) {
   1564      FillInExternalCustomTypes(c, sysPrincipal);
   1565    }
   1566 
   1567    for (uint32_t f = 0; f < std::size(formats); f++) {
   1568      // IsDataFlavorSupported doesn't take an index as an argument and just
   1569      // checks if any of the items support a particular flavor, even though
   1570      // the GetData method does take an index. Here, we just assume that
   1571      // every item being dragged has the same set of flavors.
   1572      bool supported;
   1573      dragSession->IsDataFlavorSupported(formats[f], &supported);
   1574      // if the format is supported, add an item to the array with null as
   1575      // the data. When retrieved, GetRealData will read the data.
   1576      if (supported) {
   1577        CacheExternalData(formats[f], c, sysPrincipal,
   1578                          /* hidden = */ f && hasFileData);
   1579      }
   1580    }
   1581  }
   1582 }
   1583 
   1584 void DataTransfer::CacheExternalClipboardFormats(bool aPlainTextOnly) {
   1585  // Called during the constructor for paste events to cache the formats
   1586  // available on the clipboard. As with CacheExternalDragFormats, the
   1587  // data will only be retrieved when needed.
   1588  NS_ASSERTION(mEventMessage == ePaste,
   1589               "caching clipboard data for invalid event");
   1590 
   1591  nsCOMPtr<nsIPrincipal> sysPrincipal = nsContentUtils::GetSystemPrincipal();
   1592  nsTArray<nsCString> typesArray;
   1593  GetExternalClipboardFormats(aPlainTextOnly, typesArray);
   1594  if (aPlainTextOnly) {
   1595    // The only thing that will be in types is kTextMime
   1596    MOZ_ASSERT(typesArray.IsEmpty() || typesArray.Length() == 1);
   1597    if (typesArray.Length() == 1) {
   1598      MOZ_ASSERT(typesArray.Contains(kTextMime));
   1599      CacheExternalData(kTextMime, 0, sysPrincipal, false);
   1600    }
   1601    return;
   1602  }
   1603 
   1604  CacheExternalData(typesArray, sysPrincipal);
   1605 }
   1606 
   1607 void DataTransfer::CacheTransferableFormats() {
   1608  nsCOMPtr<nsIPrincipal> sysPrincipal = nsContentUtils::GetSystemPrincipal();
   1609 
   1610  AutoTArray<nsCString, 10> typesArray;
   1611  GetExternalTransferableFormats(mTransferable, false, &typesArray);
   1612 
   1613  CacheExternalData(typesArray, sysPrincipal);
   1614 }
   1615 
   1616 void DataTransfer::CacheExternalData(const nsTArray<nsCString>& aTypes,
   1617                                     nsIPrincipal* aPrincipal) {
   1618  bool hasFileData = false;
   1619  for (const nsCString& type : aTypes) {
   1620    if (type.EqualsLiteral(kCustomTypesMime)) {
   1621      FillInExternalCustomTypes(0, aPrincipal);
   1622    } else if (type.EqualsLiteral(kFileMime) && XRE_IsContentProcess() &&
   1623               !StaticPrefs::dom_events_dataTransfer_mozFile_enabled()) {
   1624      // We will be ignoring any application/x-moz-file files found in the paste
   1625      // datatransfer within e10s, as they will fail top be sent over IPC.
   1626      // Because of that, we will unset hasFileData, whether or not it would
   1627      // have been set. (bug 1308007)
   1628      hasFileData = false;
   1629      continue;
   1630    } else {
   1631      // We expect that if kFileMime is supported, then it will be the either at
   1632      // index 0 or at index 1 in the aTypes returned by
   1633      // GetExternalClipboardFormats
   1634      if (type.EqualsLiteral(kFileMime)) {
   1635        hasFileData = true;
   1636      }
   1637 
   1638      // If we aren't the file data, and we have file data, we want to be hidden
   1639      CacheExternalData(
   1640          type.get(), 0, aPrincipal,
   1641          /* hidden = */ !type.EqualsLiteral(kFileMime) && hasFileData);
   1642    }
   1643  }
   1644 }
   1645 
   1646 void DataTransfer::FillAllExternalData() {
   1647  if (mIsExternal) {
   1648    for (uint32_t i = 0; i < MozItemCount(); ++i) {
   1649      const nsTArray<RefPtr<DataTransferItem>>& items = *mItems->MozItemsAt(i);
   1650      for (uint32_t j = 0; j < items.Length(); ++j) {
   1651        MOZ_ASSERT(items[j]->Index() == i);
   1652 
   1653        items[j]->FillInExternalData();
   1654      }
   1655    }
   1656  }
   1657 }
   1658 
   1659 void DataTransfer::FillInExternalCustomTypes(uint32_t aIndex,
   1660                                             nsIPrincipal* aPrincipal) {
   1661  RefPtr<DataTransferItem> item = new DataTransferItem(
   1662      this, NS_LITERAL_STRING_FROM_CSTRING(kCustomTypesMime),
   1663      DataTransferItem::KIND_STRING);
   1664  item->SetIndex(aIndex);
   1665 
   1666  nsCOMPtr<nsIVariant> variant = item->DataNoSecurityCheck();
   1667  if (!variant) {
   1668    return;
   1669  }
   1670 
   1671  FillInExternalCustomTypes(variant, aIndex, aPrincipal);
   1672 }
   1673 
   1674 /* static */ void DataTransfer::ParseExternalCustomTypesString(
   1675    mozilla::Span<const char> aString,
   1676    std::function<void(ParseExternalCustomTypesStringData&&)>&& aCallback) {
   1677  CheckedInt<int32_t> checkedLen(aString.Length());
   1678  if (!checkedLen.isValid()) {
   1679    return;
   1680  }
   1681 
   1682  nsCOMPtr<nsIInputStream> stringStream;
   1683  NS_NewByteInputStream(getter_AddRefs(stringStream), aString,
   1684                        NS_ASSIGNMENT_DEPEND);
   1685 
   1686  nsCOMPtr<nsIObjectInputStream> stream = NS_NewObjectInputStream(stringStream);
   1687 
   1688  uint32_t type;
   1689  do {
   1690    nsresult rv = stream->Read32(&type);
   1691    NS_ENSURE_SUCCESS_VOID(rv);
   1692    if (type == eCustomClipboardTypeId_String) {
   1693      uint32_t formatLength;
   1694      rv = stream->Read32(&formatLength);
   1695      NS_ENSURE_SUCCESS_VOID(rv);
   1696      char* formatBytes;
   1697      rv = stream->ReadBytes(formatLength, &formatBytes);
   1698      NS_ENSURE_SUCCESS_VOID(rv);
   1699      nsAutoString format;
   1700      format.Adopt(reinterpret_cast<char16_t*>(formatBytes),
   1701                   formatLength / sizeof(char16_t));
   1702 
   1703      uint32_t dataLength;
   1704      rv = stream->Read32(&dataLength);
   1705      NS_ENSURE_SUCCESS_VOID(rv);
   1706      char* dataBytes;
   1707      rv = stream->ReadBytes(dataLength, &dataBytes);
   1708      NS_ENSURE_SUCCESS_VOID(rv);
   1709      nsAutoString data;
   1710      data.Adopt(reinterpret_cast<char16_t*>(dataBytes),
   1711                 dataLength / sizeof(char16_t));
   1712 
   1713      aCallback(ParseExternalCustomTypesStringData(std::move(format),
   1714                                                   std::move(data)));
   1715    }
   1716  } while (type != eCustomClipboardTypeId_None);
   1717 }
   1718 
   1719 void DataTransfer::FillInExternalCustomTypes(nsIVariant* aData, uint32_t aIndex,
   1720                                             nsIPrincipal* aPrincipal) {
   1721  char* chrs;
   1722  uint32_t len = 0;
   1723  nsresult rv = aData->GetAsStringWithSize(&len, &chrs);
   1724  if (NS_FAILED(rv)) {
   1725    return;
   1726  }
   1727  auto freeChrs = MakeScopeExit([&]() { free(chrs); });
   1728 
   1729  ParseExternalCustomTypesString(
   1730      mozilla::Span(chrs, len),
   1731      [&](ParseExternalCustomTypesStringData&& aData) {
   1732        auto [format, data] = std::move(aData);
   1733        RefPtr<nsVariantCC> variant = new nsVariantCC();
   1734        if (NS_FAILED(variant->SetAsAString(data))) {
   1735          return;
   1736        }
   1737 
   1738        SetDataWithPrincipal(format, variant, aIndex, aPrincipal);
   1739      });
   1740 }
   1741 
   1742 void DataTransfer::SetMode(DataTransfer::Mode aMode) {
   1743  if (!StaticPrefs::dom_events_dataTransfer_protected_enabled() &&
   1744      aMode == Mode::Protected) {
   1745    mMode = Mode::ReadOnly;
   1746  } else {
   1747    mMode = aMode;
   1748  }
   1749 }
   1750 
   1751 nsIWidget* DataTransfer::GetOwnerWidget() {
   1752  RefPtr<WindowContext> wc = GetWindowContext();
   1753  NS_ENSURE_TRUE(wc, nullptr);
   1754  auto* doc = wc->GetDocument();
   1755  NS_ENSURE_TRUE(doc, nullptr);
   1756  auto* pc = doc->GetPresContext();
   1757  NS_ENSURE_TRUE(pc, nullptr);
   1758  return pc->GetRootWidget();
   1759 }
   1760 
   1761 nsIDragSession* DataTransfer::GetOwnerDragSession() {
   1762  auto* widget = GetOwnerWidget();
   1763  nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession(widget);
   1764  return dragSession;
   1765 }
   1766 
   1767 void DataTransfer::ClearForPaste() {
   1768  MOZ_ASSERT(mEventMessage == ePaste,
   1769             "ClearForPaste() should only be called on ePaste messages");
   1770  Disconnect();
   1771 
   1772  // NOTE: Disconnect may not actually clear the DataTransfer if the
   1773  // dom.events.dataTransfer.protected.enabled pref is not on, so we make
   1774  // sure we clear here, as not clearing could provide the DataTransfer
   1775  // access to information from the system clipboard at an arbitrary point
   1776  // in the future.
   1777  ClearAll();
   1778 }
   1779 
   1780 bool DataTransfer::HasPrivateHTMLFlavor() const {
   1781  MOZ_ASSERT(mEventMessage == ePaste,
   1782             "Only works for ePaste messages, where the mClipboardDataSnapshot "
   1783             "is available.");
   1784  nsIClipboardDataSnapshot* snapshot = GetClipboardDataSnapshot();
   1785  if (!snapshot) {
   1786    NS_WARNING("DataTransfer::GetClipboardDataSnapshot() returned null");
   1787    return false;
   1788  }
   1789  nsTArray<nsCString> snapshotFlavors;
   1790  if (NS_FAILED(snapshot->GetFlavorList(snapshotFlavors))) {
   1791    NS_WARNING("nsIClipboardDataSnapshot::GetFlavorList() failed");
   1792    return false;
   1793  }
   1794  return snapshotFlavors.Contains(kHTMLContext);
   1795 }
   1796 
   1797 }  // namespace mozilla::dom