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