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/. */
6 
7 #include "nsReadableUtils.h"
8 
9 // Local Includes
10 #include "nsContentAreaDragDrop.h"
11 
12 // Helper Classes
13 #include "nsString.h"
14 
15 // Interfaces needed to be included
16 #include "nsCopySupport.h"
17 #include "nsIDOMUIEvent.h"
18 #include "nsISelection.h"
19 #include "nsISelectionController.h"
20 #include "nsIDOMNode.h"
21 #include "nsIDOMNodeList.h"
22 #include "nsIDOMEvent.h"
23 #include "nsIDOMDragEvent.h"
24 #include "nsPIDOMWindow.h"
25 #include "nsIDOMRange.h"
26 #include "nsIFormControl.h"
27 #include "nsITransferable.h"
28 #include "nsComponentManagerUtils.h"
29 #include "nsXPCOM.h"
30 #include "nsISupportsPrimitives.h"
31 #include "nsServiceManagerUtils.h"
32 #include "nsNetUtil.h"
33 #include "nsIFile.h"
34 #include "nsFrameLoader.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 "nsIURIMutator.h"
43 #include "nsIDocument.h"
44 #include "nsIScriptSecurityManager.h"
45 #include "nsIPrincipal.h"
46 #include "nsIDocShellTreeItem.h"
47 #include "nsIWebBrowserPersist.h"
48 #include "nsEscape.h"
49 #include "nsContentUtils.h"
50 #include "nsIMIMEService.h"
51 #include "imgIContainer.h"
52 #include "imgIRequest.h"
53 #include "mozilla/dom/DataTransfer.h"
54 #include "nsIMIMEInfo.h"
55 #include "nsRange.h"
56 #include "TabParent.h"
57 #include "mozilla/dom/Element.h"
58 #include "mozilla/dom/HTMLAreaElement.h"
59 #include "mozilla/dom/HTMLAnchorElement.h"
60 #include "nsVariant.h"
61 
62 using namespace mozilla::dom;
63 using mozilla::IgnoreErrors;
64 
65 class MOZ_STACK_CLASS DragDataProducer {
66  public:
67   DragDataProducer(nsPIDOMWindowOuter* aWindow, nsIContent* aTarget,
68                    nsIContent* aSelectionTargetNode, bool aIsAltKeyPressed);
69   nsresult Produce(DataTransfer* aDataTransfer, bool* aCanDrag,
70                    nsISelection** aSelection, nsIContent** aDragNode,
71                    nsACString& aPrincipalURISpec);
72 
73  private:
74   void AddString(DataTransfer* aDataTransfer, const nsAString& aFlavor,
75                  const nsAString& aData, nsIPrincipal* aPrincipal,
76                  bool aHidden = false);
77   nsresult AddStringsToDataTransfer(nsIContent* aDragNode,
78                                     DataTransfer* aDataTransfer);
79   nsresult GetImageData(imgIContainer* aImage, imgIRequest* aRequest);
80   static nsresult GetDraggableSelectionData(nsISelection* inSelection,
81                                             nsIContent* inRealTargetNode,
82                                             nsIContent** outImageOrLinkNode,
83                                             bool* outDragSelectedText);
84   static already_AddRefed<nsIContent> FindParentLinkNode(nsIContent* inNode);
85   static MOZ_MUST_USE nsresult GetAnchorURL(nsIContent* inNode,
86                                             nsAString& outURL);
87   static void GetNodeString(nsIContent* inNode, nsAString& outNodeString);
88   static void CreateLinkText(const nsAString& inURL, const nsAString& inText,
89                              nsAString& outLinkText);
90 
91   nsCOMPtr<nsPIDOMWindowOuter> mWindow;
92   nsCOMPtr<nsIContent> mTarget;
93   nsCOMPtr<nsIContent> mSelectionTargetNode;
94   bool mIsAltKeyPressed;
95 
96   nsString mUrlString;
97   nsString mImageSourceString;
98   nsString mImageDestFileName;
99 #if defined(XP_MACOSX)
100   nsString mImageRequestMime;
101 #endif
102   nsString mTitleString;
103   // will be filled automatically if you fill urlstring
104   nsString mHtmlString;
105   nsString mContextString;
106   nsString mInfoString;
107 
108   bool mIsAnchor;
109   nsCOMPtr<imgIContainer> mImage;
110 };
111 
GetDragData(nsPIDOMWindowOuter * aWindow,nsIContent * aTarget,nsIContent * aSelectionTargetNode,bool aIsAltKeyPressed,DataTransfer * aDataTransfer,bool * aCanDrag,nsISelection ** aSelection,nsIContent ** aDragNode,nsACString & aPrincipalURISpec)112 nsresult nsContentAreaDragDrop::GetDragData(
113     nsPIDOMWindowOuter* aWindow, nsIContent* aTarget,
114     nsIContent* aSelectionTargetNode, bool aIsAltKeyPressed,
115     DataTransfer* aDataTransfer, bool* aCanDrag, nsISelection** aSelection,
116     nsIContent** aDragNode, nsACString& aPrincipalURISpec) {
117   NS_ENSURE_TRUE(aSelectionTargetNode, NS_ERROR_INVALID_ARG);
118 
119   *aCanDrag = true;
120 
121   DragDataProducer provider(aWindow, aTarget, aSelectionTargetNode,
122                             aIsAltKeyPressed);
123   return provider.Produce(aDataTransfer, aCanDrag, aSelection, aDragNode,
124                           aPrincipalURISpec);
125 }
126 
NS_IMPL_ISUPPORTS(nsContentAreaDragDropDataProvider,nsIFlavorDataProvider)127 NS_IMPL_ISUPPORTS(nsContentAreaDragDropDataProvider, nsIFlavorDataProvider)
128 
129 // SaveURIToFile
130 // used on platforms where it's possible to drag items (e.g. images)
131 // into the file system
132 nsresult nsContentAreaDragDropDataProvider::SaveURIToFile(nsIURI* inSourceURI,
133                                                           nsIFile* inDestFile,
134                                                           bool isPrivate) {
135   nsCOMPtr<nsIURL> sourceURL = do_QueryInterface(inSourceURI);
136   if (!sourceURL) {
137     return NS_ERROR_NO_INTERFACE;
138   }
139 
140   nsresult rv = inDestFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
141   NS_ENSURE_SUCCESS(rv, rv);
142 
143   // we rely on the fact that the WPB is refcounted by the channel etc,
144   // so we don't keep a ref to it. It will die when finished.
145   nsCOMPtr<nsIWebBrowserPersist> persist = do_CreateInstance(
146       "@mozilla.org/embedding/browser/nsWebBrowserPersist;1", &rv);
147   NS_ENSURE_SUCCESS(rv, rv);
148 
149   persist->SetPersistFlags(
150       nsIWebBrowserPersist::PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION);
151 
152   // referrer policy can be anything since the referrer is nullptr
153   return persist->SavePrivacyAwareURI(inSourceURI, nullptr, nullptr,
154                                       mozilla::net::RP_Unset, nullptr, nullptr,
155                                       inDestFile, isPrivate);
156 }
157 
158 /*
159  * Check if the provided filename extension is valid for the MIME type and
160  * return the MIME type's primary extension.
161  *
162  * @param aExtension           [in]  the extension to check
163  * @param aMimeType            [in]  the MIME type to check the extension with
164  * @param aIsValidExtension    [out] true if |aExtension| is valid for
165  *                                   |aMimeType|
166  * @param aPrimaryExtension    [out] the primary extension for the MIME type
167  *                                   to potentially be used as a replacement
168  *                                   for |aExtension|
169  */
CheckAndGetExtensionForMime(const nsCString & aExtension,const nsCString & aMimeType,bool * aIsValidExtension,nsACString * aPrimaryExtension)170 nsresult CheckAndGetExtensionForMime(const nsCString& aExtension,
171                                      const nsCString& aMimeType,
172                                      bool* aIsValidExtension,
173                                      nsACString* aPrimaryExtension) {
174   nsresult rv;
175 
176   nsCOMPtr<nsIMIMEService> mimeService = do_GetService("@mozilla.org/mime;1");
177   if (NS_WARN_IF(!mimeService)) {
178     return NS_ERROR_FAILURE;
179   }
180 
181   nsCOMPtr<nsIMIMEInfo> mimeInfo;
182   rv = mimeService->GetFromTypeAndExtension(aMimeType, EmptyCString(),
183                                             getter_AddRefs(mimeInfo));
184   NS_ENSURE_SUCCESS(rv, rv);
185 
186   rv = mimeInfo->GetPrimaryExtension(*aPrimaryExtension);
187   NS_ENSURE_SUCCESS(rv, rv);
188 
189   if (aExtension.IsEmpty()) {
190     *aIsValidExtension = false;
191     return NS_OK;
192   }
193 
194   rv = mimeInfo->ExtensionExists(aExtension, aIsValidExtension);
195   NS_ENSURE_SUCCESS(rv, rv);
196 
197   return NS_OK;
198 }
199 
200 // This is our nsIFlavorDataProvider callback. There are several
201 // assumptions here that make this work:
202 //
203 // 1. Someone put a kFilePromiseURLMime flavor into the transferable
204 //    with the source URI of the file to save (as a string). We did
205 //    that in AddStringsToDataTransfer.
206 //
207 // 2. Someone put a kFilePromiseDirectoryMime flavor into the
208 //    transferable with an nsIFile for the directory we are to
209 //    save in. That has to be done by platform-specific code (in
210 //    widget), which gets the destination directory from
211 //    OS-specific drag information.
212 //
213 NS_IMETHODIMP
GetFlavorData(nsITransferable * aTransferable,const char * aFlavor,nsISupports ** aData,uint32_t * aDataLen)214 nsContentAreaDragDropDataProvider::GetFlavorData(nsITransferable* aTransferable,
215                                                  const char* aFlavor,
216                                                  nsISupports** aData,
217                                                  uint32_t* aDataLen) {
218   NS_ENSURE_ARG_POINTER(aData && aDataLen);
219   *aData = nullptr;
220   *aDataLen = 0;
221 
222   nsresult rv = NS_ERROR_NOT_IMPLEMENTED;
223 
224   if (strcmp(aFlavor, kFilePromiseMime) == 0) {
225     // get the URI from the kFilePromiseURLMime flavor
226     NS_ENSURE_ARG(aTransferable);
227     nsCOMPtr<nsISupports> tmp;
228     uint32_t dataSize = 0;
229     aTransferable->GetTransferData(kFilePromiseURLMime, getter_AddRefs(tmp),
230                                    &dataSize);
231     nsCOMPtr<nsISupportsString> supportsString = do_QueryInterface(tmp);
232     if (!supportsString) return NS_ERROR_FAILURE;
233 
234     nsAutoString sourceURLString;
235     supportsString->GetData(sourceURLString);
236     if (sourceURLString.IsEmpty()) return NS_ERROR_FAILURE;
237 
238     nsCOMPtr<nsIURI> sourceURI;
239     rv = NS_NewURI(getter_AddRefs(sourceURI), sourceURLString);
240     NS_ENSURE_SUCCESS(rv, rv);
241 
242     aTransferable->GetTransferData(kFilePromiseDestFilename,
243                                    getter_AddRefs(tmp), &dataSize);
244     supportsString = do_QueryInterface(tmp);
245     if (!supportsString) return NS_ERROR_FAILURE;
246 
247     nsAutoString targetFilename;
248     supportsString->GetData(targetFilename);
249     if (targetFilename.IsEmpty()) return NS_ERROR_FAILURE;
250 
251 #if defined(XP_MACOSX)
252     // Use the image request's MIME type to ensure the filename's
253     // extension is compatible with the OS's handler for this type.
254     // If it isn't, or is missing, replace the extension with the
255     // primary extension. On Mac, do this in the parent process
256     // because sandboxing blocks access to MIME-handler info from
257     // content processes.
258     if (XRE_IsParentProcess()) {
259       aTransferable->GetTransferData(kImageRequestMime, getter_AddRefs(tmp),
260                                      &dataSize);
261       supportsString = do_QueryInterface(tmp);
262       if (!supportsString) return NS_ERROR_FAILURE;
263 
264       nsAutoString imageRequestMime;
265       supportsString->GetData(imageRequestMime);
266 
267       // If we have a MIME type, check the extension is compatible
268       if (!imageRequestMime.IsEmpty()) {
269         // Build a URL to get the filename extension
270         nsCOMPtr<nsIURL> imageURL = do_QueryInterface(sourceURI, &rv);
271         NS_ENSURE_SUCCESS(rv, rv);
272 
273         nsAutoCString extension;
274         rv = imageURL->GetFileExtension(extension);
275         NS_ENSURE_SUCCESS(rv, rv);
276 
277         NS_ConvertUTF16toUTF8 mimeCString(imageRequestMime);
278         bool isValidExtension;
279         nsAutoCString primaryExtension;
280         rv = CheckAndGetExtensionForMime(extension, mimeCString,
281                                          &isValidExtension, &primaryExtension);
282         NS_ENSURE_SUCCESS(rv, rv);
283 
284         if (!isValidExtension) {
285           // The filename extension is missing or incompatible
286           // with the MIME type, replace it with the primary
287           // extension.
288           nsAutoCString newFileName;
289           rv = imageURL->GetFileBaseName(newFileName);
290           NS_ENSURE_SUCCESS(rv, rv);
291           newFileName.Append(".");
292           newFileName.Append(primaryExtension);
293           targetFilename = NS_ConvertUTF8toUTF16(newFileName);
294         }
295       }
296     }
297     // make the filename safe for the filesystem
298     targetFilename.ReplaceChar(FILE_PATH_SEPARATOR FILE_ILLEGAL_CHARACTERS,
299                                '-');
300 #endif /* defined(XP_MACOSX) */
301 
302     // get the target directory from the kFilePromiseDirectoryMime
303     // flavor
304     nsCOMPtr<nsISupports> dirPrimitive;
305     dataSize = 0;
306     aTransferable->GetTransferData(kFilePromiseDirectoryMime,
307                                    getter_AddRefs(dirPrimitive), &dataSize);
308     nsCOMPtr<nsIFile> destDirectory = do_QueryInterface(dirPrimitive);
309     if (!destDirectory) return NS_ERROR_FAILURE;
310 
311     nsCOMPtr<nsIFile> file;
312     rv = destDirectory->Clone(getter_AddRefs(file));
313     NS_ENSURE_SUCCESS(rv, rv);
314 
315     file->Append(targetFilename);
316 
317     bool isPrivate;
318     aTransferable->GetIsPrivateData(&isPrivate);
319 
320     rv = SaveURIToFile(sourceURI, file, isPrivate);
321     // send back an nsIFile
322     if (NS_SUCCEEDED(rv)) {
323       CallQueryInterface(file, aData);
324       *aDataLen = sizeof(nsIFile*);
325     }
326   }
327 
328   return rv;
329 }
330 
DragDataProducer(nsPIDOMWindowOuter * aWindow,nsIContent * aTarget,nsIContent * aSelectionTargetNode,bool aIsAltKeyPressed)331 DragDataProducer::DragDataProducer(nsPIDOMWindowOuter* aWindow,
332                                    nsIContent* aTarget,
333                                    nsIContent* aSelectionTargetNode,
334                                    bool aIsAltKeyPressed)
335     : mWindow(aWindow),
336       mTarget(aTarget),
337       mSelectionTargetNode(aSelectionTargetNode),
338       mIsAltKeyPressed(aIsAltKeyPressed),
339       mIsAnchor(false) {}
340 
341 //
342 // FindParentLinkNode
343 //
344 // Finds the parent with the given link tag starting at |inNode|. If
345 // it gets up to the root without finding it, we stop looking and
346 // return null.
347 //
FindParentLinkNode(nsIContent * inNode)348 already_AddRefed<nsIContent> DragDataProducer::FindParentLinkNode(
349     nsIContent* inNode) {
350   nsIContent* content = inNode;
351   if (!content) {
352     // That must have been the document node; nothing else to do here;
353     return nullptr;
354   }
355 
356   for (; content; content = content->GetParent()) {
357     if (nsContentUtils::IsDraggableLink(content)) {
358       nsCOMPtr<nsIContent> ret = content;
359       return ret.forget();
360     }
361   }
362 
363   return nullptr;
364 }
365 
366 //
367 // GetAnchorURL
368 //
GetAnchorURL(nsIContent * inNode,nsAString & outURL)369 nsresult DragDataProducer::GetAnchorURL(nsIContent* inNode, nsAString& outURL) {
370   nsCOMPtr<nsIURI> linkURI;
371   if (!inNode || !inNode->IsLink(getter_AddRefs(linkURI))) {
372     // Not a link
373     outURL.Truncate();
374     return NS_OK;
375   }
376 
377   nsAutoCString spec;
378   nsresult rv = linkURI->GetSpec(spec);
379   NS_ENSURE_SUCCESS(rv, rv);
380   CopyUTF8toUTF16(spec, outURL);
381   return NS_OK;
382 }
383 
384 //
385 // CreateLinkText
386 //
387 // Creates the html for an anchor in the form
388 //  <a href="inURL">inText</a>
389 //
CreateLinkText(const nsAString & inURL,const nsAString & inText,nsAString & outLinkText)390 void DragDataProducer::CreateLinkText(const nsAString& inURL,
391                                       const nsAString& inText,
392                                       nsAString& outLinkText) {
393   // use a temp var in case |inText| is the same string as
394   // |outLinkText| to avoid overwriting it while building up the
395   // string in pieces.
396   nsAutoString linkText(NS_LITERAL_STRING("<a href=\"") + inURL +
397                         NS_LITERAL_STRING("\">") + inText +
398                         NS_LITERAL_STRING("</a>"));
399 
400   outLinkText = linkText;
401 }
402 
403 //
404 // GetNodeString
405 //
406 // Gets the text associated with a node
407 //
GetNodeString(nsIContent * inNode,nsAString & outNodeString)408 void DragDataProducer::GetNodeString(nsIContent* inNode,
409                                      nsAString& outNodeString) {
410   nsCOMPtr<nsINode> node = inNode;
411 
412   outNodeString.Truncate();
413 
414   // use a range to get the text-equivalent of the node
415   nsCOMPtr<nsIDocument> doc = node->OwnerDoc();
416   RefPtr<nsRange> range = doc->CreateRange(IgnoreErrors());
417   if (range) {
418     range->SelectNode(*node, IgnoreErrors());
419     range->ToString(outNodeString);
420   }
421 }
422 
GetImageData(imgIContainer * aImage,imgIRequest * aRequest)423 nsresult DragDataProducer::GetImageData(imgIContainer* aImage,
424                                         imgIRequest* aRequest) {
425   nsCOMPtr<nsIURI> imgUri;
426   aRequest->GetURI(getter_AddRefs(imgUri));
427 
428   nsCOMPtr<nsIURL> imgUrl(do_QueryInterface(imgUri));
429   if (imgUrl) {
430     nsAutoCString spec;
431     nsresult rv = imgUrl->GetSpec(spec);
432     NS_ENSURE_SUCCESS(rv, rv);
433 
434     // pass out the image source string
435     CopyUTF8toUTF16(spec, mImageSourceString);
436 
437     nsCString mimeType;
438     aRequest->GetMimeType(getter_Copies(mimeType));
439 
440 #if defined(XP_MACOSX)
441     // Save the MIME type so we can make sure the extension
442     // is compatible (and replace it if it isn't) when the
443     // image is dropped. On Mac, we need to get the OS MIME
444     // handler information in the parent due to sandboxing.
445     CopyUTF8toUTF16(mimeType, mImageRequestMime);
446 #else
447     nsCOMPtr<nsIMIMEService> mimeService = do_GetService("@mozilla.org/mime;1");
448     if (NS_WARN_IF(!mimeService)) {
449       return NS_ERROR_FAILURE;
450     }
451 
452     nsCOMPtr<nsIMIMEInfo> mimeInfo;
453     mimeService->GetFromTypeAndExtension(mimeType, EmptyCString(),
454                                          getter_AddRefs(mimeInfo));
455     if (mimeInfo) {
456       nsAutoCString extension;
457       imgUrl->GetFileExtension(extension);
458 
459       bool validExtension;
460       if (extension.IsEmpty() ||
461           NS_FAILED(mimeInfo->ExtensionExists(extension, &validExtension)) ||
462           !validExtension) {
463         // Fix the file extension in the URL
464         nsAutoCString primaryExtension;
465         mimeInfo->GetPrimaryExtension(primaryExtension);
466 
467         rv = NS_MutateURI(imgUrl)
468                  .Apply(NS_MutatorMethod(&nsIURLMutator::SetFileExtension,
469                                          primaryExtension, nullptr))
470                  .Finalize(imgUrl);
471         NS_ENSURE_SUCCESS(rv, rv);
472       }
473     }
474 #endif /* defined(XP_MACOSX) */
475 
476     nsAutoCString fileName;
477     imgUrl->GetFileName(fileName);
478 
479     NS_UnescapeURL(fileName);
480 
481 #if !defined(XP_MACOSX)
482     // make the filename safe for the filesystem
483     fileName.ReplaceChar(FILE_PATH_SEPARATOR FILE_ILLEGAL_CHARACTERS, '-');
484 #endif
485 
486     CopyUTF8toUTF16(fileName, mImageDestFileName);
487 
488     // and the image object
489     mImage = aImage;
490   }
491 
492   return NS_OK;
493 }
494 
Produce(DataTransfer * aDataTransfer,bool * aCanDrag,nsISelection ** aSelection,nsIContent ** aDragNode,nsACString & aPrincipalURISpec)495 nsresult DragDataProducer::Produce(DataTransfer* aDataTransfer, bool* aCanDrag,
496                                    nsISelection** aSelection,
497                                    nsIContent** aDragNode,
498                                    nsACString& aPrincipalURISpec) {
499   NS_PRECONDITION(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");
504 
505   *aDragNode = nullptr;
506 
507   nsresult rv;
508   nsIContent* dragNode = nullptr;
509   *aSelection = nullptr;
510 
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   nsCOMPtr<nsISelection> selection;
515   nsIContent* editingElement = mSelectionTargetNode->IsEditable()
516                                    ? mSelectionTargetNode->GetEditingHost()
517                                    : nullptr;
518   nsCOMPtr<nsITextControlElement> textControl =
519       nsITextControlElement::GetTextControlElementFromEditingHost(
520           editingElement);
521   if (textControl) {
522     nsISelectionController* selcon = textControl->GetSelectionController();
523     if (selcon) {
524       selcon->GetSelection(nsISelectionController::SELECTION_NORMAL,
525                            getter_AddRefs(selection));
526     }
527 
528     if (!selection) return NS_OK;
529   } else {
530     selection = mWindow->GetSelection();
531     if (!selection) return NS_OK;
532 
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;
541       }
542       findFormParent = findFormParent->GetParent();
543     }
544   }
545 
546   // if set, serialize the content under this node
547   nsCOMPtr<nsIContent> nodeToSerialize;
548 
549   nsCOMPtr<nsIDocShellTreeItem> dsti = mWindow->GetDocShell();
550   const bool isChromeShell =
551       dsti && dsti->ItemType() == nsIDocShellTreeItem::typeChrome;
552 
553   // In chrome shells, only allow dragging inside editable areas.
554   if (isChromeShell && !editingElement) {
555     nsCOMPtr<nsIFrameLoaderOwner> flo = do_QueryInterface(mTarget);
556     if (flo) {
557       RefPtr<nsFrameLoader> fl = flo->GetFrameLoader();
558       if (fl) {
559         TabParent* tp = static_cast<TabParent*>(fl->GetRemoteBrowser());
560         if (tp) {
561           // We have a TabParent, so it may have data for dnd in case the child
562           // process started a dnd session.
563           tp->AddInitialDnDDataTo(aDataTransfer, aPrincipalURISpec);
564         }
565       }
566     }
567     return NS_OK;
568   }
569 
570   if (isChromeShell && textControl) {
571     // Only use the selection if the target node is in the selection.
572     bool selectionContainsTarget = false;
573     nsCOMPtr<nsIDOMNode> targetNode = do_QueryInterface(mSelectionTargetNode);
574     selection->ContainsNode(targetNode, false, &selectionContainsTarget);
575     if (!selectionContainsTarget) return NS_OK;
576 
577     selection.swap(*aSelection);
578   } else {
579     // In content shells, a number of checks are made below to determine
580     // whether an image or a link is being dragged. If so, add additional
581     // data to the data transfer. This is also done for chrome shells, but
582     // only when in a non-textbox editor.
583 
584     bool haveSelectedContent = false;
585 
586     // possible parent link node
587     nsCOMPtr<nsIContent> parentLink;
588     nsCOMPtr<nsIContent> draggedNode;
589 
590     {
591       // only drag form elements by using the alt key,
592       // otherwise buttons and select widgets are hard to use
593 
594       // Note that while <object> elements implement nsIFormControl, we should
595       // really allow dragging them if they happen to be images.
596       nsCOMPtr<nsIFormControl> form(do_QueryInterface(mTarget));
597       if (form && !mIsAltKeyPressed && form->ControlType() != NS_FORM_OBJECT) {
598         *aCanDrag = false;
599         return NS_OK;
600       }
601 
602       draggedNode = mTarget;
603     }
604 
605     nsCOMPtr<nsIImageLoadingContent> image;
606 
607     nsCOMPtr<nsIContent> selectedImageOrLinkNode;
608     GetDraggableSelectionData(selection, mSelectionTargetNode,
609                               getter_AddRefs(selectedImageOrLinkNode),
610                               &haveSelectedContent);
611 
612     // either plain text or anchor text is selected
613     if (haveSelectedContent) {
614       selection.swap(*aSelection);
615     } else if (selectedImageOrLinkNode) {
616       // an image is selected
617       image = do_QueryInterface(selectedImageOrLinkNode);
618     } else {
619       // nothing is selected -
620       //
621       // look for draggable elements under the mouse
622       //
623       // if the alt key is down, don't start a drag if we're in an
624       // anchor because we want to do selection.
625       parentLink = FindParentLinkNode(draggedNode);
626       if (parentLink && mIsAltKeyPressed) {
627         *aCanDrag = false;
628         return NS_OK;
629       }
630       image = do_QueryInterface(draggedNode);
631     }
632 
633     {
634       // set for linked images, and links
635       nsCOMPtr<nsIContent> linkNode;
636 
637       RefPtr<HTMLAreaElement> areaElem =
638           HTMLAreaElement::FromContentOrNull(draggedNode);
639       if (areaElem) {
640         // use the alt text (or, if missing, the href) as the title
641         areaElem->GetAttribute(NS_LITERAL_STRING("alt"), mTitleString);
642         if (mTitleString.IsEmpty()) {
643           // this can be a relative link
644           areaElem->GetAttribute(NS_LITERAL_STRING("href"), mTitleString);
645         }
646 
647         // we'll generate HTML like <a href="absurl">alt text</a>
648         mIsAnchor = true;
649 
650         // gives an absolute link
651         nsresult rv = GetAnchorURL(draggedNode, mUrlString);
652         NS_ENSURE_SUCCESS(rv, rv);
653 
654         mHtmlString.AssignLiteral("<a href=\"");
655         mHtmlString.Append(mUrlString);
656         mHtmlString.AppendLiteral("\">");
657         mHtmlString.Append(mTitleString);
658         mHtmlString.AppendLiteral("</a>");
659 
660         dragNode = draggedNode;
661       } else if (image) {
662         mIsAnchor = true;
663         // grab the href as the url, use alt text as the title of the
664         // area if it's there.  the drag data is the image tag and src
665         // attribute.
666         nsCOMPtr<nsIURI> imageURI;
667         image->GetCurrentURI(getter_AddRefs(imageURI));
668         if (imageURI) {
669           nsAutoCString spec;
670           rv = imageURI->GetSpec(spec);
671           NS_ENSURE_SUCCESS(rv, rv);
672           CopyUTF8toUTF16(spec, mUrlString);
673         }
674 
675         nsCOMPtr<Element> imageElement(do_QueryInterface(image));
676         // XXXbz Shouldn't we use the "title" attr for title?  Using
677         // "alt" seems very wrong....
678         // XXXbz Also, what if this is an nsIImageLoadingContent
679         // that's not an <html:img>?
680         if (imageElement) {
681           imageElement->GetAttribute(NS_LITERAL_STRING("alt"), mTitleString);
682         }
683 
684         if (mTitleString.IsEmpty()) {
685           mTitleString = mUrlString;
686         }
687 
688         nsCOMPtr<imgIRequest> imgRequest;
689 
690         // grab the image data, and its request.
691         nsCOMPtr<imgIContainer> img = nsContentUtils::GetImageFromContent(
692             image, getter_AddRefs(imgRequest));
693         if (imgRequest) {
694           rv = GetImageData(img, imgRequest);
695           NS_ENSURE_SUCCESS(rv, rv);
696         }
697 
698         if (parentLink) {
699           // If we are dragging around an image in an anchor, then we
700           // are dragging the entire anchor
701           linkNode = parentLink;
702           nodeToSerialize = linkNode;
703         } else {
704           nodeToSerialize = do_QueryInterface(draggedNode);
705         }
706         dragNode = nodeToSerialize;
707       } else if (draggedNode && draggedNode->IsHTMLElement(nsGkAtoms::a)) {
708         // set linkNode. The code below will handle this
709         linkNode = do_QueryInterface(draggedNode);  // XXX test this
710         GetNodeString(draggedNode, mTitleString);
711       } else if (parentLink) {
712         // parentLink will always be null if there's selected content
713         linkNode = parentLink;
714         nodeToSerialize = linkNode;
715       } else if (!haveSelectedContent) {
716         // nothing draggable
717         return NS_OK;
718       }
719 
720       if (linkNode) {
721         mIsAnchor = true;
722         rv = GetAnchorURL(linkNode, mUrlString);
723         NS_ENSURE_SUCCESS(rv, rv);
724         dragNode = linkNode;
725       }
726     }
727   }
728 
729   if (nodeToSerialize || *aSelection) {
730     mHtmlString.Truncate();
731     mContextString.Truncate();
732     mInfoString.Truncate();
733     mTitleString.Truncate();
734 
735     nsCOMPtr<nsIDocument> doc = mWindow->GetDoc();
736     NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
737 
738     // if we have selected text, use it in preference to the node
739     nsCOMPtr<nsITransferable> transferable;
740     if (*aSelection) {
741       rv = nsCopySupport::GetTransferableForSelection(
742           *aSelection, doc, getter_AddRefs(transferable));
743     } else {
744       rv = nsCopySupport::GetTransferableForNode(nodeToSerialize, doc,
745                                                  getter_AddRefs(transferable));
746     }
747     NS_ENSURE_SUCCESS(rv, rv);
748 
749     nsCOMPtr<nsISupports> supports;
750     nsCOMPtr<nsISupportsString> data;
751     uint32_t dataSize;
752     rv = transferable->GetTransferData(kHTMLMime, getter_AddRefs(supports),
753                                        &dataSize);
754     data = do_QueryInterface(supports);
755     if (NS_SUCCEEDED(rv)) {
756       data->GetData(mHtmlString);
757     }
758     rv = transferable->GetTransferData(kHTMLContext, getter_AddRefs(supports),
759                                        &dataSize);
760     data = do_QueryInterface(supports);
761     if (NS_SUCCEEDED(rv)) {
762       data->GetData(mContextString);
763     }
764     rv = transferable->GetTransferData(kHTMLInfo, getter_AddRefs(supports),
765                                        &dataSize);
766     data = do_QueryInterface(supports);
767     if (NS_SUCCEEDED(rv)) {
768       data->GetData(mInfoString);
769     }
770     rv = transferable->GetTransferData(kUnicodeMime, getter_AddRefs(supports),
771                                        &dataSize);
772     data = do_QueryInterface(supports);
773     NS_ENSURE_SUCCESS(rv, rv);  // require plain text at a minimum
774     data->GetData(mTitleString);
775   }
776 
777   // default text value is the URL
778   if (mTitleString.IsEmpty()) {
779     mTitleString = mUrlString;
780   }
781 
782   // if we haven't constructed a html version, make one now
783   if (mHtmlString.IsEmpty() && !mUrlString.IsEmpty())
784     CreateLinkText(mUrlString, mTitleString, mHtmlString);
785 
786   // if there is no drag node, which will be the case for a selection, just
787   // use the selection target node.
788   rv = AddStringsToDataTransfer(
789       dragNode ? dragNode : mSelectionTargetNode.get(), aDataTransfer);
790   NS_ENSURE_SUCCESS(rv, rv);
791 
792   NS_IF_ADDREF(*aDragNode = dragNode);
793   return NS_OK;
794 }
795 
AddString(DataTransfer * aDataTransfer,const nsAString & aFlavor,const nsAString & aData,nsIPrincipal * aPrincipal,bool aHidden)796 void DragDataProducer::AddString(DataTransfer* aDataTransfer,
797                                  const nsAString& aFlavor,
798                                  const nsAString& aData,
799                                  nsIPrincipal* aPrincipal, bool aHidden) {
800   RefPtr<nsVariantCC> variant = new nsVariantCC();
801   variant->SetAsAString(aData);
802   aDataTransfer->SetDataWithPrincipal(aFlavor, variant, 0, aPrincipal, aHidden);
803 }
804 
AddStringsToDataTransfer(nsIContent * aDragNode,DataTransfer * aDataTransfer)805 nsresult DragDataProducer::AddStringsToDataTransfer(
806     nsIContent* aDragNode, DataTransfer* aDataTransfer) {
807   NS_ASSERTION(aDragNode, "adding strings for null node");
808 
809   // set all of the data to have the principal of the node where the data came
810   // from
811   nsIPrincipal* principal = aDragNode->NodePrincipal();
812 
813   // add a special flavor if we're an anchor to indicate that we have
814   // a URL in the drag data
815   if (!mUrlString.IsEmpty() && mIsAnchor) {
816     nsAutoString dragData(mUrlString);
817     dragData.Append('\n');
818     // Remove leading and trailing newlines in the title and replace them with
819     // space in remaining positions - they confuse PlacesUtils::unwrapNodes
820     // that expects url\ntitle formatted data for x-moz-url.
821     nsAutoString title(mTitleString);
822     title.Trim("\r\n");
823     title.ReplaceChar("\r\n", ' ');
824     dragData += title;
825 
826     AddString(aDataTransfer, NS_LITERAL_STRING(kURLMime), dragData, principal);
827     AddString(aDataTransfer, NS_LITERAL_STRING(kURLDataMime), mUrlString,
828               principal);
829     AddString(aDataTransfer, NS_LITERAL_STRING(kURLDescriptionMime),
830               mTitleString, principal);
831     AddString(aDataTransfer, NS_LITERAL_STRING("text/uri-list"), mUrlString,
832               principal);
833   }
834 
835   // add a special flavor for the html context data
836   if (!mContextString.IsEmpty())
837     AddString(aDataTransfer, NS_LITERAL_STRING(kHTMLContext), mContextString,
838               principal);
839 
840   // add a special flavor if we have html info data
841   if (!mInfoString.IsEmpty())
842     AddString(aDataTransfer, NS_LITERAL_STRING(kHTMLInfo), mInfoString,
843               principal);
844 
845   // add the full html
846   if (!mHtmlString.IsEmpty())
847     AddString(aDataTransfer, NS_LITERAL_STRING(kHTMLMime), mHtmlString,
848               principal);
849 
850   // add the plain text. we use the url for text/plain data if an anchor is
851   // being dragged, rather than the title text of the link or the alt text for
852   // an anchor image.
853   AddString(aDataTransfer, NS_LITERAL_STRING(kTextMime),
854             mIsAnchor ? mUrlString : mTitleString, principal);
855 
856   // add image data, if present. For now, all we're going to do with
857   // this is turn it into a native data flavor, so indicate that with
858   // a new flavor so as not to confuse anyone who is really registered
859   // for image/gif or image/jpg.
860   if (mImage) {
861     RefPtr<nsVariantCC> variant = new nsVariantCC();
862     variant->SetAsISupports(mImage);
863     aDataTransfer->SetDataWithPrincipal(NS_LITERAL_STRING(kNativeImageMime),
864                                         variant, 0, principal);
865 
866     // assume the image comes from a file, and add a file promise. We
867     // register ourselves as a nsIFlavorDataProvider, and will use the
868     // GetFlavorData callback to save the image to disk.
869 
870     nsCOMPtr<nsIFlavorDataProvider> dataProvider =
871         new nsContentAreaDragDropDataProvider();
872     if (dataProvider) {
873       RefPtr<nsVariantCC> variant = new nsVariantCC();
874       variant->SetAsISupports(dataProvider);
875       aDataTransfer->SetDataWithPrincipal(NS_LITERAL_STRING(kFilePromiseMime),
876                                           variant, 0, principal);
877     }
878 
879     AddString(aDataTransfer, NS_LITERAL_STRING(kFilePromiseURLMime),
880               mImageSourceString, principal);
881     AddString(aDataTransfer, NS_LITERAL_STRING(kFilePromiseDestFilename),
882               mImageDestFileName, principal);
883 #if defined(XP_MACOSX)
884     AddString(aDataTransfer, NS_LITERAL_STRING(kImageRequestMime),
885               mImageRequestMime, principal, /* aHidden= */ true);
886 #endif
887 
888     // if not an anchor, add the image url
889     if (!mIsAnchor) {
890       AddString(aDataTransfer, NS_LITERAL_STRING(kURLDataMime), mUrlString,
891                 principal);
892       AddString(aDataTransfer, NS_LITERAL_STRING("text/uri-list"), mUrlString,
893                 principal);
894     }
895   }
896 
897   return NS_OK;
898 }
899 
900 // note that this can return NS_OK, but a null out param (by design)
901 // static
GetDraggableSelectionData(nsISelection * inSelection,nsIContent * inRealTargetNode,nsIContent ** outImageOrLinkNode,bool * outDragSelectedText)902 nsresult DragDataProducer::GetDraggableSelectionData(
903     nsISelection* inSelection, nsIContent* inRealTargetNode,
904     nsIContent** outImageOrLinkNode, bool* outDragSelectedText) {
905   NS_ENSURE_ARG(inSelection);
906   NS_ENSURE_ARG(inRealTargetNode);
907   NS_ENSURE_ARG_POINTER(outImageOrLinkNode);
908 
909   *outImageOrLinkNode = nullptr;
910   *outDragSelectedText = false;
911 
912   bool selectionContainsTarget = false;
913 
914   bool isCollapsed = false;
915   inSelection->GetIsCollapsed(&isCollapsed);
916   if (!isCollapsed) {
917     nsCOMPtr<nsIDOMNode> realTargetNode = do_QueryInterface(inRealTargetNode);
918     inSelection->ContainsNode(realTargetNode, false, &selectionContainsTarget);
919 
920     if (selectionContainsTarget) {
921       // track down the anchor node, if any, for the url
922       nsCOMPtr<nsIDOMNode> selectionStart;
923       inSelection->GetAnchorNode(getter_AddRefs(selectionStart));
924 
925       nsCOMPtr<nsIDOMNode> selectionEnd;
926       inSelection->GetFocusNode(getter_AddRefs(selectionEnd));
927 
928       // look for a selection around a single node, like an image.
929       // in this case, drag the image, rather than a serialization of the HTML
930       // XXX generalize this to other draggable element types?
931       if (selectionStart == selectionEnd) {
932         nsCOMPtr<nsIContent> selStartContent =
933             do_QueryInterface(selectionStart);
934         if (selStartContent && selStartContent->HasChildNodes()) {
935           // see if just one node is selected
936           int32_t anchorOffset, focusOffset;
937           inSelection->GetAnchorOffset(&anchorOffset);
938           inSelection->GetFocusOffset(&focusOffset);
939           if (abs(anchorOffset - focusOffset) == 1) {
940             int32_t childOffset =
941                 (anchorOffset < focusOffset) ? anchorOffset : focusOffset;
942             nsIContent* childContent =
943                 selStartContent->GetChildAt_Deprecated(childOffset);
944             // if we find an image, we'll fall into the node-dragging code,
945             // rather the the selection-dragging code
946             if (nsContentUtils::IsDraggableImage(childContent)) {
947               NS_ADDREF(*outImageOrLinkNode = childContent);
948               return NS_OK;
949             }
950           }
951         }
952       }
953 
954       // indicate that a link or text is selected
955       *outDragSelectedText = true;
956     }
957   }
958 
959   return NS_OK;
960 }
961