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 nsIPrincipal
** aPrincipal
, nsIContentSecurityPolicy
** aCsp
,
69 nsICookieJarSettings
** aCookieJarSettings
);
72 void AddString(DataTransfer
* aDataTransfer
, const nsAString
& aFlavor
,
73 const nsAString
& aData
, nsIPrincipal
* aPrincipal
,
74 bool aHidden
= false);
75 nsresult
AddStringsToDataTransfer(nsIContent
* aDragNode
,
76 DataTransfer
* aDataTransfer
);
77 nsresult
GetImageData(imgIContainer
* aImage
, imgIRequest
* aRequest
);
78 static nsresult
GetDraggableSelectionData(Selection
* inSelection
,
79 nsIContent
* inRealTargetNode
,
80 nsIContent
** outImageOrLinkNode
,
81 bool* outDragSelectedText
);
82 static already_AddRefed
<nsIContent
> FindParentLinkNode(nsIContent
* inNode
);
83 [[nodiscard
]] static nsresult
GetAnchorURL(nsIContent
* inNode
,
85 static void GetNodeString(nsIContent
* inNode
, nsAString
& outNodeString
);
86 static void CreateLinkText(const nsAString
& inURL
, const nsAString
& inText
,
87 nsAString
& outLinkText
);
89 nsCOMPtr
<nsPIDOMWindowOuter
> mWindow
;
90 nsCOMPtr
<nsIContent
> mTarget
;
91 nsCOMPtr
<nsIContent
> mSelectionTargetNode
;
92 bool mIsAltKeyPressed
;
95 nsString mImageSourceString
;
96 nsString mImageDestFileName
;
97 #if defined(XP_MACOSX)
98 nsString mImageRequestMime
;
100 nsString mTitleString
;
101 // will be filled automatically if you fill urlstring
102 nsString mHtmlString
;
103 nsString mContextString
;
104 nsString mInfoString
;
107 nsCOMPtr
<imgIContainer
> mImage
;
110 nsresult
nsContentAreaDragDrop::GetDragData(
111 nsPIDOMWindowOuter
* aWindow
, nsIContent
* aTarget
,
112 nsIContent
* aSelectionTargetNode
, bool aIsAltKeyPressed
,
113 DataTransfer
* aDataTransfer
, bool* aCanDrag
, Selection
** aSelection
,
114 nsIContent
** aDragNode
, nsIPrincipal
** aPrincipal
,
115 nsIContentSecurityPolicy
** aCsp
,
116 nsICookieJarSettings
** aCookieJarSettings
) {
117 NS_ENSURE_TRUE(aSelectionTargetNode
, NS_ERROR_INVALID_ARG
);
121 DragDataProducer
provider(aWindow
, aTarget
, aSelectionTargetNode
,
123 return provider
.Produce(aDataTransfer
, aCanDrag
, aSelection
, aDragNode
,
124 aPrincipal
, aCsp
, aCookieJarSettings
);
127 NS_IMPL_ISUPPORTS(nsContentAreaDragDropDataProvider
, nsIFlavorDataProvider
)
130 // used on platforms where it's possible to drag items (e.g. images)
131 // into the file system
132 nsresult
nsContentAreaDragDropDataProvider::SaveURIToFile(
133 nsIURI
* inSourceURI
, nsIPrincipal
* inTriggeringPrincipal
,
134 nsICookieJarSettings
* inCookieJarSettings
, nsIFile
* inDestFile
,
135 nsContentPolicyType inContentPolicyType
, bool isPrivate
) {
136 nsCOMPtr
<nsIURL
> sourceURL
= do_QueryInterface(inSourceURI
);
138 return NS_ERROR_NO_INTERFACE
;
141 nsresult rv
= inDestFile
->CreateUnique(nsIFile::NORMAL_FILE_TYPE
, 0600);
142 NS_ENSURE_SUCCESS(rv
, rv
);
144 // we rely on the fact that the WPB is refcounted by the channel etc,
145 // so we don't keep a ref to it. It will die when finished.
146 nsCOMPtr
<nsIWebBrowserPersist
> persist
= do_CreateInstance(
147 "@mozilla.org/embedding/browser/nsWebBrowserPersist;1", &rv
);
148 NS_ENSURE_SUCCESS(rv
, rv
);
150 persist
->SetPersistFlags(
151 nsIWebBrowserPersist::PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION
);
153 // referrer policy can be anything since the referrer is nullptr
154 return persist
->SavePrivacyAwareURI(
155 inSourceURI
, inTriggeringPrincipal
, 0, nullptr, inCookieJarSettings
,
156 nullptr, nullptr, inDestFile
, inContentPolicyType
, isPrivate
);
160 * Check if the provided filename extension is valid for the MIME type and
161 * return the MIME type's primary extension.
163 * @param aExtension [in] the extension to check
164 * @param aMimeType [in] the MIME type to check the extension with
165 * @param aIsValidExtension [out] true if |aExtension| is valid for
167 * @param aPrimaryExtension [out] the primary extension for the MIME type
168 * to potentially be used as a replacement
171 nsresult
CheckAndGetExtensionForMime(const nsCString
& aExtension
,
172 const nsCString
& aMimeType
,
173 bool* aIsValidExtension
,
174 nsACString
* aPrimaryExtension
) {
177 nsCOMPtr
<nsIMIMEService
> mimeService
= do_GetService("@mozilla.org/mime;1");
178 if (NS_WARN_IF(!mimeService
)) {
179 return NS_ERROR_FAILURE
;
182 nsCOMPtr
<nsIMIMEInfo
> mimeInfo
;
183 rv
= mimeService
->GetFromTypeAndExtension(aMimeType
, ""_ns
,
184 getter_AddRefs(mimeInfo
));
185 NS_ENSURE_SUCCESS(rv
, rv
);
187 mimeInfo
->GetPrimaryExtension(*aPrimaryExtension
);
189 if (aExtension
.IsEmpty()) {
190 *aIsValidExtension
= false;
194 rv
= mimeInfo
->ExtensionExists(aExtension
, aIsValidExtension
);
195 NS_ENSURE_SUCCESS(rv
, rv
);
200 // This is our nsIFlavorDataProvider callback. There are several
201 // assumptions here that make this work:
203 // 1. Someone put a kFilePromiseURLMime flavor into the transferable
204 // with the source URI of the file to save (as a string). We did
205 // that in AddStringsToDataTransfer.
207 // 2. Someone put a kFilePromiseDirectoryMime flavor into the
208 // transferable with an nsIFile for the directory we are to
209 // save in. That has to be done by platform-specific code (in
210 // widget), which gets the destination directory from
211 // OS-specific drag information.
214 nsContentAreaDragDropDataProvider::GetFlavorData(nsITransferable
* aTransferable
,
216 nsISupports
** aData
) {
217 NS_ENSURE_ARG_POINTER(aData
);
220 nsresult rv
= NS_ERROR_NOT_IMPLEMENTED
;
222 if (strcmp(aFlavor
, kFilePromiseMime
) == 0) {
223 // get the URI from the kFilePromiseURLMime flavor
224 NS_ENSURE_ARG(aTransferable
);
225 nsCOMPtr
<nsISupports
> tmp
;
226 rv
= aTransferable
->GetTransferData(kFilePromiseURLMime
,
227 getter_AddRefs(tmp
));
228 NS_ENSURE_SUCCESS(rv
, rv
);
229 nsCOMPtr
<nsISupportsString
> supportsString
= do_QueryInterface(tmp
);
230 if (!supportsString
) return NS_ERROR_FAILURE
;
232 nsAutoString sourceURLString
;
233 supportsString
->GetData(sourceURLString
);
234 if (sourceURLString
.IsEmpty()) return NS_ERROR_FAILURE
;
236 nsCOMPtr
<nsIURI
> sourceURI
;
237 rv
= NS_NewURI(getter_AddRefs(sourceURI
), sourceURLString
);
238 NS_ENSURE_SUCCESS(rv
, rv
);
240 rv
= aTransferable
->GetTransferData(kFilePromiseDestFilename
,
241 getter_AddRefs(tmp
));
242 NS_ENSURE_SUCCESS(rv
, rv
);
243 supportsString
= do_QueryInterface(tmp
);
244 if (!supportsString
) return NS_ERROR_FAILURE
;
246 nsAutoString targetFilename
;
247 supportsString
->GetData(targetFilename
);
248 if (targetFilename
.IsEmpty()) return NS_ERROR_FAILURE
;
250 #if defined(XP_MACOSX)
251 // Use the image request's MIME type to ensure the filename's
252 // extension is compatible with the OS's handler for this type.
253 // If it isn't, or is missing, replace the extension with the
254 // primary extension. On Mac, do this in the parent process
255 // because sandboxing blocks access to MIME-handler info from
256 // content processes.
257 if (XRE_IsParentProcess()) {
258 rv
= aTransferable
->GetTransferData(kImageRequestMime
,
259 getter_AddRefs(tmp
));
260 NS_ENSURE_SUCCESS(rv
, rv
);
261 supportsString
= do_QueryInterface(tmp
);
262 if (!supportsString
) return NS_ERROR_FAILURE
;
264 nsAutoString imageRequestMime
;
265 supportsString
->GetData(imageRequestMime
);
267 // If we have a MIME type, check the extension is compatible
268 if (!imageRequestMime
.IsEmpty()) {
269 // Build a URL to get the filename extension
270 nsCOMPtr
<nsIURL
> imageURL
= do_QueryInterface(sourceURI
, &rv
);
271 NS_ENSURE_SUCCESS(rv
, rv
);
273 nsAutoCString extension
;
274 rv
= imageURL
->GetFileExtension(extension
);
275 NS_ENSURE_SUCCESS(rv
, rv
);
277 NS_ConvertUTF16toUTF8
mimeCString(imageRequestMime
);
278 bool isValidExtension
;
279 nsAutoCString primaryExtension
;
280 rv
= CheckAndGetExtensionForMime(extension
, mimeCString
,
281 &isValidExtension
, &primaryExtension
);
282 NS_ENSURE_SUCCESS(rv
, rv
);
284 if (!isValidExtension
&& !primaryExtension
.IsEmpty()) {
285 // The filename extension is missing or incompatible
286 // with the MIME type, replace it with the primary
288 nsAutoCString newFileName
;
289 rv
= imageURL
->GetFileBaseName(newFileName
);
290 NS_ENSURE_SUCCESS(rv
, rv
);
291 newFileName
.Append(".");
292 newFileName
.Append(primaryExtension
);
293 CopyUTF8toUTF16(newFileName
, targetFilename
);
297 // make the filename safe for the filesystem
298 targetFilename
.ReplaceChar(FILE_PATH_SEPARATOR FILE_ILLEGAL_CHARACTERS
,
300 #endif /* defined(XP_MACOSX) */
302 // get the target directory from the kFilePromiseDirectoryMime
304 nsCOMPtr
<nsISupports
> dirPrimitive
;
305 rv
= aTransferable
->GetTransferData(kFilePromiseDirectoryMime
,
306 getter_AddRefs(dirPrimitive
));
307 NS_ENSURE_SUCCESS(rv
, rv
);
308 nsCOMPtr
<nsIFile
> destDirectory
= do_QueryInterface(dirPrimitive
);
309 if (!destDirectory
) return NS_ERROR_FAILURE
;
311 nsCOMPtr
<nsIFile
> file
;
312 rv
= destDirectory
->Clone(getter_AddRefs(file
));
313 NS_ENSURE_SUCCESS(rv
, rv
);
315 file
->Append(targetFilename
);
317 bool isPrivate
= aTransferable
->GetIsPrivateData();
319 nsCOMPtr
<nsIPrincipal
> principal
= aTransferable
->GetRequestingPrincipal();
320 nsContentPolicyType contentPolicyType
=
321 aTransferable
->GetContentPolicyType();
322 nsCOMPtr
<nsICookieJarSettings
> cookieJarSettings
=
323 aTransferable
->GetCookieJarSettings();
324 rv
= SaveURIToFile(sourceURI
, principal
, cookieJarSettings
, file
,
325 contentPolicyType
, isPrivate
);
326 // send back an nsIFile
327 if (NS_SUCCEEDED(rv
)) {
328 CallQueryInterface(file
, aData
);
335 DragDataProducer::DragDataProducer(nsPIDOMWindowOuter
* aWindow
,
337 nsIContent
* aSelectionTargetNode
,
338 bool aIsAltKeyPressed
)
341 mSelectionTargetNode(aSelectionTargetNode
),
342 mIsAltKeyPressed(aIsAltKeyPressed
),
346 // FindParentLinkNode
348 // Finds the parent with the given link tag starting at |aContent|. If
349 // it gets up to the root without finding it, we stop looking and
352 already_AddRefed
<nsIContent
> DragDataProducer::FindParentLinkNode(
353 nsIContent
* aContent
) {
354 for (nsIContent
* content
= aContent
; content
;
355 content
= content
->GetFlattenedTreeParent()) {
356 if (nsContentUtils::IsDraggableLink(content
)) {
357 return do_AddRef(content
);
366 nsresult
DragDataProducer::GetAnchorURL(nsIContent
* inNode
, nsAString
& outURL
) {
367 nsCOMPtr
<nsIURI
> linkURI
;
368 if (!inNode
|| !inNode
->IsLink(getter_AddRefs(linkURI
))) {
375 nsresult rv
= linkURI
->GetSpec(spec
);
376 NS_ENSURE_SUCCESS(rv
, rv
);
377 CopyUTF8toUTF16(spec
, outURL
);
384 // Creates the html for an anchor in the form
385 // <a href="inURL">inText</a>
387 void DragDataProducer::CreateLinkText(const nsAString
& inURL
,
388 const nsAString
& inText
,
389 nsAString
& outLinkText
) {
390 // use a temp var in case |inText| is the same string as
391 // |outLinkText| to avoid overwriting it while building up the
393 nsAutoString
linkText(u
"<a href=\""_ns
+ inURL
+ u
"\">"_ns
+ inText
+
396 outLinkText
= linkText
;
402 // Gets the text associated with a node
404 void DragDataProducer::GetNodeString(nsIContent
* inNode
,
405 nsAString
& outNodeString
) {
406 nsCOMPtr
<nsINode
> node
= inNode
;
408 outNodeString
.Truncate();
410 // use a range to get the text-equivalent of the node
411 nsCOMPtr
<Document
> doc
= node
->OwnerDoc();
412 RefPtr
<nsRange
> range
= doc
->CreateRange(IgnoreErrors());
414 range
->SelectNode(*node
, IgnoreErrors());
415 range
->ToString(outNodeString
, IgnoreErrors());
419 nsresult
DragDataProducer::GetImageData(imgIContainer
* aImage
,
420 imgIRequest
* aRequest
) {
421 nsCOMPtr
<nsIURI
> imgUri
;
422 aRequest
->GetURI(getter_AddRefs(imgUri
));
424 nsCOMPtr
<nsIURL
> imgUrl(do_QueryInterface(imgUri
));
427 nsresult rv
= imgUrl
->GetSpec(spec
);
428 NS_ENSURE_SUCCESS(rv
, rv
);
430 // pass out the image source string
431 CopyUTF8toUTF16(spec
, mImageSourceString
);
434 aRequest
->GetMimeType(getter_Copies(mimeType
));
436 #if defined(XP_MACOSX)
437 // Save the MIME type so we can make sure the extension
438 // is compatible (and replace it if it isn't) when the
439 // image is dropped. On Mac, we need to get the OS MIME
440 // handler information in the parent due to sandboxing.
441 CopyUTF8toUTF16(mimeType
, mImageRequestMime
);
443 nsCOMPtr
<nsIMIMEService
> mimeService
= do_GetService("@mozilla.org/mime;1");
444 if (NS_WARN_IF(!mimeService
)) {
445 return NS_ERROR_FAILURE
;
448 nsCOMPtr
<nsIMIMEInfo
> mimeInfo
;
449 mimeService
->GetFromTypeAndExtension(mimeType
, ""_ns
,
450 getter_AddRefs(mimeInfo
));
452 nsAutoCString extension
;
453 imgUrl
->GetFileExtension(extension
);
456 if (extension
.IsEmpty() ||
457 NS_FAILED(mimeInfo
->ExtensionExists(extension
, &validExtension
)) ||
459 // Fix the file extension in the URL
460 nsAutoCString primaryExtension
;
461 mimeInfo
->GetPrimaryExtension(primaryExtension
);
462 if (!primaryExtension
.IsEmpty()) {
463 rv
= NS_MutateURI(imgUrl
)
464 .Apply(&nsIURLMutator::SetFileExtension
, primaryExtension
,
467 NS_ENSURE_SUCCESS(rv
, rv
);
471 #endif /* defined(XP_MACOSX) */
473 nsAutoCString fileName
;
474 imgUrl
->GetFileName(fileName
);
476 NS_UnescapeURL(fileName
);
478 #if !defined(XP_MACOSX)
479 // make the filename safe for the filesystem
480 fileName
.ReplaceChar(FILE_PATH_SEPARATOR FILE_ILLEGAL_CHARACTERS
, '-');
483 CopyUTF8toUTF16(fileName
, mImageDestFileName
);
485 // and the image object
492 nsresult
DragDataProducer::Produce(DataTransfer
* aDataTransfer
, bool* aCanDrag
,
493 Selection
** aSelection
,
494 nsIContent
** aDragNode
,
495 nsIPrincipal
** aPrincipal
,
496 nsIContentSecurityPolicy
** aCsp
,
497 nsICookieJarSettings
** aCookieJarSettings
) {
498 MOZ_ASSERT(aCanDrag
&& aSelection
&& aDataTransfer
&& aDragNode
,
499 "null pointer passed to Produce");
500 NS_ASSERTION(mWindow
, "window not set");
501 NS_ASSERTION(mSelectionTargetNode
,
502 "selection target node should have been set");
504 *aDragNode
= nullptr;
507 nsIContent
* dragNode
= nullptr;
508 *aSelection
= nullptr;
510 // Find the selection to see what we could be dragging and if what we're
511 // dragging is in what is selected. If this is an editable textbox, use
512 // the textbox's selection, otherwise use the window's selection.
513 RefPtr
<Selection
> selection
;
514 nsIContent
* editingElement
= mSelectionTargetNode
->IsEditable()
515 ? mSelectionTargetNode
->GetEditingHost()
517 RefPtr
<TextControlElement
> textControlElement
=
518 TextControlElement::GetTextControlElementFromEditingHost(editingElement
);
519 if (textControlElement
) {
520 nsISelectionController
* selcon
=
521 textControlElement
->GetSelectionController();
524 selcon
->GetSelection(nsISelectionController::SELECTION_NORMAL
);
527 if (!selection
) return NS_OK
;
529 selection
= mWindow
->GetSelection();
530 if (!selection
) return NS_OK
;
532 // Check if the node is inside a form control. Don't set aCanDrag to false
533 // however, as we still want to allow the drag.
534 nsCOMPtr
<nsIContent
> findFormNode
= mSelectionTargetNode
;
535 nsIContent
* findFormParent
= findFormNode
->GetParent();
536 while (findFormParent
) {
537 nsCOMPtr
<nsIFormControl
> form(do_QueryInterface(findFormParent
));
538 if (form
&& !form
->AllowDraggableChildren()) {
541 findFormParent
= findFormParent
->GetParent();
545 // if set, serialize the content under this node
546 nsCOMPtr
<nsIContent
> nodeToSerialize
;
548 BrowsingContext
* bc
= mWindow
->GetBrowsingContext();
549 const bool isChromeShell
= bc
&& bc
->IsChrome();
551 // In chrome shells, only allow dragging inside editable areas.
552 if (isChromeShell
&& !editingElement
) {
553 // This path should already be filtered out in
554 // EventStateManager::DetermineDragTargetAndDefaultData.
555 MOZ_ASSERT_UNREACHABLE("Shouldn't be generating drag data for chrome");
559 if (isChromeShell
&& textControlElement
) {
560 // Only use the selection if the target node is in the selection.
561 if (!selection
->ContainsNode(*mSelectionTargetNode
, false, IgnoreErrors()))
564 selection
.swap(*aSelection
);
566 // In content shells, a number of checks are made below to determine
567 // whether an image or a link is being dragged. If so, add additional
568 // data to the data transfer. This is also done for chrome shells, but
569 // only when in a non-textbox editor.
571 bool haveSelectedContent
= false;
573 // possible parent link node
574 nsCOMPtr
<nsIContent
> parentLink
;
575 nsCOMPtr
<nsIContent
> draggedNode
;
578 // only drag form elements by using the alt key,
579 // otherwise buttons and select widgets are hard to use
581 // Note that while <object> elements implement nsIFormControl, we should
582 // really allow dragging them if they happen to be images.
583 nsCOMPtr
<nsIFormControl
> form(do_QueryInterface(mTarget
));
584 if (form
&& !mIsAltKeyPressed
&&
585 form
->ControlType() != FormControlType::Object
) {
590 draggedNode
= mTarget
;
593 nsCOMPtr
<nsIImageLoadingContent
> image
;
595 nsCOMPtr
<nsIContent
> selectedImageOrLinkNode
;
596 GetDraggableSelectionData(selection
, mSelectionTargetNode
,
597 getter_AddRefs(selectedImageOrLinkNode
),
598 &haveSelectedContent
);
600 // either plain text or anchor text is selected
601 if (haveSelectedContent
) {
602 selection
.swap(*aSelection
);
603 } else if (selectedImageOrLinkNode
) {
604 // an image is selected
605 image
= do_QueryInterface(selectedImageOrLinkNode
);
607 // nothing is selected -
609 // look for draggable elements under the mouse
611 // if the alt key is down, don't start a drag if we're in an
612 // anchor because we want to do selection.
613 parentLink
= FindParentLinkNode(draggedNode
);
614 if (parentLink
&& mIsAltKeyPressed
) {
618 image
= do_QueryInterface(draggedNode
);
622 // set for linked images, and links
623 nsCOMPtr
<nsIContent
> linkNode
;
625 RefPtr
<HTMLAreaElement
> areaElem
=
626 HTMLAreaElement::FromNodeOrNull(draggedNode
);
628 // use the alt text (or, if missing, the href) as the title
629 areaElem
->GetAttr(nsGkAtoms::alt
, mTitleString
);
630 if (mTitleString
.IsEmpty()) {
631 // this can be a relative link
632 areaElem
->GetAttr(nsGkAtoms::href
, mTitleString
);
635 // we'll generate HTML like <a href="absurl">alt text</a>
638 // gives an absolute link
639 nsresult rv
= GetAnchorURL(draggedNode
, mUrlString
);
640 NS_ENSURE_SUCCESS(rv
, rv
);
642 mHtmlString
.AssignLiteral("<a href=\"");
643 mHtmlString
.Append(mUrlString
);
644 mHtmlString
.AppendLiteral("\">");
645 mHtmlString
.Append(mTitleString
);
646 mHtmlString
.AppendLiteral("</a>");
648 dragNode
= draggedNode
;
651 // grab the href as the url, use alt text as the title of the
652 // area if it's there. the drag data is the image tag and src
654 nsCOMPtr
<nsIURI
> imageURI
;
655 image
->GetCurrentURI(getter_AddRefs(imageURI
));
658 rv
= imageURI
->GetSpec(spec
);
659 NS_ENSURE_SUCCESS(rv
, rv
);
660 CopyUTF8toUTF16(spec
, mUrlString
);
663 nsCOMPtr
<Element
> imageElement(do_QueryInterface(image
));
664 // XXXbz Shouldn't we use the "title" attr for title? Using
665 // "alt" seems very wrong....
666 // XXXbz Also, what if this is an nsIImageLoadingContent
667 // that's not an <html:img>?
669 imageElement
->GetAttr(nsGkAtoms::alt
, mTitleString
);
672 if (mTitleString
.IsEmpty()) {
673 mTitleString
= mUrlString
;
676 nsCOMPtr
<imgIRequest
> imgRequest
;
678 // grab the image data, and its request.
679 nsCOMPtr
<imgIContainer
> img
= nsContentUtils::GetImageFromContent(
680 image
, getter_AddRefs(imgRequest
));
682 rv
= GetImageData(img
, imgRequest
);
683 NS_ENSURE_SUCCESS(rv
, rv
);
687 // If we are dragging around an image in an anchor, then we
688 // are dragging the entire anchor
689 linkNode
= parentLink
;
690 nodeToSerialize
= linkNode
;
692 nodeToSerialize
= draggedNode
;
694 dragNode
= nodeToSerialize
;
695 } else if (draggedNode
&& draggedNode
->IsHTMLElement(nsGkAtoms::a
)) {
696 // set linkNode. The code below will handle this
697 linkNode
= draggedNode
; // XXX test this
698 GetNodeString(draggedNode
, mTitleString
);
699 } else if (parentLink
) {
700 // parentLink will always be null if there's selected content
701 linkNode
= parentLink
;
702 nodeToSerialize
= linkNode
;
703 } else if (!haveSelectedContent
) {
710 rv
= GetAnchorURL(linkNode
, mUrlString
);
711 NS_ENSURE_SUCCESS(rv
, rv
);
717 if (nodeToSerialize
|| *aSelection
) {
718 mHtmlString
.Truncate();
719 mContextString
.Truncate();
720 mInfoString
.Truncate();
721 mTitleString
.Truncate();
723 nsCOMPtr
<Document
> doc
= mWindow
->GetDoc();
724 NS_ENSURE_TRUE(doc
, NS_ERROR_FAILURE
);
726 nsCOMPtr
<nsIContentSecurityPolicy
> csp
= doc
->GetCsp();
728 NS_IF_ADDREF(*aCsp
= csp
);
731 nsCOMPtr
<nsICookieJarSettings
> cookieJarSettings
= doc
->CookieJarSettings();
732 if (cookieJarSettings
) {
733 NS_IF_ADDREF(*aCookieJarSettings
= cookieJarSettings
);
736 // if we have selected text, use it in preference to the node
737 nsCOMPtr
<nsITransferable
> transferable
;
739 rv
= nsCopySupport::GetTransferableForSelection(
740 *aSelection
, doc
, getter_AddRefs(transferable
));
742 rv
= nsCopySupport::GetTransferableForNode(nodeToSerialize
, doc
,
743 getter_AddRefs(transferable
));
745 NS_ENSURE_SUCCESS(rv
, rv
);
747 nsCOMPtr
<nsISupports
> supports
;
748 nsCOMPtr
<nsISupportsString
> data
;
749 rv
= transferable
->GetTransferData(kHTMLMime
, getter_AddRefs(supports
));
750 data
= do_QueryInterface(supports
);
751 if (NS_SUCCEEDED(rv
)) {
752 data
->GetData(mHtmlString
);
754 rv
= transferable
->GetTransferData(kHTMLContext
, getter_AddRefs(supports
));
755 data
= do_QueryInterface(supports
);
756 if (NS_SUCCEEDED(rv
)) {
757 data
->GetData(mContextString
);
759 rv
= transferable
->GetTransferData(kHTMLInfo
, getter_AddRefs(supports
));
760 data
= do_QueryInterface(supports
);
761 if (NS_SUCCEEDED(rv
)) {
762 data
->GetData(mInfoString
);
764 rv
= transferable
->GetTransferData(kUnicodeMime
, getter_AddRefs(supports
));
765 data
= do_QueryInterface(supports
);
766 NS_ENSURE_SUCCESS(rv
, rv
); // require plain text at a minimum
767 data
->GetData(mTitleString
);
770 // default text value is the URL
771 if (mTitleString
.IsEmpty()) {
772 mTitleString
= mUrlString
;
775 // if we haven't constructed a html version, make one now
776 if (mHtmlString
.IsEmpty() && !mUrlString
.IsEmpty())
777 CreateLinkText(mUrlString
, mTitleString
, mHtmlString
);
779 // if there is no drag node, which will be the case for a selection, just
780 // use the selection target node.
781 rv
= AddStringsToDataTransfer(
782 dragNode
? dragNode
: mSelectionTargetNode
.get(), aDataTransfer
);
783 NS_ENSURE_SUCCESS(rv
, rv
);
785 NS_IF_ADDREF(*aDragNode
= dragNode
);
789 void DragDataProducer::AddString(DataTransfer
* aDataTransfer
,
790 const nsAString
& aFlavor
,
791 const nsAString
& aData
,
792 nsIPrincipal
* aPrincipal
, bool aHidden
) {
793 RefPtr
<nsVariantCC
> variant
= new nsVariantCC();
794 variant
->SetAsAString(aData
);
795 aDataTransfer
->SetDataWithPrincipal(aFlavor
, variant
, 0, aPrincipal
, aHidden
);
798 nsresult
DragDataProducer::AddStringsToDataTransfer(
799 nsIContent
* aDragNode
, DataTransfer
* aDataTransfer
) {
800 NS_ASSERTION(aDragNode
, "adding strings for null node");
802 // set all of the data to have the principal of the node where the data came
804 nsIPrincipal
* principal
= aDragNode
->NodePrincipal();
806 // add a special flavor if we're an anchor to indicate that we have
807 // a URL in the drag data
808 if (!mUrlString
.IsEmpty() && mIsAnchor
) {
809 nsAutoString
dragData(mUrlString
);
810 dragData
.Append('\n');
811 // Remove leading and trailing newlines in the title and replace them with
812 // space in remaining positions - they confuse PlacesUtils::unwrapNodes
813 // that expects url\ntitle formatted data for x-moz-url.
814 nsAutoString
title(mTitleString
);
816 title
.ReplaceChar("\r\n", ' ');
819 AddString(aDataTransfer
, NS_LITERAL_STRING_FROM_CSTRING(kURLMime
), dragData
,
821 AddString(aDataTransfer
, NS_LITERAL_STRING_FROM_CSTRING(kURLDataMime
),
822 mUrlString
, principal
);
823 AddString(aDataTransfer
,
824 NS_LITERAL_STRING_FROM_CSTRING(kURLDescriptionMime
), mTitleString
,
826 AddString(aDataTransfer
, u
"text/uri-list"_ns
, mUrlString
, principal
);
829 // add a special flavor for the html context data
830 if (!mContextString
.IsEmpty())
831 AddString(aDataTransfer
, NS_LITERAL_STRING_FROM_CSTRING(kHTMLContext
),
832 mContextString
, principal
);
834 // add a special flavor if we have html info data
835 if (!mInfoString
.IsEmpty())
836 AddString(aDataTransfer
, NS_LITERAL_STRING_FROM_CSTRING(kHTMLInfo
),
837 mInfoString
, principal
);
840 if (!mHtmlString
.IsEmpty())
841 AddString(aDataTransfer
, NS_LITERAL_STRING_FROM_CSTRING(kHTMLMime
),
842 mHtmlString
, principal
);
844 // add the plain text. we use the url for text/plain data if an anchor is
845 // being dragged, rather than the title text of the link or the alt text for
847 AddString(aDataTransfer
, NS_LITERAL_STRING_FROM_CSTRING(kTextMime
),
848 mIsAnchor
? mUrlString
: mTitleString
, principal
);
850 // add image data, if present. For now, all we're going to do with
851 // this is turn it into a native data flavor, so indicate that with
852 // a new flavor so as not to confuse anyone who is really registered
853 // for image/gif or image/jpg.
855 RefPtr
<nsVariantCC
> variant
= new nsVariantCC();
856 variant
->SetAsISupports(mImage
);
857 aDataTransfer
->SetDataWithPrincipal(
858 NS_LITERAL_STRING_FROM_CSTRING(kNativeImageMime
), variant
, 0,
861 // assume the image comes from a file, and add a file promise. We
862 // register ourselves as a nsIFlavorDataProvider, and will use the
863 // GetFlavorData callback to save the image to disk.
865 nsCOMPtr
<nsIFlavorDataProvider
> dataProvider
=
866 new nsContentAreaDragDropDataProvider();
868 RefPtr
<nsVariantCC
> variant
= new nsVariantCC();
869 variant
->SetAsISupports(dataProvider
);
870 aDataTransfer
->SetDataWithPrincipal(
871 NS_LITERAL_STRING_FROM_CSTRING(kFilePromiseMime
), variant
, 0,
875 AddString(aDataTransfer
,
876 NS_LITERAL_STRING_FROM_CSTRING(kFilePromiseURLMime
),
877 mImageSourceString
, principal
);
878 AddString(aDataTransfer
,
879 NS_LITERAL_STRING_FROM_CSTRING(kFilePromiseDestFilename
),
880 mImageDestFileName
, principal
);
881 #if defined(XP_MACOSX)
882 AddString(aDataTransfer
, NS_LITERAL_STRING_FROM_CSTRING(kImageRequestMime
),
883 mImageRequestMime
, principal
, /* aHidden= */ true);
886 // if not an anchor, add the image url
888 AddString(aDataTransfer
, NS_LITERAL_STRING_FROM_CSTRING(kURLDataMime
),
889 mUrlString
, principal
);
890 AddString(aDataTransfer
, u
"text/uri-list"_ns
, mUrlString
, principal
);
897 // note that this can return NS_OK, but a null out param (by design)
899 nsresult
DragDataProducer::GetDraggableSelectionData(
900 Selection
* inSelection
, nsIContent
* inRealTargetNode
,
901 nsIContent
** outImageOrLinkNode
, bool* outDragSelectedText
) {
902 NS_ENSURE_ARG(inSelection
);
903 NS_ENSURE_ARG(inRealTargetNode
);
904 NS_ENSURE_ARG_POINTER(outImageOrLinkNode
);
906 *outImageOrLinkNode
= nullptr;
907 *outDragSelectedText
= false;
909 if (!inSelection
->IsCollapsed()) {
910 if (inSelection
->ContainsNode(*inRealTargetNode
, false, IgnoreErrors())) {
911 // track down the anchor node, if any, for the url
912 nsINode
* selectionStart
= inSelection
->GetAnchorNode();
913 nsINode
* selectionEnd
= inSelection
->GetFocusNode();
915 // look for a selection around a single node, like an image.
916 // in this case, drag the image, rather than a serialization of the HTML
917 // XXX generalize this to other draggable element types?
918 if (selectionStart
== selectionEnd
) {
919 nsCOMPtr
<nsIContent
> selStartContent
=
920 nsIContent::FromNodeOrNull(selectionStart
);
921 if (selStartContent
&& selStartContent
->HasChildNodes()) {
922 // see if just one node is selected
923 uint32_t anchorOffset
= inSelection
->AnchorOffset();
924 uint32_t focusOffset
= inSelection
->FocusOffset();
925 if (anchorOffset
== focusOffset
+ 1 ||
926 focusOffset
== anchorOffset
+ 1) {
927 uint32_t childOffset
= std::min(anchorOffset
, focusOffset
);
928 nsIContent
* childContent
=
929 selStartContent
->GetChildAt_Deprecated(childOffset
);
930 // if we find an image, we'll fall into the node-dragging code,
931 // rather the the selection-dragging code
932 if (nsContentUtils::IsDraggableImage(childContent
)) {
933 NS_ADDREF(*outImageOrLinkNode
= childContent
);
940 // indicate that a link or text is selected
941 *outDragSelectedText
= true;