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