1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsReadableUtils.h"
9 #include "nsContentAreaDragDrop.h"
14 // Interfaces needed to be included
15 #include "nsCopySupport.h"
16 #include "nsIDOMUIEvent.h"
17 #include "nsISelection.h"
18 #include "nsISelectionController.h"
19 #include "nsIDOMNode.h"
20 #include "nsIDOMNodeList.h"
21 #include "nsIDOMEvent.h"
22 #include "nsIDOMDragEvent.h"
23 #include "nsPIDOMWindow.h"
24 #include "nsIDOMRange.h"
25 #include "nsIFormControl.h"
26 #include "nsIDOMHTMLAreaElement.h"
27 #include "nsIDOMHTMLAnchorElement.h"
28 #include "nsITransferable.h"
29 #include "nsComponentManagerUtils.h"
31 #include "nsISupportsPrimitives.h"
32 #include "nsServiceManagerUtils.h"
33 #include "nsNetUtil.h"
35 #include "nsIWebNavigation.h"
36 #include "nsIDocShell.h"
37 #include "nsIContent.h"
38 #include "nsIImageLoadingContent.h"
39 #include "nsITextControlElement.h"
40 #include "nsUnicharUtils.h"
42 #include "nsIDocument.h"
43 #include "nsIScriptSecurityManager.h"
44 #include "nsIPrincipal.h"
45 #include "nsIDocShellTreeItem.h"
46 #include "nsIWebBrowserPersist.h"
48 #include "nsContentUtils.h"
49 #include "nsIMIMEService.h"
50 #include "imgIContainer.h"
51 #include "imgIRequest.h"
52 #include "mozilla/dom/DataTransfer.h"
53 #include "nsIMIMEInfo.h"
55 #include "mozilla/dom/Element.h"
56 #include "mozilla/dom/HTMLAreaElement.h"
58 using namespace mozilla::dom
;
60 class MOZ_STACK_CLASS DragDataProducer
63 DragDataProducer(nsPIDOMWindow
* aWindow
,
65 nsIContent
* aSelectionTargetNode
,
66 bool aIsAltKeyPressed
);
67 nsresult
Produce(DataTransfer
* aDataTransfer
,
69 nsISelection
** aSelection
,
70 nsIContent
** aDragNode
);
73 void AddString(DataTransfer
* aDataTransfer
,
74 const nsAString
& aFlavor
,
75 const nsAString
& aData
,
76 nsIPrincipal
* aPrincipal
);
77 nsresult
AddStringsToDataTransfer(nsIContent
* aDragNode
,
78 DataTransfer
* aDataTransfer
);
79 static nsresult
GetDraggableSelectionData(nsISelection
* inSelection
,
80 nsIContent
* inRealTargetNode
,
81 nsIContent
**outImageOrLinkNode
,
82 bool* outDragSelectedText
);
83 static already_AddRefed
<nsIContent
> FindParentLinkNode(nsIContent
* inNode
);
84 static void GetAnchorURL(nsIContent
* inNode
, nsAString
& outURL
);
85 static void GetNodeString(nsIContent
* inNode
, nsAString
& outNodeString
);
86 static void CreateLinkText(const nsAString
& inURL
, const nsAString
& inText
,
87 nsAString
& outLinkText
);
89 nsCOMPtr
<nsPIDOMWindow
> mWindow
;
90 nsCOMPtr
<nsIContent
> mTarget
;
91 nsCOMPtr
<nsIContent
> mSelectionTargetNode
;
92 bool mIsAltKeyPressed
;
95 nsString mImageSourceString
;
96 nsString mImageDestFileName
;
97 nsString mTitleString
;
98 // will be filled automatically if you fill urlstring
100 nsString mContextString
;
101 nsString mInfoString
;
104 nsCOMPtr
<imgIContainer
> mImage
;
109 nsContentAreaDragDrop::GetDragData(nsPIDOMWindow
* aWindow
,
111 nsIContent
* aSelectionTargetNode
,
112 bool aIsAltKeyPressed
,
113 DataTransfer
* aDataTransfer
,
115 nsISelection
** aSelection
,
116 nsIContent
** aDragNode
)
118 NS_ENSURE_TRUE(aSelectionTargetNode
, NS_ERROR_INVALID_ARG
);
123 provider(aWindow
, aTarget
, aSelectionTargetNode
, aIsAltKeyPressed
);
124 return provider
.Produce(aDataTransfer
, aCanDrag
, aSelection
, aDragNode
);
128 NS_IMPL_ISUPPORTS(nsContentAreaDragDropDataProvider
, nsIFlavorDataProvider
)
131 // used on platforms where it's possible to drag items (e.g. images)
132 // into the file system
134 nsContentAreaDragDropDataProvider::SaveURIToFile(nsAString
& inSourceURIString
,
138 nsCOMPtr
<nsIURI
> sourceURI
;
139 nsresult rv
= NS_NewURI(getter_AddRefs(sourceURI
), inSourceURIString
);
141 return NS_ERROR_FAILURE
;
144 nsCOMPtr
<nsIURL
> sourceURL
= do_QueryInterface(sourceURI
);
146 return NS_ERROR_NO_INTERFACE
;
149 rv
= inDestFile
->CreateUnique(nsIFile::NORMAL_FILE_TYPE
, 0600);
150 NS_ENSURE_SUCCESS(rv
, rv
);
152 // we rely on the fact that the WPB is refcounted by the channel etc,
153 // so we don't keep a ref to it. It will die when finished.
154 nsCOMPtr
<nsIWebBrowserPersist
> persist
=
155 do_CreateInstance("@mozilla.org/embedding/browser/nsWebBrowserPersist;1",
157 NS_ENSURE_SUCCESS(rv
, rv
);
159 persist
->SetPersistFlags(nsIWebBrowserPersist::PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION
);
161 // referrer policy can be anything since the referrer is nullptr
162 return persist
->SavePrivacyAwareURI(sourceURI
, nullptr, nullptr,
163 mozilla::net::RP_Default
,
165 inDestFile
, isPrivate
);
168 // This is our nsIFlavorDataProvider callback. There are several
169 // assumptions here that make this work:
171 // 1. Someone put a kFilePromiseURLMime flavor into the transferable
172 // with the source URI of the file to save (as a string). We did
173 // that in AddStringsToDataTransfer.
175 // 2. Someone put a kFilePromiseDirectoryMime flavor into the
176 // transferable with an nsIFile for the directory we are to
177 // save in. That has to be done by platform-specific code (in
178 // widget), which gets the destination directory from
179 // OS-specific drag information.
182 nsContentAreaDragDropDataProvider::GetFlavorData(nsITransferable
*aTransferable
,
187 NS_ENSURE_ARG_POINTER(aData
&& aDataLen
);
191 nsresult rv
= NS_ERROR_NOT_IMPLEMENTED
;
193 if (strcmp(aFlavor
, kFilePromiseMime
) == 0) {
194 // get the URI from the kFilePromiseURLMime flavor
195 NS_ENSURE_ARG(aTransferable
);
196 nsCOMPtr
<nsISupports
> tmp
;
197 uint32_t dataSize
= 0;
198 aTransferable
->GetTransferData(kFilePromiseURLMime
,
199 getter_AddRefs(tmp
), &dataSize
);
200 nsCOMPtr
<nsISupportsString
> supportsString
=
201 do_QueryInterface(tmp
);
203 return NS_ERROR_FAILURE
;
205 nsAutoString sourceURLString
;
206 supportsString
->GetData(sourceURLString
);
207 if (sourceURLString
.IsEmpty())
208 return NS_ERROR_FAILURE
;
210 aTransferable
->GetTransferData(kFilePromiseDestFilename
,
211 getter_AddRefs(tmp
), &dataSize
);
212 supportsString
= do_QueryInterface(tmp
);
214 return NS_ERROR_FAILURE
;
216 nsAutoString targetFilename
;
217 supportsString
->GetData(targetFilename
);
218 if (targetFilename
.IsEmpty())
219 return NS_ERROR_FAILURE
;
221 // get the target directory from the kFilePromiseDirectoryMime
223 nsCOMPtr
<nsISupports
> dirPrimitive
;
225 aTransferable
->GetTransferData(kFilePromiseDirectoryMime
,
226 getter_AddRefs(dirPrimitive
), &dataSize
);
227 nsCOMPtr
<nsIFile
> destDirectory
= do_QueryInterface(dirPrimitive
);
229 return NS_ERROR_FAILURE
;
231 nsCOMPtr
<nsIFile
> file
;
232 rv
= destDirectory
->Clone(getter_AddRefs(file
));
233 NS_ENSURE_SUCCESS(rv
, rv
);
235 file
->Append(targetFilename
);
238 aTransferable
->GetIsPrivateData(&isPrivate
);
240 rv
= SaveURIToFile(sourceURLString
, file
, isPrivate
);
241 // send back an nsIFile
242 if (NS_SUCCEEDED(rv
)) {
243 CallQueryInterface(file
, aData
);
244 *aDataLen
= sizeof(nsIFile
*);
251 DragDataProducer::DragDataProducer(nsPIDOMWindow
* aWindow
,
253 nsIContent
* aSelectionTargetNode
,
254 bool aIsAltKeyPressed
)
257 mSelectionTargetNode(aSelectionTargetNode
),
258 mIsAltKeyPressed(aIsAltKeyPressed
),
265 // FindParentLinkNode
267 // Finds the parent with the given link tag starting at |inNode|. If
268 // it gets up to the root without finding it, we stop looking and
271 already_AddRefed
<nsIContent
>
272 DragDataProducer::FindParentLinkNode(nsIContent
* inNode
)
274 nsIContent
* content
= inNode
;
276 // That must have been the document node; nothing else to do here;
280 for (; content
; content
= content
->GetParent()) {
281 if (nsContentUtils::IsDraggableLink(content
)) {
282 nsCOMPtr
<nsIContent
> ret
= content
;
295 DragDataProducer::GetAnchorURL(nsIContent
* inNode
, nsAString
& outURL
)
297 nsCOMPtr
<nsIURI
> linkURI
;
298 if (!inNode
|| !inNode
->IsLink(getter_AddRefs(linkURI
))) {
305 linkURI
->GetSpec(spec
);
306 CopyUTF8toUTF16(spec
, outURL
);
313 // Creates the html for an anchor in the form
314 // <a href="inURL">inText</a>
317 DragDataProducer::CreateLinkText(const nsAString
& inURL
,
318 const nsAString
& inText
,
319 nsAString
& outLinkText
)
321 // use a temp var in case |inText| is the same string as
322 // |outLinkText| to avoid overwriting it while building up the
324 nsAutoString
linkText(NS_LITERAL_STRING("<a href=\"") +
326 NS_LITERAL_STRING("\">") +
328 NS_LITERAL_STRING("</a>") );
330 outLinkText
= linkText
;
337 // Gets the text associated with a node
340 DragDataProducer::GetNodeString(nsIContent
* inNode
,
341 nsAString
& outNodeString
)
343 nsCOMPtr
<nsINode
> node
= inNode
;
345 outNodeString
.Truncate();
347 // use a range to get the text-equivalent of the node
348 nsCOMPtr
<nsIDocument
> doc
= node
->OwnerDoc();
349 mozilla::ErrorResult rv
;
350 nsRefPtr
<nsRange
> range
= doc
->CreateRange(rv
);
352 range
->SelectNode(*node
, rv
);
353 range
->ToString(outNodeString
);
358 DragDataProducer::Produce(DataTransfer
* aDataTransfer
,
360 nsISelection
** aSelection
,
361 nsIContent
** aDragNode
)
363 NS_PRECONDITION(aCanDrag
&& aSelection
&& aDataTransfer
&& aDragNode
,
364 "null pointer passed to Produce");
365 NS_ASSERTION(mWindow
, "window not set");
366 NS_ASSERTION(mSelectionTargetNode
, "selection target node should have been set");
368 *aDragNode
= nullptr;
371 nsIContent
* dragNode
= nullptr;
372 *aSelection
= nullptr;
374 // Find the selection to see what we could be dragging and if what we're
375 // dragging is in what is selected. If this is an editable textbox, use
376 // the textbox's selection, otherwise use the window's selection.
377 nsCOMPtr
<nsISelection
> selection
;
378 nsIContent
* editingElement
= mSelectionTargetNode
->IsEditable() ?
379 mSelectionTargetNode
->GetEditingHost() : nullptr;
380 nsCOMPtr
<nsITextControlElement
> textControl
=
381 nsITextControlElement::GetTextControlElementFromEditingHost(editingElement
);
383 nsISelectionController
* selcon
= textControl
->GetSelectionController();
385 selcon
->GetSelection(nsISelectionController::SELECTION_NORMAL
, getter_AddRefs(selection
));
392 mWindow
->GetSelection(getter_AddRefs(selection
));
396 // Check if the node is inside a form control. Don't set aCanDrag to false
397 //however, as we still want to allow the drag.
398 nsCOMPtr
<nsIContent
> findFormNode
= mSelectionTargetNode
;
399 nsIContent
* findFormParent
= findFormNode
->GetParent();
400 while (findFormParent
) {
401 nsCOMPtr
<nsIFormControl
> form(do_QueryInterface(findFormParent
));
402 if (form
&& !form
->AllowDraggableChildren()) {
405 findFormParent
= findFormParent
->GetParent();
409 // if set, serialize the content under this node
410 nsCOMPtr
<nsIContent
> nodeToSerialize
;
412 nsCOMPtr
<nsIDocShellTreeItem
> dsti
= mWindow
->GetDocShell();
413 const bool isChromeShell
=
414 dsti
&& dsti
->ItemType() == nsIDocShellTreeItem::typeChrome
;
416 // In chrome shells, only allow dragging inside editable areas.
417 if (isChromeShell
&& !editingElement
)
420 if (isChromeShell
&& textControl
) {
421 // Only use the selection if the target node is in the selection.
422 bool selectionContainsTarget
= false;
423 nsCOMPtr
<nsIDOMNode
> targetNode
= do_QueryInterface(mSelectionTargetNode
);
424 selection
->ContainsNode(targetNode
, false, &selectionContainsTarget
);
425 if (!selectionContainsTarget
)
428 selection
.swap(*aSelection
);
431 // In content shells, a number of checks are made below to determine
432 // whether an image or a link is being dragged. If so, add additional
433 // data to the data transfer. This is also done for chrome shells, but
434 // only when in a non-textbox editor.
436 bool haveSelectedContent
= false;
438 // possible parent link node
439 nsCOMPtr
<nsIContent
> parentLink
;
440 nsCOMPtr
<nsIContent
> draggedNode
;
443 // only drag form elements by using the alt key,
444 // otherwise buttons and select widgets are hard to use
446 // Note that while <object> elements implement nsIFormControl, we should
447 // really allow dragging them if they happen to be images.
448 nsCOMPtr
<nsIFormControl
> form(do_QueryInterface(mTarget
));
449 if (form
&& !mIsAltKeyPressed
&& form
->GetType() != NS_FORM_OBJECT
) {
454 draggedNode
= mTarget
;
457 nsCOMPtr
<nsIDOMHTMLAreaElement
> area
; // client-side image map
458 nsCOMPtr
<nsIImageLoadingContent
> image
;
459 nsCOMPtr
<nsIDOMHTMLAnchorElement
> link
;
461 nsCOMPtr
<nsIContent
> selectedImageOrLinkNode
;
462 GetDraggableSelectionData(selection
, mSelectionTargetNode
,
463 getter_AddRefs(selectedImageOrLinkNode
),
464 &haveSelectedContent
);
466 // either plain text or anchor text is selected
467 if (haveSelectedContent
) {
468 selection
.swap(*aSelection
);
469 } else if (selectedImageOrLinkNode
) {
470 // an image is selected
471 image
= do_QueryInterface(selectedImageOrLinkNode
);
473 // nothing is selected -
475 // look for draggable elements under the mouse
477 // if the alt key is down, don't start a drag if we're in an
478 // anchor because we want to do selection.
479 parentLink
= FindParentLinkNode(draggedNode
);
480 if (parentLink
&& mIsAltKeyPressed
) {
485 area
= do_QueryInterface(draggedNode
);
486 image
= do_QueryInterface(draggedNode
);
487 link
= do_QueryInterface(draggedNode
);
491 // set for linked images, and links
492 nsCOMPtr
<nsIContent
> linkNode
;
495 // use the alt text (or, if missing, the href) as the title
496 HTMLAreaElement
* areaElem
= static_cast<HTMLAreaElement
*>(area
.get());
497 areaElem
->GetAttribute(NS_LITERAL_STRING("alt"), mTitleString
);
498 if (mTitleString
.IsEmpty()) {
499 // this can be a relative link
500 areaElem
->GetAttribute(NS_LITERAL_STRING("href"), mTitleString
);
503 // we'll generate HTML like <a href="absurl">alt text</a>
506 // gives an absolute link
507 GetAnchorURL(draggedNode
, mUrlString
);
509 mHtmlString
.AssignLiteral("<a href=\"");
510 mHtmlString
.Append(mUrlString
);
511 mHtmlString
.AppendLiteral("\">");
512 mHtmlString
.Append(mTitleString
);
513 mHtmlString
.AppendLiteral("</a>");
515 dragNode
= draggedNode
;
518 // grab the href as the url, use alt text as the title of the
519 // area if it's there. the drag data is the image tag and src
521 nsCOMPtr
<nsIDOMElement
> imageElement(do_QueryInterface(image
));
522 // XXXbz Shouldn't we use the "title" attr for title? Using
523 // "alt" seems very wrong....
525 imageElement
->GetAttribute(NS_LITERAL_STRING("alt"), mTitleString
);
528 mUrlString
.Truncate();
530 // grab the image data, and its request.
531 nsCOMPtr
<imgIRequest
> imgRequest
;
532 nsCOMPtr
<imgIContainer
> img
=
533 nsContentUtils::GetImageFromContent(image
,
534 getter_AddRefs(imgRequest
));
536 nsCOMPtr
<nsIMIMEService
> mimeService
=
537 do_GetService("@mozilla.org/mime;1");
539 // Fix the file extension in the URL if necessary
540 if (imgRequest
&& mimeService
) {
541 nsCOMPtr
<nsIURI
> imgUri
;
542 imgRequest
->GetCurrentURI(getter_AddRefs(imgUri
));
544 nsCOMPtr
<nsIURL
> imgUrl(do_QueryInterface(imgUri
));
547 nsAutoCString extension
;
548 imgUrl
->GetFileExtension(extension
);
550 nsXPIDLCString mimeType
;
551 imgRequest
->GetMimeType(getter_Copies(mimeType
));
553 nsCOMPtr
<nsIMIMEInfo
> mimeInfo
;
554 mimeService
->GetFromTypeAndExtension(mimeType
, EmptyCString(),
555 getter_AddRefs(mimeInfo
));
559 imgUrl
->GetSpec(spec
);
561 // pass out the image source string
562 CopyUTF8toUTF16(spec
, mImageSourceString
);
563 mUrlString
= mImageSourceString
;
566 if (extension
.IsEmpty() ||
567 NS_FAILED(mimeInfo
->ExtensionExists(extension
,
570 // Fix the file extension in the URL
571 nsresult rv
= imgUrl
->Clone(getter_AddRefs(imgUri
));
572 NS_ENSURE_SUCCESS(rv
, rv
);
574 imgUrl
= do_QueryInterface(imgUri
);
576 nsAutoCString primaryExtension
;
577 mimeInfo
->GetPrimaryExtension(primaryExtension
);
579 imgUrl
->SetFileExtension(primaryExtension
);
582 nsAutoCString fileName
;
583 imgUrl
->GetFileName(fileName
);
585 NS_UnescapeURL(fileName
);
587 // make the filename safe for the filesystem
588 fileName
.ReplaceChar(FILE_PATH_SEPARATOR FILE_ILLEGAL_CHARACTERS
,
591 CopyUTF8toUTF16(fileName
, mImageDestFileName
);
593 // and the image object
598 if (mUrlString
.IsEmpty()) {
599 nsCOMPtr
<nsIURI
> imageURI
;
600 image
->GetCurrentURI(getter_AddRefs(imageURI
));
603 imageURI
->GetSpec(spec
);
604 CopyUTF8toUTF16(spec
, mUrlString
);
607 if (mTitleString
.IsEmpty()) {
608 mTitleString
= mUrlString
;
612 // If we are dragging around an image in an anchor, then we
613 // are dragging the entire anchor
614 linkNode
= parentLink
;
615 nodeToSerialize
= linkNode
;
617 nodeToSerialize
= do_QueryInterface(draggedNode
);
619 dragNode
= nodeToSerialize
;
621 // set linkNode. The code below will handle this
622 linkNode
= do_QueryInterface(link
); // XXX test this
623 GetNodeString(draggedNode
, mTitleString
);
624 } else if (parentLink
) {
625 // parentLink will always be null if there's selected content
626 linkNode
= parentLink
;
627 nodeToSerialize
= linkNode
;
628 } else if (!haveSelectedContent
) {
635 GetAnchorURL(linkNode
, mUrlString
);
641 if (nodeToSerialize
|| *aSelection
) {
642 mHtmlString
.Truncate();
643 mContextString
.Truncate();
644 mInfoString
.Truncate();
645 mTitleString
.Truncate();
647 nsCOMPtr
<nsIDocument
> doc
= mWindow
->GetDoc();
648 NS_ENSURE_TRUE(doc
, NS_ERROR_FAILURE
);
650 // if we have selected text, use it in preference to the node
651 nsCOMPtr
<nsITransferable
> transferable
;
653 rv
= nsCopySupport::GetTransferableForSelection(*aSelection
, doc
,
654 getter_AddRefs(transferable
));
657 rv
= nsCopySupport::GetTransferableForNode(nodeToSerialize
, doc
,
658 getter_AddRefs(transferable
));
660 NS_ENSURE_SUCCESS(rv
, rv
);
662 nsCOMPtr
<nsISupports
> supports
;
663 nsCOMPtr
<nsISupportsString
> data
;
665 rv
= transferable
->GetTransferData(kHTMLMime
, getter_AddRefs(supports
),
667 data
= do_QueryInterface(supports
);
668 if (NS_SUCCEEDED(rv
)) {
669 data
->GetData(mHtmlString
);
671 rv
= transferable
->GetTransferData(kHTMLContext
, getter_AddRefs(supports
),
673 data
= do_QueryInterface(supports
);
674 if (NS_SUCCEEDED(rv
)) {
675 data
->GetData(mContextString
);
677 rv
= transferable
->GetTransferData(kHTMLInfo
, getter_AddRefs(supports
),
679 data
= do_QueryInterface(supports
);
680 if (NS_SUCCEEDED(rv
)) {
681 data
->GetData(mInfoString
);
683 rv
= transferable
->GetTransferData(kUnicodeMime
, getter_AddRefs(supports
),
685 data
= do_QueryInterface(supports
);
686 NS_ENSURE_SUCCESS(rv
, rv
); // require plain text at a minimum
687 data
->GetData(mTitleString
);
690 // default text value is the URL
691 if (mTitleString
.IsEmpty()) {
692 mTitleString
= mUrlString
;
695 // if we haven't constructed a html version, make one now
696 if (mHtmlString
.IsEmpty() && !mUrlString
.IsEmpty())
697 CreateLinkText(mUrlString
, mTitleString
, mHtmlString
);
699 // if there is no drag node, which will be the case for a selection, just
700 // use the selection target node.
701 rv
= AddStringsToDataTransfer(
702 dragNode
? dragNode
: mSelectionTargetNode
.get(), aDataTransfer
);
703 NS_ENSURE_SUCCESS(rv
, rv
);
705 NS_IF_ADDREF(*aDragNode
= dragNode
);
710 DragDataProducer::AddString(DataTransfer
* aDataTransfer
,
711 const nsAString
& aFlavor
,
712 const nsAString
& aData
,
713 nsIPrincipal
* aPrincipal
)
715 nsCOMPtr
<nsIWritableVariant
> variant
= do_CreateInstance(NS_VARIANT_CONTRACTID
);
717 variant
->SetAsAString(aData
);
718 aDataTransfer
->SetDataWithPrincipal(aFlavor
, variant
, 0, aPrincipal
);
723 DragDataProducer::AddStringsToDataTransfer(nsIContent
* aDragNode
,
724 DataTransfer
* aDataTransfer
)
726 NS_ASSERTION(aDragNode
, "adding strings for null node");
728 // set all of the data to have the principal of the node where the data came from
729 nsIPrincipal
* principal
= aDragNode
->NodePrincipal();
731 // add a special flavor if we're an anchor to indicate that we have
732 // a URL in the drag data
733 if (!mUrlString
.IsEmpty() && mIsAnchor
) {
734 nsAutoString
dragData(mUrlString
);
735 dragData
.Append('\n');
736 // Remove leading and trailing newlines in the title and replace them with
737 // space in remaining positions - they confuse PlacesUtils::unwrapNodes
738 // that expects url\ntitle formatted data for x-moz-url.
739 nsAutoString
title(mTitleString
);
741 title
.ReplaceChar("\r\n", ' ');
744 AddString(aDataTransfer
, NS_LITERAL_STRING(kURLMime
), dragData
, principal
);
745 AddString(aDataTransfer
, NS_LITERAL_STRING(kURLDataMime
), mUrlString
, principal
);
746 AddString(aDataTransfer
, NS_LITERAL_STRING(kURLDescriptionMime
), mTitleString
, principal
);
747 AddString(aDataTransfer
, NS_LITERAL_STRING("text/uri-list"), mUrlString
, principal
);
750 // add a special flavor for the html context data
751 if (!mContextString
.IsEmpty())
752 AddString(aDataTransfer
, NS_LITERAL_STRING(kHTMLContext
), mContextString
, principal
);
754 // add a special flavor if we have html info data
755 if (!mInfoString
.IsEmpty())
756 AddString(aDataTransfer
, NS_LITERAL_STRING(kHTMLInfo
), mInfoString
, principal
);
759 if (!mHtmlString
.IsEmpty())
760 AddString(aDataTransfer
, NS_LITERAL_STRING(kHTMLMime
), mHtmlString
, principal
);
762 // add the plain text. we use the url for text/plain data if an anchor is
763 // being dragged, rather than the title text of the link or the alt text for
765 AddString(aDataTransfer
, NS_LITERAL_STRING(kTextMime
),
766 mIsAnchor
? mUrlString
: mTitleString
, principal
);
768 // add image data, if present. For now, all we're going to do with
769 // this is turn it into a native data flavor, so indicate that with
770 // a new flavor so as not to confuse anyone who is really registered
771 // for image/gif or image/jpg.
773 nsCOMPtr
<nsIWritableVariant
> variant
= do_CreateInstance(NS_VARIANT_CONTRACTID
);
775 variant
->SetAsISupports(mImage
);
776 aDataTransfer
->SetDataWithPrincipal(NS_LITERAL_STRING(kNativeImageMime
),
777 variant
, 0, principal
);
780 // assume the image comes from a file, and add a file promise. We
781 // register ourselves as a nsIFlavorDataProvider, and will use the
782 // GetFlavorData callback to save the image to disk.
784 nsCOMPtr
<nsIFlavorDataProvider
> dataProvider
=
785 new nsContentAreaDragDropDataProvider();
787 nsCOMPtr
<nsIWritableVariant
> variant
= do_CreateInstance(NS_VARIANT_CONTRACTID
);
789 variant
->SetAsISupports(dataProvider
);
790 aDataTransfer
->SetDataWithPrincipal(NS_LITERAL_STRING(kFilePromiseMime
),
791 variant
, 0, principal
);
795 AddString(aDataTransfer
, NS_LITERAL_STRING(kFilePromiseURLMime
),
796 mImageSourceString
, principal
);
797 AddString(aDataTransfer
, NS_LITERAL_STRING(kFilePromiseDestFilename
),
798 mImageDestFileName
, principal
);
800 // if not an anchor, add the image url
802 AddString(aDataTransfer
, NS_LITERAL_STRING(kURLDataMime
), mUrlString
, principal
);
803 AddString(aDataTransfer
, NS_LITERAL_STRING("text/uri-list"), mUrlString
, principal
);
810 // note that this can return NS_OK, but a null out param (by design)
813 DragDataProducer::GetDraggableSelectionData(nsISelection
* inSelection
,
814 nsIContent
* inRealTargetNode
,
815 nsIContent
**outImageOrLinkNode
,
816 bool* outDragSelectedText
)
818 NS_ENSURE_ARG(inSelection
);
819 NS_ENSURE_ARG(inRealTargetNode
);
820 NS_ENSURE_ARG_POINTER(outImageOrLinkNode
);
822 *outImageOrLinkNode
= nullptr;
823 *outDragSelectedText
= false;
825 bool selectionContainsTarget
= false;
827 bool isCollapsed
= false;
828 inSelection
->GetIsCollapsed(&isCollapsed
);
830 nsCOMPtr
<nsIDOMNode
> realTargetNode
= do_QueryInterface(inRealTargetNode
);
831 inSelection
->ContainsNode(realTargetNode
, false,
832 &selectionContainsTarget
);
834 if (selectionContainsTarget
) {
835 // track down the anchor node, if any, for the url
836 nsCOMPtr
<nsIDOMNode
> selectionStart
;
837 inSelection
->GetAnchorNode(getter_AddRefs(selectionStart
));
839 nsCOMPtr
<nsIDOMNode
> selectionEnd
;
840 inSelection
->GetFocusNode(getter_AddRefs(selectionEnd
));
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
) {
847 selectionStart
->HasChildNodes(&hasChildren
);
849 // see if just one node is selected
850 int32_t anchorOffset
, focusOffset
;
851 inSelection
->GetAnchorOffset(&anchorOffset
);
852 inSelection
->GetFocusOffset(&focusOffset
);
853 if (abs(anchorOffset
- focusOffset
) == 1) {
854 nsCOMPtr
<nsIContent
> selStartContent
=
855 do_QueryInterface(selectionStart
);
857 if (selStartContent
) {
858 int32_t childOffset
=
859 (anchorOffset
< focusOffset
) ? anchorOffset
: focusOffset
;
860 nsIContent
*childContent
=
861 selStartContent
->GetChildAt(childOffset
);
862 // if we find an image, we'll fall into the node-dragging code,
863 // rather the the selection-dragging code
864 if (nsContentUtils::IsDraggableImage(childContent
)) {
865 NS_ADDREF(*outImageOrLinkNode
= childContent
);
873 // indicate that a link or text is selected
874 *outDragSelectedText
= true;