Bumping gaia.json for 8 gaia revision(s) a=gaia-bump
[gecko.git] / dom / base / nsContentAreaDragDrop.cpp
blob821dbce0ac3fb912695144dedc7ff1785b852bed
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"
8 // Local Includes
9 #include "nsContentAreaDragDrop.h"
11 // Helper Classes
12 #include "nsString.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"
30 #include "nsXPCOM.h"
31 #include "nsISupportsPrimitives.h"
32 #include "nsServiceManagerUtils.h"
33 #include "nsNetUtil.h"
34 #include "nsIFile.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"
41 #include "nsIURL.h"
42 #include "nsIDocument.h"
43 #include "nsIScriptSecurityManager.h"
44 #include "nsIPrincipal.h"
45 #include "nsIDocShellTreeItem.h"
46 #include "nsIWebBrowserPersist.h"
47 #include "nsEscape.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"
54 #include "nsRange.h"
55 #include "mozilla/dom/Element.h"
56 #include "mozilla/dom/HTMLAreaElement.h"
58 using namespace mozilla::dom;
60 class MOZ_STACK_CLASS DragDataProducer
62 public:
63 DragDataProducer(nsPIDOMWindow* aWindow,
64 nsIContent* aTarget,
65 nsIContent* aSelectionTargetNode,
66 bool aIsAltKeyPressed);
67 nsresult Produce(DataTransfer* aDataTransfer,
68 bool* aCanDrag,
69 nsISelection** aSelection,
70 nsIContent** aDragNode);
72 private:
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;
94 nsString mUrlString;
95 nsString mImageSourceString;
96 nsString mImageDestFileName;
97 nsString mTitleString;
98 // will be filled automatically if you fill urlstring
99 nsString mHtmlString;
100 nsString mContextString;
101 nsString mInfoString;
103 bool mIsAnchor;
104 nsCOMPtr<imgIContainer> mImage;
108 nsresult
109 nsContentAreaDragDrop::GetDragData(nsPIDOMWindow* aWindow,
110 nsIContent* aTarget,
111 nsIContent* aSelectionTargetNode,
112 bool aIsAltKeyPressed,
113 DataTransfer* aDataTransfer,
114 bool* aCanDrag,
115 nsISelection** aSelection,
116 nsIContent** aDragNode)
118 NS_ENSURE_TRUE(aSelectionTargetNode, NS_ERROR_INVALID_ARG);
120 *aCanDrag = true;
122 DragDataProducer
123 provider(aWindow, aTarget, aSelectionTargetNode, aIsAltKeyPressed);
124 return provider.Produce(aDataTransfer, aCanDrag, aSelection, aDragNode);
128 NS_IMPL_ISUPPORTS(nsContentAreaDragDropDataProvider, nsIFlavorDataProvider)
130 // SaveURIToFile
131 // used on platforms where it's possible to drag items (e.g. images)
132 // into the file system
133 nsresult
134 nsContentAreaDragDropDataProvider::SaveURIToFile(nsAString& inSourceURIString,
135 nsIFile* inDestFile,
136 bool isPrivate)
138 nsCOMPtr<nsIURI> sourceURI;
139 nsresult rv = NS_NewURI(getter_AddRefs(sourceURI), inSourceURIString);
140 if (NS_FAILED(rv)) {
141 return NS_ERROR_FAILURE;
144 nsCOMPtr<nsIURL> sourceURL = do_QueryInterface(sourceURI);
145 if (!sourceURL) {
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",
156 &rv);
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,
164 nullptr, nullptr,
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.
181 NS_IMETHODIMP
182 nsContentAreaDragDropDataProvider::GetFlavorData(nsITransferable *aTransferable,
183 const char *aFlavor,
184 nsISupports **aData,
185 uint32_t *aDataLen)
187 NS_ENSURE_ARG_POINTER(aData && aDataLen);
188 *aData = nullptr;
189 *aDataLen = 0;
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);
202 if (!supportsString)
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);
213 if (!supportsString)
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
222 // flavor
223 nsCOMPtr<nsISupports> dirPrimitive;
224 dataSize = 0;
225 aTransferable->GetTransferData(kFilePromiseDirectoryMime,
226 getter_AddRefs(dirPrimitive), &dataSize);
227 nsCOMPtr<nsIFile> destDirectory = do_QueryInterface(dirPrimitive);
228 if (!destDirectory)
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);
237 bool isPrivate;
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*);
248 return rv;
251 DragDataProducer::DragDataProducer(nsPIDOMWindow* aWindow,
252 nsIContent* aTarget,
253 nsIContent* aSelectionTargetNode,
254 bool aIsAltKeyPressed)
255 : mWindow(aWindow),
256 mTarget(aTarget),
257 mSelectionTargetNode(aSelectionTargetNode),
258 mIsAltKeyPressed(aIsAltKeyPressed),
259 mIsAnchor(false)
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
269 // return null.
271 already_AddRefed<nsIContent>
272 DragDataProducer::FindParentLinkNode(nsIContent* inNode)
274 nsIContent* content = inNode;
275 if (!content) {
276 // That must have been the document node; nothing else to do here;
277 return nullptr;
280 for (; content; content = content->GetParent()) {
281 if (nsContentUtils::IsDraggableLink(content)) {
282 nsCOMPtr<nsIContent> ret = content;
283 return ret.forget();
287 return nullptr;
292 // GetAnchorURL
294 void
295 DragDataProducer::GetAnchorURL(nsIContent* inNode, nsAString& outURL)
297 nsCOMPtr<nsIURI> linkURI;
298 if (!inNode || !inNode->IsLink(getter_AddRefs(linkURI))) {
299 // Not a link
300 outURL.Truncate();
301 return;
304 nsAutoCString spec;
305 linkURI->GetSpec(spec);
306 CopyUTF8toUTF16(spec, outURL);
311 // CreateLinkText
313 // Creates the html for an anchor in the form
314 // <a href="inURL">inText</a>
316 void
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
323 // string in pieces.
324 nsAutoString linkText(NS_LITERAL_STRING("<a href=\"") +
325 inURL +
326 NS_LITERAL_STRING("\">") +
327 inText +
328 NS_LITERAL_STRING("</a>") );
330 outLinkText = linkText;
335 // GetNodeString
337 // Gets the text associated with a node
339 void
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);
351 if (range) {
352 range->SelectNode(*node, rv);
353 range->ToString(outNodeString);
357 nsresult
358 DragDataProducer::Produce(DataTransfer* aDataTransfer,
359 bool* aCanDrag,
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;
370 nsresult rv;
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);
382 if (textControl) {
383 nsISelectionController* selcon = textControl->GetSelectionController();
384 if (selcon) {
385 selcon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
388 if (!selection)
389 return NS_OK;
391 else {
392 mWindow->GetSelection(getter_AddRefs(selection));
393 if (!selection)
394 return NS_OK;
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()) {
403 return NS_OK;
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)
418 return NS_OK;
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)
426 return NS_OK;
428 selection.swap(*aSelection);
430 else {
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) {
450 *aCanDrag = false;
451 return NS_OK;
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);
472 } else {
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) {
481 *aCanDrag = false;
482 return NS_OK;
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;
494 if (area) {
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>
504 mIsAnchor = true;
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;
516 } else if (image) {
517 mIsAnchor = true;
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
520 // attribute.
521 nsCOMPtr<nsIDOMElement> imageElement(do_QueryInterface(image));
522 // XXXbz Shouldn't we use the "title" attr for title? Using
523 // "alt" seems very wrong....
524 if (imageElement) {
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));
546 if (imgUrl) {
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));
557 if (mimeInfo) {
558 nsAutoCString spec;
559 imgUrl->GetSpec(spec);
561 // pass out the image source string
562 CopyUTF8toUTF16(spec, mImageSourceString);
563 mUrlString = mImageSourceString;
565 bool validExtension;
566 if (extension.IsEmpty() ||
567 NS_FAILED(mimeInfo->ExtensionExists(extension,
568 &validExtension)) ||
569 !validExtension) {
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,
589 '-');
591 CopyUTF8toUTF16(fileName, mImageDestFileName);
593 // and the image object
594 mImage = img;
598 if (mUrlString.IsEmpty()) {
599 nsCOMPtr<nsIURI> imageURI;
600 image->GetCurrentURI(getter_AddRefs(imageURI));
601 if (imageURI) {
602 nsAutoCString spec;
603 imageURI->GetSpec(spec);
604 CopyUTF8toUTF16(spec, mUrlString);
607 if (mTitleString.IsEmpty()) {
608 mTitleString = mUrlString;
611 if (parentLink) {
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;
616 } else {
617 nodeToSerialize = do_QueryInterface(draggedNode);
619 dragNode = nodeToSerialize;
620 } else if (link) {
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) {
629 // nothing draggable
630 return NS_OK;
633 if (linkNode) {
634 mIsAnchor = true;
635 GetAnchorURL(linkNode, mUrlString);
636 dragNode = linkNode;
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;
652 if (*aSelection) {
653 rv = nsCopySupport::GetTransferableForSelection(*aSelection, doc,
654 getter_AddRefs(transferable));
656 else {
657 rv = nsCopySupport::GetTransferableForNode(nodeToSerialize, doc,
658 getter_AddRefs(transferable));
660 NS_ENSURE_SUCCESS(rv, rv);
662 nsCOMPtr<nsISupports> supports;
663 nsCOMPtr<nsISupportsString> data;
664 uint32_t dataSize;
665 rv = transferable->GetTransferData(kHTMLMime, getter_AddRefs(supports),
666 &dataSize);
667 data = do_QueryInterface(supports);
668 if (NS_SUCCEEDED(rv)) {
669 data->GetData(mHtmlString);
671 rv = transferable->GetTransferData(kHTMLContext, getter_AddRefs(supports),
672 &dataSize);
673 data = do_QueryInterface(supports);
674 if (NS_SUCCEEDED(rv)) {
675 data->GetData(mContextString);
677 rv = transferable->GetTransferData(kHTMLInfo, getter_AddRefs(supports),
678 &dataSize);
679 data = do_QueryInterface(supports);
680 if (NS_SUCCEEDED(rv)) {
681 data->GetData(mInfoString);
683 rv = transferable->GetTransferData(kUnicodeMime, getter_AddRefs(supports),
684 &dataSize);
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);
706 return NS_OK;
709 void
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);
716 if (variant) {
717 variant->SetAsAString(aData);
718 aDataTransfer->SetDataWithPrincipal(aFlavor, variant, 0, aPrincipal);
722 nsresult
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);
740 title.Trim("\r\n");
741 title.ReplaceChar("\r\n", ' ');
742 dragData += title;
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);
758 // add the full html
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
764 // an anchor image.
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.
772 if (mImage) {
773 nsCOMPtr<nsIWritableVariant> variant = do_CreateInstance(NS_VARIANT_CONTRACTID);
774 if (variant) {
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();
786 if (dataProvider) {
787 nsCOMPtr<nsIWritableVariant> variant = do_CreateInstance(NS_VARIANT_CONTRACTID);
788 if (variant) {
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
801 if (!mIsAnchor) {
802 AddString(aDataTransfer, NS_LITERAL_STRING(kURLDataMime), mUrlString, principal);
803 AddString(aDataTransfer, NS_LITERAL_STRING("text/uri-list"), mUrlString, principal);
807 return NS_OK;
810 // note that this can return NS_OK, but a null out param (by design)
811 // static
812 nsresult
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);
829 if (!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) {
846 bool hasChildren;
847 selectionStart->HasChildNodes(&hasChildren);
848 if (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);
866 return NS_OK;
873 // indicate that a link or text is selected
874 *outDragSelectedText = true;
878 return NS_OK;