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