tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsCopySupport.cpp (34808B)


      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 "nsCopySupport.h"
      8 
      9 #include "imgIContainer.h"
     10 #include "imgIRequest.h"
     11 #include "mozilla/ScopeExit.h"
     12 #include "mozilla/dom/BrowsingContext.h"
     13 #include "mozilla/dom/DataTransfer.h"
     14 #include "mozilla/dom/Document.h"
     15 #include "nsComponentManagerUtils.h"
     16 #include "nsFocusManager.h"
     17 #include "nsFrameSelection.h"
     18 #include "nsGkAtoms.h"
     19 #include "nsGlobalWindowInner.h"
     20 #include "nsHTMLDocument.h"
     21 #include "nsIClipboard.h"
     22 #include "nsIContent.h"
     23 #include "nsIDocShell.h"
     24 #include "nsIDocumentEncoder.h"
     25 #include "nsIDocumentViewerEdit.h"
     26 #include "nsIFormControl.h"
     27 #include "nsIFrame.h"
     28 #include "nsISelectionController.h"
     29 #include "nsISupports.h"
     30 #include "nsISupportsPrimitives.h"
     31 #include "nsPIDOMWindow.h"
     32 #include "nsRange.h"
     33 #include "nsServiceManagerUtils.h"
     34 #include "nsWidgetsCID.h"
     35 #include "nsXPCOM.h"
     36 
     37 // image copy stuff
     38 #include "nsContentUtils.h"
     39 #include "nsIImageLoadingContent.h"
     40 #include "nsIInterfaceRequestorUtils.h"
     41 
     42 #ifdef XP_WIN
     43 #  include "mozilla/StaticPrefs_clipboard.h"
     44 #  include "nsCExternalHandlerService.h"
     45 #  include "nsEscape.h"
     46 #  include "nsIMIMEInfo.h"
     47 #  include "nsIMIMEService.h"
     48 #  include "nsIURIMutator.h"
     49 #  include "nsIURL.h"
     50 #  include "nsReadableUtils.h"
     51 #  include "nsXULAppAPI.h"
     52 #endif
     53 
     54 #include "mozilla/ContentEvents.h"
     55 #include "mozilla/EventDispatcher.h"
     56 #include "mozilla/IntegerRange.h"
     57 #include "mozilla/Preferences.h"
     58 #include "mozilla/PresShell.h"
     59 #include "mozilla/TextEditor.h"
     60 #include "mozilla/dom/Element.h"
     61 #include "mozilla/dom/HTMLInputElement.h"
     62 #include "mozilla/dom/Selection.h"
     63 
     64 using namespace mozilla;
     65 using namespace mozilla::dom;
     66 
     67 static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID);
     68 static NS_DEFINE_CID(kCTransferableCID, NS_TRANSFERABLE_CID);
     69 static NS_DEFINE_CID(kHTMLConverterCID, NS_HTMLFORMATCONVERTER_CID);
     70 
     71 // copy string data onto the transferable
     72 static nsresult AppendString(nsITransferable* aTransferable,
     73                             const nsAString& aString, const char* aFlavor);
     74 
     75 // copy HTML node data
     76 static nsresult AppendDOMNode(nsITransferable* aTransferable,
     77                              nsINode* aDOMNode);
     78 
     79 #ifdef XP_WIN
     80 // copy image as file promise onto the transferable
     81 static nsresult AppendImagePromise(nsITransferable* aTransferable,
     82                                   imgIRequest* aImgRequest,
     83                                   nsINode* aImageNode);
     84 #endif
     85 
     86 static nsresult EncodeForTextPlain(nsIDocumentEncoder& aEncoder,
     87                                   Document& aDocument, Selection* aSelection,
     88                                   uint32_t aAdditionalEncoderFlags,
     89                                   bool& aCanBeEncodedAsTextHTML,
     90                                   nsAString& aSerializationResult) {
     91  // We assign text/html as the MIME type first, but in fact nsHTMLCopyEncoder
     92  // force the use of text/plain depending where the selection is (e.g., a
     93  // selection inside an <input> or <textarea> element). See
     94  // nsHTMLCopyEncoder::SetSelection. We can then use this behavior to detect
     95  // whether the selection can be encoded as text/html by checking the MIME type
     96  // after nsHTMLCopyEncoder::SetSelection.
     97  nsAutoString mimeType;
     98  mimeType.AssignLiteral(kHTMLMime);
     99 
    100  // Do the first and potentially trial encoding as preformatted and raw.
    101  uint32_t flags = aAdditionalEncoderFlags |
    102                   nsIDocumentEncoder::OutputPreformatted |
    103                   nsIDocumentEncoder::OutputRaw |
    104                   nsIDocumentEncoder::OutputForPlainTextClipboardCopy |
    105                   nsIDocumentEncoder::OutputPersistNBSP;
    106 
    107  nsresult rv = aEncoder.Init(&aDocument, mimeType, flags);
    108  NS_ENSURE_SUCCESS(rv, rv);
    109 
    110  rv = aEncoder.SetSelection(aSelection);
    111  NS_ENSURE_SUCCESS(rv, rv);
    112 
    113  // SetSelection set the mime type to text/plain if the selection is inside a
    114  // text widget.
    115  rv = aEncoder.GetMimeType(mimeType);
    116  NS_ENSURE_SUCCESS(rv, rv);
    117 
    118  // XXX: For XHTML documents, we would like to use pretty-printing encoding for
    119  // text/plain, just as we do for HTML documents. This is achieved by
    120  // relying on the current nsHTMLCopyEncoder design, where the MIME
    121  // type is not updated to text/plain immediately in
    122  // nsHTMLCopyEncoder::SetSelection(), but only latter when
    123  // nsHTMLCopyEncoder::EncodeToString() is called. As a result, we still see a
    124  // text/html MIME type here for XHTML documents.
    125  if (mimeType.EqualsLiteral(kTextMime)) {
    126    // nsHTMLCopyEncoder force to use text/plain.
    127    nsAutoString buf;
    128    rv = aEncoder.EncodeToString(buf);
    129    if (NS_SUCCEEDED(rv)) {
    130      // Nothing to do. buf contains the final, preformatted, raw text/plain.
    131      aSerializationResult.Assign(buf);
    132    }
    133    return rv;
    134  }
    135 
    136  MOZ_ASSERT(mimeType.EqualsLiteral(kHTMLMime));
    137  // XXX: We currently only try to encode as text/html for HTML documents.
    138  // See bug 857915.
    139  if (aDocument.IsHTMLDocument()) {
    140    aCanBeEncodedAsTextHTML = true;
    141  }
    142 
    143  // Do the text/plain encoding, but this time use pretty printing.
    144  flags = nsIDocumentEncoder::OutputSelectionOnly |
    145          nsIDocumentEncoder::OutputForPlainTextClipboardCopy |
    146          nsIDocumentEncoder::OutputAbsoluteLinks |
    147          nsIDocumentEncoder::SkipInvisibleContent |
    148          nsIDocumentEncoder::OutputDropInvisibleBreak |
    149          (aAdditionalEncoderFlags &
    150           (nsIDocumentEncoder::OutputNoScriptContent |
    151            nsIDocumentEncoder::OutputRubyAnnotation |
    152            nsIDocumentEncoder::AllowCrossShadowBoundary));
    153 
    154  mimeType.AssignLiteral(kTextMime);
    155  rv = aEncoder.Init(&aDocument, mimeType, flags);
    156  NS_ENSURE_SUCCESS(rv, rv);
    157 
    158  rv = aEncoder.SetSelection(aSelection);
    159  NS_ENSURE_SUCCESS(rv, rv);
    160 
    161  rv = aEncoder.EncodeToString(aSerializationResult);
    162  return rv;
    163 }
    164 
    165 static nsresult EncodeAsTextHTMLWithContext(
    166    nsIDocumentEncoder& aEncoder, Document& aDocument, Selection* aSelection,
    167    uint32_t aEncoderFlags, nsAutoString& aTextHTMLEncodingResult,
    168    nsAutoString& aHTMLParentsBufResult, nsAutoString& aHTMLInfoBufResult) {
    169  nsAutoString mimeType;
    170  mimeType.AssignLiteral(kHTMLMime);
    171  nsresult rv = aEncoder.Init(&aDocument, mimeType, aEncoderFlags);
    172  NS_ENSURE_SUCCESS(rv, rv);
    173 
    174  rv = aEncoder.SetSelection(aSelection);
    175  NS_ENSURE_SUCCESS(rv, rv);
    176 
    177  rv = aEncoder.EncodeToStringWithContext(
    178      aHTMLParentsBufResult, aHTMLInfoBufResult, aTextHTMLEncodingResult);
    179  NS_ENSURE_SUCCESS(rv, rv);
    180  return rv;
    181 }
    182 
    183 struct EncodedDocumentWithContext {
    184  // Whether the document can be encoded as text/html.
    185  bool mCanBeEncodedAsTextHTML = false;
    186 
    187  // The serialized document when encoding the document with `text/plain`.
    188  nsAutoString mSerializationForTextPlain;
    189 
    190  // When `mCanBeEncodedAsTextHTML` is true, this is the serialized document
    191  // using `text/html`.
    192  nsAutoString mSerializationForTextHTML;
    193 
    194  // When `mCanBeEncodedAsTextHTML` is true, this contains the serialized
    195  // ancestor elements.
    196  nsAutoString mHTMLContextBuffer;
    197 
    198  // When `mCanBeEncodedAsTextHTML` is true, this contains numbers
    199  // identifying where in the context the serialization came from.
    200  nsAutoString mHTMLInfoBuffer;
    201 };
    202 
    203 /**
    204 * @param aSelection Can be nullptr.
    205 * @param aAdditionalEncoderFlags nsIDocumentEncoder flags.
    206 */
    207 static nsresult EncodeDocumentWithContext(
    208    Document& aDocument, Selection* aSelection,
    209    uint32_t aAdditionalEncoderFlags,
    210    EncodedDocumentWithContext& aEncodedDocumentWithContext) {
    211  nsCOMPtr<nsIDocumentEncoder> docEncoder = do_createHTMLCopyEncoder();
    212 
    213  bool canBeEncodedAsTextHTML{false};
    214  nsAutoString serializationForTextPlain;
    215  nsresult rv = EncodeForTextPlain(
    216      *docEncoder, aDocument, aSelection, aAdditionalEncoderFlags,
    217      canBeEncodedAsTextHTML, serializationForTextPlain);
    218  NS_ENSURE_SUCCESS(rv, rv);
    219 
    220  nsAutoString serializationForTextHTML;
    221  nsAutoString htmlContextBuffer;
    222  nsAutoString htmlInfoBuffer;
    223  if (canBeEncodedAsTextHTML) {
    224    // Redo the encoding, but this time use the passed-in flags.
    225    // Don't allow wrapping of CJK strings.
    226    rv = EncodeAsTextHTMLWithContext(
    227        *docEncoder, aDocument, aSelection,
    228        aAdditionalEncoderFlags |
    229            nsIDocumentEncoder::OutputDisallowLineBreaking,
    230        serializationForTextHTML, htmlContextBuffer, htmlInfoBuffer);
    231    NS_ENSURE_SUCCESS(rv, rv);
    232  }
    233 
    234  aEncodedDocumentWithContext = {
    235      canBeEncodedAsTextHTML, std::move(serializationForTextPlain),
    236      std::move(serializationForTextHTML), std::move(htmlContextBuffer),
    237      std::move(htmlInfoBuffer)};
    238 
    239  return rv;
    240 }
    241 
    242 static nsresult CreateTransferable(
    243    const EncodedDocumentWithContext& aEncodedDocumentWithContext,
    244    Document& aDocument, nsCOMPtr<nsITransferable>& aTransferable) {
    245  nsresult rv = NS_OK;
    246 
    247  aTransferable = do_CreateInstance(kCTransferableCID);
    248  NS_ENSURE_TRUE(aTransferable, NS_ERROR_NULL_POINTER);
    249 
    250  aTransferable->Init(aDocument.GetLoadContext());
    251  aTransferable->SetDataPrincipal(aDocument.NodePrincipal());
    252  if (aEncodedDocumentWithContext.mCanBeEncodedAsTextHTML) {
    253    // XXX: Now we provide the text/plain directly, do we still need to always
    254    // set a HTML converter? Or perhaps we could set the converter only when the
    255    // text/plain is not available.
    256    // Set up a format converter so that clipboard flavor queries work.
    257    // This converter isn't really used for conversions.
    258    nsCOMPtr<nsIFormatConverter> htmlConverter =
    259        do_CreateInstance(kHTMLConverterCID);
    260    aTransferable->SetConverter(htmlConverter);
    261 
    262    if (!aEncodedDocumentWithContext.mSerializationForTextHTML.IsEmpty()) {
    263      // Add the html DataFlavor to the transferable
    264      rv = AppendString(aTransferable,
    265                        aEncodedDocumentWithContext.mSerializationForTextHTML,
    266                        kHTMLMime);
    267      NS_ENSURE_SUCCESS(rv, rv);
    268    }
    269 
    270    // Add the htmlcontext DataFlavor to the transferable. Even if the context
    271    // buffer is empty, this flavor should be attached to the transferable.
    272    rv = AppendString(aTransferable,
    273                      aEncodedDocumentWithContext.mHTMLContextBuffer,
    274                      kHTMLContext);
    275    NS_ENSURE_SUCCESS(rv, rv);
    276 
    277    if (!aEncodedDocumentWithContext.mHTMLInfoBuffer.IsEmpty()) {
    278      // Add the htmlinfo DataFlavor to the transferable
    279      rv = AppendString(aTransferable,
    280                        aEncodedDocumentWithContext.mHTMLInfoBuffer, kHTMLInfo);
    281      NS_ENSURE_SUCCESS(rv, rv);
    282    }
    283 
    284    if (!aEncodedDocumentWithContext.mSerializationForTextPlain.IsEmpty()) {
    285      // Add the plain text DataFlavor to the transferable
    286      // If we didn't have this, then nsDataObj::GetData matches
    287      // text/plain against the kURLMime flavour which is not desirable
    288      // (eg. when pasting into Notepad)
    289      rv = AppendString(aTransferable,
    290                        aEncodedDocumentWithContext.mSerializationForTextPlain,
    291                        kTextMime);
    292      NS_ENSURE_SUCCESS(rv, rv);
    293    }
    294 
    295 #if !defined(BASE_BROWSER_VERSION)
    296    // Try and get source URI of the items that are being dragged
    297    nsIURI* uri = aDocument.GetDocumentURI();
    298    if (uri) {
    299      nsAutoCString spec;
    300      nsresult rv = uri->GetSpec(spec);
    301      NS_ENSURE_SUCCESS(rv, rv);
    302      if (!spec.IsEmpty()) {
    303        nsAutoString shortcut;
    304        AppendUTF8toUTF16(spec, shortcut);
    305 
    306        // Add the URL DataFlavor to the transferable. Don't use kURLMime,
    307        // as it will cause an unnecessary UniformResourceLocator to be
    308        // added which confuses some apps eg. Outlook 2000 - (See Bug
    309        // 315370). Don't use kURLDataMime, as it will cause a bogus 'url '
    310        // flavor to show up on the Mac clipboard, confusing other apps,
    311        // like Terminal (see bug 336012).
    312        rv = AppendString(aTransferable, shortcut, kURLPrivateMime);
    313        NS_ENSURE_SUCCESS(rv, rv);
    314      }
    315    }
    316 #endif
    317  } else {
    318    if (!aEncodedDocumentWithContext.mSerializationForTextPlain.IsEmpty()) {
    319      // Add the unicode DataFlavor to the transferable
    320      rv = AppendString(aTransferable,
    321                        aEncodedDocumentWithContext.mSerializationForTextPlain,
    322                        kTextMime);
    323      NS_ENSURE_SUCCESS(rv, rv);
    324    }
    325  }
    326 
    327  return rv;
    328 }
    329 
    330 static nsresult PutToClipboard(
    331    const EncodedDocumentWithContext& aEncodedDocumentWithContext,
    332    nsIClipboard::ClipboardType aClipboardID, Document& aDocument) {
    333  nsresult rv;
    334  nsCOMPtr<nsIClipboard> clipboard = do_GetService(kCClipboardCID, &rv);
    335  NS_ENSURE_SUCCESS(rv, rv);
    336  NS_ENSURE_TRUE(clipboard, NS_ERROR_NULL_POINTER);
    337 
    338  nsCOMPtr<nsITransferable> transferable;
    339  rv = CreateTransferable(aEncodedDocumentWithContext, aDocument, transferable);
    340  NS_ENSURE_SUCCESS(rv, rv);
    341 
    342  rv = clipboard->SetData(transferable, nullptr, aClipboardID,
    343                          aDocument.GetWindowContext());
    344  NS_ENSURE_SUCCESS(rv, rv);
    345 
    346  return rv;
    347 }
    348 
    349 nsresult nsCopySupport::EncodeDocumentWithContextAndPutToClipboard(
    350    Selection* aSel, Document* aDoc, nsIClipboard::ClipboardType aClipboardID,
    351    bool aWithRubyAnnotation) {
    352  NS_ENSURE_TRUE(aDoc, NS_ERROR_NULL_POINTER);
    353 
    354  uint32_t additionalFlags = nsIDocumentEncoder::SkipInvisibleContent;
    355 
    356  if (StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()) {
    357    additionalFlags |= nsIDocumentEncoder::AllowCrossShadowBoundary;
    358  }
    359 
    360  if (aWithRubyAnnotation) {
    361    additionalFlags |= nsIDocumentEncoder::OutputRubyAnnotation;
    362  }
    363 
    364  EncodedDocumentWithContext encodedDocumentWithContext;
    365  nsresult rv = EncodeDocumentWithContext(*aDoc, aSel, additionalFlags,
    366                                          encodedDocumentWithContext);
    367  NS_ENSURE_SUCCESS(rv, rv);
    368 
    369  rv = PutToClipboard(encodedDocumentWithContext, aClipboardID, *aDoc);
    370  NS_ENSURE_SUCCESS(rv, rv);
    371 
    372  return rv;
    373 }
    374 
    375 nsresult nsCopySupport::ClearSelectionCache() {
    376  nsresult rv;
    377  nsCOMPtr<nsIClipboard> clipboard = do_GetService(kCClipboardCID, &rv);
    378  clipboard->EmptyClipboard(nsIClipboard::kSelectionCache);
    379  return rv;
    380 }
    381 
    382 /**
    383 * @param aAdditionalEncoderFlags flags of `nsIDocumentEncoder`.
    384 * @param aTransferable Needs to be not `nullptr`.
    385 */
    386 static nsresult EncodeDocumentWithContextAndCreateTransferable(
    387    Document& aDocument, Selection* aSelection,
    388    uint32_t aAdditionalEncoderFlags, nsITransferable** aTransferable) {
    389  NS_ENSURE_TRUE(aTransferable, NS_ERROR_NULL_POINTER);
    390 
    391  // Clear the output parameter for the transferable.
    392  *aTransferable = nullptr;
    393 
    394  EncodedDocumentWithContext encodedDocumentWithContext;
    395  nsresult rv =
    396      EncodeDocumentWithContext(aDocument, aSelection, aAdditionalEncoderFlags,
    397                                encodedDocumentWithContext);
    398  NS_ENSURE_SUCCESS(rv, rv);
    399 
    400  nsCOMPtr<nsITransferable> transferable;
    401  rv = CreateTransferable(encodedDocumentWithContext, aDocument, transferable);
    402  NS_ENSURE_SUCCESS(rv, rv);
    403 
    404  transferable.swap(*aTransferable);
    405  return rv;
    406 }
    407 
    408 nsresult nsCopySupport::GetTransferableForSelection(
    409    Selection* aSel, Document* aDoc, nsITransferable** aTransferable) {
    410  NS_ENSURE_TRUE(aDoc, NS_ERROR_NULL_POINTER);
    411  NS_ENSURE_TRUE(aTransferable, NS_ERROR_NULL_POINTER);
    412 
    413  const uint32_t additionalFlags =
    414      StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()
    415          ? nsIDocumentEncoder::SkipInvisibleContent |
    416                nsIDocumentEncoder::AllowCrossShadowBoundary
    417          : nsIDocumentEncoder::SkipInvisibleContent;
    418 
    419  return EncodeDocumentWithContextAndCreateTransferable(
    420      *aDoc, aSel, additionalFlags, aTransferable);
    421 }
    422 
    423 nsresult nsCopySupport::GetTransferableForNode(
    424    nsINode* aNode, Document* aDoc, nsITransferable** aTransferable) {
    425  NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
    426  NS_ENSURE_TRUE(aDoc, NS_ERROR_NULL_POINTER);
    427  NS_ENSURE_TRUE(aTransferable, NS_ERROR_NULL_POINTER);
    428 
    429  // Make a temporary selection with aNode in a single range.
    430  // XXX We should try to get rid of the Selection object here.
    431  // XXX bug 1245883
    432  RefPtr<Selection> selection = new Selection(SelectionType::eNormal, nullptr);
    433  RefPtr<nsRange> range = nsRange::Create(aNode);
    434  ErrorResult result;
    435  range->SelectNode(*aNode, result);
    436  if (NS_WARN_IF(result.Failed())) {
    437    return result.StealNSResult();
    438  }
    439  selection->AddRangeAndSelectFramesAndNotifyListenersInternal(*range, aDoc,
    440                                                               result);
    441  if (NS_WARN_IF(result.Failed())) {
    442    return result.StealNSResult();
    443  }
    444  // It's not the primary selection - so don't skip invisible content.
    445  uint32_t additionalFlags = 0;
    446  return EncodeDocumentWithContextAndCreateTransferable(
    447      *aDoc, selection, additionalFlags, aTransferable);
    448 }
    449 
    450 nsresult nsCopySupport::GetContents(const nsACString& aMimeType,
    451                                    uint32_t aFlags, Selection* aSel,
    452                                    Document* aDoc, nsAString& outdata) {
    453  nsCOMPtr<nsIDocumentEncoder> docEncoder =
    454      do_createDocumentEncoder(PromiseFlatCString(aMimeType).get());
    455  NS_ENSURE_TRUE(docEncoder, NS_ERROR_FAILURE);
    456 
    457  uint32_t flags = aFlags | nsIDocumentEncoder::SkipInvisibleContent;
    458 
    459  if (aMimeType.EqualsLiteral("text/plain"))
    460    flags |= nsIDocumentEncoder::OutputPreformatted;
    461 
    462  NS_ConvertASCIItoUTF16 unicodeMimeType(aMimeType);
    463 
    464  nsresult rv = docEncoder->Init(aDoc, unicodeMimeType, flags);
    465  if (NS_FAILED(rv)) return rv;
    466 
    467  if (aSel) {
    468    rv = docEncoder->SetSelection(aSel);
    469    if (NS_FAILED(rv)) return rv;
    470  }
    471 
    472  // encode the selection
    473  return docEncoder->EncodeToString(outdata);
    474 }
    475 
    476 nsresult nsCopySupport::ImageCopy(
    477    nsIImageLoadingContent* aImageElement, nsILoadContext* aLoadContext,
    478    int32_t aCopyFlags, mozilla::dom::WindowContext* aSettingWindowContext) {
    479  nsresult rv;
    480 
    481  nsCOMPtr<nsINode> imageNode = do_QueryInterface(aImageElement, &rv);
    482  NS_ENSURE_SUCCESS(rv, rv);
    483 
    484  // create a transferable for putting data on the Clipboard
    485  nsCOMPtr<nsITransferable> trans(do_CreateInstance(kCTransferableCID, &rv));
    486  NS_ENSURE_SUCCESS(rv, rv);
    487  trans->Init(aLoadContext);
    488  trans->SetDataPrincipal(imageNode->NodePrincipal());
    489 
    490  if (aCopyFlags & nsIDocumentViewerEdit::COPY_IMAGE_TEXT) {
    491    // get the location from the element
    492    nsCOMPtr<nsIURI> uri;
    493    rv = aImageElement->GetCurrentURI(getter_AddRefs(uri));
    494    NS_ENSURE_SUCCESS(rv, rv);
    495    NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE);
    496 
    497    nsAutoCString location;
    498    rv = uri->GetSpec(location);
    499    NS_ENSURE_SUCCESS(rv, rv);
    500 
    501    // append the string to the transferable
    502    rv = AppendString(trans, NS_ConvertUTF8toUTF16(location), kTextMime);
    503    NS_ENSURE_SUCCESS(rv, rv);
    504  }
    505 
    506  if (aCopyFlags & nsIDocumentViewerEdit::COPY_IMAGE_HTML) {
    507    // append HTML data to the transferable
    508    nsCOMPtr<nsINode> node(do_QueryInterface(aImageElement, &rv));
    509    NS_ENSURE_SUCCESS(rv, rv);
    510 
    511    rv = AppendDOMNode(trans, node);
    512    NS_ENSURE_SUCCESS(rv, rv);
    513  }
    514 
    515  if (aCopyFlags & nsIDocumentViewerEdit::COPY_IMAGE_DATA) {
    516    // get the image data and its request from the element
    517    nsCOMPtr<imgIRequest> imgRequest;
    518    nsCOMPtr<imgIContainer> image = nsContentUtils::GetImageFromContent(
    519        aImageElement, getter_AddRefs(imgRequest));
    520    NS_ENSURE_TRUE(image, NS_ERROR_FAILURE);
    521 
    522    if (imgRequest) {
    523      // Remember the referrer used for this image request.
    524      nsCOMPtr<nsIReferrerInfo> referrerInfo;
    525      imgRequest->GetReferrerInfo(getter_AddRefs(referrerInfo));
    526      trans->SetReferrerInfo(referrerInfo);
    527    }
    528 
    529 #ifdef XP_WIN
    530    if (StaticPrefs::clipboard_imageAsFile_enabled()) {
    531      rv = AppendImagePromise(trans, imgRequest, imageNode);
    532      NS_ENSURE_SUCCESS(rv, rv);
    533    }
    534 #endif
    535 
    536    // copy the image data onto the transferable
    537    rv = trans->SetTransferData(kNativeImageMime, image);
    538    NS_ENSURE_SUCCESS(rv, rv);
    539  }
    540 
    541  // get clipboard
    542  nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv));
    543  NS_ENSURE_SUCCESS(rv, rv);
    544 
    545  // check whether the system supports the selection clipboard or not.
    546  if (clipboard->IsClipboardTypeSupported(nsIClipboard::kSelectionClipboard)) {
    547    // put the transferable on the clipboard
    548    rv = clipboard->SetData(trans, nullptr, nsIClipboard::kSelectionClipboard,
    549                            aSettingWindowContext);
    550    NS_ENSURE_SUCCESS(rv, rv);
    551  }
    552 
    553  return clipboard->SetData(trans, nullptr, nsIClipboard::kGlobalClipboard,
    554                            aSettingWindowContext);
    555 }
    556 
    557 static nsresult AppendString(nsITransferable* aTransferable,
    558                             const nsAString& aString, const char* aFlavor) {
    559  nsresult rv;
    560 
    561  nsCOMPtr<nsISupportsString> data(
    562      do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv));
    563  NS_ENSURE_SUCCESS(rv, rv);
    564 
    565  rv = data->SetData(aString);
    566  NS_ENSURE_SUCCESS(rv, rv);
    567 
    568  rv = aTransferable->AddDataFlavor(aFlavor);
    569  NS_ENSURE_SUCCESS(rv, rv);
    570 
    571  return aTransferable->SetTransferData(aFlavor, data);
    572 }
    573 
    574 static nsresult AppendDOMNode(nsITransferable* aTransferable,
    575                              nsINode* aDOMNode) {
    576  nsresult rv;
    577 
    578  // serializer
    579  nsCOMPtr<nsIDocumentEncoder> docEncoder = do_createHTMLCopyEncoder();
    580 
    581  // get document for the encoder
    582  nsCOMPtr<Document> document = aDOMNode->OwnerDoc();
    583 
    584  // Note that XHTML is not counted as HTML here, because we can't copy it
    585  // properly (all the copy code for non-plaintext assumes using HTML
    586  // serializers and parsers is OK, and those mess up XHTML).
    587  NS_ENSURE_TRUE(document->IsHTMLDocument(), NS_OK);
    588 
    589  // init encoder with document and node
    590  rv = docEncoder->Init(document, NS_LITERAL_STRING_FROM_CSTRING(kHTMLMime),
    591                        nsIDocumentEncoder::OutputAbsoluteLinks |
    592                            nsIDocumentEncoder::OutputEncodeBasicEntities);
    593  NS_ENSURE_SUCCESS(rv, rv);
    594 
    595  rv = docEncoder->SetNode(aDOMNode);
    596  NS_ENSURE_SUCCESS(rv, rv);
    597 
    598  // serialize to string
    599  nsAutoString html, context, info;
    600  rv = docEncoder->EncodeToStringWithContext(context, info, html);
    601  NS_ENSURE_SUCCESS(rv, rv);
    602 
    603  // copy them to the transferable
    604  if (!html.IsEmpty()) {
    605    rv = AppendString(aTransferable, html, kHTMLMime);
    606    NS_ENSURE_SUCCESS(rv, rv);
    607  }
    608 
    609  if (!info.IsEmpty()) {
    610    rv = AppendString(aTransferable, info, kHTMLInfo);
    611    NS_ENSURE_SUCCESS(rv, rv);
    612  }
    613 
    614  // add a special flavor, even if we don't have html context data
    615  return AppendString(aTransferable, context, kHTMLContext);
    616 }
    617 
    618 #ifdef XP_WIN
    619 static nsresult AppendImagePromise(nsITransferable* aTransferable,
    620                                   imgIRequest* aImgRequest,
    621                                   nsINode* aImageNode) {
    622  nsresult rv;
    623 
    624  NS_ENSURE_TRUE(aImgRequest && aImageNode, NS_OK);
    625 
    626  bool isMultipart;
    627  rv = aImgRequest->GetMultipart(&isMultipart);
    628  NS_ENSURE_SUCCESS(rv, rv);
    629  if (isMultipart) {
    630    return NS_OK;
    631  }
    632 
    633  nsCOMPtr<nsIMIMEService> mimeService = do_GetService("@mozilla.org/mime;1");
    634  if (NS_WARN_IF(!mimeService)) {
    635    return NS_ERROR_FAILURE;
    636  }
    637 
    638  nsCOMPtr<nsIURI> imgUri;
    639  rv = aImgRequest->GetFinalURI(getter_AddRefs(imgUri));
    640  NS_ENSURE_SUCCESS(rv, rv);
    641 
    642  nsAutoCString spec;
    643  rv = imgUri->GetSpec(spec);
    644  NS_ENSURE_SUCCESS(rv, rv);
    645 
    646  // pass out the image source string
    647  nsString imageSourceString;
    648  CopyUTF8toUTF16(spec, imageSourceString);
    649 
    650  nsCString mimeType;
    651  rv = aImgRequest->GetMimeType(getter_Copies(mimeType));
    652  NS_ENSURE_SUCCESS(rv, rv);
    653 
    654  nsAutoCString fileName;
    655  rv = aImgRequest->GetFileName(fileName);
    656  NS_ENSURE_SUCCESS(rv, rv);
    657 
    658  nsAutoString validFileName = NS_ConvertUTF8toUTF16(fileName);
    659  mimeService->ValidateFileNameForSaving(
    660      validFileName, mimeType, nsIMIMEService::VALIDATE_DEFAULT, validFileName);
    661 
    662  rv = AppendString(aTransferable, imageSourceString, kFilePromiseURLMime);
    663  NS_ENSURE_SUCCESS(rv, rv);
    664 
    665  rv = AppendString(aTransferable, validFileName, kFilePromiseDestFilename);
    666  NS_ENSURE_SUCCESS(rv, rv);
    667 
    668  aTransferable->SetCookieJarSettings(
    669      aImageNode->OwnerDoc()->CookieJarSettings());
    670  aTransferable->SetContentPolicyType(nsIContentPolicy::TYPE_INTERNAL_IMAGE);
    671 
    672  // add the dataless file promise flavor
    673  return aTransferable->AddDataFlavor(kFilePromiseMime);
    674 }
    675 #endif  // XP_WIN
    676 
    677 already_AddRefed<Selection> nsCopySupport::GetSelectionForCopy(
    678    Document* aDocument) {
    679  PresShell* presShell = aDocument->GetPresShell();
    680  if (NS_WARN_IF(!presShell)) {
    681    return nullptr;
    682  }
    683 
    684  RefPtr<nsFrameSelection> frameSel = presShell->GetLastFocusedFrameSelection();
    685  if (NS_WARN_IF(!frameSel)) {
    686    return nullptr;
    687  }
    688 
    689  RefPtr<Selection> sel = &frameSel->NormalSelection();
    690  return sel.forget();
    691 }
    692 
    693 bool nsCopySupport::CanCopy(Document* aDocument) {
    694  if (!aDocument) {
    695    return false;
    696  }
    697 
    698  RefPtr<Selection> sel = GetSelectionForCopy(aDocument);
    699  return sel && !sel->IsCollapsed();
    700 }
    701 
    702 static bool IsInsideRuby(nsINode* aNode) {
    703  for (; aNode; aNode = aNode->GetParent()) {
    704    if (aNode->IsHTMLElement(nsGkAtoms::ruby)) {
    705      return true;
    706    }
    707  }
    708  return false;
    709 }
    710 
    711 static bool IsSelectionInsideRuby(Selection* aSelection) {
    712  uint32_t rangeCount = aSelection->RangeCount();
    713  for (auto i : IntegerRange(rangeCount)) {
    714    MOZ_ASSERT(aSelection->RangeCount() == rangeCount);
    715    const nsRange* range = aSelection->GetRangeAt(i);
    716    if (!IsInsideRuby(range->GetClosestCommonInclusiveAncestor())) {
    717      return false;
    718    }
    719  }
    720  return true;
    721 }
    722 
    723 static Element* GetElementOrNearestFlattenedTreeParentElement(nsINode* aNode) {
    724  if (!aNode->IsContent()) {
    725    return nullptr;
    726  }
    727  for (nsIContent* content = aNode->AsContent(); content;
    728       content = content->GetFlattenedTreeParent()) {
    729    if (content->IsElement()) {
    730      return content->AsElement();
    731    }
    732  }
    733  return nullptr;
    734 }
    735 
    736 /**
    737 * This class is used while processing clipboard paste event.
    738 */
    739 class MOZ_RAII AutoHandlingPasteEvent final {
    740 public:
    741  explicit AutoHandlingPasteEvent(
    742      nsGlobalWindowInner* aWindow, DataTransfer* aDataTransfer,
    743      const EventMessage& aEventMessage,
    744      const mozilla::Maybe<nsIClipboard::ClipboardType> aClipboardType) {
    745    MOZ_ASSERT(aDataTransfer);
    746    if (aWindow && aEventMessage == ePaste &&
    747        aClipboardType == Some(nsIClipboard::kGlobalClipboard)) {
    748      aWindow->SetCurrentPasteDataTransfer(aDataTransfer);
    749      mInnerWindow = aWindow;
    750    }
    751  }
    752 
    753  ~AutoHandlingPasteEvent() {
    754    if (mInnerWindow) {
    755      mInnerWindow->SetCurrentPasteDataTransfer(nullptr);
    756    }
    757  }
    758 
    759 private:
    760  RefPtr<nsGlobalWindowInner> mInnerWindow;
    761 };
    762 
    763 bool nsCopySupport::FireClipboardEvent(
    764    EventMessage aEventMessage,
    765    mozilla::Maybe<nsIClipboard::ClipboardType> aClipboardType,
    766    PresShell* aPresShell, Selection* aSelection, DataTransfer* aDataTransfer,
    767    bool* aActionTaken) {
    768  if (aActionTaken) {
    769    *aActionTaken = false;
    770  }
    771 
    772  EventMessage originalEventMessage = aEventMessage;
    773  if (originalEventMessage == ePasteNoFormatting) {
    774    originalEventMessage = ePaste;
    775  }
    776 
    777  NS_ASSERTION(originalEventMessage == eCut || originalEventMessage == eCopy ||
    778                   originalEventMessage == ePaste,
    779               "Invalid clipboard event type");
    780 
    781  MOZ_ASSERT_IF(originalEventMessage != ePaste, aClipboardType.isSome());
    782 
    783  RefPtr<PresShell> presShell = aPresShell;
    784  if (!presShell) {
    785    return false;
    786  }
    787 
    788  nsCOMPtr<Document> doc = presShell->GetDocument();
    789  if (!doc) return false;
    790 
    791  nsCOMPtr<nsPIDOMWindowOuter> piWindow = doc->GetWindow();
    792  if (!piWindow) return false;
    793 
    794  // Event target of clipboard events should be an element node which
    795  // contains selection start container.
    796  RefPtr<Element> targetElement;
    797 
    798  // If a selection was not supplied, try to find it.
    799  RefPtr<Selection> sel = aSelection;
    800  if (!sel) {
    801    sel = GetSelectionForCopy(doc);
    802  }
    803 
    804  // Retrieve the event target node from the start of the selection.
    805  if (sel) {
    806    const nsRange* range = sel->GetRangeAt(0);
    807    if (range) {
    808      targetElement = GetElementOrNearestFlattenedTreeParentElement(
    809          range->GetStartContainer());
    810    }
    811  }
    812 
    813  // If there is no selection ranges, use the <body> or <frameset> element.
    814  if (!targetElement) {
    815    targetElement = doc->GetBody();
    816    if (!targetElement) {
    817      return false;
    818    }
    819  }
    820 
    821  // It seems to be unsafe to fire an event handler during reflow (bug 393696)
    822  if (!nsContentUtils::IsSafeToRunScript()) {
    823    nsContentUtils::WarnScriptWasIgnored(doc);
    824    return false;
    825  }
    826 
    827  BrowsingContext* bc = piWindow->GetBrowsingContext();
    828  const bool chromeShell = bc && bc->IsChrome();
    829 
    830  // next, fire the cut, copy or paste event
    831  bool doDefault = true;
    832  RefPtr<DataTransfer> clipboardData;
    833  if (chromeShell || StaticPrefs::dom_event_clipboardevents_enabled()) {
    834    MOZ_ASSERT_IF(aDataTransfer,
    835                  aDataTransfer->GetParentObject() == doc->GetScopeObject());
    836    MOZ_ASSERT_IF(aDataTransfer, (aDataTransfer->GetEventMessage() == ePaste) &&
    837                                     (aEventMessage == ePaste ||
    838                                      aEventMessage == ePasteNoFormatting));
    839    MOZ_ASSERT_IF(aDataTransfer,
    840                  aDataTransfer->ClipboardType() == aClipboardType);
    841    clipboardData = aDataTransfer
    842                        ? RefPtr<DataTransfer>(aDataTransfer)
    843                        : MakeRefPtr<DataTransfer>(
    844                              doc->GetScopeObject(), aEventMessage,
    845                              originalEventMessage == ePaste, aClipboardType);
    846 
    847    nsEventStatus status = nsEventStatus_eIgnore;
    848    InternalClipboardEvent evt(true, originalEventMessage);
    849    evt.mClipboardData = clipboardData;
    850 
    851    {
    852      AutoHandlingPasteEvent autoHandlingPasteEvent(
    853          nsGlobalWindowInner::Cast(doc->GetInnerWindow()), clipboardData,
    854          aEventMessage, aClipboardType);
    855 
    856      RefPtr<nsPresContext> presContext = presShell->GetPresContext();
    857      EventDispatcher::Dispatch(targetElement, presContext, &evt, nullptr,
    858                                &status);
    859    }
    860 
    861    // If the event was cancelled, don't do the clipboard operation
    862    doDefault = (status != nsEventStatus_eConsumeNoDefault);
    863  }
    864 
    865  // When this function exits, the event dispatch is over. We want to disconnect
    866  // our DataTransfer, which means setting its mode to `Protected` and clearing
    867  // all stored data, before we return.
    868  auto clearAfter = MakeScopeExit([&] {
    869    if (clipboardData && !aDataTransfer) {
    870      clipboardData->Disconnect();
    871 
    872      // NOTE: Disconnect may not actually clear the DataTransfer if the
    873      // dom.events.dataTransfer.protected.enabled pref is not on, so we make
    874      // sure we clear here, as not clearing could provide the DataTransfer
    875      // access to information from the system clipboard at an arbitrary point
    876      // in the future.
    877      if (originalEventMessage == ePaste) {
    878        clipboardData->ClearAll();
    879      }
    880    }
    881  });
    882 
    883  // No need to do anything special during a paste. Either an event listener
    884  // took care of it and cancelled the event, or the caller will handle it.
    885  // Return true to indicate that the event wasn't cancelled.
    886  if (originalEventMessage == ePaste) {
    887    if (aActionTaken) {
    888      *aActionTaken = true;
    889    }
    890    return doDefault;
    891  }
    892 
    893  // Update the presentation in case the event handler modified the selection,
    894  // see bug 602231.
    895  presShell->FlushPendingNotifications(FlushType::Frames);
    896  if (presShell->IsDestroying()) {
    897    return false;
    898  }
    899 
    900  // if the event was not cancelled, do the default copy. If the event was
    901  // cancelled, use the data added to the data transfer and copy that instead.
    902  uint32_t count = 0;
    903  if (doDefault) {
    904    // find the focused node
    905    nsIContent* sourceContent = targetElement.get();
    906    if (targetElement->IsInNativeAnonymousSubtree()) {
    907      sourceContent = targetElement->FindFirstNonChromeOnlyAccessContent();
    908    }
    909 
    910    // If it's <input type="password"> and there is no unmasked range or
    911    // there is unmasked range but it's collapsed or it'll be masked
    912    // automatically, the selected password shouldn't be copied into the
    913    // clipboard.
    914    if (RefPtr<HTMLInputElement> inputElement =
    915            HTMLInputElement::FromNodeOrNull(sourceContent)) {
    916      if (TextEditor* textEditor = inputElement->GetTextEditor()) {
    917        if (textEditor->IsPasswordEditor() &&
    918            !textEditor->IsCopyToClipboardAllowed()) {
    919          return false;
    920        }
    921      }
    922    }
    923 
    924    // when cutting non-editable content, do nothing
    925    // XXX this is probably the wrong editable flag to check
    926    if (originalEventMessage != eCut || targetElement->IsEditable()) {
    927      // get the data from the selection if any
    928      if (sel->AreNormalAndCrossShadowBoundaryRangesCollapsed()) {
    929        if (aActionTaken) {
    930          *aActionTaken = true;
    931        }
    932        return false;
    933      }
    934      // XXX Code which decides whether we should copy text with ruby
    935      // annotation is currenct depending on whether each range of the
    936      // selection is inside a same ruby container. But we really should
    937      // expose the full functionality in browser. See bug 1130891.
    938      bool withRubyAnnotation = IsSelectionInsideRuby(sel);
    939      nsresult rv = EncodeDocumentWithContextAndPutToClipboard(
    940          sel, doc, *aClipboardType, withRubyAnnotation);
    941      if (NS_FAILED(rv)) {
    942        return false;
    943      }
    944    } else {
    945      return false;
    946    }
    947  } else if (clipboardData) {
    948    // check to see if any data was put on the data transfer.
    949    count = clipboardData->MozItemCount();
    950    if (count) {
    951      nsCOMPtr<nsIClipboard> clipboard(
    952          do_GetService("@mozilla.org/widget/clipboard;1"));
    953      NS_ENSURE_TRUE(clipboard, false);
    954 
    955      nsCOMPtr<nsITransferable> transferable =
    956          clipboardData->GetTransferable(0, doc->GetLoadContext());
    957 
    958      NS_ENSURE_TRUE(transferable, false);
    959 
    960      // put the transferable on the clipboard
    961      WindowContext* settingWindowContext = nullptr;
    962      if (aPresShell && aPresShell->GetDocument()) {
    963        settingWindowContext = aPresShell->GetDocument()->GetWindowContext();
    964      }
    965      nsresult rv = clipboard->SetData(transferable, nullptr, *aClipboardType,
    966                                       settingWindowContext);
    967      if (NS_FAILED(rv)) {
    968        return false;
    969      }
    970    }
    971  }
    972 
    973  // Now that we have copied, update the clipboard commands. This should have
    974  // the effect of updating the enabled state of the paste menu item.
    975  if (doDefault || count) {
    976    piWindow->UpdateCommands(u"clipboard"_ns);
    977    if (aPresShell && aPresShell->GetDocument()) {
    978      // Record that a copy to the clipboard was triggered by JS code
    979      aPresShell->GetDocument()->SetClipboardCopyTriggered();
    980    }
    981  }
    982 
    983  if (aActionTaken) {
    984    *aActionTaken = true;
    985  }
    986  return doDefault;
    987 }