|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 #include "nsCopySupport.h" |
|
7 #include "nsIDocumentEncoder.h" |
|
8 #include "nsISupports.h" |
|
9 #include "nsIContent.h" |
|
10 #include "nsIComponentManager.h" |
|
11 #include "nsIServiceManager.h" |
|
12 #include "nsIClipboard.h" |
|
13 #include "nsISelection.h" |
|
14 #include "nsWidgetsCID.h" |
|
15 #include "nsXPCOM.h" |
|
16 #include "nsISupportsPrimitives.h" |
|
17 #include "nsIDOMRange.h" |
|
18 #include "nsRange.h" |
|
19 #include "imgIContainer.h" |
|
20 #include "nsIPresShell.h" |
|
21 #include "nsFocusManager.h" |
|
22 #include "mozilla/dom/DataTransfer.h" |
|
23 |
|
24 #include "nsIDocShell.h" |
|
25 #include "nsIContentViewerEdit.h" |
|
26 #include "nsIClipboardDragDropHooks.h" |
|
27 #include "nsIClipboardDragDropHookList.h" |
|
28 #include "nsIClipboardHelper.h" |
|
29 #include "nsISelectionController.h" |
|
30 |
|
31 #include "nsPIDOMWindow.h" |
|
32 #include "nsIDocument.h" |
|
33 #include "nsIDOMNode.h" |
|
34 #include "nsIDOMElement.h" |
|
35 #include "nsIDOMDocument.h" |
|
36 #include "nsIHTMLDocument.h" |
|
37 #include "nsGkAtoms.h" |
|
38 #include "nsIFrame.h" |
|
39 #include "nsIURI.h" |
|
40 #include "nsISimpleEnumerator.h" |
|
41 |
|
42 // image copy stuff |
|
43 #include "nsIImageLoadingContent.h" |
|
44 #include "nsIInterfaceRequestorUtils.h" |
|
45 #include "nsContentUtils.h" |
|
46 #include "nsContentCID.h" |
|
47 |
|
48 #include "mozilla/ContentEvents.h" |
|
49 #include "mozilla/dom/Element.h" |
|
50 #include "mozilla/EventDispatcher.h" |
|
51 #include "mozilla/Preferences.h" |
|
52 #include "mozilla/dom/Selection.h" |
|
53 |
|
54 using namespace mozilla; |
|
55 using namespace mozilla::dom; |
|
56 |
|
57 nsresult NS_NewDomSelection(nsISelection **aDomSelection); |
|
58 |
|
59 static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID); |
|
60 static NS_DEFINE_CID(kCTransferableCID, NS_TRANSFERABLE_CID); |
|
61 static NS_DEFINE_CID(kHTMLConverterCID, NS_HTMLFORMATCONVERTER_CID); |
|
62 |
|
63 // copy string data onto the transferable |
|
64 static nsresult AppendString(nsITransferable *aTransferable, |
|
65 const nsAString& aString, |
|
66 const char* aFlavor); |
|
67 |
|
68 // copy HTML node data |
|
69 static nsresult AppendDOMNode(nsITransferable *aTransferable, |
|
70 nsINode* aDOMNode); |
|
71 |
|
72 // Helper used for HTMLCopy and GetTransferableForSelection since both routines |
|
73 // share common code. |
|
74 static nsresult |
|
75 SelectionCopyHelper(nsISelection *aSel, nsIDocument *aDoc, |
|
76 bool doPutOnClipboard, int16_t aClipboardID, |
|
77 uint32_t aFlags, nsITransferable ** aTransferable) |
|
78 { |
|
79 // Clear the output parameter for the transferable, if provided. |
|
80 if (aTransferable) { |
|
81 *aTransferable = nullptr; |
|
82 } |
|
83 |
|
84 nsresult rv; |
|
85 |
|
86 nsCOMPtr<nsIDocumentEncoder> docEncoder; |
|
87 docEncoder = do_CreateInstance(NS_HTMLCOPY_ENCODER_CONTRACTID); |
|
88 NS_ENSURE_TRUE(docEncoder, NS_ERROR_FAILURE); |
|
89 |
|
90 // note that we assign text/unicode as mime type, but in fact nsHTMLCopyEncoder |
|
91 // ignore it and use text/html or text/plain depending where the selection |
|
92 // is. if it is a selection into input/textarea element or in a html content |
|
93 // with pre-wrap style : text/plain. Otherwise text/html. |
|
94 // see nsHTMLCopyEncoder::SetSelection |
|
95 nsAutoString mimeType; |
|
96 mimeType.AssignLiteral(kUnicodeMime); |
|
97 |
|
98 // Do the first and potentially trial encoding as preformatted and raw. |
|
99 uint32_t flags = aFlags | nsIDocumentEncoder::OutputPreformatted |
|
100 | nsIDocumentEncoder::OutputRaw; |
|
101 |
|
102 nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(aDoc); |
|
103 NS_ASSERTION(domDoc, "Need a document"); |
|
104 |
|
105 rv = docEncoder->Init(domDoc, mimeType, flags); |
|
106 NS_ENSURE_SUCCESS(rv, rv); |
|
107 |
|
108 rv = docEncoder->SetSelection(aSel); |
|
109 NS_ENSURE_SUCCESS(rv, rv); |
|
110 |
|
111 // SetSelection set the mime type to text/plain if the selection is inside a |
|
112 // text widget. |
|
113 rv = docEncoder->GetMimeType(mimeType); |
|
114 NS_ENSURE_SUCCESS(rv, rv); |
|
115 bool selForcedTextPlain = mimeType.EqualsLiteral(kTextMime); |
|
116 |
|
117 nsAutoString buf; |
|
118 rv = docEncoder->EncodeToString(buf); |
|
119 NS_ENSURE_SUCCESS(rv, rv); |
|
120 |
|
121 rv = docEncoder->GetMimeType(mimeType); |
|
122 NS_ENSURE_SUCCESS(rv, rv); |
|
123 |
|
124 if (!selForcedTextPlain && mimeType.EqualsLiteral(kTextMime)) { |
|
125 // SetSelection and EncodeToString use this case to signal that text/plain |
|
126 // was forced because the document is either not an nsIHTMLDocument or it's |
|
127 // XHTML. We want to pretty print XHTML but not non-nsIHTMLDocuments. |
|
128 nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(aDoc); |
|
129 if (!htmlDoc) { |
|
130 selForcedTextPlain = true; |
|
131 } |
|
132 } |
|
133 |
|
134 // The mime type is ultimately text/html if the encoder successfully encoded |
|
135 // the selection as text/html. |
|
136 bool encodedTextHTML = mimeType.EqualsLiteral(kHTMLMime); |
|
137 |
|
138 // First, prepare the text/plain clipboard flavor. |
|
139 nsAutoString textPlainBuf; |
|
140 if (selForcedTextPlain) { |
|
141 // Nothing to do. buf contains the final, preformatted, raw text/plain. |
|
142 textPlainBuf.Assign(buf); |
|
143 } else { |
|
144 // Redo the encoding, but this time use pretty printing. |
|
145 flags = |
|
146 nsIDocumentEncoder::OutputSelectionOnly | |
|
147 nsIDocumentEncoder::OutputAbsoluteLinks | |
|
148 nsIDocumentEncoder::SkipInvisibleContent | |
|
149 nsIDocumentEncoder::OutputDropInvisibleBreak | |
|
150 (aFlags & nsIDocumentEncoder::OutputNoScriptContent); |
|
151 |
|
152 mimeType.AssignLiteral(kTextMime); |
|
153 rv = docEncoder->Init(domDoc, mimeType, flags); |
|
154 NS_ENSURE_SUCCESS(rv, rv); |
|
155 |
|
156 rv = docEncoder->SetSelection(aSel); |
|
157 NS_ENSURE_SUCCESS(rv, rv); |
|
158 |
|
159 rv = docEncoder->EncodeToString(textPlainBuf); |
|
160 NS_ENSURE_SUCCESS(rv, rv); |
|
161 } |
|
162 |
|
163 // Second, prepare the text/html flavor. |
|
164 nsAutoString textHTMLBuf; |
|
165 nsAutoString htmlParentsBuf; |
|
166 nsAutoString htmlInfoBuf; |
|
167 if (encodedTextHTML) { |
|
168 // Redo the encoding, but this time use the passed-in flags. |
|
169 mimeType.AssignLiteral(kHTMLMime); |
|
170 rv = docEncoder->Init(domDoc, mimeType, aFlags); |
|
171 NS_ENSURE_SUCCESS(rv, rv); |
|
172 |
|
173 rv = docEncoder->SetSelection(aSel); |
|
174 NS_ENSURE_SUCCESS(rv, rv); |
|
175 |
|
176 rv = docEncoder->EncodeToStringWithContext(htmlParentsBuf, htmlInfoBuf, |
|
177 textHTMLBuf); |
|
178 NS_ENSURE_SUCCESS(rv, rv); |
|
179 } |
|
180 |
|
181 // Get the Clipboard |
|
182 nsCOMPtr<nsIClipboard> clipboard; |
|
183 if (doPutOnClipboard) { |
|
184 clipboard = do_GetService(kCClipboardCID, &rv); |
|
185 if (NS_FAILED(rv)) |
|
186 return rv; |
|
187 } |
|
188 |
|
189 if ((doPutOnClipboard && clipboard) || aTransferable != nullptr) { |
|
190 // Create a transferable for putting data on the Clipboard |
|
191 nsCOMPtr<nsITransferable> trans = do_CreateInstance(kCTransferableCID); |
|
192 if (trans) { |
|
193 trans->Init(aDoc->GetLoadContext()); |
|
194 if (encodedTextHTML) { |
|
195 // Set up a format converter so that clipboard flavor queries work. |
|
196 // This converter isn't really used for conversions. |
|
197 nsCOMPtr<nsIFormatConverter> htmlConverter = |
|
198 do_CreateInstance(kHTMLConverterCID); |
|
199 trans->SetConverter(htmlConverter); |
|
200 |
|
201 if (!textHTMLBuf.IsEmpty()) { |
|
202 // Add the html DataFlavor to the transferable |
|
203 rv = AppendString(trans, textHTMLBuf, kHTMLMime); |
|
204 NS_ENSURE_SUCCESS(rv, rv); |
|
205 } |
|
206 |
|
207 // Add the htmlcontext DataFlavor to the transferable |
|
208 // Even if parents is empty string, this flavor should |
|
209 // be attached to the transferable |
|
210 rv = AppendString(trans, htmlParentsBuf, kHTMLContext); |
|
211 NS_ENSURE_SUCCESS(rv, rv); |
|
212 |
|
213 if (!htmlInfoBuf.IsEmpty()) { |
|
214 // Add the htmlinfo DataFlavor to the transferable |
|
215 rv = AppendString(trans, htmlInfoBuf, kHTMLInfo); |
|
216 NS_ENSURE_SUCCESS(rv, rv); |
|
217 } |
|
218 |
|
219 if (!textPlainBuf.IsEmpty()) { |
|
220 // unicode text |
|
221 // Add the unicode DataFlavor to the transferable |
|
222 // If we didn't have this, then nsDataObj::GetData matches text/unicode against |
|
223 // the kURLMime flavour which is not desirable (eg. when pasting into Notepad) |
|
224 rv = AppendString(trans, textPlainBuf, kUnicodeMime); |
|
225 NS_ENSURE_SUCCESS(rv, rv); |
|
226 } |
|
227 |
|
228 // Try and get source URI of the items that are being dragged |
|
229 nsIURI *uri = aDoc->GetDocumentURI(); |
|
230 if (uri) { |
|
231 nsAutoCString spec; |
|
232 uri->GetSpec(spec); |
|
233 if (!spec.IsEmpty()) { |
|
234 nsAutoString shortcut; |
|
235 AppendUTF8toUTF16(spec, shortcut); |
|
236 |
|
237 // Add the URL DataFlavor to the transferable. Don't use kURLMime, as it will |
|
238 // cause an unnecessary UniformResourceLocator to be added which confuses |
|
239 // some apps eg. Outlook 2000 - (See Bug 315370). Don't use |
|
240 // kURLDataMime, as it will cause a bogus 'url ' flavor to |
|
241 // show up on the Mac clipboard, confusing other apps, like |
|
242 // Terminal (see bug 336012). |
|
243 rv = AppendString(trans, shortcut, kURLPrivateMime); |
|
244 NS_ENSURE_SUCCESS(rv, rv); |
|
245 } |
|
246 } |
|
247 } else { |
|
248 if (!textPlainBuf.IsEmpty()) { |
|
249 // Add the unicode DataFlavor to the transferable |
|
250 rv = AppendString(trans, textPlainBuf, kUnicodeMime); |
|
251 NS_ENSURE_SUCCESS(rv, rv); |
|
252 } |
|
253 } |
|
254 |
|
255 if (doPutOnClipboard && clipboard) { |
|
256 bool actuallyPutOnClipboard = true; |
|
257 nsCopySupport::DoHooks(aDoc, trans, &actuallyPutOnClipboard); |
|
258 |
|
259 // put the transferable on the clipboard |
|
260 if (actuallyPutOnClipboard) |
|
261 clipboard->SetData(trans, nullptr, aClipboardID); |
|
262 } |
|
263 |
|
264 // Return the transferable to the caller if requested. |
|
265 if (aTransferable != nullptr) { |
|
266 trans.swap(*aTransferable); |
|
267 } |
|
268 } |
|
269 } |
|
270 return rv; |
|
271 } |
|
272 |
|
273 nsresult |
|
274 nsCopySupport::HTMLCopy(nsISelection* aSel, nsIDocument* aDoc, |
|
275 int16_t aClipboardID) |
|
276 { |
|
277 return SelectionCopyHelper(aSel, aDoc, true, aClipboardID, |
|
278 nsIDocumentEncoder::SkipInvisibleContent, |
|
279 nullptr); |
|
280 } |
|
281 |
|
282 nsresult |
|
283 nsCopySupport::GetTransferableForSelection(nsISelection* aSel, |
|
284 nsIDocument* aDoc, |
|
285 nsITransferable** aTransferable) |
|
286 { |
|
287 return SelectionCopyHelper(aSel, aDoc, false, 0, |
|
288 nsIDocumentEncoder::SkipInvisibleContent, |
|
289 aTransferable); |
|
290 } |
|
291 |
|
292 nsresult |
|
293 nsCopySupport::GetTransferableForNode(nsINode* aNode, |
|
294 nsIDocument* aDoc, |
|
295 nsITransferable** aTransferable) |
|
296 { |
|
297 nsCOMPtr<nsISelection> selection; |
|
298 // Make a temporary selection with aNode in a single range. |
|
299 nsresult rv = NS_NewDomSelection(getter_AddRefs(selection)); |
|
300 NS_ENSURE_SUCCESS(rv, rv); |
|
301 nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aNode); |
|
302 NS_ENSURE_TRUE(node, NS_ERROR_FAILURE); |
|
303 nsRefPtr<nsRange> range = new nsRange(aNode); |
|
304 rv = range->SelectNode(node); |
|
305 NS_ENSURE_SUCCESS(rv, rv); |
|
306 rv = selection->AddRange(range); |
|
307 NS_ENSURE_SUCCESS(rv, rv); |
|
308 // It's not the primary selection - so don't skip invisible content. |
|
309 uint32_t flags = 0; |
|
310 return SelectionCopyHelper(selection, aDoc, false, 0, flags, |
|
311 aTransferable); |
|
312 } |
|
313 |
|
314 nsresult nsCopySupport::DoHooks(nsIDocument *aDoc, nsITransferable *aTrans, |
|
315 bool *aDoPutOnClipboard) |
|
316 { |
|
317 NS_ENSURE_ARG(aDoc); |
|
318 |
|
319 *aDoPutOnClipboard = true; |
|
320 |
|
321 nsCOMPtr<nsISupports> container = aDoc->GetContainer(); |
|
322 nsCOMPtr<nsIClipboardDragDropHookList> hookObj = do_GetInterface(container); |
|
323 if (!hookObj) return NS_ERROR_FAILURE; |
|
324 |
|
325 nsCOMPtr<nsISimpleEnumerator> enumerator; |
|
326 hookObj->GetHookEnumerator(getter_AddRefs(enumerator)); |
|
327 if (!enumerator) return NS_ERROR_FAILURE; |
|
328 |
|
329 // the logic here should follow the behavior specified in |
|
330 // nsIClipboardDragDropHooks.h |
|
331 |
|
332 nsCOMPtr<nsIClipboardDragDropHooks> override; |
|
333 nsCOMPtr<nsISupports> isupp; |
|
334 bool hasMoreHooks = false; |
|
335 nsresult rv = NS_OK; |
|
336 while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMoreHooks)) |
|
337 && hasMoreHooks) |
|
338 { |
|
339 rv = enumerator->GetNext(getter_AddRefs(isupp)); |
|
340 if (NS_FAILED(rv)) break; |
|
341 override = do_QueryInterface(isupp); |
|
342 if (override) |
|
343 { |
|
344 #ifdef DEBUG |
|
345 nsresult hookResult = |
|
346 #endif |
|
347 override->OnCopyOrDrag(nullptr, aTrans, aDoPutOnClipboard); |
|
348 NS_ASSERTION(NS_SUCCEEDED(hookResult), "OnCopyOrDrag hook failed"); |
|
349 if (!*aDoPutOnClipboard) |
|
350 break; |
|
351 } |
|
352 } |
|
353 |
|
354 return rv; |
|
355 } |
|
356 |
|
357 nsresult |
|
358 nsCopySupport::GetContents(const nsACString& aMimeType, uint32_t aFlags, nsISelection *aSel, nsIDocument *aDoc, nsAString& outdata) |
|
359 { |
|
360 nsresult rv = NS_OK; |
|
361 |
|
362 nsCOMPtr<nsIDocumentEncoder> docEncoder; |
|
363 |
|
364 nsAutoCString encoderContractID(NS_DOC_ENCODER_CONTRACTID_BASE); |
|
365 encoderContractID.Append(aMimeType); |
|
366 |
|
367 docEncoder = do_CreateInstance(encoderContractID.get()); |
|
368 NS_ENSURE_TRUE(docEncoder, NS_ERROR_FAILURE); |
|
369 |
|
370 uint32_t flags = aFlags | nsIDocumentEncoder::SkipInvisibleContent; |
|
371 |
|
372 if (aMimeType.Equals("text/plain")) |
|
373 flags |= nsIDocumentEncoder::OutputPreformatted; |
|
374 |
|
375 NS_ConvertASCIItoUTF16 unicodeMimeType(aMimeType); |
|
376 |
|
377 nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(aDoc); |
|
378 NS_ASSERTION(domDoc, "Need a document"); |
|
379 |
|
380 rv = docEncoder->Init(domDoc, unicodeMimeType, flags); |
|
381 if (NS_FAILED(rv)) return rv; |
|
382 |
|
383 if (aSel) |
|
384 { |
|
385 rv = docEncoder->SetSelection(aSel); |
|
386 if (NS_FAILED(rv)) return rv; |
|
387 } |
|
388 |
|
389 // encode the selection |
|
390 return docEncoder->EncodeToString(outdata); |
|
391 } |
|
392 |
|
393 |
|
394 nsresult |
|
395 nsCopySupport::ImageCopy(nsIImageLoadingContent* aImageElement, |
|
396 nsILoadContext* aLoadContext, |
|
397 int32_t aCopyFlags) |
|
398 { |
|
399 nsresult rv; |
|
400 |
|
401 // create a transferable for putting data on the Clipboard |
|
402 nsCOMPtr<nsITransferable> trans(do_CreateInstance(kCTransferableCID, &rv)); |
|
403 NS_ENSURE_SUCCESS(rv, rv); |
|
404 trans->Init(aLoadContext); |
|
405 |
|
406 if (aCopyFlags & nsIContentViewerEdit::COPY_IMAGE_TEXT) { |
|
407 // get the location from the element |
|
408 nsCOMPtr<nsIURI> uri; |
|
409 rv = aImageElement->GetCurrentURI(getter_AddRefs(uri)); |
|
410 NS_ENSURE_SUCCESS(rv, rv); |
|
411 NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE); |
|
412 |
|
413 nsAutoCString location; |
|
414 rv = uri->GetSpec(location); |
|
415 NS_ENSURE_SUCCESS(rv, rv); |
|
416 |
|
417 // append the string to the transferable |
|
418 rv = AppendString(trans, NS_ConvertUTF8toUTF16(location), kUnicodeMime); |
|
419 NS_ENSURE_SUCCESS(rv, rv); |
|
420 } |
|
421 |
|
422 if (aCopyFlags & nsIContentViewerEdit::COPY_IMAGE_HTML) { |
|
423 // append HTML data to the transferable |
|
424 nsCOMPtr<nsINode> node(do_QueryInterface(aImageElement, &rv)); |
|
425 NS_ENSURE_SUCCESS(rv, rv); |
|
426 |
|
427 rv = AppendDOMNode(trans, node); |
|
428 NS_ENSURE_SUCCESS(rv, rv); |
|
429 } |
|
430 |
|
431 if (aCopyFlags & nsIContentViewerEdit::COPY_IMAGE_DATA) { |
|
432 // get the image data from the element |
|
433 nsCOMPtr<imgIContainer> image = |
|
434 nsContentUtils::GetImageFromContent(aImageElement); |
|
435 NS_ENSURE_TRUE(image, NS_ERROR_FAILURE); |
|
436 |
|
437 nsCOMPtr<nsISupportsInterfacePointer> |
|
438 imgPtr(do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID, &rv)); |
|
439 NS_ENSURE_SUCCESS(rv, rv); |
|
440 |
|
441 rv = imgPtr->SetData(image); |
|
442 NS_ENSURE_SUCCESS(rv, rv); |
|
443 |
|
444 // copy the image data onto the transferable |
|
445 rv = trans->SetTransferData(kNativeImageMime, imgPtr, |
|
446 sizeof(nsISupports*)); |
|
447 NS_ENSURE_SUCCESS(rv, rv); |
|
448 } |
|
449 |
|
450 // get clipboard |
|
451 nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv)); |
|
452 NS_ENSURE_SUCCESS(rv, rv); |
|
453 |
|
454 // check whether the system supports the selection clipboard or not. |
|
455 bool selectionSupported; |
|
456 rv = clipboard->SupportsSelectionClipboard(&selectionSupported); |
|
457 NS_ENSURE_SUCCESS(rv, rv); |
|
458 |
|
459 // put the transferable on the clipboard |
|
460 if (selectionSupported) { |
|
461 rv = clipboard->SetData(trans, nullptr, nsIClipboard::kSelectionClipboard); |
|
462 NS_ENSURE_SUCCESS(rv, rv); |
|
463 } |
|
464 |
|
465 return clipboard->SetData(trans, nullptr, nsIClipboard::kGlobalClipboard); |
|
466 } |
|
467 |
|
468 static nsresult AppendString(nsITransferable *aTransferable, |
|
469 const nsAString& aString, |
|
470 const char* aFlavor) |
|
471 { |
|
472 nsresult rv; |
|
473 |
|
474 nsCOMPtr<nsISupportsString> |
|
475 data(do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv)); |
|
476 NS_ENSURE_SUCCESS(rv, rv); |
|
477 |
|
478 rv = data->SetData(aString); |
|
479 NS_ENSURE_SUCCESS(rv, rv); |
|
480 |
|
481 rv = aTransferable->AddDataFlavor(aFlavor); |
|
482 NS_ENSURE_SUCCESS(rv, rv); |
|
483 |
|
484 return aTransferable->SetTransferData(aFlavor, data, |
|
485 aString.Length() * sizeof(char16_t)); |
|
486 } |
|
487 |
|
488 static nsresult AppendDOMNode(nsITransferable *aTransferable, |
|
489 nsINode *aDOMNode) |
|
490 { |
|
491 nsresult rv; |
|
492 |
|
493 // selializer |
|
494 nsCOMPtr<nsIDocumentEncoder> |
|
495 docEncoder(do_CreateInstance(NS_HTMLCOPY_ENCODER_CONTRACTID, &rv)); |
|
496 NS_ENSURE_SUCCESS(rv, rv); |
|
497 |
|
498 // get document for the encoder |
|
499 nsCOMPtr<nsIDocument> document = aDOMNode->OwnerDoc(); |
|
500 |
|
501 // Note that XHTML is not counted as HTML here, because we can't copy it |
|
502 // properly (all the copy code for non-plaintext assumes using HTML |
|
503 // serializers and parsers is OK, and those mess up XHTML). |
|
504 nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(document, &rv); |
|
505 NS_ENSURE_SUCCESS(rv, NS_OK); |
|
506 |
|
507 NS_ENSURE_TRUE(document->IsHTML(), NS_OK); |
|
508 |
|
509 // init encoder with document and node |
|
510 rv = docEncoder->NativeInit(document, NS_LITERAL_STRING(kHTMLMime), |
|
511 nsIDocumentEncoder::OutputAbsoluteLinks | |
|
512 nsIDocumentEncoder::OutputEncodeW3CEntities); |
|
513 NS_ENSURE_SUCCESS(rv, rv); |
|
514 |
|
515 rv = docEncoder->SetNativeNode(aDOMNode); |
|
516 NS_ENSURE_SUCCESS(rv, rv); |
|
517 |
|
518 // serialize to string |
|
519 nsAutoString html, context, info; |
|
520 rv = docEncoder->EncodeToStringWithContext(context, info, html); |
|
521 NS_ENSURE_SUCCESS(rv, rv); |
|
522 |
|
523 // copy them to the transferable |
|
524 if (!html.IsEmpty()) { |
|
525 rv = AppendString(aTransferable, html, kHTMLMime); |
|
526 NS_ENSURE_SUCCESS(rv, rv); |
|
527 } |
|
528 |
|
529 if (!info.IsEmpty()) { |
|
530 rv = AppendString(aTransferable, info, kHTMLInfo); |
|
531 NS_ENSURE_SUCCESS(rv, rv); |
|
532 } |
|
533 |
|
534 // add a special flavor, even if we don't have html context data |
|
535 return AppendString(aTransferable, context, kHTMLContext); |
|
536 } |
|
537 |
|
538 nsIContent* |
|
539 nsCopySupport::GetSelectionForCopy(nsIDocument* aDocument, nsISelection** aSelection) |
|
540 { |
|
541 *aSelection = nullptr; |
|
542 |
|
543 nsIPresShell* presShell = aDocument->GetShell(); |
|
544 if (!presShell) |
|
545 return nullptr; |
|
546 |
|
547 // check if the focused node in the window has a selection |
|
548 nsCOMPtr<nsPIDOMWindow> focusedWindow; |
|
549 nsIContent* content = |
|
550 nsFocusManager::GetFocusedDescendant(aDocument->GetWindow(), false, |
|
551 getter_AddRefs(focusedWindow)); |
|
552 if (content) { |
|
553 nsIFrame* frame = content->GetPrimaryFrame(); |
|
554 if (frame) { |
|
555 nsCOMPtr<nsISelectionController> selCon; |
|
556 frame->GetSelectionController(presShell->GetPresContext(), getter_AddRefs(selCon)); |
|
557 if (selCon) { |
|
558 selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, aSelection); |
|
559 return content; |
|
560 } |
|
561 } |
|
562 } |
|
563 |
|
564 // if no selection was found, use the main selection for the window |
|
565 NS_IF_ADDREF(*aSelection = presShell->GetCurrentSelection(nsISelectionController::SELECTION_NORMAL)); |
|
566 return nullptr; |
|
567 } |
|
568 |
|
569 bool |
|
570 nsCopySupport::CanCopy(nsIDocument* aDocument) |
|
571 { |
|
572 if (!aDocument) |
|
573 return false; |
|
574 |
|
575 nsCOMPtr<nsISelection> sel; |
|
576 GetSelectionForCopy(aDocument, getter_AddRefs(sel)); |
|
577 NS_ENSURE_TRUE(sel, false); |
|
578 |
|
579 bool isCollapsed; |
|
580 sel->GetIsCollapsed(&isCollapsed); |
|
581 return !isCollapsed; |
|
582 } |
|
583 |
|
584 bool |
|
585 nsCopySupport::FireClipboardEvent(int32_t aType, int32_t aClipboardType, nsIPresShell* aPresShell, nsISelection* aSelection) |
|
586 { |
|
587 NS_ASSERTION(aType == NS_CUT || aType == NS_COPY || aType == NS_PASTE, |
|
588 "Invalid clipboard event type"); |
|
589 |
|
590 nsCOMPtr<nsIPresShell> presShell = aPresShell; |
|
591 if (!presShell) |
|
592 return false; |
|
593 |
|
594 nsCOMPtr<nsIDocument> doc = presShell->GetDocument(); |
|
595 if (!doc) |
|
596 return false; |
|
597 |
|
598 nsCOMPtr<nsPIDOMWindow> piWindow = doc->GetWindow(); |
|
599 if (!piWindow) |
|
600 return false; |
|
601 |
|
602 // if a selection was not supplied, try to find it |
|
603 nsCOMPtr<nsIContent> content; |
|
604 nsCOMPtr<nsISelection> sel = aSelection; |
|
605 if (!sel) |
|
606 content = GetSelectionForCopy(doc, getter_AddRefs(sel)); |
|
607 |
|
608 // retrieve the event target node from the start of the selection |
|
609 nsresult rv; |
|
610 if (sel) { |
|
611 // Only cut or copy when there is an uncollapsed selection |
|
612 if (aType == NS_CUT || aType == NS_COPY) { |
|
613 bool isCollapsed; |
|
614 sel->GetIsCollapsed(&isCollapsed); |
|
615 if (isCollapsed) |
|
616 return false; |
|
617 } |
|
618 |
|
619 nsCOMPtr<nsIDOMRange> range; |
|
620 rv = sel->GetRangeAt(0, getter_AddRefs(range)); |
|
621 if (NS_SUCCEEDED(rv) && range) { |
|
622 nsCOMPtr<nsIDOMNode> startContainer; |
|
623 range->GetStartContainer(getter_AddRefs(startContainer)); |
|
624 if (startContainer) |
|
625 content = do_QueryInterface(startContainer); |
|
626 } |
|
627 } |
|
628 |
|
629 // if no content node was set, just get the root |
|
630 if (!content) { |
|
631 content = doc->GetRootElement(); |
|
632 if (!content) |
|
633 return false; |
|
634 } |
|
635 |
|
636 // It seems to be unsafe to fire an event handler during reflow (bug 393696) |
|
637 if (!nsContentUtils::IsSafeToRunScript()) |
|
638 return false; |
|
639 |
|
640 nsCOMPtr<nsIDocShell> docShell = do_GetInterface(piWindow); |
|
641 const bool chromeShell = |
|
642 docShell && docShell->ItemType() == nsIDocShellTreeItem::typeChrome; |
|
643 |
|
644 // next, fire the cut, copy or paste event |
|
645 bool doDefault = true; |
|
646 nsRefPtr<DataTransfer> clipboardData; |
|
647 if (chromeShell || Preferences::GetBool("dom.event.clipboardevents.enabled", true)) { |
|
648 clipboardData = |
|
649 new DataTransfer(piWindow, aType, aType == NS_PASTE, aClipboardType); |
|
650 |
|
651 nsEventStatus status = nsEventStatus_eIgnore; |
|
652 InternalClipboardEvent evt(true, aType); |
|
653 evt.clipboardData = clipboardData; |
|
654 EventDispatcher::Dispatch(content, presShell->GetPresContext(), &evt, |
|
655 nullptr, &status); |
|
656 // If the event was cancelled, don't do the clipboard operation |
|
657 doDefault = (status != nsEventStatus_eConsumeNoDefault); |
|
658 } |
|
659 |
|
660 // No need to do anything special during a paste. Either an event listener |
|
661 // took care of it and cancelled the event, or the caller will handle it. |
|
662 // Return true to indicate that the event wasn't cancelled. |
|
663 if (aType == NS_PASTE) { |
|
664 // Clear and mark the clipboardData as readonly. This prevents someone |
|
665 // from reading the clipboard contents after the paste event has fired. |
|
666 if (clipboardData) { |
|
667 clipboardData->ClearAll(); |
|
668 clipboardData->SetReadOnly(); |
|
669 } |
|
670 |
|
671 return doDefault; |
|
672 } |
|
673 |
|
674 // Update the presentation in case the event handler modified the selection, |
|
675 // see bug 602231. |
|
676 presShell->FlushPendingNotifications(Flush_Frames); |
|
677 if (presShell->IsDestroying()) |
|
678 return false; |
|
679 |
|
680 // if the event was not cancelled, do the default copy. If the event was cancelled, |
|
681 // use the data added to the data transfer and copy that instead. |
|
682 uint32_t count = 0; |
|
683 if (doDefault) { |
|
684 // get the data from the selection if any |
|
685 bool isCollapsed; |
|
686 sel->GetIsCollapsed(&isCollapsed); |
|
687 if (isCollapsed) { |
|
688 return false; |
|
689 } |
|
690 // call the copy code |
|
691 rv = HTMLCopy(sel, doc, aClipboardType); |
|
692 if (NS_FAILED(rv)) { |
|
693 return false; |
|
694 } |
|
695 } else if (clipboardData) { |
|
696 // check to see if any data was put on the data transfer. |
|
697 clipboardData->GetMozItemCount(&count); |
|
698 if (count) { |
|
699 nsCOMPtr<nsIClipboard> clipboard(do_GetService("@mozilla.org/widget/clipboard;1")); |
|
700 NS_ENSURE_TRUE(clipboard, false); |
|
701 |
|
702 nsCOMPtr<nsITransferable> transferable = |
|
703 clipboardData->GetTransferable(0, doc->GetLoadContext()); |
|
704 |
|
705 NS_ENSURE_TRUE(transferable, false); |
|
706 |
|
707 // put the transferable on the clipboard |
|
708 rv = clipboard->SetData(transferable, nullptr, aClipboardType); |
|
709 if (NS_FAILED(rv)) { |
|
710 return false; |
|
711 } |
|
712 } |
|
713 } |
|
714 |
|
715 // Now that we have copied, update the clipboard commands. This should have |
|
716 // the effect of updating the enabled state of the paste menu item. |
|
717 if (doDefault || count) { |
|
718 piWindow->UpdateCommands(NS_LITERAL_STRING("clipboard")); |
|
719 } |
|
720 |
|
721 return doDefault; |
|
722 } |