tor-browser

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

TextEditorDataTransfer.cpp (9448B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #include "TextEditor.h"
      7 
      8 #include "EditorUtils.h"
      9 #include "HTMLEditor.h"
     10 #include "SelectionState.h"
     11 
     12 #include "mozilla/MouseEvents.h"
     13 #include "mozilla/dom/ContentParent.h"
     14 #include "mozilla/dom/DataTransfer.h"
     15 #include "mozilla/dom/Document.h"
     16 #include "mozilla/dom/DocumentInlines.h"
     17 #include "mozilla/dom/Selection.h"
     18 
     19 #include "nsAString.h"
     20 #include "nsCOMPtr.h"
     21 #include "nsContentUtils.h"
     22 #include "nsDebug.h"
     23 #include "nsError.h"
     24 #include "nsIClipboard.h"
     25 #include "nsIContent.h"
     26 #include "nsIDragService.h"
     27 #include "nsIDragSession.h"
     28 #include "nsIPrincipal.h"
     29 #include "nsIFormControl.h"
     30 #include "nsISupportsPrimitives.h"
     31 #include "nsITransferable.h"
     32 #include "nsIVariant.h"
     33 #include "nsLiteralString.h"
     34 #include "nsRange.h"
     35 #include "nsServiceManagerUtils.h"
     36 #include "nsString.h"
     37 #include "nsXPCOM.h"
     38 #include "nscore.h"
     39 
     40 namespace mozilla {
     41 
     42 using namespace dom;
     43 
     44 nsresult TextEditor::InsertTextFromTransferable(
     45    nsITransferable* aTransferable) {
     46  MOZ_ASSERT(IsEditActionDataAvailable());
     47  MOZ_ASSERT(IsTextEditor());
     48 
     49  nsAutoCString bestFlavor;
     50  nsCOMPtr<nsISupports> genericDataObj;
     51  nsresult rv = aTransferable->GetAnyTransferData(
     52      bestFlavor, getter_AddRefs(genericDataObj));
     53  NS_WARNING_ASSERTION(
     54      NS_SUCCEEDED(rv),
     55      "nsITransferable::GetAnyDataTransferData() failed, but ignored");
     56  if (NS_SUCCEEDED(rv) && (bestFlavor.EqualsLiteral(kTextMime) ||
     57                           bestFlavor.EqualsLiteral(kMozTextInternal))) {
     58    AutoTransactionsConserveSelection dontChangeMySelection(*this);
     59 
     60    nsAutoString stuffToPaste;
     61    if (nsCOMPtr<nsISupportsString> text = do_QueryInterface(genericDataObj)) {
     62      text->GetData(stuffToPaste);
     63    }
     64    MOZ_ASSERT(GetEditAction() == EditAction::ePaste);
     65    // Use native line breaks for compatibility with Chrome.
     66    // XXX Although, somebody has already converted native line breaks to
     67    //     XP line breaks.
     68    UpdateEditActionData(stuffToPaste);
     69 
     70    nsresult rv = MaybeDispatchBeforeInputEvent();
     71    if (NS_FAILED(rv)) {
     72      NS_WARNING_ASSERTION(
     73          rv == NS_ERROR_EDITOR_ACTION_CANCELED,
     74          "EditorBase::MaybeDispatchBeforeInputEvent() failed");
     75      return rv;
     76    }
     77 
     78    if (!stuffToPaste.IsEmpty()) {
     79      // Sanitize possible carriage returns in the string to be inserted
     80      nsContentUtils::PlatformToDOMLineBreaks(stuffToPaste);
     81 
     82      AutoPlaceholderBatch treatAsOneTransaction(
     83          *this, ScrollSelectionIntoView::Yes, __FUNCTION__);
     84      nsresult rv =
     85          InsertTextAsSubAction(stuffToPaste, InsertTextFor::NormalText);
     86      if (NS_FAILED(rv)) {
     87        NS_WARNING("EditorBase::InsertTextAsSubAction() failed");
     88        return rv;
     89      }
     90    }
     91  }
     92 
     93  // Try to scroll the selection into view if the paste/drop succeeded
     94  rv = ScrollSelectionFocusIntoView();
     95  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
     96                       "EditorBase::ScrollSelectionFocusIntoView() failed");
     97  return rv;
     98 }
     99 
    100 nsresult TextEditor::InsertDroppedDataTransferAsAction(
    101    AutoEditActionDataSetter& aEditActionData, DataTransfer& aDataTransfer,
    102    const EditorDOMPoint& aDroppedAt, nsIPrincipal* aSourcePrincipal) {
    103  MOZ_ASSERT(aEditActionData.GetEditAction() == EditAction::eDrop);
    104  MOZ_ASSERT(GetEditAction() == EditAction::eDrop);
    105  MOZ_ASSERT(aDroppedAt.IsSet());
    106  MOZ_ASSERT(aDataTransfer.MozItemCount() > 0);
    107 
    108  uint32_t numItems = aDataTransfer.MozItemCount();
    109  AutoTArray<nsString, 5> textArray;
    110  textArray.SetCapacity(numItems);
    111  uint32_t textLength = 0;
    112  for (uint32_t i = 0; i < numItems; ++i) {
    113    nsCOMPtr<nsIVariant> data;
    114    aDataTransfer.GetDataAtNoSecurityCheck(u"text/plain"_ns, i,
    115                                           getter_AddRefs(data));
    116    if (!data) {
    117      continue;
    118    }
    119    // Use nsString to avoid copying its storage to textArray.
    120    nsString insertText;
    121    data->GetAsAString(insertText);
    122    if (insertText.IsEmpty()) {
    123      continue;
    124    }
    125    textArray.AppendElement(insertText);
    126    textLength += insertText.Length();
    127  }
    128  // Use nsString to avoid copying its storage to aEditActionData.
    129  nsString data;
    130  data.SetCapacity(textLength);
    131  // Join the text array from end to start because we insert each items
    132  // in the aDataTransfer at same point from start to end.  Although I
    133  // don't know whether this is intentional behavior.
    134  for (nsString& text : Reversed(textArray)) {
    135    data.Append(text);
    136  }
    137  // Use native line breaks for compatibility with Chrome.
    138  // XXX Although, somebody has already converted native line breaks to
    139  //     XP line breaks.
    140  aEditActionData.SetData(data);
    141 
    142  nsresult rv = aEditActionData.MaybeDispatchBeforeInputEvent();
    143  if (NS_FAILED(rv)) {
    144    NS_WARNING_ASSERTION(rv == NS_ERROR_EDITOR_ACTION_CANCELED,
    145                         "MaybeDispatchBeforeInputEvent() failed");
    146    return rv;
    147  }
    148 
    149  // Then, insert the text.  Note that we shouldn't need to walk the array
    150  // anymore because nobody should listen to mutation events of anonymous
    151  // text node in <input>/<textarea>.
    152  nsContentUtils::PlatformToDOMLineBreaks(data);
    153  rv = InsertTextAt(data, aDroppedAt, DeleteSelectedContent::No);
    154  if (NS_WARN_IF(Destroyed())) {
    155    return NS_ERROR_EDITOR_DESTROYED;
    156  }
    157  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    158                       "EditorBase::InsertTextAt(DeleteSelectedContent::No) "
    159                       "failed, but ignored");
    160  return rv;
    161 }
    162 
    163 nsresult TextEditor::HandlePaste(AutoEditActionDataSetter& aEditActionData,
    164                                 nsIClipboard::ClipboardType aClipboardType,
    165                                 DataTransfer* aDataTransfer) {
    166  if (NS_WARN_IF(!GetDocument())) {
    167    return NS_OK;
    168  }
    169 
    170  // The data will be initialized in InsertTextFromTransferable() if we're not
    171  // an HTMLEditor.  Therefore, we cannot dispatch "beforeinput" here.
    172 
    173  // Get Clipboard Service
    174  nsresult rv;
    175  nsCOMPtr<nsIClipboard> clipboard =
    176      do_GetService("@mozilla.org/widget/clipboard;1", &rv);
    177  if (NS_FAILED(rv)) {
    178    NS_WARNING("Failed to get nsIClipboard service");
    179    return rv;
    180  }
    181 
    182  // Get the nsITransferable interface for getting the data from the clipboard
    183  Result<nsCOMPtr<nsITransferable>, nsresult> maybeTransferable =
    184      EditorUtils::CreateTransferableForPlainText(*GetDocument());
    185  if (maybeTransferable.isErr()) {
    186    NS_WARNING("EditorUtils::CreateTransferableForPlainText() failed");
    187    return maybeTransferable.unwrapErr();
    188  }
    189  nsCOMPtr<nsITransferable> transferable(maybeTransferable.unwrap());
    190  if (NS_WARN_IF(!transferable)) {
    191    NS_WARNING(
    192        "EditorUtils::CreateTransferableForPlainText() returned nullptr, but "
    193        "ignored");
    194    return NS_OK;  // XXX Why?
    195  }
    196  // Get the Data from the clipboard.
    197  rv = GetDataFromDataTransferOrClipboard(aDataTransfer, transferable,
    198                                          aClipboardType);
    199  if (NS_FAILED(rv)) {
    200    NS_WARNING("EditorBase::GetDataFromDataTransferOrClipboard() failed");
    201    return rv;
    202  }
    203 
    204  // XXX Why don't we check this first?
    205  if (!IsModifiable()) {
    206    return NS_OK;
    207  }
    208  rv = InsertTextFromTransferable(transferable);
    209  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    210                       "TextEditor::InsertTextFromTransferable() failed");
    211  return rv;
    212 }
    213 
    214 nsresult TextEditor::HandlePasteTransferable(
    215    AutoEditActionDataSetter& aEditActionData, nsITransferable& aTransferable) {
    216  if (!IsModifiable()) {
    217    return NS_OK;
    218  }
    219 
    220  // FYI: The data of beforeinput will be initialized in
    221  // InsertTextFromTransferable().  Therefore, here does not touch
    222  // aEditActionData.
    223  nsresult rv = InsertTextFromTransferable(&aTransferable);
    224  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    225                       "TextEditor::InsertTextFromTransferable() failed");
    226  return rv;
    227 }
    228 
    229 bool TextEditor::CanPaste(nsIClipboard::ClipboardType aClipboardType) const {
    230  if (AreClipboardCommandsUnconditionallyEnabled()) {
    231    return true;
    232  }
    233 
    234  // can't paste if readonly
    235  if (!IsModifiable()) {
    236    return false;
    237  }
    238 
    239  nsresult rv;
    240  nsCOMPtr<nsIClipboard> clipboard(
    241      do_GetService("@mozilla.org/widget/clipboard;1", &rv));
    242  if (NS_FAILED(rv)) {
    243    NS_WARNING("Failed to get nsIClipboard service");
    244    return false;
    245  }
    246 
    247  // the flavors that we can deal with
    248  AutoTArray<nsCString, 1> textEditorFlavors = {nsDependentCString(kTextMime)};
    249 
    250  bool haveFlavors;
    251  rv = clipboard->HasDataMatchingFlavors(textEditorFlavors, aClipboardType,
    252                                         &haveFlavors);
    253  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    254                       "nsIClipboard::HasDataMatchingFlavors() failed");
    255  return NS_SUCCEEDED(rv) && haveFlavors;
    256 }
    257 
    258 bool TextEditor::CanPasteTransferable(nsITransferable* aTransferable) {
    259  // can't paste if readonly
    260  if (!IsModifiable()) {
    261    return false;
    262  }
    263 
    264  // If |aTransferable| is null, assume that a paste will succeed.
    265  if (!aTransferable) {
    266    return true;
    267  }
    268 
    269  nsCOMPtr<nsISupports> data;
    270  nsresult rv = aTransferable->GetTransferData(kTextMime, getter_AddRefs(data));
    271  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    272                       "nsITransferable::GetTransferData(kTextMime) failed");
    273  return NS_SUCCEEDED(rv) && data;
    274 }
    275 
    276 }  // namespace mozilla