Bug 1665252 - remove allowpaymentrequest attribute from HTMLIFrameElement r=dom-worke...
[gecko.git] / dom / base / nsContentAreaDragDrop.cpp
blobddf603b1eb28115abfafb82e0c43d07390c9629d
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"
9 // Local Includes
10 #include "nsContentAreaDragDrop.h"
12 // Helper Classes
13 #include "nsString.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"
22 #include "nsXPCOM.h"
23 #include "nsISupportsPrimitives.h"
24 #include "nsServiceManagerUtils.h"
25 #include "nsNetUtil.h"
26 #include "nsIFile.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"
34 #include "nsIURL.h"
35 #include "nsIURIMutator.h"
36 #include "mozilla/dom/Document.h"
37 #include "nsIPrincipal.h"
38 #include "nsIWebBrowserPersist.h"
39 #include "nsEscape.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"
46 #include "nsRange.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 {
62 public:
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);
69 private:
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,
82 nsAString& outURL);
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;
92 nsString mUrlString;
93 nsString mImageSourceString;
94 nsString mImageDestFileName;
95 #if defined(XP_MACOSX)
96 nsString mImageRequestMime;
97 #endif
98 nsString mTitleString;
99 // will be filled automatically if you fill urlstring
100 nsString mHtmlString;
101 nsString mContextString;
102 nsString mInfoString;
104 bool mIsAnchor;
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);
116 *aCanDrag = true;
118 DragDataProducer provider(aWindow, aTarget, aSelectionTargetNode,
119 aIsAltKeyPressed);
120 return provider.Produce(aDataTransfer, aCanDrag, aSelection, aDragNode,
121 aPrincipal, aCsp);
124 NS_IMPL_ISUPPORTS(nsContentAreaDragDropDataProvider, nsIFlavorDataProvider)
126 // SaveURIToFile
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,
132 bool isPrivate) {
133 nsCOMPtr<nsIURL> sourceURL = do_QueryInterface(inSourceURI);
134 if (!sourceURL) {
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
163 * |aMimeType|
164 * @param aPrimaryExtension [out] the primary extension for the MIME type
165 * to potentially be used as a replacement
166 * for |aExtension|
168 nsresult CheckAndGetExtensionForMime(const nsCString& aExtension,
169 const nsCString& aMimeType,
170 bool* aIsValidExtension,
171 nsACString* aPrimaryExtension) {
172 nsresult rv;
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;
188 return NS_OK;
191 rv = mimeInfo->ExtensionExists(aExtension, aIsValidExtension);
192 NS_ENSURE_SUCCESS(rv, rv);
194 return NS_OK;
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.
210 NS_IMETHODIMP
211 nsContentAreaDragDropDataProvider::GetFlavorData(nsITransferable* aTransferable,
212 const char* aFlavor,
213 nsISupports** aData) {
214 NS_ENSURE_ARG_POINTER(aData);
215 *aData = nullptr;
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
284 // extension.
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,
296 '-');
297 #endif /* defined(XP_MACOSX) */
299 // get the target directory from the kFilePromiseDirectoryMime
300 // flavor
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();
319 rv =
320 SaveURIToFile(sourceURI, principal, file, contentPolicyType, isPrivate);
321 // send back an nsIFile
322 if (NS_SUCCEEDED(rv)) {
323 CallQueryInterface(file, aData);
327 return rv;
330 DragDataProducer::DragDataProducer(nsPIDOMWindowOuter* aWindow,
331 nsIContent* aTarget,
332 nsIContent* aSelectionTargetNode,
333 bool aIsAltKeyPressed)
334 : mWindow(aWindow),
335 mTarget(aTarget),
336 mSelectionTargetNode(aSelectionTargetNode),
337 mIsAltKeyPressed(aIsAltKeyPressed),
338 mIsAnchor(false) {}
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
345 // return null.
347 already_AddRefed<nsIContent> DragDataProducer::FindParentLinkNode(
348 nsIContent* inNode) {
349 nsIContent* content = inNode;
350 if (!content) {
351 // That must have been the document node; nothing else to do here;
352 return nullptr;
355 for (; content; content = content->GetParent()) {
356 if (nsContentUtils::IsDraggableLink(content)) {
357 nsCOMPtr<nsIContent> ret = content;
358 return ret.forget();
362 return nullptr;
366 // GetAnchorURL
368 nsresult DragDataProducer::GetAnchorURL(nsIContent* inNode, nsAString& outURL) {
369 nsCOMPtr<nsIURI> linkURI;
370 if (!inNode || !inNode->IsLink(getter_AddRefs(linkURI))) {
371 // Not a link
372 outURL.Truncate();
373 return NS_OK;
376 nsAutoCString spec;
377 nsresult rv = linkURI->GetSpec(spec);
378 NS_ENSURE_SUCCESS(rv, rv);
379 CopyUTF8toUTF16(spec, outURL);
380 return NS_OK;
384 // CreateLinkText
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
394 // string in pieces.
395 nsAutoString linkText(u"<a href=\""_ns + inURL + u"\">"_ns + inText +
396 u"</a>"_ns);
398 outLinkText = linkText;
402 // GetNodeString
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());
415 if (range) {
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));
427 if (imgUrl) {
428 nsAutoCString spec;
429 nsresult rv = imgUrl->GetSpec(spec);
430 NS_ENSURE_SUCCESS(rv, rv);
432 // pass out the image source string
433 CopyUTF8toUTF16(spec, mImageSourceString);
435 nsCString mimeType;
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);
444 #else
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));
453 if (mimeInfo) {
454 nsAutoCString extension;
455 imgUrl->GetFileExtension(extension);
457 bool validExtension;
458 if (extension.IsEmpty() ||
459 NS_FAILED(mimeInfo->ExtensionExists(extension, &validExtension)) ||
460 !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))
468 .Finalize(imgUrl);
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, '-');
483 #endif
485 CopyUTF8toUTF16(fileName, mImageDestFileName);
487 // and the image object
488 mImage = aImage;
491 return NS_OK;
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;
507 nsresult rv;
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()
517 : nullptr;
518 RefPtr<TextControlElement> textControlElement =
519 TextControlElement::GetTextControlElementFromEditingHost(editingElement);
520 if (textControlElement) {
521 nsISelectionController* selcon =
522 textControlElement->GetSelectionController();
523 if (selcon) {
524 selection =
525 selcon->GetSelection(nsISelectionController::SELECTION_NORMAL);
528 if (!selection) return NS_OK;
529 } else {
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()) {
540 return NS_OK;
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");
557 return NS_OK;
560 if (isChromeShell && textControlElement) {
561 // Only use the selection if the target node is in the selection.
562 if (!selection->ContainsNode(*mSelectionTargetNode, false, IgnoreErrors()))
563 return NS_OK;
565 selection.swap(*aSelection);
566 } else {
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) {
586 *aCanDrag = false;
587 return NS_OK;
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);
606 } else {
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) {
615 *aCanDrag = false;
616 return NS_OK;
618 image = do_QueryInterface(draggedNode);
622 // set for linked images, and links
623 nsCOMPtr<nsIContent> linkNode;
625 RefPtr<HTMLAreaElement> areaElem =
626 HTMLAreaElement::FromNodeOrNull(draggedNode);
627 if (areaElem) {
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>
636 mIsAnchor = true;
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;
649 } else if (image) {
650 mIsAnchor = true;
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
653 // attribute.
654 nsCOMPtr<nsIURI> imageURI;
655 image->GetCurrentURI(getter_AddRefs(imageURI));
656 if (imageURI) {
657 nsAutoCString spec;
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>?
668 if (imageElement) {
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));
681 if (imgRequest) {
682 rv = GetImageData(img, imgRequest);
683 NS_ENSURE_SUCCESS(rv, rv);
686 if (parentLink) {
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;
691 } else {
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) {
704 // nothing draggable
705 return NS_OK;
708 if (linkNode) {
709 mIsAnchor = true;
710 rv = GetAnchorURL(linkNode, mUrlString);
711 NS_ENSURE_SUCCESS(rv, rv);
712 dragNode = linkNode;
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();
727 if (csp) {
728 NS_IF_ADDREF(*aCsp = csp);
731 // if we have selected text, use it in preference to the node
732 nsCOMPtr<nsITransferable> transferable;
733 if (*aSelection) {
734 rv = nsCopySupport::GetTransferableForSelection(
735 *aSelection, doc, getter_AddRefs(transferable));
736 } else {
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);
781 return NS_OK;
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
798 // from
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);
810 title.Trim("\r\n");
811 title.ReplaceChar("\r\n", ' ');
812 dragData += title;
814 AddString(aDataTransfer, NS_LITERAL_STRING_FROM_CSTRING(kURLMime), dragData,
815 principal);
816 AddString(aDataTransfer, NS_LITERAL_STRING_FROM_CSTRING(kURLDataMime),
817 mUrlString, principal);
818 AddString(aDataTransfer,
819 NS_LITERAL_STRING_FROM_CSTRING(kURLDescriptionMime), mTitleString,
820 principal);
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);
834 // add the full html
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
841 // an anchor image.
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.
849 if (mImage) {
850 RefPtr<nsVariantCC> variant = new nsVariantCC();
851 variant->SetAsISupports(mImage);
852 aDataTransfer->SetDataWithPrincipal(
853 NS_LITERAL_STRING_FROM_CSTRING(kNativeImageMime), variant, 0,
854 principal);
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();
862 if (dataProvider) {
863 RefPtr<nsVariantCC> variant = new nsVariantCC();
864 variant->SetAsISupports(dataProvider);
865 aDataTransfer->SetDataWithPrincipal(
866 NS_LITERAL_STRING_FROM_CSTRING(kFilePromiseMime), variant, 0,
867 principal);
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);
879 #endif
881 // if not an anchor, add the image url
882 if (!mIsAnchor) {
883 AddString(aDataTransfer, NS_LITERAL_STRING_FROM_CSTRING(kURLDataMime),
884 mUrlString, principal);
885 AddString(aDataTransfer, u"text/uri-list"_ns, mUrlString, principal);
889 return NS_OK;
892 // note that this can return NS_OK, but a null out param (by design)
893 // static
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);
929 return NS_OK;
935 // indicate that a link or text is selected
936 *outDragSelectedText = true;
940 return NS_OK;