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 "nsIPrincipal.h"
38 #include "nsIWebBrowserPersist.h"
40 #include "nsContentUtils.h"
41 #include "nsIMIMEService.h"
42 #include "imgIContainer.h"
43 #include "imgIRequest.h"
44 #include "mozilla/dom/DataTransfer.h"
45 #include "nsIMIMEInfo.h"
47 #include "BrowserParent.h"
48 #include "mozilla/TextControlElement.h"
49 #include "mozilla/dom/BrowsingContext.h"
50 #include "mozilla/dom/Element.h"
51 #include "mozilla/dom/HTMLAreaElement.h"
52 #include "mozilla/dom/HTMLAnchorElement.h"
53 #include "mozilla/dom/Selection.h"
54 #include "nsVariant.h"
55 #include "nsQueryObject.h"
57 using namespace mozilla
;
58 using namespace mozilla::dom
;
59 using mozilla::IgnoreErrors
;
61 class MOZ_STACK_CLASS DragDataProducer
{
63 DragDataProducer(nsPIDOMWindowOuter
* aWindow
, nsIContent
* aTarget
,
64 nsIContent
* aSelectionTargetNode
, bool aIsAltKeyPressed
);
65 nsresult
Produce(DataTransfer
* aDataTransfer
, bool* aCanDrag
,
66 Selection
** aSelection
, nsIContent
** aDragNode
,
67 nsIPrincipal
** aPrincipal
, nsIContentSecurityPolicy
** aCsp
);
70 void AddString(DataTransfer
* aDataTransfer
, const nsAString
& aFlavor
,
71 const nsAString
& aData
, nsIPrincipal
* aPrincipal
,
72 bool aHidden
= false);
73 nsresult
AddStringsToDataTransfer(nsIContent
* aDragNode
,
74 DataTransfer
* aDataTransfer
);
75 nsresult
GetImageData(imgIContainer
* aImage
, imgIRequest
* aRequest
);
76 static nsresult
GetDraggableSelectionData(Selection
* inSelection
,
77 nsIContent
* inRealTargetNode
,
78 nsIContent
** outImageOrLinkNode
,
79 bool* outDragSelectedText
);
80 static already_AddRefed
<nsIContent
> FindParentLinkNode(nsIContent
* inNode
);
81 static MOZ_MUST_USE nsresult
GetAnchorURL(nsIContent
* inNode
,
83 static void GetNodeString(nsIContent
* inNode
, nsAString
& outNodeString
);
84 static void CreateLinkText(const nsAString
& inURL
, const nsAString
& inText
,
85 nsAString
& outLinkText
);
87 nsCOMPtr
<nsPIDOMWindowOuter
> mWindow
;
88 nsCOMPtr
<nsIContent
> mTarget
;
89 nsCOMPtr
<nsIContent
> mSelectionTargetNode
;
90 bool mIsAltKeyPressed
;
93 nsString mImageSourceString
;
94 nsString mImageDestFileName
;
95 #if defined(XP_MACOSX)
96 nsString mImageRequestMime
;
98 nsString mTitleString
;
99 // will be filled automatically if you fill urlstring
100 nsString mHtmlString
;
101 nsString mContextString
;
102 nsString mInfoString
;
105 nsCOMPtr
<imgIContainer
> mImage
;
108 nsresult
nsContentAreaDragDrop::GetDragData(
109 nsPIDOMWindowOuter
* aWindow
, nsIContent
* aTarget
,
110 nsIContent
* aSelectionTargetNode
, bool aIsAltKeyPressed
,
111 DataTransfer
* aDataTransfer
, bool* aCanDrag
, Selection
** aSelection
,
112 nsIContent
** aDragNode
, nsIPrincipal
** aPrincipal
,
113 nsIContentSecurityPolicy
** aCsp
) {
114 NS_ENSURE_TRUE(aSelectionTargetNode
, NS_ERROR_INVALID_ARG
);
118 DragDataProducer
provider(aWindow
, aTarget
, aSelectionTargetNode
,
120 return provider
.Produce(aDataTransfer
, aCanDrag
, aSelection
, aDragNode
,
124 NS_IMPL_ISUPPORTS(nsContentAreaDragDropDataProvider
, nsIFlavorDataProvider
)
127 // used on platforms where it's possible to drag items (e.g. images)
128 // into the file system
129 nsresult
nsContentAreaDragDropDataProvider::SaveURIToFile(
130 nsIURI
* inSourceURI
, nsIPrincipal
* inTriggeringPrincipal
,
131 nsIFile
* inDestFile
, nsContentPolicyType inContentPolicyType
,
133 nsCOMPtr
<nsIURL
> sourceURL
= do_QueryInterface(inSourceURI
);
135 return NS_ERROR_NO_INTERFACE
;
138 nsresult rv
= inDestFile
->CreateUnique(nsIFile::NORMAL_FILE_TYPE
, 0600);
139 NS_ENSURE_SUCCESS(rv
, rv
);
141 // we rely on the fact that the WPB is refcounted by the channel etc,
142 // so we don't keep a ref to it. It will die when finished.
143 nsCOMPtr
<nsIWebBrowserPersist
> persist
= do_CreateInstance(
144 "@mozilla.org/embedding/browser/nsWebBrowserPersist;1", &rv
);
145 NS_ENSURE_SUCCESS(rv
, rv
);
147 persist
->SetPersistFlags(
148 nsIWebBrowserPersist::PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION
);
150 // referrer policy can be anything since the referrer is nullptr
151 return persist
->SavePrivacyAwareURI(inSourceURI
, inTriggeringPrincipal
, 0,
152 nullptr, nullptr, nullptr, inDestFile
,
153 inContentPolicyType
, isPrivate
);
157 * Check if the provided filename extension is valid for the MIME type and
158 * return the MIME type's primary extension.
160 * @param aExtension [in] the extension to check
161 * @param aMimeType [in] the MIME type to check the extension with
162 * @param aIsValidExtension [out] true if |aExtension| is valid for
164 * @param aPrimaryExtension [out] the primary extension for the MIME type
165 * to potentially be used as a replacement
168 nsresult
CheckAndGetExtensionForMime(const nsCString
& aExtension
,
169 const nsCString
& aMimeType
,
170 bool* aIsValidExtension
,
171 nsACString
* aPrimaryExtension
) {
174 nsCOMPtr
<nsIMIMEService
> mimeService
= do_GetService("@mozilla.org/mime;1");
175 if (NS_WARN_IF(!mimeService
)) {
176 return NS_ERROR_FAILURE
;
179 nsCOMPtr
<nsIMIMEInfo
> mimeInfo
;
180 rv
= mimeService
->GetFromTypeAndExtension(aMimeType
, ""_ns
,
181 getter_AddRefs(mimeInfo
));
182 NS_ENSURE_SUCCESS(rv
, rv
);
184 mimeInfo
->GetPrimaryExtension(*aPrimaryExtension
);
186 if (aExtension
.IsEmpty()) {
187 *aIsValidExtension
= false;
191 rv
= mimeInfo
->ExtensionExists(aExtension
, aIsValidExtension
);
192 NS_ENSURE_SUCCESS(rv
, rv
);
197 // This is our nsIFlavorDataProvider callback. There are several
198 // assumptions here that make this work:
200 // 1. Someone put a kFilePromiseURLMime flavor into the transferable
201 // with the source URI of the file to save (as a string). We did
202 // that in AddStringsToDataTransfer.
204 // 2. Someone put a kFilePromiseDirectoryMime flavor into the
205 // transferable with an nsIFile for the directory we are to
206 // save in. That has to be done by platform-specific code (in
207 // widget), which gets the destination directory from
208 // OS-specific drag information.
211 nsContentAreaDragDropDataProvider::GetFlavorData(nsITransferable
* aTransferable
,
213 nsISupports
** aData
) {
214 NS_ENSURE_ARG_POINTER(aData
);
217 nsresult rv
= NS_ERROR_NOT_IMPLEMENTED
;
219 if (strcmp(aFlavor
, kFilePromiseMime
) == 0) {
220 // get the URI from the kFilePromiseURLMime flavor
221 NS_ENSURE_ARG(aTransferable
);
222 nsCOMPtr
<nsISupports
> tmp
;
223 rv
= aTransferable
->GetTransferData(kFilePromiseURLMime
,
224 getter_AddRefs(tmp
));
225 NS_ENSURE_SUCCESS(rv
, rv
);
226 nsCOMPtr
<nsISupportsString
> supportsString
= do_QueryInterface(tmp
);
227 if (!supportsString
) return NS_ERROR_FAILURE
;
229 nsAutoString sourceURLString
;
230 supportsString
->GetData(sourceURLString
);
231 if (sourceURLString
.IsEmpty()) return NS_ERROR_FAILURE
;
233 nsCOMPtr
<nsIURI
> sourceURI
;
234 rv
= NS_NewURI(getter_AddRefs(sourceURI
), sourceURLString
);
235 NS_ENSURE_SUCCESS(rv
, rv
);
237 rv
= aTransferable
->GetTransferData(kFilePromiseDestFilename
,
238 getter_AddRefs(tmp
));
239 NS_ENSURE_SUCCESS(rv
, rv
);
240 supportsString
= do_QueryInterface(tmp
);
241 if (!supportsString
) return NS_ERROR_FAILURE
;
243 nsAutoString targetFilename
;
244 supportsString
->GetData(targetFilename
);
245 if (targetFilename
.IsEmpty()) return NS_ERROR_FAILURE
;
247 #if defined(XP_MACOSX)
248 // Use the image request's MIME type to ensure the filename's
249 // extension is compatible with the OS's handler for this type.
250 // If it isn't, or is missing, replace the extension with the
251 // primary extension. On Mac, do this in the parent process
252 // because sandboxing blocks access to MIME-handler info from
253 // content processes.
254 if (XRE_IsParentProcess()) {
255 rv
= aTransferable
->GetTransferData(kImageRequestMime
,
256 getter_AddRefs(tmp
));
257 NS_ENSURE_SUCCESS(rv
, rv
);
258 supportsString
= do_QueryInterface(tmp
);
259 if (!supportsString
) return NS_ERROR_FAILURE
;
261 nsAutoString imageRequestMime
;
262 supportsString
->GetData(imageRequestMime
);
264 // If we have a MIME type, check the extension is compatible
265 if (!imageRequestMime
.IsEmpty()) {
266 // Build a URL to get the filename extension
267 nsCOMPtr
<nsIURL
> imageURL
= do_QueryInterface(sourceURI
, &rv
);
268 NS_ENSURE_SUCCESS(rv
, rv
);
270 nsAutoCString extension
;
271 rv
= imageURL
->GetFileExtension(extension
);
272 NS_ENSURE_SUCCESS(rv
, rv
);
274 NS_ConvertUTF16toUTF8
mimeCString(imageRequestMime
);
275 bool isValidExtension
;
276 nsAutoCString primaryExtension
;
277 rv
= CheckAndGetExtensionForMime(extension
, mimeCString
,
278 &isValidExtension
, &primaryExtension
);
279 NS_ENSURE_SUCCESS(rv
, rv
);
281 if (!isValidExtension
&& !primaryExtension
.IsEmpty()) {
282 // The filename extension is missing or incompatible
283 // with the MIME type, replace it with the primary
285 nsAutoCString newFileName
;
286 rv
= imageURL
->GetFileBaseName(newFileName
);
287 NS_ENSURE_SUCCESS(rv
, rv
);
288 newFileName
.Append(".");
289 newFileName
.Append(primaryExtension
);
290 CopyUTF8toUTF16(newFileName
, targetFilename
);
294 // make the filename safe for the filesystem
295 targetFilename
.ReplaceChar(FILE_PATH_SEPARATOR FILE_ILLEGAL_CHARACTERS
,
297 #endif /* defined(XP_MACOSX) */
299 // get the target directory from the kFilePromiseDirectoryMime
301 nsCOMPtr
<nsISupports
> dirPrimitive
;
302 rv
= aTransferable
->GetTransferData(kFilePromiseDirectoryMime
,
303 getter_AddRefs(dirPrimitive
));
304 NS_ENSURE_SUCCESS(rv
, rv
);
305 nsCOMPtr
<nsIFile
> destDirectory
= do_QueryInterface(dirPrimitive
);
306 if (!destDirectory
) return NS_ERROR_FAILURE
;
308 nsCOMPtr
<nsIFile
> file
;
309 rv
= destDirectory
->Clone(getter_AddRefs(file
));
310 NS_ENSURE_SUCCESS(rv
, rv
);
312 file
->Append(targetFilename
);
314 bool isPrivate
= aTransferable
->GetIsPrivateData();
316 nsCOMPtr
<nsIPrincipal
> principal
= aTransferable
->GetRequestingPrincipal();
317 nsContentPolicyType contentPolicyType
=
318 aTransferable
->GetContentPolicyType();
320 SaveURIToFile(sourceURI
, principal
, file
, contentPolicyType
, isPrivate
);
321 // send back an nsIFile
322 if (NS_SUCCEEDED(rv
)) {
323 CallQueryInterface(file
, aData
);
330 DragDataProducer::DragDataProducer(nsPIDOMWindowOuter
* aWindow
,
332 nsIContent
* aSelectionTargetNode
,
333 bool aIsAltKeyPressed
)
336 mSelectionTargetNode(aSelectionTargetNode
),
337 mIsAltKeyPressed(aIsAltKeyPressed
),
341 // FindParentLinkNode
343 // Finds the parent with the given link tag starting at |inNode|. If
344 // it gets up to the root without finding it, we stop looking and
347 already_AddRefed
<nsIContent
> DragDataProducer::FindParentLinkNode(
348 nsIContent
* inNode
) {
349 nsIContent
* content
= inNode
;
351 // That must have been the document node; nothing else to do here;
355 for (; content
; content
= content
->GetParent()) {
356 if (nsContentUtils::IsDraggableLink(content
)) {
357 nsCOMPtr
<nsIContent
> ret
= content
;
368 nsresult
DragDataProducer::GetAnchorURL(nsIContent
* inNode
, nsAString
& outURL
) {
369 nsCOMPtr
<nsIURI
> linkURI
;
370 if (!inNode
|| !inNode
->IsLink(getter_AddRefs(linkURI
))) {
377 nsresult rv
= linkURI
->GetSpec(spec
);
378 NS_ENSURE_SUCCESS(rv
, rv
);
379 CopyUTF8toUTF16(spec
, outURL
);
386 // Creates the html for an anchor in the form
387 // <a href="inURL">inText</a>
389 void DragDataProducer::CreateLinkText(const nsAString
& inURL
,
390 const nsAString
& inText
,
391 nsAString
& outLinkText
) {
392 // use a temp var in case |inText| is the same string as
393 // |outLinkText| to avoid overwriting it while building up the
395 nsAutoString
linkText(u
"<a href=\""_ns
+ inURL
+ u
"\">"_ns
+ inText
+
398 outLinkText
= linkText
;
404 // Gets the text associated with a node
406 void DragDataProducer::GetNodeString(nsIContent
* inNode
,
407 nsAString
& outNodeString
) {
408 nsCOMPtr
<nsINode
> node
= inNode
;
410 outNodeString
.Truncate();
412 // use a range to get the text-equivalent of the node
413 nsCOMPtr
<Document
> doc
= node
->OwnerDoc();
414 RefPtr
<nsRange
> range
= doc
->CreateRange(IgnoreErrors());
416 range
->SelectNode(*node
, IgnoreErrors());
417 range
->ToString(outNodeString
, IgnoreErrors());
421 nsresult
DragDataProducer::GetImageData(imgIContainer
* aImage
,
422 imgIRequest
* aRequest
) {
423 nsCOMPtr
<nsIURI
> imgUri
;
424 aRequest
->GetURI(getter_AddRefs(imgUri
));
426 nsCOMPtr
<nsIURL
> imgUrl(do_QueryInterface(imgUri
));
429 nsresult rv
= imgUrl
->GetSpec(spec
);
430 NS_ENSURE_SUCCESS(rv
, rv
);
432 // pass out the image source string
433 CopyUTF8toUTF16(spec
, mImageSourceString
);
436 aRequest
->GetMimeType(getter_Copies(mimeType
));
438 #if defined(XP_MACOSX)
439 // Save the MIME type so we can make sure the extension
440 // is compatible (and replace it if it isn't) when the
441 // image is dropped. On Mac, we need to get the OS MIME
442 // handler information in the parent due to sandboxing.
443 CopyUTF8toUTF16(mimeType
, mImageRequestMime
);
445 nsCOMPtr
<nsIMIMEService
> mimeService
= do_GetService("@mozilla.org/mime;1");
446 if (NS_WARN_IF(!mimeService
)) {
447 return NS_ERROR_FAILURE
;
450 nsCOMPtr
<nsIMIMEInfo
> mimeInfo
;
451 mimeService
->GetFromTypeAndExtension(mimeType
, ""_ns
,
452 getter_AddRefs(mimeInfo
));
454 nsAutoCString extension
;
455 imgUrl
->GetFileExtension(extension
);
458 if (extension
.IsEmpty() ||
459 NS_FAILED(mimeInfo
->ExtensionExists(extension
, &validExtension
)) ||
461 // Fix the file extension in the URL
462 nsAutoCString primaryExtension
;
463 mimeInfo
->GetPrimaryExtension(primaryExtension
);
464 if (!primaryExtension
.IsEmpty()) {
465 rv
= NS_MutateURI(imgUrl
)
466 .Apply(NS_MutatorMethod(&nsIURLMutator::SetFileExtension
,
467 primaryExtension
, nullptr))
469 NS_ENSURE_SUCCESS(rv
, rv
);
473 #endif /* defined(XP_MACOSX) */
475 nsAutoCString fileName
;
476 imgUrl
->GetFileName(fileName
);
478 NS_UnescapeURL(fileName
);
480 #if !defined(XP_MACOSX)
481 // make the filename safe for the filesystem
482 fileName
.ReplaceChar(FILE_PATH_SEPARATOR FILE_ILLEGAL_CHARACTERS
, '-');
485 CopyUTF8toUTF16(fileName
, mImageDestFileName
);
487 // and the image object
494 nsresult
DragDataProducer::Produce(DataTransfer
* aDataTransfer
, bool* aCanDrag
,
495 Selection
** aSelection
,
496 nsIContent
** aDragNode
,
497 nsIPrincipal
** aPrincipal
,
498 nsIContentSecurityPolicy
** aCsp
) {
499 MOZ_ASSERT(aCanDrag
&& aSelection
&& aDataTransfer
&& aDragNode
,
500 "null pointer passed to Produce");
501 NS_ASSERTION(mWindow
, "window not set");
502 NS_ASSERTION(mSelectionTargetNode
,
503 "selection target node should have been set");
505 *aDragNode
= nullptr;
508 nsIContent
* dragNode
= nullptr;
509 *aSelection
= nullptr;
511 // Find the selection to see what we could be dragging and if what we're
512 // dragging is in what is selected. If this is an editable textbox, use
513 // the textbox's selection, otherwise use the window's selection.
514 RefPtr
<Selection
> selection
;
515 nsIContent
* editingElement
= mSelectionTargetNode
->IsEditable()
516 ? mSelectionTargetNode
->GetEditingHost()
518 RefPtr
<TextControlElement
> textControlElement
=
519 TextControlElement::GetTextControlElementFromEditingHost(editingElement
);
520 if (textControlElement
) {
521 nsISelectionController
* selcon
=
522 textControlElement
->GetSelectionController();
525 selcon
->GetSelection(nsISelectionController::SELECTION_NORMAL
);
528 if (!selection
) return NS_OK
;
530 selection
= mWindow
->GetSelection();
531 if (!selection
) return NS_OK
;
533 // Check if the node is inside a form control. Don't set aCanDrag to false
534 // however, as we still want to allow the drag.
535 nsCOMPtr
<nsIContent
> findFormNode
= mSelectionTargetNode
;
536 nsIContent
* findFormParent
= findFormNode
->GetParent();
537 while (findFormParent
) {
538 nsCOMPtr
<nsIFormControl
> form(do_QueryInterface(findFormParent
));
539 if (form
&& !form
->AllowDraggableChildren()) {
542 findFormParent
= findFormParent
->GetParent();
546 // if set, serialize the content under this node
547 nsCOMPtr
<nsIContent
> nodeToSerialize
;
549 BrowsingContext
* bc
= mWindow
->GetBrowsingContext();
550 const bool isChromeShell
= bc
&& bc
->IsChrome();
552 // In chrome shells, only allow dragging inside editable areas.
553 if (isChromeShell
&& !editingElement
) {
554 // This path should already be filtered out in
555 // EventStateManager::DetermineDragTargetAndDefaultData.
556 MOZ_ASSERT_UNREACHABLE("Shouldn't be generating drag data for chrome");
560 if (isChromeShell
&& textControlElement
) {
561 // Only use the selection if the target node is in the selection.
562 if (!selection
->ContainsNode(*mSelectionTargetNode
, false, IgnoreErrors()))
565 selection
.swap(*aSelection
);
567 // In content shells, a number of checks are made below to determine
568 // whether an image or a link is being dragged. If so, add additional
569 // data to the data transfer. This is also done for chrome shells, but
570 // only when in a non-textbox editor.
572 bool haveSelectedContent
= false;
574 // possible parent link node
575 nsCOMPtr
<nsIContent
> parentLink
;
576 nsCOMPtr
<nsIContent
> draggedNode
;
579 // only drag form elements by using the alt key,
580 // otherwise buttons and select widgets are hard to use
582 // Note that while <object> elements implement nsIFormControl, we should
583 // really allow dragging them if they happen to be images.
584 nsCOMPtr
<nsIFormControl
> form(do_QueryInterface(mTarget
));
585 if (form
&& !mIsAltKeyPressed
&& form
->ControlType() != NS_FORM_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 // if we have selected text, use it in preference to the node
732 nsCOMPtr
<nsITransferable
> transferable
;
734 rv
= nsCopySupport::GetTransferableForSelection(
735 *aSelection
, doc
, getter_AddRefs(transferable
));
737 rv
= nsCopySupport::GetTransferableForNode(nodeToSerialize
, doc
,
738 getter_AddRefs(transferable
));
740 NS_ENSURE_SUCCESS(rv
, rv
);
742 nsCOMPtr
<nsISupports
> supports
;
743 nsCOMPtr
<nsISupportsString
> data
;
744 rv
= transferable
->GetTransferData(kHTMLMime
, getter_AddRefs(supports
));
745 data
= do_QueryInterface(supports
);
746 if (NS_SUCCEEDED(rv
)) {
747 data
->GetData(mHtmlString
);
749 rv
= transferable
->GetTransferData(kHTMLContext
, getter_AddRefs(supports
));
750 data
= do_QueryInterface(supports
);
751 if (NS_SUCCEEDED(rv
)) {
752 data
->GetData(mContextString
);
754 rv
= transferable
->GetTransferData(kHTMLInfo
, getter_AddRefs(supports
));
755 data
= do_QueryInterface(supports
);
756 if (NS_SUCCEEDED(rv
)) {
757 data
->GetData(mInfoString
);
759 rv
= transferable
->GetTransferData(kUnicodeMime
, getter_AddRefs(supports
));
760 data
= do_QueryInterface(supports
);
761 NS_ENSURE_SUCCESS(rv
, rv
); // require plain text at a minimum
762 data
->GetData(mTitleString
);
765 // default text value is the URL
766 if (mTitleString
.IsEmpty()) {
767 mTitleString
= mUrlString
;
770 // if we haven't constructed a html version, make one now
771 if (mHtmlString
.IsEmpty() && !mUrlString
.IsEmpty())
772 CreateLinkText(mUrlString
, mTitleString
, mHtmlString
);
774 // if there is no drag node, which will be the case for a selection, just
775 // use the selection target node.
776 rv
= AddStringsToDataTransfer(
777 dragNode
? dragNode
: mSelectionTargetNode
.get(), aDataTransfer
);
778 NS_ENSURE_SUCCESS(rv
, rv
);
780 NS_IF_ADDREF(*aDragNode
= dragNode
);
784 void DragDataProducer::AddString(DataTransfer
* aDataTransfer
,
785 const nsAString
& aFlavor
,
786 const nsAString
& aData
,
787 nsIPrincipal
* aPrincipal
, bool aHidden
) {
788 RefPtr
<nsVariantCC
> variant
= new nsVariantCC();
789 variant
->SetAsAString(aData
);
790 aDataTransfer
->SetDataWithPrincipal(aFlavor
, variant
, 0, aPrincipal
, aHidden
);
793 nsresult
DragDataProducer::AddStringsToDataTransfer(
794 nsIContent
* aDragNode
, DataTransfer
* aDataTransfer
) {
795 NS_ASSERTION(aDragNode
, "adding strings for null node");
797 // set all of the data to have the principal of the node where the data came
799 nsIPrincipal
* principal
= aDragNode
->NodePrincipal();
801 // add a special flavor if we're an anchor to indicate that we have
802 // a URL in the drag data
803 if (!mUrlString
.IsEmpty() && mIsAnchor
) {
804 nsAutoString
dragData(mUrlString
);
805 dragData
.Append('\n');
806 // Remove leading and trailing newlines in the title and replace them with
807 // space in remaining positions - they confuse PlacesUtils::unwrapNodes
808 // that expects url\ntitle formatted data for x-moz-url.
809 nsAutoString
title(mTitleString
);
811 title
.ReplaceChar("\r\n", ' ');
814 AddString(aDataTransfer
, NS_LITERAL_STRING_FROM_CSTRING(kURLMime
), dragData
,
816 AddString(aDataTransfer
, NS_LITERAL_STRING_FROM_CSTRING(kURLDataMime
),
817 mUrlString
, principal
);
818 AddString(aDataTransfer
,
819 NS_LITERAL_STRING_FROM_CSTRING(kURLDescriptionMime
), mTitleString
,
821 AddString(aDataTransfer
, u
"text/uri-list"_ns
, mUrlString
, principal
);
824 // add a special flavor for the html context data
825 if (!mContextString
.IsEmpty())
826 AddString(aDataTransfer
, NS_LITERAL_STRING_FROM_CSTRING(kHTMLContext
),
827 mContextString
, principal
);
829 // add a special flavor if we have html info data
830 if (!mInfoString
.IsEmpty())
831 AddString(aDataTransfer
, NS_LITERAL_STRING_FROM_CSTRING(kHTMLInfo
),
832 mInfoString
, principal
);
835 if (!mHtmlString
.IsEmpty())
836 AddString(aDataTransfer
, NS_LITERAL_STRING_FROM_CSTRING(kHTMLMime
),
837 mHtmlString
, principal
);
839 // add the plain text. we use the url for text/plain data if an anchor is
840 // being dragged, rather than the title text of the link or the alt text for
842 AddString(aDataTransfer
, NS_LITERAL_STRING_FROM_CSTRING(kTextMime
),
843 mIsAnchor
? mUrlString
: mTitleString
, principal
);
845 // add image data, if present. For now, all we're going to do with
846 // this is turn it into a native data flavor, so indicate that with
847 // a new flavor so as not to confuse anyone who is really registered
848 // for image/gif or image/jpg.
850 RefPtr
<nsVariantCC
> variant
= new nsVariantCC();
851 variant
->SetAsISupports(mImage
);
852 aDataTransfer
->SetDataWithPrincipal(
853 NS_LITERAL_STRING_FROM_CSTRING(kNativeImageMime
), variant
, 0,
856 // assume the image comes from a file, and add a file promise. We
857 // register ourselves as a nsIFlavorDataProvider, and will use the
858 // GetFlavorData callback to save the image to disk.
860 nsCOMPtr
<nsIFlavorDataProvider
> dataProvider
=
861 new nsContentAreaDragDropDataProvider();
863 RefPtr
<nsVariantCC
> variant
= new nsVariantCC();
864 variant
->SetAsISupports(dataProvider
);
865 aDataTransfer
->SetDataWithPrincipal(
866 NS_LITERAL_STRING_FROM_CSTRING(kFilePromiseMime
), variant
, 0,
870 AddString(aDataTransfer
,
871 NS_LITERAL_STRING_FROM_CSTRING(kFilePromiseURLMime
),
872 mImageSourceString
, principal
);
873 AddString(aDataTransfer
,
874 NS_LITERAL_STRING_FROM_CSTRING(kFilePromiseDestFilename
),
875 mImageDestFileName
, principal
);
876 #if defined(XP_MACOSX)
877 AddString(aDataTransfer
, NS_LITERAL_STRING_FROM_CSTRING(kImageRequestMime
),
878 mImageRequestMime
, principal
, /* aHidden= */ true);
881 // if not an anchor, add the image url
883 AddString(aDataTransfer
, NS_LITERAL_STRING_FROM_CSTRING(kURLDataMime
),
884 mUrlString
, principal
);
885 AddString(aDataTransfer
, u
"text/uri-list"_ns
, mUrlString
, principal
);
892 // note that this can return NS_OK, but a null out param (by design)
894 nsresult
DragDataProducer::GetDraggableSelectionData(
895 Selection
* inSelection
, nsIContent
* inRealTargetNode
,
896 nsIContent
** outImageOrLinkNode
, bool* outDragSelectedText
) {
897 NS_ENSURE_ARG(inSelection
);
898 NS_ENSURE_ARG(inRealTargetNode
);
899 NS_ENSURE_ARG_POINTER(outImageOrLinkNode
);
901 *outImageOrLinkNode
= nullptr;
902 *outDragSelectedText
= false;
904 if (!inSelection
->IsCollapsed()) {
905 if (inSelection
->ContainsNode(*inRealTargetNode
, false, IgnoreErrors())) {
906 // track down the anchor node, if any, for the url
907 nsINode
* selectionStart
= inSelection
->GetAnchorNode();
908 nsINode
* selectionEnd
= inSelection
->GetFocusNode();
910 // look for a selection around a single node, like an image.
911 // in this case, drag the image, rather than a serialization of the HTML
912 // XXX generalize this to other draggable element types?
913 if (selectionStart
== selectionEnd
) {
914 nsCOMPtr
<nsIContent
> selStartContent
=
915 nsIContent::FromNodeOrNull(selectionStart
);
916 if (selStartContent
&& selStartContent
->HasChildNodes()) {
917 // see if just one node is selected
918 uint32_t anchorOffset
= inSelection
->AnchorOffset();
919 uint32_t focusOffset
= inSelection
->FocusOffset();
920 if (anchorOffset
== focusOffset
+ 1 ||
921 focusOffset
== anchorOffset
+ 1) {
922 uint32_t childOffset
= std::min(anchorOffset
, focusOffset
);
923 nsIContent
* childContent
=
924 selStartContent
->GetChildAt_Deprecated(childOffset
);
925 // if we find an image, we'll fall into the node-dragging code,
926 // rather the the selection-dragging code
927 if (nsContentUtils::IsDraggableImage(childContent
)) {
928 NS_ADDREF(*outImageOrLinkNode
= childContent
);
935 // indicate that a link or text is selected
936 *outDragSelectedText
= true;