content/base/src/nsContentAreaDragDrop.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     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
    61 {
    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);
    88   static void GetSelectedLink(nsISelection* inSelection,
    89                               nsIContent **outLinkNode);
    91   nsCOMPtr<nsPIDOMWindow> mWindow;
    92   nsCOMPtr<nsIContent> mTarget;
    93   nsCOMPtr<nsIContent> mSelectionTargetNode;
    94   bool mIsAltKeyPressed;
    96   nsString mUrlString;
    97   nsString mImageSourceString;
    98   nsString mImageDestFileName;
    99   nsString mTitleString;
   100   // will be filled automatically if you fill urlstring
   101   nsString mHtmlString;
   102   nsString mContextString;
   103   nsString mInfoString;
   105   bool mIsAnchor;
   106   nsCOMPtr<imgIContainer> mImage;
   107 };
   110 nsresult
   111 nsContentAreaDragDrop::GetDragData(nsPIDOMWindow* aWindow,
   112                                    nsIContent* aTarget,
   113                                    nsIContent* aSelectionTargetNode,
   114                                    bool aIsAltKeyPressed,
   115                                    DataTransfer* aDataTransfer,
   116                                    bool* aCanDrag,
   117                                    nsISelection** aSelection,
   118                                    nsIContent** aDragNode)
   119 {
   120   NS_ENSURE_TRUE(aSelectionTargetNode, NS_ERROR_INVALID_ARG);
   122   *aCanDrag = true;
   124   DragDataProducer
   125     provider(aWindow, aTarget, aSelectionTargetNode, aIsAltKeyPressed);
   126   return provider.Produce(aDataTransfer, aCanDrag, aSelection, aDragNode);
   127 }
   130 NS_IMPL_ISUPPORTS(nsContentAreaDragDropDataProvider, nsIFlavorDataProvider)
   132 // SaveURIToFile
   133 // used on platforms where it's possible to drag items (e.g. images)
   134 // into the file system
   135 nsresult
   136 nsContentAreaDragDropDataProvider::SaveURIToFile(nsAString& inSourceURIString,
   137                                                  nsIFile* inDestFile,
   138                                                  bool isPrivate)
   139 {
   140   nsCOMPtr<nsIURI> sourceURI;
   141   nsresult rv = NS_NewURI(getter_AddRefs(sourceURI), inSourceURIString);
   142   if (NS_FAILED(rv)) {
   143     return NS_ERROR_FAILURE;
   144   }
   146   nsCOMPtr<nsIURL> sourceURL = do_QueryInterface(sourceURI);
   147   if (!sourceURL) {
   148     return NS_ERROR_NO_INTERFACE;
   149   }
   151   rv = inDestFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
   152   NS_ENSURE_SUCCESS(rv, rv);
   154   // we rely on the fact that the WPB is refcounted by the channel etc,
   155   // so we don't keep a ref to it. It will die when finished.
   156   nsCOMPtr<nsIWebBrowserPersist> persist =
   157     do_CreateInstance("@mozilla.org/embedding/browser/nsWebBrowserPersist;1",
   158                       &rv);
   159   NS_ENSURE_SUCCESS(rv, rv);
   161   persist->SetPersistFlags(nsIWebBrowserPersist::PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION);
   163   return persist->SavePrivacyAwareURI(sourceURI, nullptr, nullptr, nullptr, nullptr,
   164                                       inDestFile, isPrivate);
   165 }
   167 // This is our nsIFlavorDataProvider callback. There are several
   168 // assumptions here that make this work:
   169 //
   170 // 1. Someone put a kFilePromiseURLMime flavor into the transferable
   171 //    with the source URI of the file to save (as a string). We did
   172 //    that in AddStringsToDataTransfer.
   173 //
   174 // 2. Someone put a kFilePromiseDirectoryMime flavor into the
   175 //    transferable with an nsIFile for the directory we are to
   176 //    save in. That has to be done by platform-specific code (in
   177 //    widget), which gets the destination directory from
   178 //    OS-specific drag information.
   179 //
   180 NS_IMETHODIMP
   181 nsContentAreaDragDropDataProvider::GetFlavorData(nsITransferable *aTransferable,
   182                                                  const char *aFlavor,
   183                                                  nsISupports **aData,
   184                                                  uint32_t *aDataLen)
   185 {
   186   NS_ENSURE_ARG_POINTER(aData && aDataLen);
   187   *aData = nullptr;
   188   *aDataLen = 0;
   190   nsresult rv = NS_ERROR_NOT_IMPLEMENTED;
   192   if (strcmp(aFlavor, kFilePromiseMime) == 0) {
   193     // get the URI from the kFilePromiseURLMime flavor
   194     NS_ENSURE_ARG(aTransferable);
   195     nsCOMPtr<nsISupports> tmp;
   196     uint32_t dataSize = 0;
   197     aTransferable->GetTransferData(kFilePromiseURLMime,
   198                                    getter_AddRefs(tmp), &dataSize);
   199     nsCOMPtr<nsISupportsString> supportsString =
   200       do_QueryInterface(tmp);
   201     if (!supportsString)
   202       return NS_ERROR_FAILURE;
   204     nsAutoString sourceURLString;
   205     supportsString->GetData(sourceURLString);
   206     if (sourceURLString.IsEmpty())
   207       return NS_ERROR_FAILURE;
   209     aTransferable->GetTransferData(kFilePromiseDestFilename,
   210                                    getter_AddRefs(tmp), &dataSize);
   211     supportsString = do_QueryInterface(tmp);
   212     if (!supportsString)
   213       return NS_ERROR_FAILURE;
   215     nsAutoString targetFilename;
   216     supportsString->GetData(targetFilename);
   217     if (targetFilename.IsEmpty())
   218       return NS_ERROR_FAILURE;
   220     // get the target directory from the kFilePromiseDirectoryMime
   221     // flavor
   222     nsCOMPtr<nsISupports> dirPrimitive;
   223     dataSize = 0;
   224     aTransferable->GetTransferData(kFilePromiseDirectoryMime,
   225                                    getter_AddRefs(dirPrimitive), &dataSize);
   226     nsCOMPtr<nsIFile> destDirectory = do_QueryInterface(dirPrimitive);
   227     if (!destDirectory)
   228       return NS_ERROR_FAILURE;
   230     nsCOMPtr<nsIFile> file;
   231     rv = destDirectory->Clone(getter_AddRefs(file));
   232     NS_ENSURE_SUCCESS(rv, rv);
   234     file->Append(targetFilename);
   236     bool isPrivate;
   237     aTransferable->GetIsPrivateData(&isPrivate);
   239     rv = SaveURIToFile(sourceURLString, file, isPrivate);
   240     // send back an nsIFile
   241     if (NS_SUCCEEDED(rv)) {
   242       CallQueryInterface(file, aData);
   243       *aDataLen = sizeof(nsIFile*);
   244     }
   245   }
   247   return rv;
   248 }
   250 DragDataProducer::DragDataProducer(nsPIDOMWindow* aWindow,
   251                                    nsIContent* aTarget,
   252                                    nsIContent* aSelectionTargetNode,
   253                                    bool aIsAltKeyPressed)
   254   : mWindow(aWindow),
   255     mTarget(aTarget),
   256     mSelectionTargetNode(aSelectionTargetNode),
   257     mIsAltKeyPressed(aIsAltKeyPressed),
   258     mIsAnchor(false)
   259 {
   260 }
   263 //
   264 // FindParentLinkNode
   265 //
   266 // Finds the parent with the given link tag starting at |inNode|. If
   267 // it gets up to the root without finding it, we stop looking and
   268 // return null.
   269 //
   270 already_AddRefed<nsIContent>
   271 DragDataProducer::FindParentLinkNode(nsIContent* inNode)
   272 {
   273   nsIContent* content = inNode;
   274   if (!content) {
   275     // That must have been the document node; nothing else to do here;
   276     return nullptr;
   277   }
   279   for (; content; content = content->GetParent()) {
   280     if (nsContentUtils::IsDraggableLink(content)) {
   281       nsCOMPtr<nsIContent> ret = content;
   282       return ret.forget();
   283     }
   284   }
   286   return nullptr;
   287 }
   290 //
   291 // GetAnchorURL
   292 //
   293 void
   294 DragDataProducer::GetAnchorURL(nsIContent* inNode, nsAString& outURL)
   295 {
   296   nsCOMPtr<nsIURI> linkURI;
   297   if (!inNode || !inNode->IsLink(getter_AddRefs(linkURI))) {
   298     // Not a link
   299     outURL.Truncate();
   300     return;
   301   }
   303   nsAutoCString spec;
   304   linkURI->GetSpec(spec);
   305   CopyUTF8toUTF16(spec, outURL);
   306 }
   309 //
   310 // CreateLinkText
   311 //
   312 // Creates the html for an anchor in the form
   313 //  <a href="inURL">inText</a>
   314 //
   315 void
   316 DragDataProducer::CreateLinkText(const nsAString& inURL,
   317                                  const nsAString & inText,
   318                                  nsAString& outLinkText)
   319 {
   320   // use a temp var in case |inText| is the same string as
   321   // |outLinkText| to avoid overwriting it while building up the
   322   // string in pieces.
   323   nsAutoString linkText(NS_LITERAL_STRING("<a href=\"") +
   324                         inURL +
   325                         NS_LITERAL_STRING("\">") +
   326                         inText +
   327                         NS_LITERAL_STRING("</a>") );
   329   outLinkText = linkText;
   330 }
   333 //
   334 // GetNodeString
   335 //
   336 // Gets the text associated with a node
   337 //
   338 void
   339 DragDataProducer::GetNodeString(nsIContent* inNode,
   340                                 nsAString & outNodeString)
   341 {
   342   nsCOMPtr<nsINode> node = inNode;
   344   outNodeString.Truncate();
   346   // use a range to get the text-equivalent of the node
   347   nsCOMPtr<nsIDocument> doc = node->OwnerDoc();
   348   mozilla::ErrorResult rv;
   349   nsRefPtr<nsRange> range = doc->CreateRange(rv);
   350   if (range) {
   351     range->SelectNode(*node, rv);
   352     range->ToString(outNodeString);
   353   }
   354 }
   356 nsresult
   357 DragDataProducer::Produce(DataTransfer* aDataTransfer,
   358                           bool* aCanDrag,
   359                           nsISelection** aSelection,
   360                           nsIContent** aDragNode)
   361 {
   362   NS_PRECONDITION(aCanDrag && aSelection && aDataTransfer && aDragNode,
   363                   "null pointer passed to Produce");
   364   NS_ASSERTION(mWindow, "window not set");
   365   NS_ASSERTION(mSelectionTargetNode, "selection target node should have been set");
   367   *aDragNode = nullptr;
   369   nsresult rv;
   370   nsIContent* dragNode = nullptr;
   371   *aSelection = nullptr;
   373   // Find the selection to see what we could be dragging and if what we're
   374   // dragging is in what is selected. If this is an editable textbox, use
   375   // the textbox's selection, otherwise use the window's selection.
   376   nsCOMPtr<nsISelection> selection;
   377   nsIContent* editingElement = mSelectionTargetNode->IsEditable() ?
   378                                mSelectionTargetNode->GetEditingHost() : nullptr;
   379   nsCOMPtr<nsITextControlElement> textControl =
   380     nsITextControlElement::GetTextControlElementFromEditingHost(editingElement);
   381   if (textControl) {
   382     nsISelectionController* selcon = textControl->GetSelectionController();
   383     if (selcon) {
   384       selcon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
   385     }
   387     if (!selection)
   388       return NS_OK;
   389   }
   390   else {
   391     mWindow->GetSelection(getter_AddRefs(selection));
   392     if (!selection)
   393       return NS_OK;
   395     // Check if the node is inside a form control. Don't set aCanDrag to false
   396     //however, as we still want to allow the drag.
   397     nsCOMPtr<nsIContent> findFormNode = mSelectionTargetNode;
   398     nsIContent* findFormParent = findFormNode->GetParent();
   399     while (findFormParent) {
   400       nsCOMPtr<nsIFormControl> form(do_QueryInterface(findFormParent));
   401       if (form && !form->AllowDraggableChildren()) {
   402         return NS_OK;
   403       }
   404       findFormParent = findFormParent->GetParent();
   405     }
   406   }
   408   // if set, serialize the content under this node
   409   nsCOMPtr<nsIContent> nodeToSerialize;
   411   nsCOMPtr<nsIWebNavigation> webnav = do_GetInterface(mWindow);
   412   nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(webnav);
   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);
   429   }
   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;
   442     {
   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;
   452       }
   454       draggedNode = mTarget;
   455     }
   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       link = do_QueryInterface(selectedImageOrLinkNode);
   469       if (link && mIsAltKeyPressed) {
   470         // if alt is pressed, select the link text instead of drag the link
   471         *aCanDrag = false;
   472         return NS_OK;
   473       }
   475       selection.swap(*aSelection);
   476     } else if (selectedImageOrLinkNode) {
   477       // an image is selected
   478       image = do_QueryInterface(selectedImageOrLinkNode);
   479     } else {
   480       // nothing is selected -
   481       //
   482       // look for draggable elements under the mouse
   483       //
   484       // if the alt key is down, don't start a drag if we're in an
   485       // anchor because we want to do selection.
   486       parentLink = FindParentLinkNode(draggedNode);
   487       if (parentLink && mIsAltKeyPressed) {
   488         *aCanDrag = false;
   489         return NS_OK;
   490       }
   492       area  = do_QueryInterface(draggedNode);
   493       image = do_QueryInterface(draggedNode);
   494       link  = do_QueryInterface(draggedNode);
   495     }
   497     {
   498       // set for linked images, and links
   499       nsCOMPtr<nsIContent> linkNode;
   501       if (area) {
   502         // use the alt text (or, if missing, the href) as the title
   503         HTMLAreaElement* areaElem = static_cast<HTMLAreaElement*>(area.get());
   504         areaElem->GetAttribute(NS_LITERAL_STRING("alt"), mTitleString);
   505         if (mTitleString.IsEmpty()) {
   506           // this can be a relative link
   507           areaElem->GetAttribute(NS_LITERAL_STRING("href"), mTitleString);
   508         }
   510         // we'll generate HTML like <a href="absurl">alt text</a>
   511         mIsAnchor = true;
   513         // gives an absolute link
   514         GetAnchorURL(draggedNode, mUrlString);
   516         mHtmlString.AssignLiteral("<a href=\"");
   517         mHtmlString.Append(mUrlString);
   518         mHtmlString.AppendLiteral("\">");
   519         mHtmlString.Append(mTitleString);
   520         mHtmlString.AppendLiteral("</a>");
   522         dragNode = draggedNode;
   523       } else if (image) {
   524         mIsAnchor = true;
   525         // grab the href as the url, use alt text as the title of the
   526         // area if it's there.  the drag data is the image tag and src
   527         // attribute.
   528         nsCOMPtr<nsIURI> imageURI;
   529         image->GetCurrentURI(getter_AddRefs(imageURI));
   530         if (imageURI) {
   531           nsAutoCString spec;
   532           imageURI->GetSpec(spec);
   533           CopyUTF8toUTF16(spec, mUrlString);
   534         }
   536         nsCOMPtr<nsIDOMElement> imageElement(do_QueryInterface(image));
   537         // XXXbz Shouldn't we use the "title" attr for title?  Using
   538         // "alt" seems very wrong....
   539         if (imageElement) {
   540           imageElement->GetAttribute(NS_LITERAL_STRING("alt"), mTitleString);
   541         }
   543         if (mTitleString.IsEmpty()) {
   544           mTitleString = mUrlString;
   545         }
   547         nsCOMPtr<imgIRequest> imgRequest;
   549         // grab the image data, and its request.
   550         nsCOMPtr<imgIContainer> img =
   551           nsContentUtils::GetImageFromContent(image,
   552                                               getter_AddRefs(imgRequest));
   554         nsCOMPtr<nsIMIMEService> mimeService =
   555           do_GetService("@mozilla.org/mime;1");
   557         // Fix the file extension in the URL if necessary
   558         if (imgRequest && mimeService) {
   559           nsCOMPtr<nsIURI> imgUri;
   560           imgRequest->GetURI(getter_AddRefs(imgUri));
   562           nsCOMPtr<nsIURL> imgUrl(do_QueryInterface(imgUri));
   564           if (imgUrl) {
   565             nsAutoCString extension;
   566             imgUrl->GetFileExtension(extension);
   568             nsXPIDLCString mimeType;
   569             imgRequest->GetMimeType(getter_Copies(mimeType));
   571             nsCOMPtr<nsIMIMEInfo> mimeInfo;
   572             mimeService->GetFromTypeAndExtension(mimeType, EmptyCString(),
   573                                                  getter_AddRefs(mimeInfo));
   575             if (mimeInfo) {
   576               nsAutoCString spec;
   577               imgUrl->GetSpec(spec);
   579               // pass out the image source string
   580               CopyUTF8toUTF16(spec, mImageSourceString);
   582               bool validExtension;
   583               if (extension.IsEmpty() || 
   584                   NS_FAILED(mimeInfo->ExtensionExists(extension,
   585                                                       &validExtension)) ||
   586                   !validExtension) {
   587                 // Fix the file extension in the URL
   588                 nsresult rv = imgUrl->Clone(getter_AddRefs(imgUri));
   589                 NS_ENSURE_SUCCESS(rv, rv);
   591                 imgUrl = do_QueryInterface(imgUri);
   593                 nsAutoCString primaryExtension;
   594                 mimeInfo->GetPrimaryExtension(primaryExtension);
   596                 imgUrl->SetFileExtension(primaryExtension);
   597               }
   599               nsAutoCString fileName;
   600               imgUrl->GetFileName(fileName);
   602               NS_UnescapeURL(fileName);
   604               // make the filename safe for the filesystem
   605               fileName.ReplaceChar(FILE_PATH_SEPARATOR FILE_ILLEGAL_CHARACTERS,
   606                                    '-');
   608               CopyUTF8toUTF16(fileName, mImageDestFileName);
   610               // and the image object
   611               mImage = img;
   612             }
   613           }
   614         }
   616         if (parentLink) {
   617           // If we are dragging around an image in an anchor, then we
   618           // are dragging the entire anchor
   619           linkNode = parentLink;
   620           nodeToSerialize = linkNode;
   621         } else {
   622           nodeToSerialize = do_QueryInterface(draggedNode);
   623         }
   624         dragNode = nodeToSerialize;
   625       } else if (link) {
   626         // set linkNode. The code below will handle this
   627         linkNode = do_QueryInterface(link);    // XXX test this
   628         GetNodeString(draggedNode, mTitleString);
   629       } else if (parentLink) {
   630         // parentLink will always be null if there's selected content
   631         linkNode = parentLink;
   632         nodeToSerialize = linkNode;
   633       } else if (!haveSelectedContent) {
   634         // nothing draggable
   635         return NS_OK;
   636       }
   638       if (linkNode) {
   639         mIsAnchor = true;
   640         GetAnchorURL(linkNode, mUrlString);
   641         dragNode = linkNode;
   642       }
   643     }
   644   }
   646   if (nodeToSerialize || *aSelection) {
   647     mHtmlString.Truncate();
   648     mContextString.Truncate();
   649     mInfoString.Truncate();
   650     mTitleString.Truncate();
   652     nsCOMPtr<nsIDocument> doc = mWindow->GetDoc();
   653     NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
   655     // if we have selected text, use it in preference to the node
   656     nsCOMPtr<nsITransferable> transferable;
   657     if (*aSelection) {
   658       rv = nsCopySupport::GetTransferableForSelection(*aSelection, doc,
   659                                                       getter_AddRefs(transferable));
   660     }
   661     else {
   662       rv = nsCopySupport::GetTransferableForNode(nodeToSerialize, doc,
   663                                                  getter_AddRefs(transferable));
   664     }
   665     NS_ENSURE_SUCCESS(rv, rv);
   667     nsCOMPtr<nsISupports> supports;
   668     nsCOMPtr<nsISupportsString> data;
   669     uint32_t dataSize;
   670     rv = transferable->GetTransferData(kHTMLMime, getter_AddRefs(supports),
   671                                        &dataSize);
   672     data = do_QueryInterface(supports);
   673     if (NS_SUCCEEDED(rv)) {
   674       data->GetData(mHtmlString);
   675     }
   676     rv = transferable->GetTransferData(kHTMLContext, getter_AddRefs(supports),
   677                                        &dataSize);
   678     data = do_QueryInterface(supports);
   679     if (NS_SUCCEEDED(rv)) {
   680       data->GetData(mContextString);
   681     }
   682     rv = transferable->GetTransferData(kHTMLInfo, getter_AddRefs(supports),
   683                                        &dataSize);
   684     data = do_QueryInterface(supports);
   685     if (NS_SUCCEEDED(rv)) {
   686       data->GetData(mInfoString);
   687     }
   688     rv = transferable->GetTransferData(kUnicodeMime, getter_AddRefs(supports),
   689                                        &dataSize);
   690     data = do_QueryInterface(supports);
   691     NS_ENSURE_SUCCESS(rv, rv); // require plain text at a minimum
   692     data->GetData(mTitleString);
   693   }
   695   // default text value is the URL
   696   if (mTitleString.IsEmpty()) {
   697     mTitleString = mUrlString;
   698   }
   700   // if we haven't constructed a html version, make one now
   701   if (mHtmlString.IsEmpty() && !mUrlString.IsEmpty())
   702     CreateLinkText(mUrlString, mTitleString, mHtmlString);
   704   // if there is no drag node, which will be the case for a selection, just
   705   // use the selection target node.
   706   rv = AddStringsToDataTransfer(
   707          dragNode ? dragNode : mSelectionTargetNode.get(), aDataTransfer);
   708   NS_ENSURE_SUCCESS(rv, rv);
   710   NS_IF_ADDREF(*aDragNode = dragNode);
   711   return NS_OK;
   712 }
   714 void
   715 DragDataProducer::AddString(DataTransfer* aDataTransfer,
   716                             const nsAString& aFlavor,
   717                             const nsAString& aData,
   718                             nsIPrincipal* aPrincipal)
   719 {
   720   nsCOMPtr<nsIWritableVariant> variant = do_CreateInstance(NS_VARIANT_CONTRACTID);
   721   if (variant) {
   722     variant->SetAsAString(aData);
   723     aDataTransfer->SetDataWithPrincipal(aFlavor, variant, 0, aPrincipal);
   724   }
   725 }
   727 nsresult
   728 DragDataProducer::AddStringsToDataTransfer(nsIContent* aDragNode,
   729                                            DataTransfer* aDataTransfer)
   730 {
   731   NS_ASSERTION(aDragNode, "adding strings for null node");
   733   // set all of the data to have the principal of the node where the data came from
   734   nsIPrincipal* principal = aDragNode->NodePrincipal();
   736   // add a special flavor if we're an anchor to indicate that we have
   737   // a URL in the drag data
   738   if (!mUrlString.IsEmpty() && mIsAnchor) {
   739     nsAutoString dragData(mUrlString);
   740     dragData.AppendLiteral("\n");
   741     // Remove leading and trailing newlines in the title and replace them with
   742     // space in remaining positions - they confuse PlacesUtils::unwrapNodes
   743     // that expects url\ntitle formatted data for x-moz-url.
   744     nsAutoString title(mTitleString);
   745     title.Trim("\r\n");
   746     title.ReplaceChar("\r\n", ' ');
   747     dragData += title;
   749     AddString(aDataTransfer, NS_LITERAL_STRING(kURLMime), dragData, principal);
   750     AddString(aDataTransfer, NS_LITERAL_STRING(kURLDataMime), mUrlString, principal);
   751     AddString(aDataTransfer, NS_LITERAL_STRING(kURLDescriptionMime), mTitleString, principal);
   752     AddString(aDataTransfer, NS_LITERAL_STRING("text/uri-list"), mUrlString, principal);
   753   }
   755   // add a special flavor for the html context data
   756   if (!mContextString.IsEmpty())
   757     AddString(aDataTransfer, NS_LITERAL_STRING(kHTMLContext), mContextString, principal);
   759   // add a special flavor if we have html info data
   760   if (!mInfoString.IsEmpty())
   761     AddString(aDataTransfer, NS_LITERAL_STRING(kHTMLInfo), mInfoString, principal);
   763   // add the full html
   764   if (!mHtmlString.IsEmpty())
   765     AddString(aDataTransfer, NS_LITERAL_STRING(kHTMLMime), mHtmlString, principal);
   767   // add the plain text. we use the url for text/plain data if an anchor is
   768   // being dragged, rather than the title text of the link or the alt text for
   769   // an anchor image.
   770   AddString(aDataTransfer, NS_LITERAL_STRING(kTextMime),
   771             mIsAnchor ? mUrlString : mTitleString, principal);
   773   // add image data, if present. For now, all we're going to do with
   774   // this is turn it into a native data flavor, so indicate that with
   775   // a new flavor so as not to confuse anyone who is really registered
   776   // for image/gif or image/jpg.
   777   if (mImage) {
   778     nsCOMPtr<nsIWritableVariant> variant = do_CreateInstance(NS_VARIANT_CONTRACTID);
   779     if (variant) {
   780       variant->SetAsISupports(mImage);
   781       aDataTransfer->SetDataWithPrincipal(NS_LITERAL_STRING(kNativeImageMime),
   782                                           variant, 0, principal);
   783     }
   785     // assume the image comes from a file, and add a file promise. We
   786     // register ourselves as a nsIFlavorDataProvider, and will use the
   787     // GetFlavorData callback to save the image to disk.
   789     nsCOMPtr<nsIFlavorDataProvider> dataProvider =
   790       new nsContentAreaDragDropDataProvider();
   791     if (dataProvider) {
   792       nsCOMPtr<nsIWritableVariant> variant = do_CreateInstance(NS_VARIANT_CONTRACTID);
   793       if (variant) {
   794         variant->SetAsISupports(dataProvider);
   795         aDataTransfer->SetDataWithPrincipal(NS_LITERAL_STRING(kFilePromiseMime),
   796                                             variant, 0, principal);
   797       }
   798     }
   800     AddString(aDataTransfer, NS_LITERAL_STRING(kFilePromiseURLMime),
   801               mImageSourceString, principal);
   802     AddString(aDataTransfer, NS_LITERAL_STRING(kFilePromiseDestFilename),
   803               mImageDestFileName, principal);
   805     // if not an anchor, add the image url
   806     if (!mIsAnchor) {
   807       AddString(aDataTransfer, NS_LITERAL_STRING(kURLDataMime), mUrlString, principal);
   808       AddString(aDataTransfer, NS_LITERAL_STRING("text/uri-list"), mUrlString, principal);
   809     }
   810   }
   812   return NS_OK;
   813 }
   815 // note that this can return NS_OK, but a null out param (by design)
   816 // static
   817 nsresult
   818 DragDataProducer::GetDraggableSelectionData(nsISelection* inSelection,
   819                                             nsIContent* inRealTargetNode,
   820                                             nsIContent **outImageOrLinkNode,
   821                                             bool* outDragSelectedText)
   822 {
   823   NS_ENSURE_ARG(inSelection);
   824   NS_ENSURE_ARG(inRealTargetNode);
   825   NS_ENSURE_ARG_POINTER(outImageOrLinkNode);
   827   *outImageOrLinkNode = nullptr;
   828   *outDragSelectedText = false;
   830   bool selectionContainsTarget = false;
   832   bool isCollapsed = false;
   833   inSelection->GetIsCollapsed(&isCollapsed);
   834   if (!isCollapsed) {
   835     nsCOMPtr<nsIDOMNode> realTargetNode = do_QueryInterface(inRealTargetNode);
   836     inSelection->ContainsNode(realTargetNode, false,
   837                               &selectionContainsTarget);
   839     if (selectionContainsTarget) {
   840       // track down the anchor node, if any, for the url
   841       nsCOMPtr<nsIDOMNode> selectionStart;
   842       inSelection->GetAnchorNode(getter_AddRefs(selectionStart));
   844       nsCOMPtr<nsIDOMNode> selectionEnd;
   845       inSelection->GetFocusNode(getter_AddRefs(selectionEnd));
   847       // look for a selection around a single node, like an image.
   848       // in this case, drag the image, rather than a serialization of the HTML
   849       // XXX generalize this to other draggable element types?
   850       if (selectionStart == selectionEnd) {
   851         bool hasChildren;
   852         selectionStart->HasChildNodes(&hasChildren);
   853         if (hasChildren) {
   854           // see if just one node is selected
   855           int32_t anchorOffset, focusOffset;
   856           inSelection->GetAnchorOffset(&anchorOffset);
   857           inSelection->GetFocusOffset(&focusOffset);
   858           if (abs(anchorOffset - focusOffset) == 1) {
   859             nsCOMPtr<nsIContent> selStartContent =
   860               do_QueryInterface(selectionStart);
   862             if (selStartContent) {
   863               int32_t childOffset =
   864                 (anchorOffset < focusOffset) ? anchorOffset : focusOffset;
   865               nsIContent *childContent =
   866                 selStartContent->GetChildAt(childOffset);
   867               // if we find an image, we'll fall into the node-dragging code,
   868               // rather the the selection-dragging code
   869               if (nsContentUtils::IsDraggableImage(childContent)) {
   870                 NS_ADDREF(*outImageOrLinkNode = childContent);
   871                 return NS_OK;
   872               }
   873             }
   874           }
   875         }
   876       }
   878       // see if the selection is a link;  if so, its node will be returned
   879       GetSelectedLink(inSelection, outImageOrLinkNode);
   881       // indicate that a link or text is selected
   882       *outDragSelectedText = true;
   883     }
   884   }
   886   return NS_OK;
   887 }
   889 // static
   890 void
   891 DragDataProducer::GetSelectedLink(nsISelection* inSelection,
   892                                   nsIContent **outLinkNode)
   893 {
   894   *outLinkNode = nullptr;
   896   nsCOMPtr<nsIDOMNode> selectionStartNode;
   897   inSelection->GetAnchorNode(getter_AddRefs(selectionStartNode));
   898   nsCOMPtr<nsIDOMNode> selectionEndNode;
   899   inSelection->GetFocusNode(getter_AddRefs(selectionEndNode));
   901   // simple case:  only one node is selected
   902   // see if it or its parent is an anchor, then exit
   904   if (selectionStartNode == selectionEndNode) {
   905     nsCOMPtr<nsIContent> selectionStart = do_QueryInterface(selectionStartNode);
   906     nsCOMPtr<nsIContent> link = FindParentLinkNode(selectionStart);
   907     if (link) {
   908       link.swap(*outLinkNode);
   909     }
   911     return;
   912   }
   914   // more complicated case:  multiple nodes are selected
   916   // Unless you use the Alt key while selecting anchor text, it is
   917   // nearly impossible to avoid overlapping into adjacent nodes.
   918   // Deal with this by trimming off the leading and/or trailing
   919   // nodes of the selection if the strings they produce are empty.
   921   // first, use a range determine if the selection was marked LTR or RTL;
   922   // if the latter, swap endpoints so we trim in the right direction
   924   int32_t startOffset, endOffset;
   925   {
   926     nsCOMPtr<nsIDOMRange> range;
   927     inSelection->GetRangeAt(0, getter_AddRefs(range));
   928     if (!range) {
   929       return;
   930     }
   932     nsCOMPtr<nsIDOMNode> tempNode;
   933     range->GetStartContainer( getter_AddRefs(tempNode));
   934     if (tempNode != selectionStartNode) {
   935       selectionEndNode = selectionStartNode;
   936       selectionStartNode = tempNode;
   937       inSelection->GetAnchorOffset(&endOffset);
   938       inSelection->GetFocusOffset(&startOffset);
   939     } else {
   940       inSelection->GetAnchorOffset(&startOffset);
   941       inSelection->GetFocusOffset(&endOffset);
   942     }
   943   }
   945   // trim leading node if the string is empty or
   946   // the selection starts at the end of the text
   948   nsAutoString nodeStr;
   949   selectionStartNode->GetNodeValue(nodeStr);
   950   if (nodeStr.IsEmpty() ||
   951       startOffset+1 >= static_cast<int32_t>(nodeStr.Length())) {
   952     nsCOMPtr<nsIDOMNode> curr = selectionStartNode;
   953     nsIDOMNode* next;
   955     while (curr) {
   956       curr->GetNextSibling(&next);
   958       if (next) {
   959         selectionStartNode = dont_AddRef(next);
   960         break;
   961       }
   963       curr->GetParentNode(&next);
   964       curr = dont_AddRef(next);
   965     }
   966   }
   968   // trim trailing node if the selection ends before its text begins
   970   if (endOffset == 0) {
   971     nsCOMPtr<nsIDOMNode> curr = selectionEndNode;
   972     nsIDOMNode* next;
   974     while (curr) {
   975       curr->GetPreviousSibling(&next);
   977       if (next){
   978         selectionEndNode = dont_AddRef(next);
   979         break;
   980       }
   982       curr->GetParentNode(&next);
   983       curr = dont_AddRef(next);
   984     }
   985   }
   987   // see if the leading & trailing nodes are part of the
   988   // same anchor - if so, return the anchor node
   989   nsCOMPtr<nsIContent> selectionStart = do_QueryInterface(selectionStartNode);
   990   nsCOMPtr<nsIContent> link = FindParentLinkNode(selectionStart);
   991   if (link) {
   992     nsCOMPtr<nsIContent> selectionEnd = do_QueryInterface(selectionEndNode);
   993     nsCOMPtr<nsIContent> link2 = FindParentLinkNode(selectionEnd);
   995     if (link == link2) {
   996       NS_IF_ADDREF(*outLinkNode = link);
   997     }
   998   }
  1000   return;

mercurial