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"
10 #include "nsContentAreaDragDrop.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"
23 #include "nsISupportsPrimitives.h"
24 #include "nsServiceManagerUtils.h"
25 #include "nsNetUtil.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"
35 #include "nsIURIMutator.h"
36 #include "mozilla/dom/Document.h"
37 #include "nsICookieJarSettings.h"
38 #include "nsIPrincipal.h"
39 #include "nsIWebBrowserPersist.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"
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
{
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
);
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
,
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
;
94 nsString mImageSourceString
;
95 nsString mImageDestFileName
;
96 #if defined(XP_MACOSX)
97 nsString mImageRequestMime
;
99 nsString mTitleString
;
100 // will be filled automatically if you fill urlstring
101 nsString mHtmlString
;
102 nsString mContextString
;
103 nsString mInfoString
;
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
);
119 DragDataProducer
provider(aWindow
, aTarget
, aSelectionTargetNode
,
121 return provider
.Produce(aDataTransfer
, aCanDrag
, aSelection
, aDragNode
, aCsp
,
125 NS_IMPL_ISUPPORTS(nsContentAreaDragDropDataProvider
, nsIFlavorDataProvider
)
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
);
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
165 * @param aPrimaryExtension [out] the primary extension for the MIME type
166 * to potentially be used as a replacement
169 nsresult
CheckAndGetExtensionForMime(const nsCString
& aExtension
,
170 const nsCString
& aMimeType
,
171 bool* aIsValidExtension
,
172 nsACString
* aPrimaryExtension
) {
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;
192 rv
= mimeInfo
->ExtensionExists(aExtension
, aIsValidExtension
);
193 NS_ENSURE_SUCCESS(rv
, rv
);
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.
212 nsContentAreaDragDropDataProvider::GetFlavorData(nsITransferable
* aTransferable
,
214 nsISupports
** aData
) {
215 NS_ENSURE_ARG_POINTER(aData
);
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
);
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
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
);
314 DragDataProducer::DragDataProducer(nsPIDOMWindowOuter
* aWindow
,
316 nsIContent
* aSelectionTargetNode
,
317 bool aIsAltKeyPressed
)
320 mSelectionTargetNode(aSelectionTargetNode
),
321 mIsAltKeyPressed(aIsAltKeyPressed
),
324 static nsIContent
* FindDragTarget(nsIContent
* aContent
) {
325 for (nsIContent
* content
= aContent
; content
;
326 content
= content
->GetFlattenedTreeParent()) {
327 if (nsContentUtils::ContentIsDraggable(content
)) {
337 nsresult
DragDataProducer::GetAnchorURL(nsIContent
* aContent
, nsAString
& aURL
) {
339 auto* element
= Element::FromNodeOrNull(aContent
);
340 if (!element
|| !element
->IsLink()) {
344 nsCOMPtr
<nsIURI
> linkURI
= element
->GetHrefURI();
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
);
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
371 nsAutoString
linkText(u
"<a href=\""_ns
+ inURL
+ u
"\">"_ns
+ inText
+
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
));
384 nsresult rv
= imgUrl
->GetSpec(spec
);
385 NS_ENSURE_SUCCESS(rv
, rv
);
387 // pass out the image source string
388 CopyUTF8toUTF16(spec
, mImageSourceString
);
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
);
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
,
415 // and the image object
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;
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()
446 RefPtr
<TextControlElement
> textControlElement
=
447 TextControlElement::GetTextControlElementFromEditingHost(editingElement
);
448 if (textControlElement
) {
449 nsISelectionController
* selcon
=
450 textControlElement
->GetSelectionController();
453 selcon
->GetSelection(nsISelectionController::SELECTION_NORMAL
);
456 if (!selection
) return NS_OK
;
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()) {
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");
488 if (isChromeShell
&& textControlElement
) {
489 // Only use the selection if the target node is in the selection.
490 if (!selection
->ContainsNode(*mSelectionTargetNode
, false, IgnoreErrors()))
493 selection
.swap(*aSelection
);
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
) {
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
);
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
) {
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>
568 mHtmlString
.AssignLiteral("<a href=\"");
569 mHtmlString
.Append(mUrlString
);
570 mHtmlString
.AppendLiteral("\">");
571 mHtmlString
.Append(mTitleString
);
572 mHtmlString
.AppendLiteral("</a>");
574 dragNode
= draggedNode
;
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
579 nsCOMPtr
<nsIURI
> imageURI
;
580 image
->GetCurrentURI(getter_AddRefs(imageURI
));
581 nsCOMPtr
<Element
> imageElement(do_QueryInterface(image
));
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
);
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>?
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
));
613 rv
= GetImageData(img
, imgRequest
);
614 NS_ENSURE_SUCCESS(rv
, rv
);
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
;
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
) {
636 rv
= GetAnchorURL(linkNode
, mUrlString
);
637 NS_ENSURE_SUCCESS(rv
, rv
);
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();
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
;
666 rv
= nsCopySupport::GetTransferableForSelection(
667 *aSelection
, doc
, getter_AddRefs(transferable
));
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
);
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
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
);
743 title
.ReplaceChar(u
"\r\n", ' ');
746 AddString(aDataTransfer
, NS_LITERAL_STRING_FROM_CSTRING(kURLMime
), dragData
,
748 AddString(aDataTransfer
, NS_LITERAL_STRING_FROM_CSTRING(kURLDataMime
),
749 mUrlString
, principal
);
750 AddString(aDataTransfer
,
751 NS_LITERAL_STRING_FROM_CSTRING(kURLDescriptionMime
), mTitleString
,
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
);
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
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.
782 RefPtr
<nsVariantCC
> variant
= new nsVariantCC();
783 variant
->SetAsISupports(mImage
);
784 aDataTransfer
->SetDataWithPrincipal(
785 NS_LITERAL_STRING_FROM_CSTRING(kNativeImageMime
), variant
, 0,
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();
795 RefPtr
<nsVariantCC
> variant
= new nsVariantCC();
796 variant
->SetAsISupports(dataProvider
);
797 aDataTransfer
->SetDataWithPrincipal(
798 NS_LITERAL_STRING_FROM_CSTRING(kFilePromiseMime
), variant
, 0,
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);
813 // if not an anchor, add the image url
815 AddString(aDataTransfer
, NS_LITERAL_STRING_FROM_CSTRING(kURLDataMime
),
816 mUrlString
, principal
);
817 AddString(aDataTransfer
, u
"text/uri-list"_ns
, mUrlString
, principal
);
824 // note that this can return NS_OK, but a null out param (by design)
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
);
867 // indicate that a link or text is selected
868 *outDragSelectedText
= true;