michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsCopySupport.h" michael@0: #include "nsIDocumentEncoder.h" michael@0: #include "nsISupports.h" michael@0: #include "nsIContent.h" michael@0: #include "nsIComponentManager.h" michael@0: #include "nsIServiceManager.h" michael@0: #include "nsIClipboard.h" michael@0: #include "nsISelection.h" michael@0: #include "nsWidgetsCID.h" michael@0: #include "nsXPCOM.h" michael@0: #include "nsISupportsPrimitives.h" michael@0: #include "nsIDOMRange.h" michael@0: #include "nsRange.h" michael@0: #include "imgIContainer.h" michael@0: #include "nsIPresShell.h" michael@0: #include "nsFocusManager.h" michael@0: #include "mozilla/dom/DataTransfer.h" michael@0: michael@0: #include "nsIDocShell.h" michael@0: #include "nsIContentViewerEdit.h" michael@0: #include "nsIClipboardDragDropHooks.h" michael@0: #include "nsIClipboardDragDropHookList.h" michael@0: #include "nsIClipboardHelper.h" michael@0: #include "nsISelectionController.h" michael@0: michael@0: #include "nsPIDOMWindow.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsIDOMNode.h" michael@0: #include "nsIDOMElement.h" michael@0: #include "nsIDOMDocument.h" michael@0: #include "nsIHTMLDocument.h" michael@0: #include "nsGkAtoms.h" michael@0: #include "nsIFrame.h" michael@0: #include "nsIURI.h" michael@0: #include "nsISimpleEnumerator.h" michael@0: michael@0: // image copy stuff michael@0: #include "nsIImageLoadingContent.h" michael@0: #include "nsIInterfaceRequestorUtils.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsContentCID.h" michael@0: michael@0: #include "mozilla/ContentEvents.h" michael@0: #include "mozilla/dom/Element.h" michael@0: #include "mozilla/EventDispatcher.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "mozilla/dom/Selection.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::dom; michael@0: michael@0: nsresult NS_NewDomSelection(nsISelection **aDomSelection); michael@0: michael@0: static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID); michael@0: static NS_DEFINE_CID(kCTransferableCID, NS_TRANSFERABLE_CID); michael@0: static NS_DEFINE_CID(kHTMLConverterCID, NS_HTMLFORMATCONVERTER_CID); michael@0: michael@0: // copy string data onto the transferable michael@0: static nsresult AppendString(nsITransferable *aTransferable, michael@0: const nsAString& aString, michael@0: const char* aFlavor); michael@0: michael@0: // copy HTML node data michael@0: static nsresult AppendDOMNode(nsITransferable *aTransferable, michael@0: nsINode* aDOMNode); michael@0: michael@0: // Helper used for HTMLCopy and GetTransferableForSelection since both routines michael@0: // share common code. michael@0: static nsresult michael@0: SelectionCopyHelper(nsISelection *aSel, nsIDocument *aDoc, michael@0: bool doPutOnClipboard, int16_t aClipboardID, michael@0: uint32_t aFlags, nsITransferable ** aTransferable) michael@0: { michael@0: // Clear the output parameter for the transferable, if provided. michael@0: if (aTransferable) { michael@0: *aTransferable = nullptr; michael@0: } michael@0: michael@0: nsresult rv; michael@0: michael@0: nsCOMPtr docEncoder; michael@0: docEncoder = do_CreateInstance(NS_HTMLCOPY_ENCODER_CONTRACTID); michael@0: NS_ENSURE_TRUE(docEncoder, NS_ERROR_FAILURE); michael@0: michael@0: // note that we assign text/unicode as mime type, but in fact nsHTMLCopyEncoder michael@0: // ignore it and use text/html or text/plain depending where the selection michael@0: // is. if it is a selection into input/textarea element or in a html content michael@0: // with pre-wrap style : text/plain. Otherwise text/html. michael@0: // see nsHTMLCopyEncoder::SetSelection michael@0: nsAutoString mimeType; michael@0: mimeType.AssignLiteral(kUnicodeMime); michael@0: michael@0: // Do the first and potentially trial encoding as preformatted and raw. michael@0: uint32_t flags = aFlags | nsIDocumentEncoder::OutputPreformatted michael@0: | nsIDocumentEncoder::OutputRaw; michael@0: michael@0: nsCOMPtr domDoc = do_QueryInterface(aDoc); michael@0: NS_ASSERTION(domDoc, "Need a document"); michael@0: michael@0: rv = docEncoder->Init(domDoc, mimeType, flags); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = docEncoder->SetSelection(aSel); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // SetSelection set the mime type to text/plain if the selection is inside a michael@0: // text widget. michael@0: rv = docEncoder->GetMimeType(mimeType); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: bool selForcedTextPlain = mimeType.EqualsLiteral(kTextMime); michael@0: michael@0: nsAutoString buf; michael@0: rv = docEncoder->EncodeToString(buf); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = docEncoder->GetMimeType(mimeType); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (!selForcedTextPlain && mimeType.EqualsLiteral(kTextMime)) { michael@0: // SetSelection and EncodeToString use this case to signal that text/plain michael@0: // was forced because the document is either not an nsIHTMLDocument or it's michael@0: // XHTML. We want to pretty print XHTML but not non-nsIHTMLDocuments. michael@0: nsCOMPtr htmlDoc = do_QueryInterface(aDoc); michael@0: if (!htmlDoc) { michael@0: selForcedTextPlain = true; michael@0: } michael@0: } michael@0: michael@0: // The mime type is ultimately text/html if the encoder successfully encoded michael@0: // the selection as text/html. michael@0: bool encodedTextHTML = mimeType.EqualsLiteral(kHTMLMime); michael@0: michael@0: // First, prepare the text/plain clipboard flavor. michael@0: nsAutoString textPlainBuf; michael@0: if (selForcedTextPlain) { michael@0: // Nothing to do. buf contains the final, preformatted, raw text/plain. michael@0: textPlainBuf.Assign(buf); michael@0: } else { michael@0: // Redo the encoding, but this time use pretty printing. michael@0: flags = michael@0: nsIDocumentEncoder::OutputSelectionOnly | michael@0: nsIDocumentEncoder::OutputAbsoluteLinks | michael@0: nsIDocumentEncoder::SkipInvisibleContent | michael@0: nsIDocumentEncoder::OutputDropInvisibleBreak | michael@0: (aFlags & nsIDocumentEncoder::OutputNoScriptContent); michael@0: michael@0: mimeType.AssignLiteral(kTextMime); michael@0: rv = docEncoder->Init(domDoc, mimeType, flags); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = docEncoder->SetSelection(aSel); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = docEncoder->EncodeToString(textPlainBuf); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: // Second, prepare the text/html flavor. michael@0: nsAutoString textHTMLBuf; michael@0: nsAutoString htmlParentsBuf; michael@0: nsAutoString htmlInfoBuf; michael@0: if (encodedTextHTML) { michael@0: // Redo the encoding, but this time use the passed-in flags. michael@0: mimeType.AssignLiteral(kHTMLMime); michael@0: rv = docEncoder->Init(domDoc, mimeType, aFlags); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = docEncoder->SetSelection(aSel); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = docEncoder->EncodeToStringWithContext(htmlParentsBuf, htmlInfoBuf, michael@0: textHTMLBuf); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: // Get the Clipboard michael@0: nsCOMPtr clipboard; michael@0: if (doPutOnClipboard) { michael@0: clipboard = do_GetService(kCClipboardCID, &rv); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: } michael@0: michael@0: if ((doPutOnClipboard && clipboard) || aTransferable != nullptr) { michael@0: // Create a transferable for putting data on the Clipboard michael@0: nsCOMPtr trans = do_CreateInstance(kCTransferableCID); michael@0: if (trans) { michael@0: trans->Init(aDoc->GetLoadContext()); michael@0: if (encodedTextHTML) { michael@0: // Set up a format converter so that clipboard flavor queries work. michael@0: // This converter isn't really used for conversions. michael@0: nsCOMPtr htmlConverter = michael@0: do_CreateInstance(kHTMLConverterCID); michael@0: trans->SetConverter(htmlConverter); michael@0: michael@0: if (!textHTMLBuf.IsEmpty()) { michael@0: // Add the html DataFlavor to the transferable michael@0: rv = AppendString(trans, textHTMLBuf, kHTMLMime); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: // Add the htmlcontext DataFlavor to the transferable michael@0: // Even if parents is empty string, this flavor should michael@0: // be attached to the transferable michael@0: rv = AppendString(trans, htmlParentsBuf, kHTMLContext); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (!htmlInfoBuf.IsEmpty()) { michael@0: // Add the htmlinfo DataFlavor to the transferable michael@0: rv = AppendString(trans, htmlInfoBuf, kHTMLInfo); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: if (!textPlainBuf.IsEmpty()) { michael@0: // unicode text michael@0: // Add the unicode DataFlavor to the transferable michael@0: // If we didn't have this, then nsDataObj::GetData matches text/unicode against michael@0: // the kURLMime flavour which is not desirable (eg. when pasting into Notepad) michael@0: rv = AppendString(trans, textPlainBuf, kUnicodeMime); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: // Try and get source URI of the items that are being dragged michael@0: nsIURI *uri = aDoc->GetDocumentURI(); michael@0: if (uri) { michael@0: nsAutoCString spec; michael@0: uri->GetSpec(spec); michael@0: if (!spec.IsEmpty()) { michael@0: nsAutoString shortcut; michael@0: AppendUTF8toUTF16(spec, shortcut); michael@0: michael@0: // Add the URL DataFlavor to the transferable. Don't use kURLMime, as it will michael@0: // cause an unnecessary UniformResourceLocator to be added which confuses michael@0: // some apps eg. Outlook 2000 - (See Bug 315370). Don't use michael@0: // kURLDataMime, as it will cause a bogus 'url ' flavor to michael@0: // show up on the Mac clipboard, confusing other apps, like michael@0: // Terminal (see bug 336012). michael@0: rv = AppendString(trans, shortcut, kURLPrivateMime); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: } michael@0: } else { michael@0: if (!textPlainBuf.IsEmpty()) { michael@0: // Add the unicode DataFlavor to the transferable michael@0: rv = AppendString(trans, textPlainBuf, kUnicodeMime); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: } michael@0: michael@0: if (doPutOnClipboard && clipboard) { michael@0: bool actuallyPutOnClipboard = true; michael@0: nsCopySupport::DoHooks(aDoc, trans, &actuallyPutOnClipboard); michael@0: michael@0: // put the transferable on the clipboard michael@0: if (actuallyPutOnClipboard) michael@0: clipboard->SetData(trans, nullptr, aClipboardID); michael@0: } michael@0: michael@0: // Return the transferable to the caller if requested. michael@0: if (aTransferable != nullptr) { michael@0: trans.swap(*aTransferable); michael@0: } michael@0: } michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: nsCopySupport::HTMLCopy(nsISelection* aSel, nsIDocument* aDoc, michael@0: int16_t aClipboardID) michael@0: { michael@0: return SelectionCopyHelper(aSel, aDoc, true, aClipboardID, michael@0: nsIDocumentEncoder::SkipInvisibleContent, michael@0: nullptr); michael@0: } michael@0: michael@0: nsresult michael@0: nsCopySupport::GetTransferableForSelection(nsISelection* aSel, michael@0: nsIDocument* aDoc, michael@0: nsITransferable** aTransferable) michael@0: { michael@0: return SelectionCopyHelper(aSel, aDoc, false, 0, michael@0: nsIDocumentEncoder::SkipInvisibleContent, michael@0: aTransferable); michael@0: } michael@0: michael@0: nsresult michael@0: nsCopySupport::GetTransferableForNode(nsINode* aNode, michael@0: nsIDocument* aDoc, michael@0: nsITransferable** aTransferable) michael@0: { michael@0: nsCOMPtr selection; michael@0: // Make a temporary selection with aNode in a single range. michael@0: nsresult rv = NS_NewDomSelection(getter_AddRefs(selection)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: nsCOMPtr node = do_QueryInterface(aNode); michael@0: NS_ENSURE_TRUE(node, NS_ERROR_FAILURE); michael@0: nsRefPtr range = new nsRange(aNode); michael@0: rv = range->SelectNode(node); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: rv = selection->AddRange(range); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: // It's not the primary selection - so don't skip invisible content. michael@0: uint32_t flags = 0; michael@0: return SelectionCopyHelper(selection, aDoc, false, 0, flags, michael@0: aTransferable); michael@0: } michael@0: michael@0: nsresult nsCopySupport::DoHooks(nsIDocument *aDoc, nsITransferable *aTrans, michael@0: bool *aDoPutOnClipboard) michael@0: { michael@0: NS_ENSURE_ARG(aDoc); michael@0: michael@0: *aDoPutOnClipboard = true; michael@0: michael@0: nsCOMPtr container = aDoc->GetContainer(); michael@0: nsCOMPtr hookObj = do_GetInterface(container); michael@0: if (!hookObj) return NS_ERROR_FAILURE; michael@0: michael@0: nsCOMPtr enumerator; michael@0: hookObj->GetHookEnumerator(getter_AddRefs(enumerator)); michael@0: if (!enumerator) return NS_ERROR_FAILURE; michael@0: michael@0: // the logic here should follow the behavior specified in michael@0: // nsIClipboardDragDropHooks.h michael@0: michael@0: nsCOMPtr override; michael@0: nsCOMPtr isupp; michael@0: bool hasMoreHooks = false; michael@0: nsresult rv = NS_OK; michael@0: while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMoreHooks)) michael@0: && hasMoreHooks) michael@0: { michael@0: rv = enumerator->GetNext(getter_AddRefs(isupp)); michael@0: if (NS_FAILED(rv)) break; michael@0: override = do_QueryInterface(isupp); michael@0: if (override) michael@0: { michael@0: #ifdef DEBUG michael@0: nsresult hookResult = michael@0: #endif michael@0: override->OnCopyOrDrag(nullptr, aTrans, aDoPutOnClipboard); michael@0: NS_ASSERTION(NS_SUCCEEDED(hookResult), "OnCopyOrDrag hook failed"); michael@0: if (!*aDoPutOnClipboard) michael@0: break; michael@0: } michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: nsCopySupport::GetContents(const nsACString& aMimeType, uint32_t aFlags, nsISelection *aSel, nsIDocument *aDoc, nsAString& outdata) michael@0: { michael@0: nsresult rv = NS_OK; michael@0: michael@0: nsCOMPtr docEncoder; michael@0: michael@0: nsAutoCString encoderContractID(NS_DOC_ENCODER_CONTRACTID_BASE); michael@0: encoderContractID.Append(aMimeType); michael@0: michael@0: docEncoder = do_CreateInstance(encoderContractID.get()); michael@0: NS_ENSURE_TRUE(docEncoder, NS_ERROR_FAILURE); michael@0: michael@0: uint32_t flags = aFlags | nsIDocumentEncoder::SkipInvisibleContent; michael@0: michael@0: if (aMimeType.Equals("text/plain")) michael@0: flags |= nsIDocumentEncoder::OutputPreformatted; michael@0: michael@0: NS_ConvertASCIItoUTF16 unicodeMimeType(aMimeType); michael@0: michael@0: nsCOMPtr domDoc = do_QueryInterface(aDoc); michael@0: NS_ASSERTION(domDoc, "Need a document"); michael@0: michael@0: rv = docEncoder->Init(domDoc, unicodeMimeType, flags); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: if (aSel) michael@0: { michael@0: rv = docEncoder->SetSelection(aSel); michael@0: if (NS_FAILED(rv)) return rv; michael@0: } michael@0: michael@0: // encode the selection michael@0: return docEncoder->EncodeToString(outdata); michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: nsCopySupport::ImageCopy(nsIImageLoadingContent* aImageElement, michael@0: nsILoadContext* aLoadContext, michael@0: int32_t aCopyFlags) michael@0: { michael@0: nsresult rv; michael@0: michael@0: // create a transferable for putting data on the Clipboard michael@0: nsCOMPtr trans(do_CreateInstance(kCTransferableCID, &rv)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: trans->Init(aLoadContext); michael@0: michael@0: if (aCopyFlags & nsIContentViewerEdit::COPY_IMAGE_TEXT) { michael@0: // get the location from the element michael@0: nsCOMPtr uri; michael@0: rv = aImageElement->GetCurrentURI(getter_AddRefs(uri)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE); michael@0: michael@0: nsAutoCString location; michael@0: rv = uri->GetSpec(location); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // append the string to the transferable michael@0: rv = AppendString(trans, NS_ConvertUTF8toUTF16(location), kUnicodeMime); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: if (aCopyFlags & nsIContentViewerEdit::COPY_IMAGE_HTML) { michael@0: // append HTML data to the transferable michael@0: nsCOMPtr node(do_QueryInterface(aImageElement, &rv)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = AppendDOMNode(trans, node); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: if (aCopyFlags & nsIContentViewerEdit::COPY_IMAGE_DATA) { michael@0: // get the image data from the element michael@0: nsCOMPtr image = michael@0: nsContentUtils::GetImageFromContent(aImageElement); michael@0: NS_ENSURE_TRUE(image, NS_ERROR_FAILURE); michael@0: michael@0: nsCOMPtr michael@0: imgPtr(do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID, &rv)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = imgPtr->SetData(image); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // copy the image data onto the transferable michael@0: rv = trans->SetTransferData(kNativeImageMime, imgPtr, michael@0: sizeof(nsISupports*)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: // get clipboard michael@0: nsCOMPtr clipboard(do_GetService(kCClipboardCID, &rv)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // check whether the system supports the selection clipboard or not. michael@0: bool selectionSupported; michael@0: rv = clipboard->SupportsSelectionClipboard(&selectionSupported); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // put the transferable on the clipboard michael@0: if (selectionSupported) { michael@0: rv = clipboard->SetData(trans, nullptr, nsIClipboard::kSelectionClipboard); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: return clipboard->SetData(trans, nullptr, nsIClipboard::kGlobalClipboard); michael@0: } michael@0: michael@0: static nsresult AppendString(nsITransferable *aTransferable, michael@0: const nsAString& aString, michael@0: const char* aFlavor) michael@0: { michael@0: nsresult rv; michael@0: michael@0: nsCOMPtr michael@0: data(do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = data->SetData(aString); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = aTransferable->AddDataFlavor(aFlavor); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return aTransferable->SetTransferData(aFlavor, data, michael@0: aString.Length() * sizeof(char16_t)); michael@0: } michael@0: michael@0: static nsresult AppendDOMNode(nsITransferable *aTransferable, michael@0: nsINode *aDOMNode) michael@0: { michael@0: nsresult rv; michael@0: michael@0: // selializer michael@0: nsCOMPtr michael@0: docEncoder(do_CreateInstance(NS_HTMLCOPY_ENCODER_CONTRACTID, &rv)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // get document for the encoder michael@0: nsCOMPtr document = aDOMNode->OwnerDoc(); michael@0: michael@0: // Note that XHTML is not counted as HTML here, because we can't copy it michael@0: // properly (all the copy code for non-plaintext assumes using HTML michael@0: // serializers and parsers is OK, and those mess up XHTML). michael@0: nsCOMPtr htmlDoc = do_QueryInterface(document, &rv); michael@0: NS_ENSURE_SUCCESS(rv, NS_OK); michael@0: michael@0: NS_ENSURE_TRUE(document->IsHTML(), NS_OK); michael@0: michael@0: // init encoder with document and node michael@0: rv = docEncoder->NativeInit(document, NS_LITERAL_STRING(kHTMLMime), michael@0: nsIDocumentEncoder::OutputAbsoluteLinks | michael@0: nsIDocumentEncoder::OutputEncodeW3CEntities); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = docEncoder->SetNativeNode(aDOMNode); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // serialize to string michael@0: nsAutoString html, context, info; michael@0: rv = docEncoder->EncodeToStringWithContext(context, info, html); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // copy them to the transferable michael@0: if (!html.IsEmpty()) { michael@0: rv = AppendString(aTransferable, html, kHTMLMime); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: if (!info.IsEmpty()) { michael@0: rv = AppendString(aTransferable, info, kHTMLInfo); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: // add a special flavor, even if we don't have html context data michael@0: return AppendString(aTransferable, context, kHTMLContext); michael@0: } michael@0: michael@0: nsIContent* michael@0: nsCopySupport::GetSelectionForCopy(nsIDocument* aDocument, nsISelection** aSelection) michael@0: { michael@0: *aSelection = nullptr; michael@0: michael@0: nsIPresShell* presShell = aDocument->GetShell(); michael@0: if (!presShell) michael@0: return nullptr; michael@0: michael@0: // check if the focused node in the window has a selection michael@0: nsCOMPtr focusedWindow; michael@0: nsIContent* content = michael@0: nsFocusManager::GetFocusedDescendant(aDocument->GetWindow(), false, michael@0: getter_AddRefs(focusedWindow)); michael@0: if (content) { michael@0: nsIFrame* frame = content->GetPrimaryFrame(); michael@0: if (frame) { michael@0: nsCOMPtr selCon; michael@0: frame->GetSelectionController(presShell->GetPresContext(), getter_AddRefs(selCon)); michael@0: if (selCon) { michael@0: selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, aSelection); michael@0: return content; michael@0: } michael@0: } michael@0: } michael@0: michael@0: // if no selection was found, use the main selection for the window michael@0: NS_IF_ADDREF(*aSelection = presShell->GetCurrentSelection(nsISelectionController::SELECTION_NORMAL)); michael@0: return nullptr; michael@0: } michael@0: michael@0: bool michael@0: nsCopySupport::CanCopy(nsIDocument* aDocument) michael@0: { michael@0: if (!aDocument) michael@0: return false; michael@0: michael@0: nsCOMPtr sel; michael@0: GetSelectionForCopy(aDocument, getter_AddRefs(sel)); michael@0: NS_ENSURE_TRUE(sel, false); michael@0: michael@0: bool isCollapsed; michael@0: sel->GetIsCollapsed(&isCollapsed); michael@0: return !isCollapsed; michael@0: } michael@0: michael@0: bool michael@0: nsCopySupport::FireClipboardEvent(int32_t aType, int32_t aClipboardType, nsIPresShell* aPresShell, nsISelection* aSelection) michael@0: { michael@0: NS_ASSERTION(aType == NS_CUT || aType == NS_COPY || aType == NS_PASTE, michael@0: "Invalid clipboard event type"); michael@0: michael@0: nsCOMPtr presShell = aPresShell; michael@0: if (!presShell) michael@0: return false; michael@0: michael@0: nsCOMPtr doc = presShell->GetDocument(); michael@0: if (!doc) michael@0: return false; michael@0: michael@0: nsCOMPtr piWindow = doc->GetWindow(); michael@0: if (!piWindow) michael@0: return false; michael@0: michael@0: // if a selection was not supplied, try to find it michael@0: nsCOMPtr content; michael@0: nsCOMPtr sel = aSelection; michael@0: if (!sel) michael@0: content = GetSelectionForCopy(doc, getter_AddRefs(sel)); michael@0: michael@0: // retrieve the event target node from the start of the selection michael@0: nsresult rv; michael@0: if (sel) { michael@0: // Only cut or copy when there is an uncollapsed selection michael@0: if (aType == NS_CUT || aType == NS_COPY) { michael@0: bool isCollapsed; michael@0: sel->GetIsCollapsed(&isCollapsed); michael@0: if (isCollapsed) michael@0: return false; michael@0: } michael@0: michael@0: nsCOMPtr range; michael@0: rv = sel->GetRangeAt(0, getter_AddRefs(range)); michael@0: if (NS_SUCCEEDED(rv) && range) { michael@0: nsCOMPtr startContainer; michael@0: range->GetStartContainer(getter_AddRefs(startContainer)); michael@0: if (startContainer) michael@0: content = do_QueryInterface(startContainer); michael@0: } michael@0: } michael@0: michael@0: // if no content node was set, just get the root michael@0: if (!content) { michael@0: content = doc->GetRootElement(); michael@0: if (!content) michael@0: return false; michael@0: } michael@0: michael@0: // It seems to be unsafe to fire an event handler during reflow (bug 393696) michael@0: if (!nsContentUtils::IsSafeToRunScript()) michael@0: return false; michael@0: michael@0: nsCOMPtr docShell = do_GetInterface(piWindow); michael@0: const bool chromeShell = michael@0: docShell && docShell->ItemType() == nsIDocShellTreeItem::typeChrome; michael@0: michael@0: // next, fire the cut, copy or paste event michael@0: bool doDefault = true; michael@0: nsRefPtr clipboardData; michael@0: if (chromeShell || Preferences::GetBool("dom.event.clipboardevents.enabled", true)) { michael@0: clipboardData = michael@0: new DataTransfer(piWindow, aType, aType == NS_PASTE, aClipboardType); michael@0: michael@0: nsEventStatus status = nsEventStatus_eIgnore; michael@0: InternalClipboardEvent evt(true, aType); michael@0: evt.clipboardData = clipboardData; michael@0: EventDispatcher::Dispatch(content, presShell->GetPresContext(), &evt, michael@0: nullptr, &status); michael@0: // If the event was cancelled, don't do the clipboard operation michael@0: doDefault = (status != nsEventStatus_eConsumeNoDefault); michael@0: } michael@0: michael@0: // No need to do anything special during a paste. Either an event listener michael@0: // took care of it and cancelled the event, or the caller will handle it. michael@0: // Return true to indicate that the event wasn't cancelled. michael@0: if (aType == NS_PASTE) { michael@0: // Clear and mark the clipboardData as readonly. This prevents someone michael@0: // from reading the clipboard contents after the paste event has fired. michael@0: if (clipboardData) { michael@0: clipboardData->ClearAll(); michael@0: clipboardData->SetReadOnly(); michael@0: } michael@0: michael@0: return doDefault; michael@0: } michael@0: michael@0: // Update the presentation in case the event handler modified the selection, michael@0: // see bug 602231. michael@0: presShell->FlushPendingNotifications(Flush_Frames); michael@0: if (presShell->IsDestroying()) michael@0: return false; michael@0: michael@0: // if the event was not cancelled, do the default copy. If the event was cancelled, michael@0: // use the data added to the data transfer and copy that instead. michael@0: uint32_t count = 0; michael@0: if (doDefault) { michael@0: // get the data from the selection if any michael@0: bool isCollapsed; michael@0: sel->GetIsCollapsed(&isCollapsed); michael@0: if (isCollapsed) { michael@0: return false; michael@0: } michael@0: // call the copy code michael@0: rv = HTMLCopy(sel, doc, aClipboardType); michael@0: if (NS_FAILED(rv)) { michael@0: return false; michael@0: } michael@0: } else if (clipboardData) { michael@0: // check to see if any data was put on the data transfer. michael@0: clipboardData->GetMozItemCount(&count); michael@0: if (count) { michael@0: nsCOMPtr clipboard(do_GetService("@mozilla.org/widget/clipboard;1")); michael@0: NS_ENSURE_TRUE(clipboard, false); michael@0: michael@0: nsCOMPtr transferable = michael@0: clipboardData->GetTransferable(0, doc->GetLoadContext()); michael@0: michael@0: NS_ENSURE_TRUE(transferable, false); michael@0: michael@0: // put the transferable on the clipboard michael@0: rv = clipboard->SetData(transferable, nullptr, aClipboardType); michael@0: if (NS_FAILED(rv)) { michael@0: return false; michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Now that we have copied, update the clipboard commands. This should have michael@0: // the effect of updating the enabled state of the paste menu item. michael@0: if (doDefault || count) { michael@0: piWindow->UpdateCommands(NS_LITERAL_STRING("clipboard")); michael@0: } michael@0: michael@0: return doDefault; michael@0: }