tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsContentAreaDragDrop.cpp (31154B)


      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 "BrowserParent.h"
     17 #include "imgIContainer.h"
     18 #include "imgIRequest.h"
     19 #include "mozilla/TextControlElement.h"
     20 #include "mozilla/dom/BrowsingContext.h"
     21 #include "mozilla/dom/DataTransfer.h"
     22 #include "mozilla/dom/Document.h"
     23 #include "mozilla/dom/Element.h"
     24 #include "mozilla/dom/HTMLAnchorElement.h"
     25 #include "mozilla/dom/HTMLAreaElement.h"
     26 #include "mozilla/dom/Selection.h"
     27 #include "nsComponentManagerUtils.h"
     28 #include "nsContentUtils.h"
     29 #include "nsCopySupport.h"
     30 #include "nsEscape.h"
     31 #include "nsFrameLoader.h"
     32 #include "nsFrameLoaderOwner.h"
     33 #include "nsIContent.h"
     34 #include "nsIContentInlines.h"
     35 #include "nsIContentPolicy.h"
     36 #include "nsICookieJarSettings.h"
     37 #include "nsIFile.h"
     38 #include "nsIFormControl.h"
     39 #include "nsIImageLoadingContent.h"
     40 #include "nsIMIMEInfo.h"
     41 #include "nsIMIMEService.h"
     42 #include "nsIPrincipal.h"
     43 #include "nsISelectionController.h"
     44 #include "nsISupportsPrimitives.h"
     45 #include "nsITransferable.h"
     46 #include "nsIURIMutator.h"
     47 #include "nsIURL.h"
     48 #include "nsIWebBrowserPersist.h"
     49 #include "nsNetUtil.h"
     50 #include "nsPIDOMWindow.h"
     51 #include "nsQueryObject.h"
     52 #include "nsRange.h"
     53 #include "nsServiceManagerUtils.h"
     54 #include "nsUnicharUtils.h"
     55 #include "nsVariant.h"
     56 #include "nsXPCOM.h"
     57 
     58 using namespace mozilla;
     59 using namespace mozilla::dom;
     60 using mozilla::IgnoreErrors;
     61 
     62 class MOZ_STACK_CLASS DragDataProducer {
     63 public:
     64  DragDataProducer(nsPIDOMWindowOuter* aWindow, nsIContent* aTarget,
     65                   nsIContent* aSelectionTargetNode, bool aIsAltKeyPressed);
     66  nsresult Produce(DataTransfer* aDataTransfer, bool* aCanDrag,
     67                   Selection** aSelection, nsIContent** aDragNode,
     68                   nsIPolicyContainer** aPolicyContainer,
     69                   nsICookieJarSettings** aCookieJarSettings);
     70 
     71 private:
     72  // @param aHidden true, iff the data should be hidden from non-chrome code.
     73  void AddString(DataTransfer* aDataTransfer, const nsAString& aFlavor,
     74                 const nsAString& aData, nsIPrincipal* aPrincipal,
     75                 bool aHidden = false);
     76  nsresult AddStringsToDataTransfer(nsIContent* aDragNode,
     77                                    DataTransfer* aDataTransfer);
     78  nsresult GetImageData(imgIContainer* aImage, imgIRequest* aRequest);
     79  static nsresult GetDraggableSelectionData(Selection* inSelection,
     80                                            nsIContent* inRealTargetNode,
     81                                            nsIContent** outImageOrLinkNode,
     82                                            bool* outDragSelectedText);
     83  [[nodiscard]] static nsresult GetAnchorURL(nsIContent* inNode,
     84                                             nsAString& outURL);
     85  static void CreateLinkText(const nsAString& inURL, const nsAString& inText,
     86                             nsAString& outLinkText);
     87 
     88  nsCOMPtr<nsPIDOMWindowOuter> mWindow;
     89  nsCOMPtr<nsIContent> mTarget;
     90  nsCOMPtr<nsIContent> mSelectionTargetNode;
     91  bool mIsAltKeyPressed;
     92 
     93  nsString mUrlString;
     94  nsString mImageSourceString;
     95  nsString mImageDestFileName;
     96 #if defined(XP_MACOSX)
     97  nsString mImageRequestMime;
     98 #endif
     99  nsString mTitleString;
    100  // will be filled automatically if you fill urlstring
    101  nsString mHtmlString;
    102  nsString mContextString;
    103  nsString mInfoString;
    104 
    105  bool mIsAnchor;
    106  nsCOMPtr<imgIContainer> mImage;
    107 };
    108 
    109 nsresult nsContentAreaDragDrop::GetDragData(
    110    nsPIDOMWindowOuter* aWindow, nsIContent* aTarget,
    111    nsIContent* aSelectionTargetNode, bool aIsAltKeyPressed,
    112    DataTransfer* aDataTransfer, bool* aCanDrag, Selection** aSelection,
    113    nsIContent** aDragNode, nsIPolicyContainer** aPolicyContainer,
    114    nsICookieJarSettings** aCookieJarSettings) {
    115  NS_ENSURE_TRUE(aSelectionTargetNode, NS_ERROR_INVALID_ARG);
    116 
    117  *aCanDrag = true;
    118 
    119  DragDataProducer provider(aWindow, aTarget, aSelectionTargetNode,
    120                            aIsAltKeyPressed);
    121  return provider.Produce(aDataTransfer, aCanDrag, aSelection, aDragNode,
    122                          aPolicyContainer, aCookieJarSettings);
    123 }
    124 
    125 NS_IMPL_ISUPPORTS(nsContentAreaDragDropDataProvider, nsIFlavorDataProvider)
    126 
    127 // SaveURIToFile
    128 // used on platforms where it's possible to drag items (e.g. images)
    129 // into the file system
    130 nsresult nsContentAreaDragDropDataProvider::SaveURIToFile(
    131    nsIURI* inSourceURI, nsIPrincipal* inTriggeringPrincipal,
    132    nsICookieJarSettings* inCookieJarSettings, nsIFile* inDestFile,
    133    nsContentPolicyType inContentPolicyType, bool isPrivate) {
    134  nsCOMPtr<nsIURL> sourceURL = do_QueryInterface(inSourceURI);
    135  if (!sourceURL) {
    136    return NS_ERROR_NO_INTERFACE;
    137  }
    138 
    139  nsresult rv = inDestFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
    140  NS_ENSURE_SUCCESS(rv, rv);
    141 
    142  // we rely on the fact that the WPB is refcounted by the channel etc,
    143  // so we don't keep a ref to it. It will die when finished.
    144  nsCOMPtr<nsIWebBrowserPersist> persist = do_CreateInstance(
    145      "@mozilla.org/embedding/browser/nsWebBrowserPersist;1", &rv);
    146  NS_ENSURE_SUCCESS(rv, rv);
    147 
    148  persist->SetPersistFlags(
    149      nsIWebBrowserPersist::PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION |
    150      // Do not HTTPS-Only/-First upgrade this request. If we reach this point,
    151      // any potential upgrades should have already happened, or the URI may
    152      // have already been exempt.
    153      nsIWebBrowserPersist::PERSIST_FLAGS_DISABLE_HTTPS_ONLY);
    154 
    155  // referrer policy can be anything since the referrer is nullptr
    156  return persist->SaveURI(inSourceURI, inTriggeringPrincipal, 0, nullptr,
    157                          inCookieJarSettings, nullptr, nullptr, inDestFile,
    158                          inContentPolicyType, isPrivate);
    159 }
    160 
    161 // This is our nsIFlavorDataProvider callback. There are several
    162 // assumptions here that make this work:
    163 //
    164 // 1. Someone put a kFilePromiseURLMime flavor into the transferable
    165 //    with the source URI of the file to save (as a string). We did
    166 //    that in AddStringsToDataTransfer.
    167 //
    168 // 2. Someone put a kFilePromiseDirectoryMime flavor into the
    169 //    transferable with an nsIFile for the directory we are to
    170 //    save in. That has to be done by platform-specific code (in
    171 //    widget), which gets the destination directory from
    172 //    OS-specific drag information.
    173 //
    174 NS_IMETHODIMP
    175 nsContentAreaDragDropDataProvider::GetFlavorData(nsITransferable* aTransferable,
    176                                                 const char* aFlavor,
    177                                                 nsISupports** aData) {
    178  NS_ENSURE_ARG_POINTER(aData);
    179  *aData = nullptr;
    180 
    181  nsresult rv = NS_ERROR_NOT_IMPLEMENTED;
    182 
    183  if (strcmp(aFlavor, kFilePromiseMime) == 0) {
    184    // get the URI from the kFilePromiseURLMime flavor
    185    NS_ENSURE_ARG(aTransferable);
    186    nsCOMPtr<nsISupports> tmp;
    187    rv = aTransferable->GetTransferData(kFilePromiseURLMime,
    188                                        getter_AddRefs(tmp));
    189    NS_ENSURE_SUCCESS(rv, rv);
    190    nsCOMPtr<nsISupportsString> supportsString = do_QueryInterface(tmp);
    191    if (!supportsString) return NS_ERROR_FAILURE;
    192 
    193    nsAutoString sourceURLString;
    194    supportsString->GetData(sourceURLString);
    195    if (sourceURLString.IsEmpty()) return NS_ERROR_FAILURE;
    196 
    197    nsCOMPtr<nsIURI> sourceURI;
    198    rv = NS_NewURI(getter_AddRefs(sourceURI), sourceURLString);
    199    NS_ENSURE_SUCCESS(rv, rv);
    200 
    201    rv = aTransferable->GetTransferData(kFilePromiseDestFilename,
    202                                        getter_AddRefs(tmp));
    203    NS_ENSURE_SUCCESS(rv, rv);
    204    supportsString = do_QueryInterface(tmp);
    205    if (!supportsString) return NS_ERROR_FAILURE;
    206 
    207    nsAutoString targetFilename;
    208    supportsString->GetData(targetFilename);
    209    if (targetFilename.IsEmpty()) return NS_ERROR_FAILURE;
    210 
    211 #if defined(XP_MACOSX)
    212    // Use the image request's MIME type to ensure the filename's
    213    // extension is compatible with the OS's handler for this type.
    214    // If it isn't, or is missing, replace the extension with the
    215    // primary extension. On Mac, do this in the parent process
    216    // because sandboxing blocks access to MIME-handler info from
    217    // content processes.
    218    if (XRE_IsParentProcess()) {
    219      rv = aTransferable->GetTransferData(kImageRequestMime,
    220                                          getter_AddRefs(tmp));
    221      NS_ENSURE_SUCCESS(rv, rv);
    222      supportsString = do_QueryInterface(tmp);
    223      if (!supportsString) return NS_ERROR_FAILURE;
    224 
    225      nsAutoString contentType;
    226      supportsString->GetData(contentType);
    227 
    228      nsCOMPtr<nsIMIMEService> mimeService =
    229          do_GetService("@mozilla.org/mime;1");
    230      if (NS_WARN_IF(!mimeService)) {
    231        return NS_ERROR_FAILURE;
    232      }
    233 
    234      mimeService->ValidateFileNameForSaving(
    235          targetFilename, NS_ConvertUTF16toUTF8(contentType),
    236          nsIMIMEService::VALIDATE_DEFAULT, targetFilename);
    237    } else {
    238      // make the filename safe for the filesystem
    239      targetFilename.ReplaceChar(
    240          u"" FILE_PATH_SEPARATOR FILE_ILLEGAL_CHARACTERS, u'-');
    241    }
    242 #endif /* defined(XP_MACOSX) */
    243 
    244    // get the target directory from the kFilePromiseDirectoryMime
    245    // flavor
    246    nsCOMPtr<nsISupports> dirPrimitive;
    247    rv = aTransferable->GetTransferData(kFilePromiseDirectoryMime,
    248                                        getter_AddRefs(dirPrimitive));
    249    NS_ENSURE_SUCCESS(rv, rv);
    250    nsCOMPtr<nsIFile> destDirectory = do_QueryInterface(dirPrimitive);
    251    if (!destDirectory) return NS_ERROR_FAILURE;
    252 
    253    nsCOMPtr<nsIFile> file;
    254    rv = destDirectory->Clone(getter_AddRefs(file));
    255    NS_ENSURE_SUCCESS(rv, rv);
    256 
    257    file->Append(targetFilename);
    258 
    259    bool isPrivate = aTransferable->GetIsPrivateData();
    260 
    261    nsCOMPtr<nsIPrincipal> principal = aTransferable->GetDataPrincipal();
    262    nsContentPolicyType contentPolicyType =
    263        aTransferable->GetContentPolicyType();
    264    nsCOMPtr<nsICookieJarSettings> cookieJarSettings =
    265        aTransferable->GetCookieJarSettings();
    266    rv = SaveURIToFile(sourceURI, principal, cookieJarSettings, file,
    267                       contentPolicyType, isPrivate);
    268    // send back an nsIFile
    269    if (NS_SUCCEEDED(rv)) {
    270      CallQueryInterface(file, aData);
    271    }
    272  }
    273 
    274  return rv;
    275 }
    276 
    277 DragDataProducer::DragDataProducer(nsPIDOMWindowOuter* aWindow,
    278                                   nsIContent* aTarget,
    279                                   nsIContent* aSelectionTargetNode,
    280                                   bool aIsAltKeyPressed)
    281    : mWindow(aWindow),
    282      mTarget(aTarget),
    283      mSelectionTargetNode(aSelectionTargetNode),
    284      mIsAltKeyPressed(aIsAltKeyPressed),
    285      mIsAnchor(false) {}
    286 
    287 static nsIContent* FindDragTarget(nsIContent* aContent) {
    288  for (nsIContent* content = aContent; content;
    289       content = content->GetFlattenedTreeParent()) {
    290    if (nsContentUtils::ContentIsDraggable(content)) {
    291      return content;
    292    }
    293  }
    294  return nullptr;
    295 }
    296 
    297 //
    298 // GetAnchorURL
    299 //
    300 nsresult DragDataProducer::GetAnchorURL(nsIContent* aContent, nsAString& aURL) {
    301  aURL.Truncate();
    302  auto* element = Element::FromNodeOrNull(aContent);
    303  if (!element || !element->IsLink()) {
    304    return NS_OK;
    305  }
    306 
    307  nsCOMPtr<nsIURI> linkURI = element->GetHrefURI();
    308  if (!linkURI) {
    309    return NS_OK;
    310  }
    311 
    312  nsAutoCString spec;
    313  nsresult rv = linkURI->GetSpec(spec);
    314  NS_ENSURE_SUCCESS(rv, rv);
    315  nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
    316  rv = secMan->CheckLoadURIStrWithPrincipal(aContent->NodePrincipal(), spec, 0);
    317  NS_ENSURE_SUCCESS(rv, rv);
    318  CopyUTF8toUTF16(spec, aURL);
    319  return NS_OK;
    320 }
    321 
    322 //
    323 // CreateLinkText
    324 //
    325 // Creates the html for an anchor in the form
    326 //  <a href="inURL">inText</a>
    327 //
    328 void DragDataProducer::CreateLinkText(const nsAString& inURL,
    329                                      const nsAString& inText,
    330                                      nsAString& outLinkText) {
    331  // use a temp var in case |inText| is the same string as
    332  // |outLinkText| to avoid overwriting it while building up the
    333  // string in pieces.
    334  nsAutoString linkText(u"<a href=\""_ns + inURL + u"\">"_ns + inText +
    335                        u"</a>"_ns);
    336 
    337  outLinkText = linkText;
    338 }
    339 
    340 nsresult DragDataProducer::GetImageData(imgIContainer* aImage,
    341                                        imgIRequest* aRequest) {
    342  MOZ_ASSERT(aImage);
    343  MOZ_ASSERT(aRequest);
    344 
    345  nsCOMPtr<nsIURI> imgUri = aRequest->GetURI();
    346  if (!imgUri) {
    347    return NS_ERROR_FAILURE;
    348  }
    349 
    350  nsAutoCString spec;
    351  nsresult rv = imgUri->GetSpec(spec);
    352  NS_ENSURE_SUCCESS(rv, rv);
    353 
    354  // pass out the image source string
    355  CopyUTF8toUTF16(spec, mImageSourceString);
    356 
    357  nsCString mimeType;
    358  aRequest->GetMimeType(getter_Copies(mimeType));
    359 
    360  nsAutoCString fileName;
    361  aRequest->GetFileName(fileName);
    362 
    363 #if defined(XP_MACOSX)
    364  // Save the MIME type so we can make sure the extension
    365  // is compatible (and replace it if it isn't) when the
    366  // image is dropped. On Mac, we need to get the OS MIME
    367  // handler information in the parent due to sandboxing.
    368  CopyUTF8toUTF16(mimeType, mImageRequestMime);
    369  CopyUTF8toUTF16(fileName, mImageDestFileName);
    370 #else
    371  nsCOMPtr<nsIMIMEService> mimeService = do_GetService("@mozilla.org/mime;1");
    372  if (NS_WARN_IF(!mimeService)) {
    373    return NS_ERROR_FAILURE;
    374  }
    375 
    376  CopyUTF8toUTF16(fileName, mImageDestFileName);
    377  mimeService->ValidateFileNameForSaving(mImageDestFileName, mimeType,
    378                                         nsIMIMEService::VALIDATE_DEFAULT,
    379                                         mImageDestFileName);
    380 #endif
    381 
    382  // and the image object
    383  mImage = aImage;
    384 
    385  return NS_OK;
    386 }
    387 
    388 nsresult DragDataProducer::Produce(DataTransfer* aDataTransfer, bool* aCanDrag,
    389                                   Selection** aSelection,
    390                                   nsIContent** aDragNode,
    391                                   nsIPolicyContainer** aPolicyContainer,
    392                                   nsICookieJarSettings** aCookieJarSettings) {
    393  MOZ_ASSERT(aCanDrag && aSelection && aDataTransfer && aDragNode,
    394             "null pointer passed to Produce");
    395  NS_ASSERTION(mWindow, "window not set");
    396  NS_ASSERTION(mSelectionTargetNode,
    397               "selection target node should have been set");
    398 
    399  *aDragNode = nullptr;
    400 
    401  nsresult rv;
    402  nsIContent* dragNode = nullptr;
    403  *aSelection = nullptr;
    404 
    405  // Find the selection to see what we could be dragging and if what we're
    406  // dragging is in what is selected. If this is an editable textbox, use
    407  // the textbox's selection, otherwise use the window's selection.
    408  RefPtr<Selection> selection;
    409  nsIContent* editingElement = mSelectionTargetNode->IsEditable()
    410                                   ? mSelectionTargetNode->GetEditingHost()
    411                                   : nullptr;
    412  RefPtr<TextControlElement> textControlElement =
    413      TextControlElement::GetTextControlElementFromEditingHost(editingElement);
    414  if (textControlElement) {
    415    nsISelectionController* selcon =
    416        textControlElement->GetSelectionController();
    417    if (selcon) {
    418      selection =
    419          selcon->GetSelection(nsISelectionController::SELECTION_NORMAL);
    420    }
    421 
    422    if (!selection) return NS_OK;
    423  } else {
    424    selection = mWindow->GetSelection();
    425    if (!selection) return NS_OK;
    426 
    427    // Check if the node is inside a form control. Don't set aCanDrag to false
    428    // however, as we still want to allow the drag.
    429    nsCOMPtr<nsIContent> findFormNode = mSelectionTargetNode;
    430    nsIContent* findFormParent = findFormNode->GetParent();
    431    while (findFormParent) {
    432      const auto* form = nsIFormControl::FromNode(findFormParent);
    433      if (form && !form->AllowDraggableChildren()) {
    434        return NS_OK;
    435      }
    436      findFormParent = findFormParent->GetParent();
    437    }
    438  }
    439 
    440  // if set, serialize the content under this node
    441  nsCOMPtr<nsIContent> nodeToSerialize;
    442 
    443  BrowsingContext* bc = mWindow->GetBrowsingContext();
    444  const bool isChromeShell = bc && bc->IsChrome();
    445 
    446  // In chrome shells, only allow dragging inside editable areas.
    447  if (isChromeShell && !editingElement) {
    448    // This path should already be filtered out in
    449    // EventStateManager::DetermineDragTargetAndDefaultData.
    450    MOZ_ASSERT_UNREACHABLE("Shouldn't be generating drag data for chrome");
    451    return NS_OK;
    452  }
    453 
    454  if (isChromeShell && textControlElement) {
    455    // Only use the selection if the target node is in the selection.
    456    if (!selection->ContainsNode(*mSelectionTargetNode, false, IgnoreErrors()))
    457      return NS_OK;
    458 
    459    selection.swap(*aSelection);
    460  } else {
    461    // In content shells, a number of checks are made below to determine
    462    // whether an image or a link is being dragged. If so, add additional
    463    // data to the data transfer. This is also done for chrome shells, but
    464    // only when in a non-textbox editor.
    465 
    466    bool haveSelectedContent = false;
    467 
    468    // only drag form elements by using the alt key,
    469    // otherwise select widgets are hard to use
    470 
    471    // Note that while <object> elements implement nsIFormControl, we should
    472    // really allow dragging them if they happen to be images.
    473    // XXX Other browsers allow dragging ohter type of form element as well.
    474    if (!mIsAltKeyPressed) {
    475      if (const auto* form = nsIFormControl::FromNodeOrNull(mTarget)) {
    476        if (form->IsConceptButton()) {
    477          return NS_OK;
    478        }
    479        if (form->ControlType() != FormControlType::Object) {
    480          *aCanDrag = false;
    481          return NS_OK;
    482        }
    483      }
    484    }
    485 
    486    // possible parent link node
    487    nsCOMPtr<nsIContent> parentLink;
    488    nsCOMPtr<nsIContent> draggedNode = FindDragTarget(mTarget);
    489 
    490    nsCOMPtr<nsIImageLoadingContent> image;
    491 
    492    nsCOMPtr<nsIContent> selectedImageOrLinkNode;
    493    GetDraggableSelectionData(selection, mSelectionTargetNode,
    494                              getter_AddRefs(selectedImageOrLinkNode),
    495                              &haveSelectedContent);
    496 
    497    // either plain text or anchor text is selected
    498    if (haveSelectedContent) {
    499      selection.swap(*aSelection);
    500    } else if (selectedImageOrLinkNode) {
    501      // an image is selected
    502      image = do_QueryInterface(selectedImageOrLinkNode);
    503    } else {
    504      // nothing is selected -
    505      //
    506      // look for draggable elements under the mouse
    507      //
    508      // if the alt key is down, don't start a drag if we're in an
    509      // anchor because we want to do selection.
    510      parentLink = nsContentUtils::GetClosestLinkInFlatTree(draggedNode);
    511      if (parentLink && mIsAltKeyPressed) {
    512        *aCanDrag = false;
    513        return NS_OK;
    514      }
    515      image = do_QueryInterface(draggedNode);
    516    }
    517 
    518    {
    519      // set for linked images, and links
    520      nsCOMPtr<nsIContent> linkNode;
    521      if (const auto* areaElem = HTMLAreaElement::FromNodeOrNull(draggedNode)) {
    522        // use the alt text (or, if missing, the href) as the title
    523        areaElem->GetAttr(nsGkAtoms::alt, mTitleString);
    524        if (mTitleString.IsEmpty()) {
    525          // this can be a relative link
    526          areaElem->GetAttr(nsGkAtoms::href, mTitleString);
    527        }
    528 
    529        // gives an absolute link
    530        nsresult rv = GetAnchorURL(draggedNode, mUrlString);
    531        NS_ENSURE_SUCCESS(rv, rv);
    532 
    533        // we'll generate HTML like <a href="absurl">alt text</a>
    534        mIsAnchor = true;
    535 
    536        mHtmlString.AssignLiteral("<a href=\"");
    537        mHtmlString.Append(mUrlString);
    538        mHtmlString.AppendLiteral("\">");
    539        mHtmlString.Append(mTitleString);
    540        mHtmlString.AppendLiteral("</a>");
    541 
    542        dragNode = draggedNode;
    543      } else if (image) {
    544        // grab the href as the url, use alt text as the title of the
    545        // area if it's there.  the drag data is the image tag and src
    546        // attribute.
    547        nsCOMPtr<nsIURI> imageURI;
    548        image->GetCurrentURI(getter_AddRefs(imageURI));
    549        nsCOMPtr<Element> imageElement(do_QueryInterface(image));
    550        if (imageURI) {
    551          nsAutoCString spec;
    552          rv = imageURI->GetSpec(spec);
    553          NS_ENSURE_SUCCESS(rv, rv);
    554          nsIScriptSecurityManager* secMan =
    555              nsContentUtils::GetSecurityManager();
    556          rv = secMan->CheckLoadURIStrWithPrincipal(
    557              imageElement->NodePrincipal(), spec, 0);
    558          NS_ENSURE_SUCCESS(rv, rv);
    559          mIsAnchor = true;
    560          CopyUTF8toUTF16(spec, mUrlString);
    561        }
    562 
    563        // XXXbz Shouldn't we use the "title" attr for title?  Using
    564        // "alt" seems very wrong....
    565        // XXXbz Also, what if this is an nsIImageLoadingContent
    566        // that's not an <html:img>?
    567        if (imageElement) {
    568          imageElement->GetAttr(nsGkAtoms::alt, mTitleString);
    569        }
    570 
    571        if (mTitleString.IsEmpty()) {
    572          mTitleString = mUrlString;
    573        }
    574 
    575        nsCOMPtr<imgIRequest> imgRequest;
    576 
    577        // grab the image data, and its request.
    578        nsCOMPtr<imgIContainer> img = nsContentUtils::GetImageFromContent(
    579            image, getter_AddRefs(imgRequest));
    580        if (imgRequest) {
    581          rv = GetImageData(img, imgRequest);
    582          NS_ENSURE_SUCCESS(rv, rv);
    583        }
    584 
    585        if (parentLink) {
    586          // If we are dragging around an image in an anchor, then we
    587          // are dragging the entire anchor
    588          linkNode = parentLink;
    589          nodeToSerialize = linkNode;
    590        } else {
    591          nodeToSerialize = draggedNode;
    592        }
    593        dragNode = nodeToSerialize;
    594      } else if (parentLink) {
    595        // parentLink will always be null if there's selected content
    596        linkNode = parentLink;
    597        nodeToSerialize = linkNode;
    598      } else if (!haveSelectedContent) {
    599        // nothing draggable
    600        return NS_OK;
    601      }
    602 
    603      if (linkNode) {
    604        rv = GetAnchorURL(linkNode, mUrlString);
    605        NS_ENSURE_SUCCESS(rv, rv);
    606        mIsAnchor = true;
    607        dragNode = linkNode;
    608      }
    609    }
    610  }
    611 
    612  if (nodeToSerialize || *aSelection) {
    613    mHtmlString.Truncate();
    614    mContextString.Truncate();
    615    mInfoString.Truncate();
    616    mTitleString.Truncate();
    617 
    618    nsCOMPtr<Document> doc = mWindow->GetDoc();
    619    NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
    620 
    621    nsCOMPtr<nsIPolicyContainer> policyContainer = doc->GetPolicyContainer();
    622    if (policyContainer) {
    623      policyContainer.forget(aPolicyContainer);
    624    }
    625 
    626    nsCOMPtr<nsICookieJarSettings> cookieJarSettings = doc->CookieJarSettings();
    627    if (cookieJarSettings) {
    628      NS_IF_ADDREF(*aCookieJarSettings = cookieJarSettings);
    629    }
    630 
    631    // if we have selected text, use it in preference to the node
    632    nsCOMPtr<nsITransferable> transferable;
    633    if (*aSelection) {
    634      rv = nsCopySupport::GetTransferableForSelection(
    635          *aSelection, doc, getter_AddRefs(transferable));
    636    } else {
    637      rv = nsCopySupport::GetTransferableForNode(nodeToSerialize, doc,
    638                                                 getter_AddRefs(transferable));
    639    }
    640    NS_ENSURE_SUCCESS(rv, rv);
    641 
    642    nsCOMPtr<nsISupports> supports;
    643    nsCOMPtr<nsISupportsString> data;
    644    rv = transferable->GetTransferData(kHTMLMime, getter_AddRefs(supports));
    645    data = do_QueryInterface(supports);
    646    if (NS_SUCCEEDED(rv)) {
    647      data->GetData(mHtmlString);
    648      // Do not add NULs to DND text.
    649      mHtmlString.StripChar(L'\0');
    650    }
    651    rv = transferable->GetTransferData(kHTMLContext, getter_AddRefs(supports));
    652    data = do_QueryInterface(supports);
    653    if (NS_SUCCEEDED(rv)) {
    654      data->GetData(mContextString);
    655    }
    656    rv = transferable->GetTransferData(kHTMLInfo, getter_AddRefs(supports));
    657    data = do_QueryInterface(supports);
    658    if (NS_SUCCEEDED(rv)) {
    659      data->GetData(mInfoString);
    660    }
    661    rv = transferable->GetTransferData(kTextMime, getter_AddRefs(supports));
    662    data = do_QueryInterface(supports);
    663    NS_ENSURE_SUCCESS(rv, rv);  // require plain text at a minimum
    664    data->GetData(mTitleString);
    665    // Do not add NULs to DND text.
    666    mTitleString.StripChar(L'\0');
    667  }
    668 
    669  // default text value is the URL
    670  if (mTitleString.IsEmpty()) {
    671    mTitleString = mUrlString;
    672  }
    673 
    674  // if we haven't constructed a html version, make one now
    675  if (mHtmlString.IsEmpty() && !mUrlString.IsEmpty())
    676    CreateLinkText(mUrlString, mTitleString, mHtmlString);
    677 
    678  // if there is no drag node, which will be the case for a selection, just
    679  // use the selection target node.
    680  rv = AddStringsToDataTransfer(
    681      dragNode ? dragNode : mSelectionTargetNode.get(), aDataTransfer);
    682  NS_ENSURE_SUCCESS(rv, rv);
    683 
    684  NS_IF_ADDREF(*aDragNode = dragNode);
    685  return NS_OK;
    686 }
    687 
    688 void DragDataProducer::AddString(DataTransfer* aDataTransfer,
    689                                 const nsAString& aFlavor,
    690                                 const nsAString& aData,
    691                                 nsIPrincipal* aPrincipal, bool aHidden) {
    692  RefPtr<nsVariantCC> variant = new nsVariantCC();
    693  variant->SetAsAString(aData);
    694  aDataTransfer->SetDataWithPrincipal(aFlavor, variant, 0, aPrincipal, aHidden);
    695 }
    696 
    697 nsresult DragDataProducer::AddStringsToDataTransfer(
    698    nsIContent* aDragNode, DataTransfer* aDataTransfer) {
    699  NS_ASSERTION(aDragNode, "adding strings for null node");
    700 
    701  // set all of the data to have the principal of the node where the data came
    702  // from
    703  nsIPrincipal* principal = aDragNode->NodePrincipal();
    704 
    705  // add a special flavor if we're an anchor to indicate that we have
    706  // a URL in the drag data
    707  if (!mUrlString.IsEmpty() && mIsAnchor) {
    708    nsAutoString dragData(mUrlString);
    709    dragData.Append('\n');
    710    // Remove leading and trailing newlines in the title and replace them with
    711    // space in remaining positions - they confuse PlacesUtils::unwrapNodes
    712    // that expects url\ntitle formatted data for x-moz-url.
    713    nsAutoString title(mTitleString);
    714    title.Trim("\r\n");
    715    title.ReplaceChar(u"\r\n", ' ');
    716    dragData += title;
    717 
    718    AddString(aDataTransfer, NS_LITERAL_STRING_FROM_CSTRING(kURLMime), dragData,
    719              principal);
    720    AddString(aDataTransfer, NS_LITERAL_STRING_FROM_CSTRING(kURLDataMime),
    721              mUrlString, principal);
    722    AddString(aDataTransfer,
    723              NS_LITERAL_STRING_FROM_CSTRING(kURLDescriptionMime), mTitleString,
    724              principal);
    725    AddString(aDataTransfer, u"text/uri-list"_ns, mUrlString, principal);
    726  }
    727 
    728  // add a special flavor for the html context data
    729  if (!mContextString.IsEmpty())
    730    AddString(aDataTransfer, NS_LITERAL_STRING_FROM_CSTRING(kHTMLContext),
    731              mContextString, principal);
    732 
    733  // add a special flavor if we have html info data
    734  if (!mInfoString.IsEmpty())
    735    AddString(aDataTransfer, NS_LITERAL_STRING_FROM_CSTRING(kHTMLInfo),
    736              mInfoString, principal);
    737 
    738  // add the full html
    739  if (!mHtmlString.IsEmpty())
    740    AddString(aDataTransfer, NS_LITERAL_STRING_FROM_CSTRING(kHTMLMime),
    741              mHtmlString, principal);
    742 
    743  // add the plain text. we use the url for text/plain data if an anchor is
    744  // being dragged, rather than the title text of the link or the alt text for
    745  // an anchor image.
    746  AddString(aDataTransfer, NS_LITERAL_STRING_FROM_CSTRING(kTextMime),
    747            mIsAnchor ? mUrlString : mTitleString, principal);
    748 
    749  // add image data, if present. For now, all we're going to do with
    750  // this is turn it into a native data flavor, so indicate that with
    751  // a new flavor so as not to confuse anyone who is really registered
    752  // for image/gif or image/jpg.
    753  if (mImage) {
    754    RefPtr<nsVariantCC> variant = new nsVariantCC();
    755    variant->SetAsISupports(mImage);
    756    aDataTransfer->SetDataWithPrincipal(
    757        NS_LITERAL_STRING_FROM_CSTRING(kNativeImageMime), variant, 0,
    758        principal);
    759 
    760    // assume the image comes from a file, and add a file promise. We
    761    // register ourselves as a nsIFlavorDataProvider, and will use the
    762    // GetFlavorData callback to save the image to disk.
    763 
    764    nsCOMPtr<nsIFlavorDataProvider> dataProvider =
    765        new nsContentAreaDragDropDataProvider();
    766    if (dataProvider) {
    767      RefPtr<nsVariantCC> variant = new nsVariantCC();
    768      variant->SetAsISupports(dataProvider);
    769      aDataTransfer->SetDataWithPrincipal(
    770          NS_LITERAL_STRING_FROM_CSTRING(kFilePromiseMime), variant, 0,
    771          principal);
    772    }
    773 
    774    AddString(aDataTransfer,
    775              NS_LITERAL_STRING_FROM_CSTRING(kFilePromiseURLMime),
    776              mImageSourceString, principal);
    777    AddString(aDataTransfer,
    778              NS_LITERAL_STRING_FROM_CSTRING(kFilePromiseDestFilename),
    779              mImageDestFileName, principal);
    780 #if defined(XP_MACOSX)
    781    AddString(aDataTransfer, NS_LITERAL_STRING_FROM_CSTRING(kImageRequestMime),
    782              mImageRequestMime, principal, /* aHidden= */ true);
    783 #endif
    784 
    785    // if not an anchor, add the image url
    786    if (!mIsAnchor) {
    787      AddString(aDataTransfer, NS_LITERAL_STRING_FROM_CSTRING(kURLDataMime),
    788                mUrlString, principal);
    789      AddString(aDataTransfer, u"text/uri-list"_ns, mUrlString, principal);
    790    }
    791  }
    792 
    793  return NS_OK;
    794 }
    795 
    796 // note that this can return NS_OK, but a null out param (by design)
    797 // static
    798 nsresult DragDataProducer::GetDraggableSelectionData(
    799    Selection* inSelection, nsIContent* inRealTargetNode,
    800    nsIContent** outImageOrLinkNode, bool* outDragSelectedText) {
    801  NS_ENSURE_ARG(inSelection);
    802  NS_ENSURE_ARG(inRealTargetNode);
    803  NS_ENSURE_ARG_POINTER(outImageOrLinkNode);
    804 
    805  *outImageOrLinkNode = nullptr;
    806  *outDragSelectedText = false;
    807 
    808  if (!inSelection->AreNormalAndCrossShadowBoundaryRangesCollapsed()) {
    809    if (inSelection->ContainsNode(*inRealTargetNode, false, IgnoreErrors())) {
    810      // track down the anchor node, if any, for the url
    811      nsINode* selectionStart =
    812          inSelection->GetMayCrossShadowBoundaryAnchorNode();
    813      nsINode* selectionEnd = inSelection->GetMayCrossShadowBoundaryFocusNode();
    814 
    815      // look for a selection around a single node, like an image.
    816      // in this case, drag the image, rather than a serialization of the HTML
    817      // XXX generalize this to other draggable element types?
    818      if (selectionStart == selectionEnd) {
    819        nsCOMPtr<nsIContent> selStartContent =
    820            nsIContent::FromNodeOrNull(selectionStart);
    821        if (selStartContent && selStartContent->HasChildNodes()) {
    822          // see if just one node is selected
    823          uint32_t anchorOffset = inSelection->AnchorOffset();
    824          uint32_t focusOffset = inSelection->FocusOffset();
    825          if (anchorOffset == focusOffset + 1 ||
    826              focusOffset == anchorOffset + 1) {
    827            uint32_t childOffset = std::min(anchorOffset, focusOffset);
    828            nsIContent* childContent =
    829                selStartContent->GetChildAt_Deprecated(childOffset);
    830            // if we find an image, we'll fall into the node-dragging code,
    831            // rather the the selection-dragging code
    832            if (nsContentUtils::IsDraggableImage(childContent)) {
    833              NS_ADDREF(*outImageOrLinkNode = childContent);
    834              return NS_OK;
    835            }
    836          }
    837        }
    838      }
    839 
    840      // indicate that a link or text is selected
    841      *outDragSelectedText = true;
    842    }
    843  }
    844 
    845  return NS_OK;
    846 }