1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/base/src/nsContentAreaDragDrop.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1001 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "nsReadableUtils.h" 1.10 + 1.11 +// Local Includes 1.12 +#include "nsContentAreaDragDrop.h" 1.13 + 1.14 +// Helper Classes 1.15 +#include "nsString.h" 1.16 + 1.17 +// Interfaces needed to be included 1.18 +#include "nsCopySupport.h" 1.19 +#include "nsIDOMUIEvent.h" 1.20 +#include "nsISelection.h" 1.21 +#include "nsISelectionController.h" 1.22 +#include "nsIDOMNode.h" 1.23 +#include "nsIDOMNodeList.h" 1.24 +#include "nsIDOMEvent.h" 1.25 +#include "nsIDOMDragEvent.h" 1.26 +#include "nsPIDOMWindow.h" 1.27 +#include "nsIDOMRange.h" 1.28 +#include "nsIFormControl.h" 1.29 +#include "nsIDOMHTMLAreaElement.h" 1.30 +#include "nsIDOMHTMLAnchorElement.h" 1.31 +#include "nsITransferable.h" 1.32 +#include "nsComponentManagerUtils.h" 1.33 +#include "nsXPCOM.h" 1.34 +#include "nsISupportsPrimitives.h" 1.35 +#include "nsServiceManagerUtils.h" 1.36 +#include "nsNetUtil.h" 1.37 +#include "nsIFile.h" 1.38 +#include "nsIWebNavigation.h" 1.39 +#include "nsIDocShell.h" 1.40 +#include "nsIContent.h" 1.41 +#include "nsIImageLoadingContent.h" 1.42 +#include "nsITextControlElement.h" 1.43 +#include "nsUnicharUtils.h" 1.44 +#include "nsIURL.h" 1.45 +#include "nsIDocument.h" 1.46 +#include "nsIScriptSecurityManager.h" 1.47 +#include "nsIPrincipal.h" 1.48 +#include "nsIDocShellTreeItem.h" 1.49 +#include "nsIWebBrowserPersist.h" 1.50 +#include "nsEscape.h" 1.51 +#include "nsContentUtils.h" 1.52 +#include "nsIMIMEService.h" 1.53 +#include "imgIContainer.h" 1.54 +#include "imgIRequest.h" 1.55 +#include "mozilla/dom/DataTransfer.h" 1.56 +#include "nsIMIMEInfo.h" 1.57 +#include "nsRange.h" 1.58 +#include "mozilla/dom/Element.h" 1.59 +#include "mozilla/dom/HTMLAreaElement.h" 1.60 + 1.61 +using namespace mozilla::dom; 1.62 + 1.63 +class MOZ_STACK_CLASS DragDataProducer 1.64 +{ 1.65 +public: 1.66 + DragDataProducer(nsPIDOMWindow* aWindow, 1.67 + nsIContent* aTarget, 1.68 + nsIContent* aSelectionTargetNode, 1.69 + bool aIsAltKeyPressed); 1.70 + nsresult Produce(DataTransfer* aDataTransfer, 1.71 + bool* aCanDrag, 1.72 + nsISelection** aSelection, 1.73 + nsIContent** aDragNode); 1.74 + 1.75 +private: 1.76 + void AddString(DataTransfer* aDataTransfer, 1.77 + const nsAString& aFlavor, 1.78 + const nsAString& aData, 1.79 + nsIPrincipal* aPrincipal); 1.80 + nsresult AddStringsToDataTransfer(nsIContent* aDragNode, 1.81 + DataTransfer* aDataTransfer); 1.82 + static nsresult GetDraggableSelectionData(nsISelection* inSelection, 1.83 + nsIContent* inRealTargetNode, 1.84 + nsIContent **outImageOrLinkNode, 1.85 + bool* outDragSelectedText); 1.86 + static already_AddRefed<nsIContent> FindParentLinkNode(nsIContent* inNode); 1.87 + static void GetAnchorURL(nsIContent* inNode, nsAString& outURL); 1.88 + static void GetNodeString(nsIContent* inNode, nsAString & outNodeString); 1.89 + static void CreateLinkText(const nsAString& inURL, const nsAString & inText, 1.90 + nsAString& outLinkText); 1.91 + static void GetSelectedLink(nsISelection* inSelection, 1.92 + nsIContent **outLinkNode); 1.93 + 1.94 + nsCOMPtr<nsPIDOMWindow> mWindow; 1.95 + nsCOMPtr<nsIContent> mTarget; 1.96 + nsCOMPtr<nsIContent> mSelectionTargetNode; 1.97 + bool mIsAltKeyPressed; 1.98 + 1.99 + nsString mUrlString; 1.100 + nsString mImageSourceString; 1.101 + nsString mImageDestFileName; 1.102 + nsString mTitleString; 1.103 + // will be filled automatically if you fill urlstring 1.104 + nsString mHtmlString; 1.105 + nsString mContextString; 1.106 + nsString mInfoString; 1.107 + 1.108 + bool mIsAnchor; 1.109 + nsCOMPtr<imgIContainer> mImage; 1.110 +}; 1.111 + 1.112 + 1.113 +nsresult 1.114 +nsContentAreaDragDrop::GetDragData(nsPIDOMWindow* aWindow, 1.115 + nsIContent* aTarget, 1.116 + nsIContent* aSelectionTargetNode, 1.117 + bool aIsAltKeyPressed, 1.118 + DataTransfer* aDataTransfer, 1.119 + bool* aCanDrag, 1.120 + nsISelection** aSelection, 1.121 + nsIContent** aDragNode) 1.122 +{ 1.123 + NS_ENSURE_TRUE(aSelectionTargetNode, NS_ERROR_INVALID_ARG); 1.124 + 1.125 + *aCanDrag = true; 1.126 + 1.127 + DragDataProducer 1.128 + provider(aWindow, aTarget, aSelectionTargetNode, aIsAltKeyPressed); 1.129 + return provider.Produce(aDataTransfer, aCanDrag, aSelection, aDragNode); 1.130 +} 1.131 + 1.132 + 1.133 +NS_IMPL_ISUPPORTS(nsContentAreaDragDropDataProvider, nsIFlavorDataProvider) 1.134 + 1.135 +// SaveURIToFile 1.136 +// used on platforms where it's possible to drag items (e.g. images) 1.137 +// into the file system 1.138 +nsresult 1.139 +nsContentAreaDragDropDataProvider::SaveURIToFile(nsAString& inSourceURIString, 1.140 + nsIFile* inDestFile, 1.141 + bool isPrivate) 1.142 +{ 1.143 + nsCOMPtr<nsIURI> sourceURI; 1.144 + nsresult rv = NS_NewURI(getter_AddRefs(sourceURI), inSourceURIString); 1.145 + if (NS_FAILED(rv)) { 1.146 + return NS_ERROR_FAILURE; 1.147 + } 1.148 + 1.149 + nsCOMPtr<nsIURL> sourceURL = do_QueryInterface(sourceURI); 1.150 + if (!sourceURL) { 1.151 + return NS_ERROR_NO_INTERFACE; 1.152 + } 1.153 + 1.154 + rv = inDestFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600); 1.155 + NS_ENSURE_SUCCESS(rv, rv); 1.156 + 1.157 + // we rely on the fact that the WPB is refcounted by the channel etc, 1.158 + // so we don't keep a ref to it. It will die when finished. 1.159 + nsCOMPtr<nsIWebBrowserPersist> persist = 1.160 + do_CreateInstance("@mozilla.org/embedding/browser/nsWebBrowserPersist;1", 1.161 + &rv); 1.162 + NS_ENSURE_SUCCESS(rv, rv); 1.163 + 1.164 + persist->SetPersistFlags(nsIWebBrowserPersist::PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION); 1.165 + 1.166 + return persist->SavePrivacyAwareURI(sourceURI, nullptr, nullptr, nullptr, nullptr, 1.167 + inDestFile, isPrivate); 1.168 +} 1.169 + 1.170 +// This is our nsIFlavorDataProvider callback. There are several 1.171 +// assumptions here that make this work: 1.172 +// 1.173 +// 1. Someone put a kFilePromiseURLMime flavor into the transferable 1.174 +// with the source URI of the file to save (as a string). We did 1.175 +// that in AddStringsToDataTransfer. 1.176 +// 1.177 +// 2. Someone put a kFilePromiseDirectoryMime flavor into the 1.178 +// transferable with an nsIFile for the directory we are to 1.179 +// save in. That has to be done by platform-specific code (in 1.180 +// widget), which gets the destination directory from 1.181 +// OS-specific drag information. 1.182 +// 1.183 +NS_IMETHODIMP 1.184 +nsContentAreaDragDropDataProvider::GetFlavorData(nsITransferable *aTransferable, 1.185 + const char *aFlavor, 1.186 + nsISupports **aData, 1.187 + uint32_t *aDataLen) 1.188 +{ 1.189 + NS_ENSURE_ARG_POINTER(aData && aDataLen); 1.190 + *aData = nullptr; 1.191 + *aDataLen = 0; 1.192 + 1.193 + nsresult rv = NS_ERROR_NOT_IMPLEMENTED; 1.194 + 1.195 + if (strcmp(aFlavor, kFilePromiseMime) == 0) { 1.196 + // get the URI from the kFilePromiseURLMime flavor 1.197 + NS_ENSURE_ARG(aTransferable); 1.198 + nsCOMPtr<nsISupports> tmp; 1.199 + uint32_t dataSize = 0; 1.200 + aTransferable->GetTransferData(kFilePromiseURLMime, 1.201 + getter_AddRefs(tmp), &dataSize); 1.202 + nsCOMPtr<nsISupportsString> supportsString = 1.203 + do_QueryInterface(tmp); 1.204 + if (!supportsString) 1.205 + return NS_ERROR_FAILURE; 1.206 + 1.207 + nsAutoString sourceURLString; 1.208 + supportsString->GetData(sourceURLString); 1.209 + if (sourceURLString.IsEmpty()) 1.210 + return NS_ERROR_FAILURE; 1.211 + 1.212 + aTransferable->GetTransferData(kFilePromiseDestFilename, 1.213 + getter_AddRefs(tmp), &dataSize); 1.214 + supportsString = do_QueryInterface(tmp); 1.215 + if (!supportsString) 1.216 + return NS_ERROR_FAILURE; 1.217 + 1.218 + nsAutoString targetFilename; 1.219 + supportsString->GetData(targetFilename); 1.220 + if (targetFilename.IsEmpty()) 1.221 + return NS_ERROR_FAILURE; 1.222 + 1.223 + // get the target directory from the kFilePromiseDirectoryMime 1.224 + // flavor 1.225 + nsCOMPtr<nsISupports> dirPrimitive; 1.226 + dataSize = 0; 1.227 + aTransferable->GetTransferData(kFilePromiseDirectoryMime, 1.228 + getter_AddRefs(dirPrimitive), &dataSize); 1.229 + nsCOMPtr<nsIFile> destDirectory = do_QueryInterface(dirPrimitive); 1.230 + if (!destDirectory) 1.231 + return NS_ERROR_FAILURE; 1.232 + 1.233 + nsCOMPtr<nsIFile> file; 1.234 + rv = destDirectory->Clone(getter_AddRefs(file)); 1.235 + NS_ENSURE_SUCCESS(rv, rv); 1.236 + 1.237 + file->Append(targetFilename); 1.238 + 1.239 + bool isPrivate; 1.240 + aTransferable->GetIsPrivateData(&isPrivate); 1.241 + 1.242 + rv = SaveURIToFile(sourceURLString, file, isPrivate); 1.243 + // send back an nsIFile 1.244 + if (NS_SUCCEEDED(rv)) { 1.245 + CallQueryInterface(file, aData); 1.246 + *aDataLen = sizeof(nsIFile*); 1.247 + } 1.248 + } 1.249 + 1.250 + return rv; 1.251 +} 1.252 + 1.253 +DragDataProducer::DragDataProducer(nsPIDOMWindow* aWindow, 1.254 + nsIContent* aTarget, 1.255 + nsIContent* aSelectionTargetNode, 1.256 + bool aIsAltKeyPressed) 1.257 + : mWindow(aWindow), 1.258 + mTarget(aTarget), 1.259 + mSelectionTargetNode(aSelectionTargetNode), 1.260 + mIsAltKeyPressed(aIsAltKeyPressed), 1.261 + mIsAnchor(false) 1.262 +{ 1.263 +} 1.264 + 1.265 + 1.266 +// 1.267 +// FindParentLinkNode 1.268 +// 1.269 +// Finds the parent with the given link tag starting at |inNode|. If 1.270 +// it gets up to the root without finding it, we stop looking and 1.271 +// return null. 1.272 +// 1.273 +already_AddRefed<nsIContent> 1.274 +DragDataProducer::FindParentLinkNode(nsIContent* inNode) 1.275 +{ 1.276 + nsIContent* content = inNode; 1.277 + if (!content) { 1.278 + // That must have been the document node; nothing else to do here; 1.279 + return nullptr; 1.280 + } 1.281 + 1.282 + for (; content; content = content->GetParent()) { 1.283 + if (nsContentUtils::IsDraggableLink(content)) { 1.284 + nsCOMPtr<nsIContent> ret = content; 1.285 + return ret.forget(); 1.286 + } 1.287 + } 1.288 + 1.289 + return nullptr; 1.290 +} 1.291 + 1.292 + 1.293 +// 1.294 +// GetAnchorURL 1.295 +// 1.296 +void 1.297 +DragDataProducer::GetAnchorURL(nsIContent* inNode, nsAString& outURL) 1.298 +{ 1.299 + nsCOMPtr<nsIURI> linkURI; 1.300 + if (!inNode || !inNode->IsLink(getter_AddRefs(linkURI))) { 1.301 + // Not a link 1.302 + outURL.Truncate(); 1.303 + return; 1.304 + } 1.305 + 1.306 + nsAutoCString spec; 1.307 + linkURI->GetSpec(spec); 1.308 + CopyUTF8toUTF16(spec, outURL); 1.309 +} 1.310 + 1.311 + 1.312 +// 1.313 +// CreateLinkText 1.314 +// 1.315 +// Creates the html for an anchor in the form 1.316 +// <a href="inURL">inText</a> 1.317 +// 1.318 +void 1.319 +DragDataProducer::CreateLinkText(const nsAString& inURL, 1.320 + const nsAString & inText, 1.321 + nsAString& outLinkText) 1.322 +{ 1.323 + // use a temp var in case |inText| is the same string as 1.324 + // |outLinkText| to avoid overwriting it while building up the 1.325 + // string in pieces. 1.326 + nsAutoString linkText(NS_LITERAL_STRING("<a href=\"") + 1.327 + inURL + 1.328 + NS_LITERAL_STRING("\">") + 1.329 + inText + 1.330 + NS_LITERAL_STRING("</a>") ); 1.331 + 1.332 + outLinkText = linkText; 1.333 +} 1.334 + 1.335 + 1.336 +// 1.337 +// GetNodeString 1.338 +// 1.339 +// Gets the text associated with a node 1.340 +// 1.341 +void 1.342 +DragDataProducer::GetNodeString(nsIContent* inNode, 1.343 + nsAString & outNodeString) 1.344 +{ 1.345 + nsCOMPtr<nsINode> node = inNode; 1.346 + 1.347 + outNodeString.Truncate(); 1.348 + 1.349 + // use a range to get the text-equivalent of the node 1.350 + nsCOMPtr<nsIDocument> doc = node->OwnerDoc(); 1.351 + mozilla::ErrorResult rv; 1.352 + nsRefPtr<nsRange> range = doc->CreateRange(rv); 1.353 + if (range) { 1.354 + range->SelectNode(*node, rv); 1.355 + range->ToString(outNodeString); 1.356 + } 1.357 +} 1.358 + 1.359 +nsresult 1.360 +DragDataProducer::Produce(DataTransfer* aDataTransfer, 1.361 + bool* aCanDrag, 1.362 + nsISelection** aSelection, 1.363 + nsIContent** aDragNode) 1.364 +{ 1.365 + NS_PRECONDITION(aCanDrag && aSelection && aDataTransfer && aDragNode, 1.366 + "null pointer passed to Produce"); 1.367 + NS_ASSERTION(mWindow, "window not set"); 1.368 + NS_ASSERTION(mSelectionTargetNode, "selection target node should have been set"); 1.369 + 1.370 + *aDragNode = nullptr; 1.371 + 1.372 + nsresult rv; 1.373 + nsIContent* dragNode = nullptr; 1.374 + *aSelection = nullptr; 1.375 + 1.376 + // Find the selection to see what we could be dragging and if what we're 1.377 + // dragging is in what is selected. If this is an editable textbox, use 1.378 + // the textbox's selection, otherwise use the window's selection. 1.379 + nsCOMPtr<nsISelection> selection; 1.380 + nsIContent* editingElement = mSelectionTargetNode->IsEditable() ? 1.381 + mSelectionTargetNode->GetEditingHost() : nullptr; 1.382 + nsCOMPtr<nsITextControlElement> textControl = 1.383 + nsITextControlElement::GetTextControlElementFromEditingHost(editingElement); 1.384 + if (textControl) { 1.385 + nsISelectionController* selcon = textControl->GetSelectionController(); 1.386 + if (selcon) { 1.387 + selcon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection)); 1.388 + } 1.389 + 1.390 + if (!selection) 1.391 + return NS_OK; 1.392 + } 1.393 + else { 1.394 + mWindow->GetSelection(getter_AddRefs(selection)); 1.395 + if (!selection) 1.396 + return NS_OK; 1.397 + 1.398 + // Check if the node is inside a form control. Don't set aCanDrag to false 1.399 + //however, as we still want to allow the drag. 1.400 + nsCOMPtr<nsIContent> findFormNode = mSelectionTargetNode; 1.401 + nsIContent* findFormParent = findFormNode->GetParent(); 1.402 + while (findFormParent) { 1.403 + nsCOMPtr<nsIFormControl> form(do_QueryInterface(findFormParent)); 1.404 + if (form && !form->AllowDraggableChildren()) { 1.405 + return NS_OK; 1.406 + } 1.407 + findFormParent = findFormParent->GetParent(); 1.408 + } 1.409 + } 1.410 + 1.411 + // if set, serialize the content under this node 1.412 + nsCOMPtr<nsIContent> nodeToSerialize; 1.413 + 1.414 + nsCOMPtr<nsIWebNavigation> webnav = do_GetInterface(mWindow); 1.415 + nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(webnav); 1.416 + const bool isChromeShell = 1.417 + dsti && dsti->ItemType() == nsIDocShellTreeItem::typeChrome; 1.418 + 1.419 + // In chrome shells, only allow dragging inside editable areas. 1.420 + if (isChromeShell && !editingElement) 1.421 + return NS_OK; 1.422 + 1.423 + if (isChromeShell && textControl) { 1.424 + // Only use the selection if the target node is in the selection. 1.425 + bool selectionContainsTarget = false; 1.426 + nsCOMPtr<nsIDOMNode> targetNode = do_QueryInterface(mSelectionTargetNode); 1.427 + selection->ContainsNode(targetNode, false, &selectionContainsTarget); 1.428 + if (!selectionContainsTarget) 1.429 + return NS_OK; 1.430 + 1.431 + selection.swap(*aSelection); 1.432 + } 1.433 + else { 1.434 + // In content shells, a number of checks are made below to determine 1.435 + // whether an image or a link is being dragged. If so, add additional 1.436 + // data to the data transfer. This is also done for chrome shells, but 1.437 + // only when in a non-textbox editor. 1.438 + 1.439 + bool haveSelectedContent = false; 1.440 + 1.441 + // possible parent link node 1.442 + nsCOMPtr<nsIContent> parentLink; 1.443 + nsCOMPtr<nsIContent> draggedNode; 1.444 + 1.445 + { 1.446 + // only drag form elements by using the alt key, 1.447 + // otherwise buttons and select widgets are hard to use 1.448 + 1.449 + // Note that while <object> elements implement nsIFormControl, we should 1.450 + // really allow dragging them if they happen to be images. 1.451 + nsCOMPtr<nsIFormControl> form(do_QueryInterface(mTarget)); 1.452 + if (form && !mIsAltKeyPressed && form->GetType() != NS_FORM_OBJECT) { 1.453 + *aCanDrag = false; 1.454 + return NS_OK; 1.455 + } 1.456 + 1.457 + draggedNode = mTarget; 1.458 + } 1.459 + 1.460 + nsCOMPtr<nsIDOMHTMLAreaElement> area; // client-side image map 1.461 + nsCOMPtr<nsIImageLoadingContent> image; 1.462 + nsCOMPtr<nsIDOMHTMLAnchorElement> link; 1.463 + 1.464 + nsCOMPtr<nsIContent> selectedImageOrLinkNode; 1.465 + GetDraggableSelectionData(selection, mSelectionTargetNode, 1.466 + getter_AddRefs(selectedImageOrLinkNode), 1.467 + &haveSelectedContent); 1.468 + 1.469 + // either plain text or anchor text is selected 1.470 + if (haveSelectedContent) { 1.471 + link = do_QueryInterface(selectedImageOrLinkNode); 1.472 + if (link && mIsAltKeyPressed) { 1.473 + // if alt is pressed, select the link text instead of drag the link 1.474 + *aCanDrag = false; 1.475 + return NS_OK; 1.476 + } 1.477 + 1.478 + selection.swap(*aSelection); 1.479 + } else if (selectedImageOrLinkNode) { 1.480 + // an image is selected 1.481 + image = do_QueryInterface(selectedImageOrLinkNode); 1.482 + } else { 1.483 + // nothing is selected - 1.484 + // 1.485 + // look for draggable elements under the mouse 1.486 + // 1.487 + // if the alt key is down, don't start a drag if we're in an 1.488 + // anchor because we want to do selection. 1.489 + parentLink = FindParentLinkNode(draggedNode); 1.490 + if (parentLink && mIsAltKeyPressed) { 1.491 + *aCanDrag = false; 1.492 + return NS_OK; 1.493 + } 1.494 + 1.495 + area = do_QueryInterface(draggedNode); 1.496 + image = do_QueryInterface(draggedNode); 1.497 + link = do_QueryInterface(draggedNode); 1.498 + } 1.499 + 1.500 + { 1.501 + // set for linked images, and links 1.502 + nsCOMPtr<nsIContent> linkNode; 1.503 + 1.504 + if (area) { 1.505 + // use the alt text (or, if missing, the href) as the title 1.506 + HTMLAreaElement* areaElem = static_cast<HTMLAreaElement*>(area.get()); 1.507 + areaElem->GetAttribute(NS_LITERAL_STRING("alt"), mTitleString); 1.508 + if (mTitleString.IsEmpty()) { 1.509 + // this can be a relative link 1.510 + areaElem->GetAttribute(NS_LITERAL_STRING("href"), mTitleString); 1.511 + } 1.512 + 1.513 + // we'll generate HTML like <a href="absurl">alt text</a> 1.514 + mIsAnchor = true; 1.515 + 1.516 + // gives an absolute link 1.517 + GetAnchorURL(draggedNode, mUrlString); 1.518 + 1.519 + mHtmlString.AssignLiteral("<a href=\""); 1.520 + mHtmlString.Append(mUrlString); 1.521 + mHtmlString.AppendLiteral("\">"); 1.522 + mHtmlString.Append(mTitleString); 1.523 + mHtmlString.AppendLiteral("</a>"); 1.524 + 1.525 + dragNode = draggedNode; 1.526 + } else if (image) { 1.527 + mIsAnchor = true; 1.528 + // grab the href as the url, use alt text as the title of the 1.529 + // area if it's there. the drag data is the image tag and src 1.530 + // attribute. 1.531 + nsCOMPtr<nsIURI> imageURI; 1.532 + image->GetCurrentURI(getter_AddRefs(imageURI)); 1.533 + if (imageURI) { 1.534 + nsAutoCString spec; 1.535 + imageURI->GetSpec(spec); 1.536 + CopyUTF8toUTF16(spec, mUrlString); 1.537 + } 1.538 + 1.539 + nsCOMPtr<nsIDOMElement> imageElement(do_QueryInterface(image)); 1.540 + // XXXbz Shouldn't we use the "title" attr for title? Using 1.541 + // "alt" seems very wrong.... 1.542 + if (imageElement) { 1.543 + imageElement->GetAttribute(NS_LITERAL_STRING("alt"), mTitleString); 1.544 + } 1.545 + 1.546 + if (mTitleString.IsEmpty()) { 1.547 + mTitleString = mUrlString; 1.548 + } 1.549 + 1.550 + nsCOMPtr<imgIRequest> imgRequest; 1.551 + 1.552 + // grab the image data, and its request. 1.553 + nsCOMPtr<imgIContainer> img = 1.554 + nsContentUtils::GetImageFromContent(image, 1.555 + getter_AddRefs(imgRequest)); 1.556 + 1.557 + nsCOMPtr<nsIMIMEService> mimeService = 1.558 + do_GetService("@mozilla.org/mime;1"); 1.559 + 1.560 + // Fix the file extension in the URL if necessary 1.561 + if (imgRequest && mimeService) { 1.562 + nsCOMPtr<nsIURI> imgUri; 1.563 + imgRequest->GetURI(getter_AddRefs(imgUri)); 1.564 + 1.565 + nsCOMPtr<nsIURL> imgUrl(do_QueryInterface(imgUri)); 1.566 + 1.567 + if (imgUrl) { 1.568 + nsAutoCString extension; 1.569 + imgUrl->GetFileExtension(extension); 1.570 + 1.571 + nsXPIDLCString mimeType; 1.572 + imgRequest->GetMimeType(getter_Copies(mimeType)); 1.573 + 1.574 + nsCOMPtr<nsIMIMEInfo> mimeInfo; 1.575 + mimeService->GetFromTypeAndExtension(mimeType, EmptyCString(), 1.576 + getter_AddRefs(mimeInfo)); 1.577 + 1.578 + if (mimeInfo) { 1.579 + nsAutoCString spec; 1.580 + imgUrl->GetSpec(spec); 1.581 + 1.582 + // pass out the image source string 1.583 + CopyUTF8toUTF16(spec, mImageSourceString); 1.584 + 1.585 + bool validExtension; 1.586 + if (extension.IsEmpty() || 1.587 + NS_FAILED(mimeInfo->ExtensionExists(extension, 1.588 + &validExtension)) || 1.589 + !validExtension) { 1.590 + // Fix the file extension in the URL 1.591 + nsresult rv = imgUrl->Clone(getter_AddRefs(imgUri)); 1.592 + NS_ENSURE_SUCCESS(rv, rv); 1.593 + 1.594 + imgUrl = do_QueryInterface(imgUri); 1.595 + 1.596 + nsAutoCString primaryExtension; 1.597 + mimeInfo->GetPrimaryExtension(primaryExtension); 1.598 + 1.599 + imgUrl->SetFileExtension(primaryExtension); 1.600 + } 1.601 + 1.602 + nsAutoCString fileName; 1.603 + imgUrl->GetFileName(fileName); 1.604 + 1.605 + NS_UnescapeURL(fileName); 1.606 + 1.607 + // make the filename safe for the filesystem 1.608 + fileName.ReplaceChar(FILE_PATH_SEPARATOR FILE_ILLEGAL_CHARACTERS, 1.609 + '-'); 1.610 + 1.611 + CopyUTF8toUTF16(fileName, mImageDestFileName); 1.612 + 1.613 + // and the image object 1.614 + mImage = img; 1.615 + } 1.616 + } 1.617 + } 1.618 + 1.619 + if (parentLink) { 1.620 + // If we are dragging around an image in an anchor, then we 1.621 + // are dragging the entire anchor 1.622 + linkNode = parentLink; 1.623 + nodeToSerialize = linkNode; 1.624 + } else { 1.625 + nodeToSerialize = do_QueryInterface(draggedNode); 1.626 + } 1.627 + dragNode = nodeToSerialize; 1.628 + } else if (link) { 1.629 + // set linkNode. The code below will handle this 1.630 + linkNode = do_QueryInterface(link); // XXX test this 1.631 + GetNodeString(draggedNode, mTitleString); 1.632 + } else if (parentLink) { 1.633 + // parentLink will always be null if there's selected content 1.634 + linkNode = parentLink; 1.635 + nodeToSerialize = linkNode; 1.636 + } else if (!haveSelectedContent) { 1.637 + // nothing draggable 1.638 + return NS_OK; 1.639 + } 1.640 + 1.641 + if (linkNode) { 1.642 + mIsAnchor = true; 1.643 + GetAnchorURL(linkNode, mUrlString); 1.644 + dragNode = linkNode; 1.645 + } 1.646 + } 1.647 + } 1.648 + 1.649 + if (nodeToSerialize || *aSelection) { 1.650 + mHtmlString.Truncate(); 1.651 + mContextString.Truncate(); 1.652 + mInfoString.Truncate(); 1.653 + mTitleString.Truncate(); 1.654 + 1.655 + nsCOMPtr<nsIDocument> doc = mWindow->GetDoc(); 1.656 + NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE); 1.657 + 1.658 + // if we have selected text, use it in preference to the node 1.659 + nsCOMPtr<nsITransferable> transferable; 1.660 + if (*aSelection) { 1.661 + rv = nsCopySupport::GetTransferableForSelection(*aSelection, doc, 1.662 + getter_AddRefs(transferable)); 1.663 + } 1.664 + else { 1.665 + rv = nsCopySupport::GetTransferableForNode(nodeToSerialize, doc, 1.666 + getter_AddRefs(transferable)); 1.667 + } 1.668 + NS_ENSURE_SUCCESS(rv, rv); 1.669 + 1.670 + nsCOMPtr<nsISupports> supports; 1.671 + nsCOMPtr<nsISupportsString> data; 1.672 + uint32_t dataSize; 1.673 + rv = transferable->GetTransferData(kHTMLMime, getter_AddRefs(supports), 1.674 + &dataSize); 1.675 + data = do_QueryInterface(supports); 1.676 + if (NS_SUCCEEDED(rv)) { 1.677 + data->GetData(mHtmlString); 1.678 + } 1.679 + rv = transferable->GetTransferData(kHTMLContext, getter_AddRefs(supports), 1.680 + &dataSize); 1.681 + data = do_QueryInterface(supports); 1.682 + if (NS_SUCCEEDED(rv)) { 1.683 + data->GetData(mContextString); 1.684 + } 1.685 + rv = transferable->GetTransferData(kHTMLInfo, getter_AddRefs(supports), 1.686 + &dataSize); 1.687 + data = do_QueryInterface(supports); 1.688 + if (NS_SUCCEEDED(rv)) { 1.689 + data->GetData(mInfoString); 1.690 + } 1.691 + rv = transferable->GetTransferData(kUnicodeMime, getter_AddRefs(supports), 1.692 + &dataSize); 1.693 + data = do_QueryInterface(supports); 1.694 + NS_ENSURE_SUCCESS(rv, rv); // require plain text at a minimum 1.695 + data->GetData(mTitleString); 1.696 + } 1.697 + 1.698 + // default text value is the URL 1.699 + if (mTitleString.IsEmpty()) { 1.700 + mTitleString = mUrlString; 1.701 + } 1.702 + 1.703 + // if we haven't constructed a html version, make one now 1.704 + if (mHtmlString.IsEmpty() && !mUrlString.IsEmpty()) 1.705 + CreateLinkText(mUrlString, mTitleString, mHtmlString); 1.706 + 1.707 + // if there is no drag node, which will be the case for a selection, just 1.708 + // use the selection target node. 1.709 + rv = AddStringsToDataTransfer( 1.710 + dragNode ? dragNode : mSelectionTargetNode.get(), aDataTransfer); 1.711 + NS_ENSURE_SUCCESS(rv, rv); 1.712 + 1.713 + NS_IF_ADDREF(*aDragNode = dragNode); 1.714 + return NS_OK; 1.715 +} 1.716 + 1.717 +void 1.718 +DragDataProducer::AddString(DataTransfer* aDataTransfer, 1.719 + const nsAString& aFlavor, 1.720 + const nsAString& aData, 1.721 + nsIPrincipal* aPrincipal) 1.722 +{ 1.723 + nsCOMPtr<nsIWritableVariant> variant = do_CreateInstance(NS_VARIANT_CONTRACTID); 1.724 + if (variant) { 1.725 + variant->SetAsAString(aData); 1.726 + aDataTransfer->SetDataWithPrincipal(aFlavor, variant, 0, aPrincipal); 1.727 + } 1.728 +} 1.729 + 1.730 +nsresult 1.731 +DragDataProducer::AddStringsToDataTransfer(nsIContent* aDragNode, 1.732 + DataTransfer* aDataTransfer) 1.733 +{ 1.734 + NS_ASSERTION(aDragNode, "adding strings for null node"); 1.735 + 1.736 + // set all of the data to have the principal of the node where the data came from 1.737 + nsIPrincipal* principal = aDragNode->NodePrincipal(); 1.738 + 1.739 + // add a special flavor if we're an anchor to indicate that we have 1.740 + // a URL in the drag data 1.741 + if (!mUrlString.IsEmpty() && mIsAnchor) { 1.742 + nsAutoString dragData(mUrlString); 1.743 + dragData.AppendLiteral("\n"); 1.744 + // Remove leading and trailing newlines in the title and replace them with 1.745 + // space in remaining positions - they confuse PlacesUtils::unwrapNodes 1.746 + // that expects url\ntitle formatted data for x-moz-url. 1.747 + nsAutoString title(mTitleString); 1.748 + title.Trim("\r\n"); 1.749 + title.ReplaceChar("\r\n", ' '); 1.750 + dragData += title; 1.751 + 1.752 + AddString(aDataTransfer, NS_LITERAL_STRING(kURLMime), dragData, principal); 1.753 + AddString(aDataTransfer, NS_LITERAL_STRING(kURLDataMime), mUrlString, principal); 1.754 + AddString(aDataTransfer, NS_LITERAL_STRING(kURLDescriptionMime), mTitleString, principal); 1.755 + AddString(aDataTransfer, NS_LITERAL_STRING("text/uri-list"), mUrlString, principal); 1.756 + } 1.757 + 1.758 + // add a special flavor for the html context data 1.759 + if (!mContextString.IsEmpty()) 1.760 + AddString(aDataTransfer, NS_LITERAL_STRING(kHTMLContext), mContextString, principal); 1.761 + 1.762 + // add a special flavor if we have html info data 1.763 + if (!mInfoString.IsEmpty()) 1.764 + AddString(aDataTransfer, NS_LITERAL_STRING(kHTMLInfo), mInfoString, principal); 1.765 + 1.766 + // add the full html 1.767 + if (!mHtmlString.IsEmpty()) 1.768 + AddString(aDataTransfer, NS_LITERAL_STRING(kHTMLMime), mHtmlString, principal); 1.769 + 1.770 + // add the plain text. we use the url for text/plain data if an anchor is 1.771 + // being dragged, rather than the title text of the link or the alt text for 1.772 + // an anchor image. 1.773 + AddString(aDataTransfer, NS_LITERAL_STRING(kTextMime), 1.774 + mIsAnchor ? mUrlString : mTitleString, principal); 1.775 + 1.776 + // add image data, if present. For now, all we're going to do with 1.777 + // this is turn it into a native data flavor, so indicate that with 1.778 + // a new flavor so as not to confuse anyone who is really registered 1.779 + // for image/gif or image/jpg. 1.780 + if (mImage) { 1.781 + nsCOMPtr<nsIWritableVariant> variant = do_CreateInstance(NS_VARIANT_CONTRACTID); 1.782 + if (variant) { 1.783 + variant->SetAsISupports(mImage); 1.784 + aDataTransfer->SetDataWithPrincipal(NS_LITERAL_STRING(kNativeImageMime), 1.785 + variant, 0, principal); 1.786 + } 1.787 + 1.788 + // assume the image comes from a file, and add a file promise. We 1.789 + // register ourselves as a nsIFlavorDataProvider, and will use the 1.790 + // GetFlavorData callback to save the image to disk. 1.791 + 1.792 + nsCOMPtr<nsIFlavorDataProvider> dataProvider = 1.793 + new nsContentAreaDragDropDataProvider(); 1.794 + if (dataProvider) { 1.795 + nsCOMPtr<nsIWritableVariant> variant = do_CreateInstance(NS_VARIANT_CONTRACTID); 1.796 + if (variant) { 1.797 + variant->SetAsISupports(dataProvider); 1.798 + aDataTransfer->SetDataWithPrincipal(NS_LITERAL_STRING(kFilePromiseMime), 1.799 + variant, 0, principal); 1.800 + } 1.801 + } 1.802 + 1.803 + AddString(aDataTransfer, NS_LITERAL_STRING(kFilePromiseURLMime), 1.804 + mImageSourceString, principal); 1.805 + AddString(aDataTransfer, NS_LITERAL_STRING(kFilePromiseDestFilename), 1.806 + mImageDestFileName, principal); 1.807 + 1.808 + // if not an anchor, add the image url 1.809 + if (!mIsAnchor) { 1.810 + AddString(aDataTransfer, NS_LITERAL_STRING(kURLDataMime), mUrlString, principal); 1.811 + AddString(aDataTransfer, NS_LITERAL_STRING("text/uri-list"), mUrlString, principal); 1.812 + } 1.813 + } 1.814 + 1.815 + return NS_OK; 1.816 +} 1.817 + 1.818 +// note that this can return NS_OK, but a null out param (by design) 1.819 +// static 1.820 +nsresult 1.821 +DragDataProducer::GetDraggableSelectionData(nsISelection* inSelection, 1.822 + nsIContent* inRealTargetNode, 1.823 + nsIContent **outImageOrLinkNode, 1.824 + bool* outDragSelectedText) 1.825 +{ 1.826 + NS_ENSURE_ARG(inSelection); 1.827 + NS_ENSURE_ARG(inRealTargetNode); 1.828 + NS_ENSURE_ARG_POINTER(outImageOrLinkNode); 1.829 + 1.830 + *outImageOrLinkNode = nullptr; 1.831 + *outDragSelectedText = false; 1.832 + 1.833 + bool selectionContainsTarget = false; 1.834 + 1.835 + bool isCollapsed = false; 1.836 + inSelection->GetIsCollapsed(&isCollapsed); 1.837 + if (!isCollapsed) { 1.838 + nsCOMPtr<nsIDOMNode> realTargetNode = do_QueryInterface(inRealTargetNode); 1.839 + inSelection->ContainsNode(realTargetNode, false, 1.840 + &selectionContainsTarget); 1.841 + 1.842 + if (selectionContainsTarget) { 1.843 + // track down the anchor node, if any, for the url 1.844 + nsCOMPtr<nsIDOMNode> selectionStart; 1.845 + inSelection->GetAnchorNode(getter_AddRefs(selectionStart)); 1.846 + 1.847 + nsCOMPtr<nsIDOMNode> selectionEnd; 1.848 + inSelection->GetFocusNode(getter_AddRefs(selectionEnd)); 1.849 + 1.850 + // look for a selection around a single node, like an image. 1.851 + // in this case, drag the image, rather than a serialization of the HTML 1.852 + // XXX generalize this to other draggable element types? 1.853 + if (selectionStart == selectionEnd) { 1.854 + bool hasChildren; 1.855 + selectionStart->HasChildNodes(&hasChildren); 1.856 + if (hasChildren) { 1.857 + // see if just one node is selected 1.858 + int32_t anchorOffset, focusOffset; 1.859 + inSelection->GetAnchorOffset(&anchorOffset); 1.860 + inSelection->GetFocusOffset(&focusOffset); 1.861 + if (abs(anchorOffset - focusOffset) == 1) { 1.862 + nsCOMPtr<nsIContent> selStartContent = 1.863 + do_QueryInterface(selectionStart); 1.864 + 1.865 + if (selStartContent) { 1.866 + int32_t childOffset = 1.867 + (anchorOffset < focusOffset) ? anchorOffset : focusOffset; 1.868 + nsIContent *childContent = 1.869 + selStartContent->GetChildAt(childOffset); 1.870 + // if we find an image, we'll fall into the node-dragging code, 1.871 + // rather the the selection-dragging code 1.872 + if (nsContentUtils::IsDraggableImage(childContent)) { 1.873 + NS_ADDREF(*outImageOrLinkNode = childContent); 1.874 + return NS_OK; 1.875 + } 1.876 + } 1.877 + } 1.878 + } 1.879 + } 1.880 + 1.881 + // see if the selection is a link; if so, its node will be returned 1.882 + GetSelectedLink(inSelection, outImageOrLinkNode); 1.883 + 1.884 + // indicate that a link or text is selected 1.885 + *outDragSelectedText = true; 1.886 + } 1.887 + } 1.888 + 1.889 + return NS_OK; 1.890 +} 1.891 + 1.892 +// static 1.893 +void 1.894 +DragDataProducer::GetSelectedLink(nsISelection* inSelection, 1.895 + nsIContent **outLinkNode) 1.896 +{ 1.897 + *outLinkNode = nullptr; 1.898 + 1.899 + nsCOMPtr<nsIDOMNode> selectionStartNode; 1.900 + inSelection->GetAnchorNode(getter_AddRefs(selectionStartNode)); 1.901 + nsCOMPtr<nsIDOMNode> selectionEndNode; 1.902 + inSelection->GetFocusNode(getter_AddRefs(selectionEndNode)); 1.903 + 1.904 + // simple case: only one node is selected 1.905 + // see if it or its parent is an anchor, then exit 1.906 + 1.907 + if (selectionStartNode == selectionEndNode) { 1.908 + nsCOMPtr<nsIContent> selectionStart = do_QueryInterface(selectionStartNode); 1.909 + nsCOMPtr<nsIContent> link = FindParentLinkNode(selectionStart); 1.910 + if (link) { 1.911 + link.swap(*outLinkNode); 1.912 + } 1.913 + 1.914 + return; 1.915 + } 1.916 + 1.917 + // more complicated case: multiple nodes are selected 1.918 + 1.919 + // Unless you use the Alt key while selecting anchor text, it is 1.920 + // nearly impossible to avoid overlapping into adjacent nodes. 1.921 + // Deal with this by trimming off the leading and/or trailing 1.922 + // nodes of the selection if the strings they produce are empty. 1.923 + 1.924 + // first, use a range determine if the selection was marked LTR or RTL; 1.925 + // if the latter, swap endpoints so we trim in the right direction 1.926 + 1.927 + int32_t startOffset, endOffset; 1.928 + { 1.929 + nsCOMPtr<nsIDOMRange> range; 1.930 + inSelection->GetRangeAt(0, getter_AddRefs(range)); 1.931 + if (!range) { 1.932 + return; 1.933 + } 1.934 + 1.935 + nsCOMPtr<nsIDOMNode> tempNode; 1.936 + range->GetStartContainer( getter_AddRefs(tempNode)); 1.937 + if (tempNode != selectionStartNode) { 1.938 + selectionEndNode = selectionStartNode; 1.939 + selectionStartNode = tempNode; 1.940 + inSelection->GetAnchorOffset(&endOffset); 1.941 + inSelection->GetFocusOffset(&startOffset); 1.942 + } else { 1.943 + inSelection->GetAnchorOffset(&startOffset); 1.944 + inSelection->GetFocusOffset(&endOffset); 1.945 + } 1.946 + } 1.947 + 1.948 + // trim leading node if the string is empty or 1.949 + // the selection starts at the end of the text 1.950 + 1.951 + nsAutoString nodeStr; 1.952 + selectionStartNode->GetNodeValue(nodeStr); 1.953 + if (nodeStr.IsEmpty() || 1.954 + startOffset+1 >= static_cast<int32_t>(nodeStr.Length())) { 1.955 + nsCOMPtr<nsIDOMNode> curr = selectionStartNode; 1.956 + nsIDOMNode* next; 1.957 + 1.958 + while (curr) { 1.959 + curr->GetNextSibling(&next); 1.960 + 1.961 + if (next) { 1.962 + selectionStartNode = dont_AddRef(next); 1.963 + break; 1.964 + } 1.965 + 1.966 + curr->GetParentNode(&next); 1.967 + curr = dont_AddRef(next); 1.968 + } 1.969 + } 1.970 + 1.971 + // trim trailing node if the selection ends before its text begins 1.972 + 1.973 + if (endOffset == 0) { 1.974 + nsCOMPtr<nsIDOMNode> curr = selectionEndNode; 1.975 + nsIDOMNode* next; 1.976 + 1.977 + while (curr) { 1.978 + curr->GetPreviousSibling(&next); 1.979 + 1.980 + if (next){ 1.981 + selectionEndNode = dont_AddRef(next); 1.982 + break; 1.983 + } 1.984 + 1.985 + curr->GetParentNode(&next); 1.986 + curr = dont_AddRef(next); 1.987 + } 1.988 + } 1.989 + 1.990 + // see if the leading & trailing nodes are part of the 1.991 + // same anchor - if so, return the anchor node 1.992 + nsCOMPtr<nsIContent> selectionStart = do_QueryInterface(selectionStartNode); 1.993 + nsCOMPtr<nsIContent> link = FindParentLinkNode(selectionStart); 1.994 + if (link) { 1.995 + nsCOMPtr<nsIContent> selectionEnd = do_QueryInterface(selectionEndNode); 1.996 + nsCOMPtr<nsIContent> link2 = FindParentLinkNode(selectionEnd); 1.997 + 1.998 + if (link == link2) { 1.999 + NS_IF_ADDREF(*outLinkNode = link); 1.1000 + } 1.1001 + } 1.1002 + 1.1003 + return; 1.1004 +}