Backed out changeset 2450366cf7ca (bug 1891629) for causing win msix mochitest failures
[gecko.git] / dom / base / nsContentAreaDragDrop.cpp
blob3ca21e725a412c2c1d327772a5df1d990b45197b
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/. */
7 #include "nsReadableUtils.h"
9 // Local Includes
10 #include "nsContentAreaDragDrop.h"
12 // Helper Classes
13 #include "nsString.h"
15 // Interfaces needed to be included
16 #include "nsCopySupport.h"
17 #include "nsISelectionController.h"
18 #include "nsPIDOMWindow.h"
19 #include "nsIFormControl.h"
20 #include "nsITransferable.h"
21 #include "nsComponentManagerUtils.h"
22 #include "nsXPCOM.h"
23 #include "nsISupportsPrimitives.h"
24 #include "nsServiceManagerUtils.h"
25 #include "nsNetUtil.h"
26 #include "nsIFile.h"
27 #include "nsFrameLoader.h"
28 #include "nsFrameLoaderOwner.h"
29 #include "nsIContent.h"
30 #include "nsIContentInlines.h"
31 #include "nsIContentPolicy.h"
32 #include "nsIImageLoadingContent.h"
33 #include "nsUnicharUtils.h"
34 #include "nsIURL.h"
35 #include "nsIURIMutator.h"
36 #include "mozilla/dom/Document.h"
37 #include "nsICookieJarSettings.h"
38 #include "nsIPrincipal.h"
39 #include "nsIWebBrowserPersist.h"
40 #include "nsEscape.h"
41 #include "nsContentUtils.h"
42 #include "nsIMIMEService.h"
43 #include "imgIContainer.h"
44 #include "imgIRequest.h"
45 #include "mozilla/dom/DataTransfer.h"
46 #include "nsIMIMEInfo.h"
47 #include "nsRange.h"
48 #include "BrowserParent.h"
49 #include "mozilla/TextControlElement.h"
50 #include "mozilla/dom/BrowsingContext.h"
51 #include "mozilla/dom/Element.h"
52 #include "mozilla/dom/HTMLAreaElement.h"
53 #include "mozilla/dom/HTMLAnchorElement.h"
54 #include "mozilla/dom/Selection.h"
55 #include "nsVariant.h"
56 #include "nsQueryObject.h"
58 using namespace mozilla;
59 using namespace mozilla::dom;
60 using mozilla::IgnoreErrors;
62 class MOZ_STACK_CLASS DragDataProducer {
63 public:
64 DragDataProducer(nsPIDOMWindowOuter* aWindow, nsIContent* aTarget,
65 nsIContent* aSelectionTargetNode, bool aIsAltKeyPressed);
66 nsresult Produce(DataTransfer* aDataTransfer, bool* aCanDrag,
67 Selection** aSelection, nsIContent** aDragNode,
68 nsIContentSecurityPolicy** aCsp,
69 nsICookieJarSettings** aCookieJarSettings);
71 private:
72 // @param aHidden true, iff the data should be hidden from non-chrome code.
73 void AddString(DataTransfer* aDataTransfer, const nsAString& aFlavor,
74 const nsAString& aData, nsIPrincipal* aPrincipal,
75 bool aHidden = false);
76 nsresult AddStringsToDataTransfer(nsIContent* aDragNode,
77 DataTransfer* aDataTransfer);
78 nsresult GetImageData(imgIContainer* aImage, imgIRequest* aRequest);
79 static nsresult GetDraggableSelectionData(Selection* inSelection,
80 nsIContent* inRealTargetNode,
81 nsIContent** outImageOrLinkNode,
82 bool* outDragSelectedText);
83 [[nodiscard]] static nsresult GetAnchorURL(nsIContent* inNode,
84 nsAString& outURL);
85 static void CreateLinkText(const nsAString& inURL, const nsAString& inText,
86 nsAString& outLinkText);
88 nsCOMPtr<nsPIDOMWindowOuter> mWindow;
89 nsCOMPtr<nsIContent> mTarget;
90 nsCOMPtr<nsIContent> mSelectionTargetNode;
91 bool mIsAltKeyPressed;
93 nsString mUrlString;
94 nsString mImageSourceString;
95 nsString mImageDestFileName;
96 #if defined(XP_MACOSX)
97 nsString mImageRequestMime;
98 #endif
99 nsString mTitleString;
100 // will be filled automatically if you fill urlstring
101 nsString mHtmlString;
102 nsString mContextString;
103 nsString mInfoString;
105 bool mIsAnchor;
106 nsCOMPtr<imgIContainer> mImage;
109 nsresult nsContentAreaDragDrop::GetDragData(
110 nsPIDOMWindowOuter* aWindow, nsIContent* aTarget,
111 nsIContent* aSelectionTargetNode, bool aIsAltKeyPressed,
112 DataTransfer* aDataTransfer, bool* aCanDrag, Selection** aSelection,
113 nsIContent** aDragNode, nsIContentSecurityPolicy** aCsp,
114 nsICookieJarSettings** aCookieJarSettings) {
115 NS_ENSURE_TRUE(aSelectionTargetNode, NS_ERROR_INVALID_ARG);
117 *aCanDrag = true;
119 DragDataProducer provider(aWindow, aTarget, aSelectionTargetNode,
120 aIsAltKeyPressed);
121 return provider.Produce(aDataTransfer, aCanDrag, aSelection, aDragNode, aCsp,
122 aCookieJarSettings);
125 NS_IMPL_ISUPPORTS(nsContentAreaDragDropDataProvider, nsIFlavorDataProvider)
127 // SaveURIToFile
128 // used on platforms where it's possible to drag items (e.g. images)
129 // into the file system
130 nsresult nsContentAreaDragDropDataProvider::SaveURIToFile(
131 nsIURI* inSourceURI, nsIPrincipal* inTriggeringPrincipal,
132 nsICookieJarSettings* inCookieJarSettings, nsIFile* inDestFile,
133 nsContentPolicyType inContentPolicyType, bool isPrivate) {
134 nsCOMPtr<nsIURL> sourceURL = do_QueryInterface(inSourceURI);
135 if (!sourceURL) {
136 return NS_ERROR_NO_INTERFACE;
139 nsresult rv = inDestFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
140 NS_ENSURE_SUCCESS(rv, rv);
142 // we rely on the fact that the WPB is refcounted by the channel etc,
143 // so we don't keep a ref to it. It will die when finished.
144 nsCOMPtr<nsIWebBrowserPersist> persist = do_CreateInstance(
145 "@mozilla.org/embedding/browser/nsWebBrowserPersist;1", &rv);
146 NS_ENSURE_SUCCESS(rv, rv);
148 persist->SetPersistFlags(
149 nsIWebBrowserPersist::PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION);
151 // referrer policy can be anything since the referrer is nullptr
152 return persist->SaveURI(inSourceURI, inTriggeringPrincipal, 0, nullptr,
153 inCookieJarSettings, nullptr, nullptr, inDestFile,
154 inContentPolicyType, isPrivate);
158 * Check if the provided filename extension is valid for the MIME type and
159 * return the MIME type's primary extension.
161 * @param aExtension [in] the extension to check
162 * @param aMimeType [in] the MIME type to check the extension with
163 * @param aIsValidExtension [out] true if |aExtension| is valid for
164 * |aMimeType|
165 * @param aPrimaryExtension [out] the primary extension for the MIME type
166 * to potentially be used as a replacement
167 * for |aExtension|
169 nsresult CheckAndGetExtensionForMime(const nsCString& aExtension,
170 const nsCString& aMimeType,
171 bool* aIsValidExtension,
172 nsACString* aPrimaryExtension) {
173 nsresult rv;
175 nsCOMPtr<nsIMIMEService> mimeService = do_GetService("@mozilla.org/mime;1");
176 if (NS_WARN_IF(!mimeService)) {
177 return NS_ERROR_FAILURE;
180 nsCOMPtr<nsIMIMEInfo> mimeInfo;
181 rv = mimeService->GetFromTypeAndExtension(aMimeType, ""_ns,
182 getter_AddRefs(mimeInfo));
183 NS_ENSURE_SUCCESS(rv, rv);
185 mimeInfo->GetPrimaryExtension(*aPrimaryExtension);
187 if (aExtension.IsEmpty()) {
188 *aIsValidExtension = false;
189 return NS_OK;
192 rv = mimeInfo->ExtensionExists(aExtension, aIsValidExtension);
193 NS_ENSURE_SUCCESS(rv, rv);
195 return NS_OK;
198 // This is our nsIFlavorDataProvider callback. There are several
199 // assumptions here that make this work:
201 // 1. Someone put a kFilePromiseURLMime flavor into the transferable
202 // with the source URI of the file to save (as a string). We did
203 // that in AddStringsToDataTransfer.
205 // 2. Someone put a kFilePromiseDirectoryMime flavor into the
206 // transferable with an nsIFile for the directory we are to
207 // save in. That has to be done by platform-specific code (in
208 // widget), which gets the destination directory from
209 // OS-specific drag information.
211 NS_IMETHODIMP
212 nsContentAreaDragDropDataProvider::GetFlavorData(nsITransferable* aTransferable,
213 const char* aFlavor,
214 nsISupports** aData) {
215 NS_ENSURE_ARG_POINTER(aData);
216 *aData = nullptr;
218 nsresult rv = NS_ERROR_NOT_IMPLEMENTED;
220 if (strcmp(aFlavor, kFilePromiseMime) == 0) {
221 // get the URI from the kFilePromiseURLMime flavor
222 NS_ENSURE_ARG(aTransferable);
223 nsCOMPtr<nsISupports> tmp;
224 rv = aTransferable->GetTransferData(kFilePromiseURLMime,
225 getter_AddRefs(tmp));
226 NS_ENSURE_SUCCESS(rv, rv);
227 nsCOMPtr<nsISupportsString> supportsString = do_QueryInterface(tmp);
228 if (!supportsString) return NS_ERROR_FAILURE;
230 nsAutoString sourceURLString;
231 supportsString->GetData(sourceURLString);
232 if (sourceURLString.IsEmpty()) return NS_ERROR_FAILURE;
234 nsCOMPtr<nsIURI> sourceURI;
235 rv = NS_NewURI(getter_AddRefs(sourceURI), sourceURLString);
236 NS_ENSURE_SUCCESS(rv, rv);
238 rv = aTransferable->GetTransferData(kFilePromiseDestFilename,
239 getter_AddRefs(tmp));
240 NS_ENSURE_SUCCESS(rv, rv);
241 supportsString = do_QueryInterface(tmp);
242 if (!supportsString) return NS_ERROR_FAILURE;
244 nsAutoString targetFilename;
245 supportsString->GetData(targetFilename);
246 if (targetFilename.IsEmpty()) return NS_ERROR_FAILURE;
248 #if defined(XP_MACOSX)
249 // Use the image request's MIME type to ensure the filename's
250 // extension is compatible with the OS's handler for this type.
251 // If it isn't, or is missing, replace the extension with the
252 // primary extension. On Mac, do this in the parent process
253 // because sandboxing blocks access to MIME-handler info from
254 // content processes.
255 if (XRE_IsParentProcess()) {
256 rv = aTransferable->GetTransferData(kImageRequestMime,
257 getter_AddRefs(tmp));
258 NS_ENSURE_SUCCESS(rv, rv);
259 supportsString = do_QueryInterface(tmp);
260 if (!supportsString) return NS_ERROR_FAILURE;
262 nsAutoString contentType;
263 supportsString->GetData(contentType);
265 nsCOMPtr<nsIMIMEService> mimeService =
266 do_GetService("@mozilla.org/mime;1");
267 if (NS_WARN_IF(!mimeService)) {
268 return NS_ERROR_FAILURE;
271 mimeService->ValidateFileNameForSaving(
272 targetFilename, NS_ConvertUTF16toUTF8(contentType),
273 nsIMIMEService::VALIDATE_DEFAULT, targetFilename);
274 } else {
275 // make the filename safe for the filesystem
276 targetFilename.ReplaceChar(
277 u"" FILE_PATH_SEPARATOR FILE_ILLEGAL_CHARACTERS, u'-');
279 #endif /* defined(XP_MACOSX) */
281 // get the target directory from the kFilePromiseDirectoryMime
282 // flavor
283 nsCOMPtr<nsISupports> dirPrimitive;
284 rv = aTransferable->GetTransferData(kFilePromiseDirectoryMime,
285 getter_AddRefs(dirPrimitive));
286 NS_ENSURE_SUCCESS(rv, rv);
287 nsCOMPtr<nsIFile> destDirectory = do_QueryInterface(dirPrimitive);
288 if (!destDirectory) return NS_ERROR_FAILURE;
290 nsCOMPtr<nsIFile> file;
291 rv = destDirectory->Clone(getter_AddRefs(file));
292 NS_ENSURE_SUCCESS(rv, rv);
294 file->Append(targetFilename);
296 bool isPrivate = aTransferable->GetIsPrivateData();
298 nsCOMPtr<nsIPrincipal> principal = aTransferable->GetRequestingPrincipal();
299 nsContentPolicyType contentPolicyType =
300 aTransferable->GetContentPolicyType();
301 nsCOMPtr<nsICookieJarSettings> cookieJarSettings =
302 aTransferable->GetCookieJarSettings();
303 rv = SaveURIToFile(sourceURI, principal, cookieJarSettings, file,
304 contentPolicyType, isPrivate);
305 // send back an nsIFile
306 if (NS_SUCCEEDED(rv)) {
307 CallQueryInterface(file, aData);
311 return rv;
314 DragDataProducer::DragDataProducer(nsPIDOMWindowOuter* aWindow,
315 nsIContent* aTarget,
316 nsIContent* aSelectionTargetNode,
317 bool aIsAltKeyPressed)
318 : mWindow(aWindow),
319 mTarget(aTarget),
320 mSelectionTargetNode(aSelectionTargetNode),
321 mIsAltKeyPressed(aIsAltKeyPressed),
322 mIsAnchor(false) {}
324 static nsIContent* FindDragTarget(nsIContent* aContent) {
325 for (nsIContent* content = aContent; content;
326 content = content->GetFlattenedTreeParent()) {
327 if (nsContentUtils::ContentIsDraggable(content)) {
328 return content;
331 return nullptr;
335 // GetAnchorURL
337 nsresult DragDataProducer::GetAnchorURL(nsIContent* aContent, nsAString& aURL) {
338 aURL.Truncate();
339 auto* element = Element::FromNodeOrNull(aContent);
340 if (!element || !element->IsLink()) {
341 return NS_OK;
344 nsCOMPtr<nsIURI> linkURI = element->GetHrefURI();
345 if (!linkURI) {
346 return NS_OK;
349 nsAutoCString spec;
350 nsresult rv = linkURI->GetSpec(spec);
351 NS_ENSURE_SUCCESS(rv, rv);
352 nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
353 rv = secMan->CheckLoadURIStrWithPrincipal(aContent->NodePrincipal(), spec, 0);
354 NS_ENSURE_SUCCESS(rv, rv);
355 CopyUTF8toUTF16(spec, aURL);
356 return NS_OK;
360 // CreateLinkText
362 // Creates the html for an anchor in the form
363 // <a href="inURL">inText</a>
365 void DragDataProducer::CreateLinkText(const nsAString& inURL,
366 const nsAString& inText,
367 nsAString& outLinkText) {
368 // use a temp var in case |inText| is the same string as
369 // |outLinkText| to avoid overwriting it while building up the
370 // string in pieces.
371 nsAutoString linkText(u"<a href=\""_ns + inURL + u"\">"_ns + inText +
372 u"</a>"_ns);
374 outLinkText = linkText;
377 nsresult DragDataProducer::GetImageData(imgIContainer* aImage,
378 imgIRequest* aRequest) {
379 nsCOMPtr<nsIURI> imgUri = aRequest->GetURI();
381 nsCOMPtr<nsIURL> imgUrl(do_QueryInterface(imgUri));
382 if (imgUrl) {
383 nsAutoCString spec;
384 nsresult rv = imgUrl->GetSpec(spec);
385 NS_ENSURE_SUCCESS(rv, rv);
387 // pass out the image source string
388 CopyUTF8toUTF16(spec, mImageSourceString);
390 nsCString mimeType;
391 aRequest->GetMimeType(getter_Copies(mimeType));
393 nsAutoCString fileName;
394 aRequest->GetFileName(fileName);
396 #if defined(XP_MACOSX)
397 // Save the MIME type so we can make sure the extension
398 // is compatible (and replace it if it isn't) when the
399 // image is dropped. On Mac, we need to get the OS MIME
400 // handler information in the parent due to sandboxing.
401 CopyUTF8toUTF16(mimeType, mImageRequestMime);
402 CopyUTF8toUTF16(fileName, mImageDestFileName);
403 #else
404 nsCOMPtr<nsIMIMEService> mimeService = do_GetService("@mozilla.org/mime;1");
405 if (NS_WARN_IF(!mimeService)) {
406 return NS_ERROR_FAILURE;
409 CopyUTF8toUTF16(fileName, mImageDestFileName);
410 mimeService->ValidateFileNameForSaving(mImageDestFileName, mimeType,
411 nsIMIMEService::VALIDATE_DEFAULT,
412 mImageDestFileName);
413 #endif
415 // and the image object
416 mImage = aImage;
419 return NS_OK;
422 nsresult DragDataProducer::Produce(DataTransfer* aDataTransfer, bool* aCanDrag,
423 Selection** aSelection,
424 nsIContent** aDragNode,
425 nsIContentSecurityPolicy** aCsp,
426 nsICookieJarSettings** aCookieJarSettings) {
427 MOZ_ASSERT(aCanDrag && aSelection && aDataTransfer && aDragNode,
428 "null pointer passed to Produce");
429 NS_ASSERTION(mWindow, "window not set");
430 NS_ASSERTION(mSelectionTargetNode,
431 "selection target node should have been set");
433 *aDragNode = nullptr;
435 nsresult rv;
436 nsIContent* dragNode = nullptr;
437 *aSelection = nullptr;
439 // Find the selection to see what we could be dragging and if what we're
440 // dragging is in what is selected. If this is an editable textbox, use
441 // the textbox's selection, otherwise use the window's selection.
442 RefPtr<Selection> selection;
443 nsIContent* editingElement = mSelectionTargetNode->IsEditable()
444 ? mSelectionTargetNode->GetEditingHost()
445 : nullptr;
446 RefPtr<TextControlElement> textControlElement =
447 TextControlElement::GetTextControlElementFromEditingHost(editingElement);
448 if (textControlElement) {
449 nsISelectionController* selcon =
450 textControlElement->GetSelectionController();
451 if (selcon) {
452 selection =
453 selcon->GetSelection(nsISelectionController::SELECTION_NORMAL);
456 if (!selection) return NS_OK;
457 } else {
458 selection = mWindow->GetSelection();
459 if (!selection) return NS_OK;
461 // Check if the node is inside a form control. Don't set aCanDrag to false
462 // however, as we still want to allow the drag.
463 nsCOMPtr<nsIContent> findFormNode = mSelectionTargetNode;
464 nsIContent* findFormParent = findFormNode->GetParent();
465 while (findFormParent) {
466 nsCOMPtr<nsIFormControl> form(do_QueryInterface(findFormParent));
467 if (form && !form->AllowDraggableChildren()) {
468 return NS_OK;
470 findFormParent = findFormParent->GetParent();
474 // if set, serialize the content under this node
475 nsCOMPtr<nsIContent> nodeToSerialize;
477 BrowsingContext* bc = mWindow->GetBrowsingContext();
478 const bool isChromeShell = bc && bc->IsChrome();
480 // In chrome shells, only allow dragging inside editable areas.
481 if (isChromeShell && !editingElement) {
482 // This path should already be filtered out in
483 // EventStateManager::DetermineDragTargetAndDefaultData.
484 MOZ_ASSERT_UNREACHABLE("Shouldn't be generating drag data for chrome");
485 return NS_OK;
488 if (isChromeShell && textControlElement) {
489 // Only use the selection if the target node is in the selection.
490 if (!selection->ContainsNode(*mSelectionTargetNode, false, IgnoreErrors()))
491 return NS_OK;
493 selection.swap(*aSelection);
494 } else {
495 // In content shells, a number of checks are made below to determine
496 // whether an image or a link is being dragged. If so, add additional
497 // data to the data transfer. This is also done for chrome shells, but
498 // only when in a non-textbox editor.
500 bool haveSelectedContent = false;
502 // possible parent link node
503 nsCOMPtr<nsIContent> parentLink;
504 nsCOMPtr<nsIContent> draggedNode;
507 // only drag form elements by using the alt key,
508 // otherwise buttons and select widgets are hard to use
510 // Note that while <object> elements implement nsIFormControl, we should
511 // really allow dragging them if they happen to be images.
512 nsCOMPtr<nsIFormControl> form(do_QueryInterface(mTarget));
513 if (form && !mIsAltKeyPressed &&
514 form->ControlType() != FormControlType::Object) {
515 *aCanDrag = false;
516 return NS_OK;
519 draggedNode = FindDragTarget(mTarget);
522 nsCOMPtr<nsIImageLoadingContent> image;
524 nsCOMPtr<nsIContent> selectedImageOrLinkNode;
525 GetDraggableSelectionData(selection, mSelectionTargetNode,
526 getter_AddRefs(selectedImageOrLinkNode),
527 &haveSelectedContent);
529 // either plain text or anchor text is selected
530 if (haveSelectedContent) {
531 selection.swap(*aSelection);
532 } else if (selectedImageOrLinkNode) {
533 // an image is selected
534 image = do_QueryInterface(selectedImageOrLinkNode);
535 } else {
536 // nothing is selected -
538 // look for draggable elements under the mouse
540 // if the alt key is down, don't start a drag if we're in an
541 // anchor because we want to do selection.
542 parentLink = nsContentUtils::GetClosestLinkInFlatTree(draggedNode);
543 if (parentLink && mIsAltKeyPressed) {
544 *aCanDrag = false;
545 return NS_OK;
547 image = do_QueryInterface(draggedNode);
551 // set for linked images, and links
552 nsCOMPtr<nsIContent> linkNode;
553 if (const auto* areaElem = HTMLAreaElement::FromNodeOrNull(draggedNode)) {
554 // use the alt text (or, if missing, the href) as the title
555 areaElem->GetAttr(nsGkAtoms::alt, mTitleString);
556 if (mTitleString.IsEmpty()) {
557 // this can be a relative link
558 areaElem->GetAttr(nsGkAtoms::href, mTitleString);
561 // gives an absolute link
562 nsresult rv = GetAnchorURL(draggedNode, mUrlString);
563 NS_ENSURE_SUCCESS(rv, rv);
565 // we'll generate HTML like <a href="absurl">alt text</a>
566 mIsAnchor = true;
568 mHtmlString.AssignLiteral("<a href=\"");
569 mHtmlString.Append(mUrlString);
570 mHtmlString.AppendLiteral("\">");
571 mHtmlString.Append(mTitleString);
572 mHtmlString.AppendLiteral("</a>");
574 dragNode = draggedNode;
575 } else if (image) {
576 // grab the href as the url, use alt text as the title of the
577 // area if it's there. the drag data is the image tag and src
578 // attribute.
579 nsCOMPtr<nsIURI> imageURI;
580 image->GetCurrentURI(getter_AddRefs(imageURI));
581 nsCOMPtr<Element> imageElement(do_QueryInterface(image));
582 if (imageURI) {
583 nsAutoCString spec;
584 rv = imageURI->GetSpec(spec);
585 NS_ENSURE_SUCCESS(rv, rv);
586 nsIScriptSecurityManager* secMan =
587 nsContentUtils::GetSecurityManager();
588 rv = secMan->CheckLoadURIStrWithPrincipal(
589 imageElement->NodePrincipal(), spec, 0);
590 NS_ENSURE_SUCCESS(rv, rv);
591 mIsAnchor = true;
592 CopyUTF8toUTF16(spec, mUrlString);
595 // XXXbz Shouldn't we use the "title" attr for title? Using
596 // "alt" seems very wrong....
597 // XXXbz Also, what if this is an nsIImageLoadingContent
598 // that's not an <html:img>?
599 if (imageElement) {
600 imageElement->GetAttr(nsGkAtoms::alt, mTitleString);
603 if (mTitleString.IsEmpty()) {
604 mTitleString = mUrlString;
607 nsCOMPtr<imgIRequest> imgRequest;
609 // grab the image data, and its request.
610 nsCOMPtr<imgIContainer> img = nsContentUtils::GetImageFromContent(
611 image, getter_AddRefs(imgRequest));
612 if (imgRequest) {
613 rv = GetImageData(img, imgRequest);
614 NS_ENSURE_SUCCESS(rv, rv);
617 if (parentLink) {
618 // If we are dragging around an image in an anchor, then we
619 // are dragging the entire anchor
620 linkNode = parentLink;
621 nodeToSerialize = linkNode;
622 } else {
623 nodeToSerialize = draggedNode;
625 dragNode = nodeToSerialize;
626 } else if (parentLink) {
627 // parentLink will always be null if there's selected content
628 linkNode = parentLink;
629 nodeToSerialize = linkNode;
630 } else if (!haveSelectedContent) {
631 // nothing draggable
632 return NS_OK;
635 if (linkNode) {
636 rv = GetAnchorURL(linkNode, mUrlString);
637 NS_ENSURE_SUCCESS(rv, rv);
638 mIsAnchor = true;
639 dragNode = linkNode;
644 if (nodeToSerialize || *aSelection) {
645 mHtmlString.Truncate();
646 mContextString.Truncate();
647 mInfoString.Truncate();
648 mTitleString.Truncate();
650 nsCOMPtr<Document> doc = mWindow->GetDoc();
651 NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
653 nsCOMPtr<nsIContentSecurityPolicy> csp = doc->GetCsp();
654 if (csp) {
655 NS_IF_ADDREF(*aCsp = csp);
658 nsCOMPtr<nsICookieJarSettings> cookieJarSettings = doc->CookieJarSettings();
659 if (cookieJarSettings) {
660 NS_IF_ADDREF(*aCookieJarSettings = cookieJarSettings);
663 // if we have selected text, use it in preference to the node
664 nsCOMPtr<nsITransferable> transferable;
665 if (*aSelection) {
666 rv = nsCopySupport::GetTransferableForSelection(
667 *aSelection, doc, getter_AddRefs(transferable));
668 } else {
669 rv = nsCopySupport::GetTransferableForNode(nodeToSerialize, doc,
670 getter_AddRefs(transferable));
672 NS_ENSURE_SUCCESS(rv, rv);
674 nsCOMPtr<nsISupports> supports;
675 nsCOMPtr<nsISupportsString> data;
676 rv = transferable->GetTransferData(kHTMLMime, getter_AddRefs(supports));
677 data = do_QueryInterface(supports);
678 if (NS_SUCCEEDED(rv)) {
679 data->GetData(mHtmlString);
681 rv = transferable->GetTransferData(kHTMLContext, getter_AddRefs(supports));
682 data = do_QueryInterface(supports);
683 if (NS_SUCCEEDED(rv)) {
684 data->GetData(mContextString);
686 rv = transferable->GetTransferData(kHTMLInfo, getter_AddRefs(supports));
687 data = do_QueryInterface(supports);
688 if (NS_SUCCEEDED(rv)) {
689 data->GetData(mInfoString);
691 rv = transferable->GetTransferData(kTextMime, getter_AddRefs(supports));
692 data = do_QueryInterface(supports);
693 NS_ENSURE_SUCCESS(rv, rv); // require plain text at a minimum
694 data->GetData(mTitleString);
697 // default text value is the URL
698 if (mTitleString.IsEmpty()) {
699 mTitleString = mUrlString;
702 // if we haven't constructed a html version, make one now
703 if (mHtmlString.IsEmpty() && !mUrlString.IsEmpty())
704 CreateLinkText(mUrlString, mTitleString, mHtmlString);
706 // if there is no drag node, which will be the case for a selection, just
707 // use the selection target node.
708 rv = AddStringsToDataTransfer(
709 dragNode ? dragNode : mSelectionTargetNode.get(), aDataTransfer);
710 NS_ENSURE_SUCCESS(rv, rv);
712 NS_IF_ADDREF(*aDragNode = dragNode);
713 return NS_OK;
716 void DragDataProducer::AddString(DataTransfer* aDataTransfer,
717 const nsAString& aFlavor,
718 const nsAString& aData,
719 nsIPrincipal* aPrincipal, bool aHidden) {
720 RefPtr<nsVariantCC> variant = new nsVariantCC();
721 variant->SetAsAString(aData);
722 aDataTransfer->SetDataWithPrincipal(aFlavor, variant, 0, aPrincipal, aHidden);
725 nsresult DragDataProducer::AddStringsToDataTransfer(
726 nsIContent* aDragNode, DataTransfer* aDataTransfer) {
727 NS_ASSERTION(aDragNode, "adding strings for null node");
729 // set all of the data to have the principal of the node where the data came
730 // from
731 nsIPrincipal* principal = aDragNode->NodePrincipal();
733 // add a special flavor if we're an anchor to indicate that we have
734 // a URL in the drag data
735 if (!mUrlString.IsEmpty() && mIsAnchor) {
736 nsAutoString dragData(mUrlString);
737 dragData.Append('\n');
738 // Remove leading and trailing newlines in the title and replace them with
739 // space in remaining positions - they confuse PlacesUtils::unwrapNodes
740 // that expects url\ntitle formatted data for x-moz-url.
741 nsAutoString title(mTitleString);
742 title.Trim("\r\n");
743 title.ReplaceChar(u"\r\n", ' ');
744 dragData += title;
746 AddString(aDataTransfer, NS_LITERAL_STRING_FROM_CSTRING(kURLMime), dragData,
747 principal);
748 AddString(aDataTransfer, NS_LITERAL_STRING_FROM_CSTRING(kURLDataMime),
749 mUrlString, principal);
750 AddString(aDataTransfer,
751 NS_LITERAL_STRING_FROM_CSTRING(kURLDescriptionMime), mTitleString,
752 principal);
753 AddString(aDataTransfer, u"text/uri-list"_ns, mUrlString, principal);
756 // add a special flavor for the html context data
757 if (!mContextString.IsEmpty())
758 AddString(aDataTransfer, NS_LITERAL_STRING_FROM_CSTRING(kHTMLContext),
759 mContextString, principal);
761 // add a special flavor if we have html info data
762 if (!mInfoString.IsEmpty())
763 AddString(aDataTransfer, NS_LITERAL_STRING_FROM_CSTRING(kHTMLInfo),
764 mInfoString, principal);
766 // add the full html
767 if (!mHtmlString.IsEmpty())
768 AddString(aDataTransfer, NS_LITERAL_STRING_FROM_CSTRING(kHTMLMime),
769 mHtmlString, principal);
771 // add the plain text. we use the url for text/plain data if an anchor is
772 // being dragged, rather than the title text of the link or the alt text for
773 // an anchor image.
774 AddString(aDataTransfer, NS_LITERAL_STRING_FROM_CSTRING(kTextMime),
775 mIsAnchor ? mUrlString : mTitleString, principal);
777 // add image data, if present. For now, all we're going to do with
778 // this is turn it into a native data flavor, so indicate that with
779 // a new flavor so as not to confuse anyone who is really registered
780 // for image/gif or image/jpg.
781 if (mImage) {
782 RefPtr<nsVariantCC> variant = new nsVariantCC();
783 variant->SetAsISupports(mImage);
784 aDataTransfer->SetDataWithPrincipal(
785 NS_LITERAL_STRING_FROM_CSTRING(kNativeImageMime), variant, 0,
786 principal);
788 // assume the image comes from a file, and add a file promise. We
789 // register ourselves as a nsIFlavorDataProvider, and will use the
790 // GetFlavorData callback to save the image to disk.
792 nsCOMPtr<nsIFlavorDataProvider> dataProvider =
793 new nsContentAreaDragDropDataProvider();
794 if (dataProvider) {
795 RefPtr<nsVariantCC> variant = new nsVariantCC();
796 variant->SetAsISupports(dataProvider);
797 aDataTransfer->SetDataWithPrincipal(
798 NS_LITERAL_STRING_FROM_CSTRING(kFilePromiseMime), variant, 0,
799 principal);
802 AddString(aDataTransfer,
803 NS_LITERAL_STRING_FROM_CSTRING(kFilePromiseURLMime),
804 mImageSourceString, principal);
805 AddString(aDataTransfer,
806 NS_LITERAL_STRING_FROM_CSTRING(kFilePromiseDestFilename),
807 mImageDestFileName, principal);
808 #if defined(XP_MACOSX)
809 AddString(aDataTransfer, NS_LITERAL_STRING_FROM_CSTRING(kImageRequestMime),
810 mImageRequestMime, principal, /* aHidden= */ true);
811 #endif
813 // if not an anchor, add the image url
814 if (!mIsAnchor) {
815 AddString(aDataTransfer, NS_LITERAL_STRING_FROM_CSTRING(kURLDataMime),
816 mUrlString, principal);
817 AddString(aDataTransfer, u"text/uri-list"_ns, mUrlString, principal);
821 return NS_OK;
824 // note that this can return NS_OK, but a null out param (by design)
825 // static
826 nsresult DragDataProducer::GetDraggableSelectionData(
827 Selection* inSelection, nsIContent* inRealTargetNode,
828 nsIContent** outImageOrLinkNode, bool* outDragSelectedText) {
829 NS_ENSURE_ARG(inSelection);
830 NS_ENSURE_ARG(inRealTargetNode);
831 NS_ENSURE_ARG_POINTER(outImageOrLinkNode);
833 *outImageOrLinkNode = nullptr;
834 *outDragSelectedText = false;
836 if (!inSelection->IsCollapsed()) {
837 if (inSelection->ContainsNode(*inRealTargetNode, false, IgnoreErrors())) {
838 // track down the anchor node, if any, for the url
839 nsINode* selectionStart = inSelection->GetAnchorNode();
840 nsINode* selectionEnd = inSelection->GetFocusNode();
842 // look for a selection around a single node, like an image.
843 // in this case, drag the image, rather than a serialization of the HTML
844 // XXX generalize this to other draggable element types?
845 if (selectionStart == selectionEnd) {
846 nsCOMPtr<nsIContent> selStartContent =
847 nsIContent::FromNodeOrNull(selectionStart);
848 if (selStartContent && selStartContent->HasChildNodes()) {
849 // see if just one node is selected
850 uint32_t anchorOffset = inSelection->AnchorOffset();
851 uint32_t focusOffset = inSelection->FocusOffset();
852 if (anchorOffset == focusOffset + 1 ||
853 focusOffset == anchorOffset + 1) {
854 uint32_t childOffset = std::min(anchorOffset, focusOffset);
855 nsIContent* childContent =
856 selStartContent->GetChildAt_Deprecated(childOffset);
857 // if we find an image, we'll fall into the node-dragging code,
858 // rather the the selection-dragging code
859 if (nsContentUtils::IsDraggableImage(childContent)) {
860 NS_ADDREF(*outImageOrLinkNode = childContent);
861 return NS_OK;
867 // indicate that a link or text is selected
868 *outDragSelectedText = true;
872 return NS_OK;