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