Thu, 15 Jan 2015 21:03:48 +0100
Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)
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/. */
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"
24 #include "nsIDocShell.h"
25 #include "nsIContentViewerEdit.h"
26 #include "nsIClipboardDragDropHooks.h"
27 #include "nsIClipboardDragDropHookList.h"
28 #include "nsIClipboardHelper.h"
29 #include "nsISelectionController.h"
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"
42 // image copy stuff
43 #include "nsIImageLoadingContent.h"
44 #include "nsIInterfaceRequestorUtils.h"
45 #include "nsContentUtils.h"
46 #include "nsContentCID.h"
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"
54 using namespace mozilla;
55 using namespace mozilla::dom;
57 nsresult NS_NewDomSelection(nsISelection **aDomSelection);
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);
63 // copy string data onto the transferable
64 static nsresult AppendString(nsITransferable *aTransferable,
65 const nsAString& aString,
66 const char* aFlavor);
68 // copy HTML node data
69 static nsresult AppendDOMNode(nsITransferable *aTransferable,
70 nsINode* aDOMNode);
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 }
84 nsresult rv;
86 nsCOMPtr<nsIDocumentEncoder> docEncoder;
87 docEncoder = do_CreateInstance(NS_HTMLCOPY_ENCODER_CONTRACTID);
88 NS_ENSURE_TRUE(docEncoder, NS_ERROR_FAILURE);
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);
98 // Do the first and potentially trial encoding as preformatted and raw.
99 uint32_t flags = aFlags | nsIDocumentEncoder::OutputPreformatted
100 | nsIDocumentEncoder::OutputRaw;
102 nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(aDoc);
103 NS_ASSERTION(domDoc, "Need a document");
105 rv = docEncoder->Init(domDoc, mimeType, flags);
106 NS_ENSURE_SUCCESS(rv, rv);
108 rv = docEncoder->SetSelection(aSel);
109 NS_ENSURE_SUCCESS(rv, rv);
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);
117 nsAutoString buf;
118 rv = docEncoder->EncodeToString(buf);
119 NS_ENSURE_SUCCESS(rv, rv);
121 rv = docEncoder->GetMimeType(mimeType);
122 NS_ENSURE_SUCCESS(rv, rv);
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 }
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);
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);
152 mimeType.AssignLiteral(kTextMime);
153 rv = docEncoder->Init(domDoc, mimeType, flags);
154 NS_ENSURE_SUCCESS(rv, rv);
156 rv = docEncoder->SetSelection(aSel);
157 NS_ENSURE_SUCCESS(rv, rv);
159 rv = docEncoder->EncodeToString(textPlainBuf);
160 NS_ENSURE_SUCCESS(rv, rv);
161 }
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);
173 rv = docEncoder->SetSelection(aSel);
174 NS_ENSURE_SUCCESS(rv, rv);
176 rv = docEncoder->EncodeToStringWithContext(htmlParentsBuf, htmlInfoBuf,
177 textHTMLBuf);
178 NS_ENSURE_SUCCESS(rv, rv);
179 }
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 }
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);
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 }
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);
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 }
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 }
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);
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 }
255 if (doPutOnClipboard && clipboard) {
256 bool actuallyPutOnClipboard = true;
257 nsCopySupport::DoHooks(aDoc, trans, &actuallyPutOnClipboard);
259 // put the transferable on the clipboard
260 if (actuallyPutOnClipboard)
261 clipboard->SetData(trans, nullptr, aClipboardID);
262 }
264 // Return the transferable to the caller if requested.
265 if (aTransferable != nullptr) {
266 trans.swap(*aTransferable);
267 }
268 }
269 }
270 return rv;
271 }
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 }
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 }
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 }
314 nsresult nsCopySupport::DoHooks(nsIDocument *aDoc, nsITransferable *aTrans,
315 bool *aDoPutOnClipboard)
316 {
317 NS_ENSURE_ARG(aDoc);
319 *aDoPutOnClipboard = true;
321 nsCOMPtr<nsISupports> container = aDoc->GetContainer();
322 nsCOMPtr<nsIClipboardDragDropHookList> hookObj = do_GetInterface(container);
323 if (!hookObj) return NS_ERROR_FAILURE;
325 nsCOMPtr<nsISimpleEnumerator> enumerator;
326 hookObj->GetHookEnumerator(getter_AddRefs(enumerator));
327 if (!enumerator) return NS_ERROR_FAILURE;
329 // the logic here should follow the behavior specified in
330 // nsIClipboardDragDropHooks.h
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 }
354 return rv;
355 }
357 nsresult
358 nsCopySupport::GetContents(const nsACString& aMimeType, uint32_t aFlags, nsISelection *aSel, nsIDocument *aDoc, nsAString& outdata)
359 {
360 nsresult rv = NS_OK;
362 nsCOMPtr<nsIDocumentEncoder> docEncoder;
364 nsAutoCString encoderContractID(NS_DOC_ENCODER_CONTRACTID_BASE);
365 encoderContractID.Append(aMimeType);
367 docEncoder = do_CreateInstance(encoderContractID.get());
368 NS_ENSURE_TRUE(docEncoder, NS_ERROR_FAILURE);
370 uint32_t flags = aFlags | nsIDocumentEncoder::SkipInvisibleContent;
372 if (aMimeType.Equals("text/plain"))
373 flags |= nsIDocumentEncoder::OutputPreformatted;
375 NS_ConvertASCIItoUTF16 unicodeMimeType(aMimeType);
377 nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(aDoc);
378 NS_ASSERTION(domDoc, "Need a document");
380 rv = docEncoder->Init(domDoc, unicodeMimeType, flags);
381 if (NS_FAILED(rv)) return rv;
383 if (aSel)
384 {
385 rv = docEncoder->SetSelection(aSel);
386 if (NS_FAILED(rv)) return rv;
387 }
389 // encode the selection
390 return docEncoder->EncodeToString(outdata);
391 }
394 nsresult
395 nsCopySupport::ImageCopy(nsIImageLoadingContent* aImageElement,
396 nsILoadContext* aLoadContext,
397 int32_t aCopyFlags)
398 {
399 nsresult rv;
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);
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);
413 nsAutoCString location;
414 rv = uri->GetSpec(location);
415 NS_ENSURE_SUCCESS(rv, rv);
417 // append the string to the transferable
418 rv = AppendString(trans, NS_ConvertUTF8toUTF16(location), kUnicodeMime);
419 NS_ENSURE_SUCCESS(rv, rv);
420 }
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);
427 rv = AppendDOMNode(trans, node);
428 NS_ENSURE_SUCCESS(rv, rv);
429 }
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);
437 nsCOMPtr<nsISupportsInterfacePointer>
438 imgPtr(do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID, &rv));
439 NS_ENSURE_SUCCESS(rv, rv);
441 rv = imgPtr->SetData(image);
442 NS_ENSURE_SUCCESS(rv, rv);
444 // copy the image data onto the transferable
445 rv = trans->SetTransferData(kNativeImageMime, imgPtr,
446 sizeof(nsISupports*));
447 NS_ENSURE_SUCCESS(rv, rv);
448 }
450 // get clipboard
451 nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv));
452 NS_ENSURE_SUCCESS(rv, rv);
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);
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 }
465 return clipboard->SetData(trans, nullptr, nsIClipboard::kGlobalClipboard);
466 }
468 static nsresult AppendString(nsITransferable *aTransferable,
469 const nsAString& aString,
470 const char* aFlavor)
471 {
472 nsresult rv;
474 nsCOMPtr<nsISupportsString>
475 data(do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv));
476 NS_ENSURE_SUCCESS(rv, rv);
478 rv = data->SetData(aString);
479 NS_ENSURE_SUCCESS(rv, rv);
481 rv = aTransferable->AddDataFlavor(aFlavor);
482 NS_ENSURE_SUCCESS(rv, rv);
484 return aTransferable->SetTransferData(aFlavor, data,
485 aString.Length() * sizeof(char16_t));
486 }
488 static nsresult AppendDOMNode(nsITransferable *aTransferable,
489 nsINode *aDOMNode)
490 {
491 nsresult rv;
493 // selializer
494 nsCOMPtr<nsIDocumentEncoder>
495 docEncoder(do_CreateInstance(NS_HTMLCOPY_ENCODER_CONTRACTID, &rv));
496 NS_ENSURE_SUCCESS(rv, rv);
498 // get document for the encoder
499 nsCOMPtr<nsIDocument> document = aDOMNode->OwnerDoc();
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);
507 NS_ENSURE_TRUE(document->IsHTML(), NS_OK);
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);
515 rv = docEncoder->SetNativeNode(aDOMNode);
516 NS_ENSURE_SUCCESS(rv, rv);
518 // serialize to string
519 nsAutoString html, context, info;
520 rv = docEncoder->EncodeToStringWithContext(context, info, html);
521 NS_ENSURE_SUCCESS(rv, rv);
523 // copy them to the transferable
524 if (!html.IsEmpty()) {
525 rv = AppendString(aTransferable, html, kHTMLMime);
526 NS_ENSURE_SUCCESS(rv, rv);
527 }
529 if (!info.IsEmpty()) {
530 rv = AppendString(aTransferable, info, kHTMLInfo);
531 NS_ENSURE_SUCCESS(rv, rv);
532 }
534 // add a special flavor, even if we don't have html context data
535 return AppendString(aTransferable, context, kHTMLContext);
536 }
538 nsIContent*
539 nsCopySupport::GetSelectionForCopy(nsIDocument* aDocument, nsISelection** aSelection)
540 {
541 *aSelection = nullptr;
543 nsIPresShell* presShell = aDocument->GetShell();
544 if (!presShell)
545 return nullptr;
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 }
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 }
569 bool
570 nsCopySupport::CanCopy(nsIDocument* aDocument)
571 {
572 if (!aDocument)
573 return false;
575 nsCOMPtr<nsISelection> sel;
576 GetSelectionForCopy(aDocument, getter_AddRefs(sel));
577 NS_ENSURE_TRUE(sel, false);
579 bool isCollapsed;
580 sel->GetIsCollapsed(&isCollapsed);
581 return !isCollapsed;
582 }
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");
590 nsCOMPtr<nsIPresShell> presShell = aPresShell;
591 if (!presShell)
592 return false;
594 nsCOMPtr<nsIDocument> doc = presShell->GetDocument();
595 if (!doc)
596 return false;
598 nsCOMPtr<nsPIDOMWindow> piWindow = doc->GetWindow();
599 if (!piWindow)
600 return false;
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));
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 }
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 }
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 }
636 // It seems to be unsafe to fire an event handler during reflow (bug 393696)
637 if (!nsContentUtils::IsSafeToRunScript())
638 return false;
640 nsCOMPtr<nsIDocShell> docShell = do_GetInterface(piWindow);
641 const bool chromeShell =
642 docShell && docShell->ItemType() == nsIDocShellTreeItem::typeChrome;
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);
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 }
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 }
671 return doDefault;
672 }
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;
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);
702 nsCOMPtr<nsITransferable> transferable =
703 clipboardData->GetTransferable(0, doc->GetLoadContext());
705 NS_ENSURE_TRUE(transferable, false);
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 }
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 }
721 return doDefault;
722 }