tor-browser

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

TextEditor.cpp (49760B)


      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 <algorithm>
      9 
     10 #include "EditAction.h"
     11 #include "EditAggregateTransaction.h"
     12 #include "EditorDOMPoint.h"
     13 #include "HTMLEditor.h"
     14 #include "HTMLEditUtils.h"
     15 #include "InternetCiter.h"
     16 #include "PlaceholderTransaction.h"
     17 #include "gfxFontUtils.h"
     18 
     19 #include "mozilla/dom/DocumentInlines.h"
     20 #include "mozilla/Assertions.h"
     21 #include "mozilla/ContentIterator.h"
     22 #include "mozilla/IMEStateManager.h"
     23 #include "mozilla/Logging.h"
     24 #include "mozilla/LookAndFeel.h"
     25 #include "mozilla/mozalloc.h"
     26 #include "mozilla/Preferences.h"
     27 #include "mozilla/PresShell.h"
     28 #include "mozilla/StaticPrefs_dom.h"
     29 #include "mozilla/StaticPrefs_editor.h"
     30 #include "mozilla/TextComposition.h"
     31 #include "mozilla/TextEvents.h"
     32 #include "mozilla/TextServicesDocument.h"
     33 #include "mozilla/Try.h"
     34 #include "mozilla/dom/CharacterDataBuffer.h"
     35 #include "mozilla/dom/Event.h"
     36 #include "mozilla/dom/Element.h"
     37 #include "mozilla/dom/Selection.h"
     38 #include "mozilla/dom/StaticRange.h"
     39 
     40 #include "nsAString.h"
     41 #include "nsCRT.h"
     42 #include "nsCaret.h"
     43 #include "nsCharTraits.h"
     44 #include "nsComponentManagerUtils.h"
     45 #include "nsContentList.h"
     46 #include "nsDebug.h"
     47 #include "nsDependentSubstring.h"
     48 #include "nsError.h"
     49 #include "nsFocusManager.h"
     50 #include "nsGkAtoms.h"
     51 #include "nsIContent.h"
     52 #include "nsINode.h"
     53 #include "nsIPrincipal.h"
     54 #include "nsISelectionController.h"
     55 #include "nsISupportsPrimitives.h"
     56 #include "nsITransferable.h"
     57 #include "nsIWeakReferenceUtils.h"
     58 #include "nsNameSpaceManager.h"
     59 #include "nsLiteralString.h"
     60 #include "nsPresContext.h"
     61 #include "nsReadableUtils.h"
     62 #include "nsServiceManagerUtils.h"
     63 #include "nsString.h"
     64 #include "nsStringFwd.h"
     65 #include "nsTextNode.h"
     66 #include "nsUnicharUtils.h"
     67 #include "nsXPCOM.h"
     68 
     69 class nsIOutputStream;
     70 class nsISupports;
     71 
     72 namespace mozilla {
     73 
     74 // This logs the important things for the lifecycle of the TextEditor.
     75 LazyLogModule gTextEditorLog("TextEditor");
     76 
     77 static void LogOrWarn(const TextEditor* aTextEditor, LazyLogModule& aLog,
     78                      LogLevel aLogLevel, const char* aStr) {
     79 #ifdef DEBUG
     80  if (MOZ_LOG_TEST(aLog, aLogLevel)) {
     81    MOZ_LOG(aLog, aLogLevel, ("%p: %s", aTextEditor, aStr));
     82  } else {
     83    NS_WARNING(aStr);
     84  }
     85 #else
     86  MOZ_LOG(aLog, aLogLevel, ("%p: %s", aTextEditor, aStr));
     87 #endif
     88 }
     89 
     90 using namespace dom;
     91 
     92 using LeafNodeType = HTMLEditUtils::LeafNodeType;
     93 using LeafNodeTypes = HTMLEditUtils::LeafNodeTypes;
     94 
     95 template EditorDOMPoint TextEditor::FindBetterInsertionPoint(
     96    const EditorDOMPoint& aPoint) const;
     97 template EditorRawDOMPoint TextEditor::FindBetterInsertionPoint(
     98    const EditorRawDOMPoint& aPoint) const;
     99 
    100 TextEditor::TextEditor() : EditorBase(EditorBase::EditorType::Text) {
    101  // printf("Size of TextEditor: %zu\n", sizeof(TextEditor));
    102  static_assert(
    103      sizeof(TextEditor) <= 512,
    104      "TextEditor instance should be allocatable in the quantum class bins");
    105  MOZ_LOG(gTextEditorLog, LogLevel::Info,
    106          ("%p: New instance is created", this));
    107 }
    108 
    109 TextEditor::~TextEditor() {
    110  // Remove event listeners. Note that if we had an HTML editor,
    111  //  it installed its own instead of these
    112  RemoveEventListeners();
    113 
    114  MOZ_LOG(gTextEditorLog, LogLevel::Info, ("%p: Deleted", this));
    115 }
    116 
    117 NS_IMPL_CYCLE_COLLECTION_CLASS(TextEditor)
    118 
    119 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(TextEditor, EditorBase)
    120  if (tmp->mPasswordMaskData) {
    121    tmp->mPasswordMaskData->CancelTimer(PasswordMaskData::ReleaseTimer::No);
    122    NS_IMPL_CYCLE_COLLECTION_UNLINK(mPasswordMaskData->mTimer)
    123  }
    124 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    125 
    126 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(TextEditor, EditorBase)
    127  if (tmp->mPasswordMaskData) {
    128    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPasswordMaskData->mTimer)
    129  }
    130 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    131 
    132 NS_IMPL_ADDREF_INHERITED(TextEditor, EditorBase)
    133 NS_IMPL_RELEASE_INHERITED(TextEditor, EditorBase)
    134 
    135 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TextEditor)
    136  NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
    137  NS_INTERFACE_MAP_ENTRY(nsINamed)
    138 NS_INTERFACE_MAP_END_INHERITING(EditorBase)
    139 
    140 NS_IMETHODIMP TextEditor::EndOfDocument() {
    141  AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
    142  if (NS_WARN_IF(!editActionData.CanHandle())) {
    143    return NS_ERROR_NOT_INITIALIZED;
    144  }
    145  nsresult rv = CollapseSelectionToEndOfTextNode();
    146  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    147                       "TextEditor::CollapseSelectionToEndOfTextNode() failed");
    148  // This is low level API for embedders and chrome script so that we can return
    149  // raw error code here.
    150  return rv;
    151 }
    152 
    153 nsresult TextEditor::CollapseSelectionToEndOfTextNode() {
    154  MOZ_ASSERT(IsEditActionDataAvailable());
    155 
    156  Element* anonymousDivElement = GetRoot();
    157  if (NS_WARN_IF(!anonymousDivElement)) {
    158    return NS_ERROR_NULL_POINTER;
    159  }
    160 
    161  RefPtr<Text> textNode =
    162      Text::FromNodeOrNull(anonymousDivElement->GetFirstChild());
    163  MOZ_ASSERT(textNode);
    164  nsresult rv = CollapseSelectionToEndOf(*textNode);
    165  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    166                       "EditorBase::CollapseSelectionToEndOf() failed");
    167  return rv;
    168 }
    169 
    170 nsresult TextEditor::Init(Document& aDocument, Element& aAnonymousDivElement,
    171                          nsISelectionController& aSelectionController,
    172                          uint32_t aFlags,
    173                          UniquePtr<PasswordMaskData>&& aPasswordMaskData) {
    174  MOZ_ASSERT(!mInitSucceeded,
    175             "TextEditor::Init() called again without calling PreDestroy()?");
    176  MOZ_ASSERT(!(aFlags & nsIEditor::eEditorPasswordMask) == !aPasswordMaskData);
    177 
    178  MOZ_LOG(gTextEditorLog, LogLevel::Info,
    179          ("%p: Init(aDocument=%p, aAnonymousDivElement=%s, "
    180           "aSelectionController=%p, aPasswordMaskData=%p)",
    181           this, &aDocument, ToString(RefPtr{&aAnonymousDivElement}).c_str(),
    182           &aSelectionController, aPasswordMaskData.get()));
    183 
    184  mPasswordMaskData = std::move(aPasswordMaskData);
    185 
    186  // Init the base editor
    187  nsresult rv = InitInternal(aDocument, &aAnonymousDivElement,
    188                             aSelectionController, aFlags);
    189  if (NS_FAILED(rv)) {
    190    LogOrWarn(this, gTextEditorLog, LogLevel::Error,
    191              "EditorBase::InitInternal() failed");
    192    return rv;
    193  }
    194 
    195  AutoEditActionDataSetter editActionData(*this, EditAction::eInitializing);
    196  if (MOZ_UNLIKELY(!editActionData.CanHandle())) {
    197    LogOrWarn(this, gTextEditorLog, LogLevel::Error,
    198              "AutoEditActionDataSetter::CanHandle() failed");
    199    return NS_ERROR_FAILURE;
    200  }
    201 
    202  // We set the initialized state here rather than at the end of the function,
    203  // since InitEditorContentAndSelection() can perform some transactions
    204  // and can warn if mInitSucceeded is still false.
    205  MOZ_ASSERT(!mInitSucceeded, "TextEditor::Init() shouldn't be nested");
    206  mInitSucceeded = true;
    207  editActionData.OnEditorInitialized();
    208 
    209  rv = InitEditorContentAndSelection();
    210  if (NS_FAILED(rv)) {
    211    LogOrWarn(this, gTextEditorLog, LogLevel::Error,
    212              "TextEditor::InitEditorContentAndSelection() failed");
    213    // XXX Shouldn't we expose `NS_ERROR_EDITOR_DESTROYED` even though this
    214    //     is a public method?
    215    mInitSucceeded = false;
    216    editActionData.OnEditorDestroy();
    217    return EditorBase::ToGenericNSResult(rv);
    218  }
    219 
    220  // Throw away the old transaction manager if this is not the first time that
    221  // we're initializing the editor.
    222  ClearUndoRedo();
    223  EnableUndoRedo();
    224  return NS_OK;
    225 }
    226 
    227 nsresult TextEditor::InitEditorContentAndSelection() {
    228  MOZ_ASSERT(IsEditActionDataAvailable());
    229 
    230  MOZ_TRY(EnsureEmptyTextFirstChild());
    231 
    232  // If the selection hasn't been set up yet, set it up collapsed to the end of
    233  // our editable content.
    234  if (!SelectionRef().RangeCount()) {
    235    nsresult rv = CollapseSelectionToEndOfTextNode();
    236    if (NS_FAILED(rv)) {
    237      LogOrWarn(this, gTextEditorLog, LogLevel::Error,
    238                "EditorBase::CollapseSelectionToEndOfTextNode() failed");
    239      return rv;
    240    }
    241  }
    242 
    243  if (!IsSingleLineEditor()) {
    244    nsresult rv = EnsurePaddingBRElementInMultilineEditor();
    245    if (NS_FAILED(rv)) {
    246      LogOrWarn(this, gTextEditorLog, LogLevel::Error,
    247                "EditorBase::EnsurePaddingBRElementInMultilineEditor() failed");
    248      return rv;
    249    }
    250  }
    251 
    252  return NS_OK;
    253 }
    254 
    255 nsresult TextEditor::PostCreate() {
    256  MOZ_LOG(gTextEditorLog, LogLevel::Info,
    257          ("%p: PostCreate(), mDidPostCreate=%s", this,
    258           TrueOrFalse(mDidPostCreate)));
    259 
    260  AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
    261  if (MOZ_UNLIKELY(!editActionData.CanHandle())) {
    262    LogOrWarn(this, gTextEditorLog, LogLevel::Error,
    263              "AutoEditActionDataSetter::CanHandle() failed");
    264    return NS_ERROR_NOT_INITIALIZED;
    265  }
    266 
    267  nsresult rv = PostCreateInternal();
    268 
    269  // Restore unmasked range if there is.
    270  if (IsPasswordEditor() && !IsAllMasked()) {
    271    DebugOnly<nsresult> rvIgnored =
    272        SetUnmaskRangeAndNotify(UnmaskedStart(), UnmaskedLength());
    273    NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
    274                         "TextEditor::SetUnmaskRangeAndNotify() failed to "
    275                         "restore unmasked range, but ignored");
    276  }
    277 
    278  if (NS_FAILED(rv)) {
    279    LogOrWarn(this, gTextEditorLog, LogLevel::Error,
    280              "EditorBase::PostCreateInternal() failed");
    281    return rv;
    282  }
    283 
    284  return NS_OK;
    285 }
    286 
    287 UniquePtr<PasswordMaskData> TextEditor::PreDestroy() {
    288  MOZ_LOG(gTextEditorLog, LogLevel::Info,
    289          ("%p: PreDestroy() mDidPreDestroy=%s", this,
    290           TrueOrFalse(mDidPreDestroy)));
    291 
    292  if (mDidPreDestroy) {
    293    return nullptr;
    294  }
    295 
    296  UniquePtr<PasswordMaskData> passwordMaskData = std::move(mPasswordMaskData);
    297  if (passwordMaskData) {
    298    // Disable auto-masking timer since nobody can catch the notification
    299    // from the timer and canceling the unmasking.
    300    passwordMaskData->CancelTimer(PasswordMaskData::ReleaseTimer::Yes);
    301    // Similary, keeping preventing echoing password temporarily across
    302    // TextEditor instances is hard.  So, we should forget it.
    303    passwordMaskData->mEchoingPasswordPrevented = false;
    304  }
    305 
    306  PreDestroyInternal();
    307 
    308  return passwordMaskData;
    309 }
    310 
    311 Result<widget::IMEState, nsresult> TextEditor::GetPreferredIMEState() const {
    312  using IMEState = widget::IMEState;
    313  using IMEEnabled = widget::IMEEnabled;
    314 
    315  if (IsReadonly()) {
    316    return IMEState{IMEEnabled::Disabled, IMEState::DONT_CHANGE_OPEN_STATE};
    317  }
    318 
    319  Element* const textControlElement = GetExposedRoot();
    320  if (NS_WARN_IF(!textControlElement)) {
    321    return Err(NS_ERROR_FAILURE);
    322  }
    323  MOZ_ASSERT(textControlElement->IsTextControlElement());
    324 
    325  nsIFrame* const textControlFrame = textControlElement->GetPrimaryFrame();
    326  if (NS_WARN_IF(!textControlFrame)) {
    327    return Err(NS_ERROR_FAILURE);
    328  }
    329 
    330  switch (textControlFrame->StyleUIReset()->mIMEMode) {
    331    case StyleImeMode::Auto:
    332    default:
    333      return IMEState{
    334          IsPasswordEditor() ? IMEEnabled::Password : IMEEnabled::Enabled,
    335          IMEState::DONT_CHANGE_OPEN_STATE};
    336    case StyleImeMode::Disabled:
    337      // we should use password state for |ime-mode: disabled;|.
    338      return IMEState{IMEEnabled::Password, IMEState::DONT_CHANGE_OPEN_STATE};
    339    case StyleImeMode::Active:
    340      return IMEState{IMEEnabled::Enabled, IMEState::OPEN};
    341    case StyleImeMode::Inactive:
    342      return IMEState{IMEEnabled::Enabled, IMEState::CLOSED};
    343    case StyleImeMode::Normal:
    344      return IMEState{IMEEnabled::Enabled, IMEState::DONT_CHANGE_OPEN_STATE};
    345  }
    346 }
    347 
    348 nsresult TextEditor::HandleKeyPressEvent(WidgetKeyboardEvent* aKeyboardEvent) {
    349  // NOTE: When you change this method, you should also change:
    350  //   * editor/libeditor/tests/test_texteditor_keyevent_handling.html
    351  //   * editor/libeditor/tests/test_htmleditor_keyevent_handling.html
    352  //
    353  // And also when you add new key handling, you need to change the subclass's
    354  // HandleKeyPressEvent()'s switch statement.
    355 
    356  if (NS_WARN_IF(!aKeyboardEvent)) {
    357    return NS_ERROR_UNEXPECTED;
    358  }
    359 
    360  if (IsReadonly()) {
    361    HandleKeyPressEventInReadOnlyMode(*aKeyboardEvent);
    362    return NS_OK;
    363  }
    364 
    365  MOZ_ASSERT(aKeyboardEvent->mMessage == eKeyPress,
    366             "HandleKeyPressEvent gets non-keypress event");
    367 
    368  switch (aKeyboardEvent->mKeyCode) {
    369    case NS_VK_META:
    370    case NS_VK_WIN:
    371    case NS_VK_SHIFT:
    372    case NS_VK_CONTROL:
    373    case NS_VK_ALT:
    374      // FYI: This shouldn't occur since modifier key shouldn't cause eKeyPress
    375      //      event.
    376      aKeyboardEvent->PreventDefault();
    377      return NS_OK;
    378 
    379    case NS_VK_BACK:
    380    case NS_VK_DELETE:
    381    case NS_VK_TAB: {
    382      nsresult rv = EditorBase::HandleKeyPressEvent(aKeyboardEvent);
    383      NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    384                           "EditorBase::HandleKeyPressEvent() failed");
    385      return rv;
    386    }
    387    case NS_VK_RETURN: {
    388      if (!aKeyboardEvent->IsInputtingLineBreak()) {
    389        return NS_OK;
    390      }
    391      if (!IsSingleLineEditor()) {
    392        aKeyboardEvent->PreventDefault();
    393      }
    394      // We need to dispatch "beforeinput" event at least even if we're a
    395      // single line text editor.
    396      nsresult rv = InsertLineBreakAsAction();
    397      NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    398                           "TextEditor::InsertLineBreakAsAction() failed");
    399      return rv;
    400    }
    401  }
    402 
    403  if (!aKeyboardEvent->IsInputtingText()) {
    404    // we don't PreventDefault() here or keybindings like control-x won't work
    405    return NS_OK;
    406  }
    407  aKeyboardEvent->PreventDefault();
    408  // If we dispatch 2 keypress events for a surrogate pair and we set only
    409  // first `.key` value to the surrogate pair, the preceding one has it and the
    410  // other has empty string.  In this case, we should handle only the first one
    411  // with the key value.
    412  if (!StaticPrefs::dom_event_keypress_dispatch_once_per_surrogate_pair() &&
    413      !StaticPrefs::dom_event_keypress_key_allow_lone_surrogate() &&
    414      aKeyboardEvent->mKeyValue.IsEmpty() &&
    415      IS_SURROGATE(aKeyboardEvent->mCharCode)) {
    416    return NS_OK;
    417  }
    418  // Our widget shouldn't set `\r` to `mKeyValue`, but it may be synthesized
    419  // keyboard event and its value may be `\r`.  In such case, we should treat
    420  // it as `\n` for the backward compatibility because we stopped converting
    421  // `\r` and `\r\n` to `\n` at getting `HTMLInputElement.value` and
    422  // `HTMLTextAreaElement.value` for the performance (i.e., we don't need to
    423  // take care in `HTMLEditor`).
    424  nsAutoString str(aKeyboardEvent->mKeyValue);
    425  if (str.IsEmpty()) {
    426    MOZ_ASSERT(aKeyboardEvent->mCharCode <= 0xFFFF,
    427               "Non-BMP character needs special handling");
    428    str.Assign(aKeyboardEvent->mCharCode == nsCRT::CR
    429                   ? static_cast<char16_t>(nsCRT::LF)
    430                   : static_cast<char16_t>(aKeyboardEvent->mCharCode));
    431  } else {
    432    MOZ_ASSERT(str.Find(u"\r\n"_ns) == kNotFound,
    433               "This assumes that typed text does not include CRLF");
    434    str.ReplaceChar('\r', '\n');
    435  }
    436  nsresult rv = OnInputText(str);
    437  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EditorBase::OnInputText() failed");
    438  return rv;
    439 }
    440 
    441 NS_IMETHODIMP TextEditor::InsertLineBreak() {
    442  AutoEditActionDataSetter editActionData(*this, EditAction::eInsertLineBreak);
    443  nsresult rv = editActionData.CanHandleAndMaybeDispatchBeforeInputEvent();
    444  if (NS_FAILED(rv)) {
    445    NS_WARNING_ASSERTION(rv == NS_ERROR_EDITOR_ACTION_CANCELED,
    446                         "CanHandleAndMaybeDispatchBeforeInputEvent() failed");
    447    return EditorBase::ToGenericNSResult(rv);
    448  }
    449 
    450  if (NS_WARN_IF(IsSingleLineEditor())) {
    451    return NS_ERROR_FAILURE;
    452  }
    453 
    454  AutoPlaceholderBatch treatAsOneTransaction(
    455      *this, ScrollSelectionIntoView::Yes, __FUNCTION__);
    456  rv = InsertLineBreakAsSubAction();
    457  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    458                       "TextEditor::InsertLineBreakAsSubAction() failed");
    459  return EditorBase::ToGenericNSResult(rv);
    460 }
    461 
    462 nsresult TextEditor::ComputeTextValue(nsAString& aString) const {
    463  // This is a public method so that anytime this may be called, e.g., before
    464  // initialized or after destroyed.  However, GetTextNode() is an internal API
    465  // for friend classes and internal use.  Therefore, it asserts the conditions
    466  // whether the anonymous subtree is available.  Therefore, this method needs
    467  // to get the Text with the anonymous <div>.
    468  const Element* const anonymousDivElement = GetRoot();
    469  if (MOZ_UNLIKELY(!anonymousDivElement)) {
    470    // If we're temporarily destroyed while we're handling something, we can get
    471    // the value from the cached text node which was maybe modified by us.  This
    472    // helps TextControlState and TextInputListener to notify the text control
    473    // element of value changes, etc, at UnbindFromFrame().
    474    const Text* const cachedTextNode = GetCachedTextNode();
    475    if (NS_WARN_IF(!cachedTextNode)) {
    476      return NS_ERROR_NOT_INITIALIZED;
    477    }
    478    cachedTextNode->GetData(aString);
    479    return NS_OK;
    480  }
    481 
    482  const auto* const text =
    483      Text::FromNodeOrNull(anonymousDivElement->GetFirstChild());
    484  if (MOZ_UNLIKELY(!text)) {
    485    MOZ_ASSERT_UNREACHABLE("how?");
    486    return NS_ERROR_UNEXPECTED;
    487  }
    488 
    489  text->GetData(aString);
    490  return NS_OK;
    491 }
    492 
    493 nsresult TextEditor::InsertLineBreakAsAction(nsIPrincipal* aPrincipal) {
    494  AutoEditActionDataSetter editActionData(*this, EditAction::eInsertLineBreak,
    495                                          aPrincipal);
    496  nsresult rv = editActionData.CanHandleAndMaybeDispatchBeforeInputEvent();
    497  if (NS_FAILED(rv)) {
    498    NS_WARNING_ASSERTION(rv == NS_ERROR_EDITOR_ACTION_CANCELED,
    499                         "CanHandleAndMaybeDispatchBeforeInputEvent() failed");
    500    return EditorBase::ToGenericNSResult(rv);
    501  }
    502 
    503  if (IsSingleLineEditor()) {
    504    return NS_OK;
    505  }
    506 
    507  // XXX This may be called by execCommand() with "insertParagraph".
    508  //     In such case, naming the transaction "TypingTxnName" is odd.
    509  AutoPlaceholderBatch treatAsOneTransaction(*this, *nsGkAtoms::TypingTxnName,
    510                                             ScrollSelectionIntoView::Yes,
    511                                             __FUNCTION__);
    512  rv = InsertLineBreakAsSubAction();
    513  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    514                       "EditorBase::InsertLineBreakAsSubAction() failed");
    515  return EditorBase::ToGenericNSResult(rv);
    516 }
    517 
    518 nsresult TextEditor::SetTextAsAction(
    519    const nsAString& aString,
    520    AllowBeforeInputEventCancelable aAllowBeforeInputEventCancelable,
    521    nsIPrincipal* aPrincipal) {
    522  MOZ_ASSERT(aString.FindChar(nsCRT::CR) == kNotFound);
    523 
    524  AutoEditActionDataSetter editActionData(*this, EditAction::eSetText,
    525                                          aPrincipal);
    526  if (aAllowBeforeInputEventCancelable == AllowBeforeInputEventCancelable::No) {
    527    editActionData.MakeBeforeInputEventNonCancelable();
    528  }
    529  nsresult rv = editActionData.CanHandleAndMaybeDispatchBeforeInputEvent();
    530  if (NS_FAILED(rv)) {
    531    NS_WARNING_ASSERTION(rv == NS_ERROR_EDITOR_ACTION_CANCELED,
    532                         "CanHandleAndMaybeDispatchBeforeInputEvent() failed");
    533    return EditorBase::ToGenericNSResult(rv);
    534  }
    535 
    536  AutoPlaceholderBatch treatAsOneTransaction(
    537      *this, ScrollSelectionIntoView::Yes, __FUNCTION__);
    538  rv = SetTextAsSubAction(aString);
    539  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    540                       "TextEditor::SetTextAsSubAction() failed");
    541  return EditorBase::ToGenericNSResult(rv);
    542 }
    543 
    544 nsresult TextEditor::SetTextAsSubAction(const nsAString& aString) {
    545  MOZ_ASSERT(IsEditActionDataAvailable());
    546  MOZ_ASSERT(mPlaceholderBatch);
    547 
    548  if (NS_WARN_IF(!mInitSucceeded)) {
    549    return NS_ERROR_NOT_INITIALIZED;
    550  }
    551 
    552  IgnoredErrorResult ignoredError;
    553  AutoEditSubActionNotifier startToHandleEditSubAction(
    554      *this, EditSubAction::eSetText, nsIEditor::eNext, ignoredError);
    555  if (NS_WARN_IF(ignoredError.ErrorCodeIs(NS_ERROR_EDITOR_DESTROYED))) {
    556    return ignoredError.StealNSResult();
    557  }
    558  NS_WARNING_ASSERTION(
    559      !ignoredError.Failed(),
    560      "TextEditor::OnStartToHandleTopLevelEditSubAction() failed, but ignored");
    561 
    562  if (!IsIMEComposing() && !IsUndoRedoEnabled() &&
    563      GetEditAction() != EditAction::eReplaceText && mMaxTextLength < 0) {
    564    Result<EditActionResult, nsresult> result =
    565        SetTextWithoutTransaction(aString);
    566    if (MOZ_UNLIKELY(result.isErr())) {
    567      NS_WARNING("TextEditor::SetTextWithoutTransaction() failed");
    568      return result.unwrapErr();
    569    }
    570    if (!result.inspect().Ignored()) {
    571      return NS_OK;
    572    }
    573  }
    574 
    575  {
    576    // Note that do not notify selectionchange caused by selecting all text
    577    // because it's preparation of our delete implementation so web apps
    578    // shouldn't receive such selectionchange before the first mutation.
    579    AutoUpdateViewBatch preventSelectionChangeEvent(*this, __FUNCTION__);
    580 
    581    // XXX We should make ReplaceSelectionAsSubAction() take range.  Then,
    582    //     we can saving the expensive cost of modifying `Selection` here.
    583    if (NS_SUCCEEDED(SelectEntireDocument())) {
    584      DebugOnly<nsresult> rvIgnored = ReplaceSelectionAsSubAction(aString);
    585      NS_WARNING_ASSERTION(
    586          NS_SUCCEEDED(rvIgnored),
    587          "EditorBase::ReplaceSelectionAsSubAction() failed, but ignored");
    588    }
    589  }
    590 
    591  // Destroying AutoUpdateViewBatch may cause destroying us.
    592  return NS_WARN_IF(Destroyed()) ? NS_ERROR_EDITOR_DESTROYED : NS_OK;
    593 }
    594 
    595 already_AddRefed<Element> TextEditor::GetInputEventTargetElement() const {
    596  RefPtr<Element> target = Element::FromEventTargetOrNull(mEventTarget);
    597  return target.forget();
    598 }
    599 
    600 bool TextEditor::IsEmpty() const {
    601  // This is a public method so that anytime this may be called, e.g., before
    602  // initialized or after destroyed.  However, GetTextNode() is an internal API
    603  // for friend classes and internal use.  Therefore, it asserts the conditions
    604  // whether the anonymous subtree is available.  Therefore, this method needs
    605  // to get the Text with the anonymous <div>.
    606  if (MOZ_UNLIKELY(!mInitSucceeded)) {
    607    // If we're temporarily destroyed while we're handling something, we can get
    608    // the value from the cached text node which was maybe modified by us.  This
    609    // helps TextControlState and TextInputListener to notify the text control
    610    // element of value changes, etc, at UnbindFromFrame().
    611    const Text* const cachedTextNode = GetCachedTextNode();
    612    return NS_WARN_IF(!cachedTextNode) || !cachedTextNode->TextDataLength();
    613  }
    614  const Text* const textNode = GetTextNode();
    615  return !textNode || !textNode->TextDataLength();
    616 }
    617 
    618 NS_IMETHODIMP TextEditor::GetTextLength(uint32_t* aCount) {
    619  MOZ_ASSERT(aCount);
    620  // This is a public method so that anytime this may be called, e.g., before
    621  // initialized or after destroyed.  However, GetTextNode() is an internal API
    622  // for friend classes and internal use.  Therefore, it asserts the conditions
    623  // whether the anonymous subtree is available.  Therefore, this method needs
    624  // to get the Text with the anonymous <div>.
    625  if (MOZ_UNLIKELY(!mInitSucceeded)) {
    626    // If we're temporarily destroyed while we're handling something, we can get
    627    // the value from the cached text node which was maybe modified by us.
    628    const Text* const textNode = GetCachedTextNode();
    629    if (NS_WARN_IF(!textNode)) {
    630      return NS_ERROR_FAILURE;
    631    }
    632    *aCount = textNode->TextDataLength();
    633    return NS_OK;
    634  }
    635 
    636  const Text* const textNode = GetTextNode();
    637  *aCount = textNode ? textNode->TextDataLength() : 0u;
    638  return NS_OK;
    639 }
    640 
    641 bool TextEditor::IsCopyToClipboardAllowedInternal() const {
    642  MOZ_ASSERT(IsEditActionDataAvailable());
    643  if (!EditorBase::IsCopyToClipboardAllowedInternal()) {
    644    return false;
    645  }
    646 
    647  if (!IsSingleLineEditor() || !IsPasswordEditor() ||
    648      NS_WARN_IF(!mPasswordMaskData)) {
    649    return true;
    650  }
    651 
    652  // If we're a password editor, we should allow selected text to be copied
    653  // to the clipboard only when selection range is in unmasked range.
    654  if (IsAllMasked() || IsMaskingPassword() || !UnmaskedLength()) {
    655    return false;
    656  }
    657 
    658  // If there are 2 or more ranges, we don't allow to copy/cut for now since
    659  // we need to check whether all ranges are in unmasked range or not.
    660  // Anyway, such operation in password field does not make sense.
    661  if (SelectionRef().RangeCount() > 1) {
    662    return false;
    663  }
    664 
    665  uint32_t selectionStart = 0, selectionEnd = 0;
    666  nsContentUtils::GetSelectionInTextControl(&SelectionRef(), mRootElement,
    667                                            selectionStart, selectionEnd);
    668  return UnmaskedStart() <= selectionStart && UnmaskedEnd() >= selectionEnd;
    669 }
    670 
    671 nsresult TextEditor::HandlePasteAsQuotation(
    672    AutoEditActionDataSetter& aEditActionData,
    673    nsIClipboard::ClipboardType aClipboardType, DataTransfer* aDataTransfer) {
    674  MOZ_ASSERT(aClipboardType == nsIClipboard::kGlobalClipboard ||
    675             aClipboardType == nsIClipboard::kSelectionClipboard);
    676  if (NS_WARN_IF(!GetDocument())) {
    677    return NS_OK;
    678  }
    679 
    680  // XXX Why don't we dispatch ePaste event here?
    681 
    682  // Get the nsITransferable interface for getting the data from the clipboard
    683  Result<nsCOMPtr<nsITransferable>, nsresult> maybeTransferable =
    684      EditorUtils::CreateTransferableForPlainText(*GetDocument());
    685  if (maybeTransferable.isErr()) {
    686    NS_WARNING("EditorUtils::CreateTransferableForPlainText() failed");
    687    return maybeTransferable.unwrapErr();
    688  }
    689  nsCOMPtr<nsITransferable> trans(maybeTransferable.unwrap());
    690  if (!trans) {
    691    NS_WARNING(
    692        "EditorUtils::CreateTransferableForPlainText() returned nullptr, but "
    693        "ignored");
    694    return NS_OK;
    695  }
    696 
    697  // Get the Data from the clipboard
    698  nsresult rv =
    699      GetDataFromDataTransferOrClipboard(aDataTransfer, trans, aClipboardType);
    700 
    701  // Now we ask the transferable for the data
    702  // it still owns the data, we just have a pointer to it.
    703  // If it can't support a "text" output of the data the call will fail
    704  nsCOMPtr<nsISupports> genericDataObj;
    705  nsAutoCString flavor;
    706  rv = trans->GetAnyTransferData(flavor, getter_AddRefs(genericDataObj));
    707  if (NS_FAILED(rv)) {
    708    NS_WARNING("nsITransferable::GetAnyTransferData() failed");
    709    return rv;
    710  }
    711 
    712  if (!flavor.EqualsLiteral(kTextMime) &&
    713      !flavor.EqualsLiteral(kMozTextInternal)) {
    714    return NS_OK;
    715  }
    716 
    717  nsCOMPtr<nsISupportsString> text = do_QueryInterface(genericDataObj);
    718  if (!text) {
    719    return NS_OK;
    720  }
    721 
    722  nsString stuffToPaste;
    723  DebugOnly<nsresult> rvIgnored = text->GetData(stuffToPaste);
    724  NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
    725                       "nsISupportsString::GetData() failed, but ignored");
    726  if (stuffToPaste.IsEmpty()) {
    727    return NS_OK;
    728  }
    729 
    730  aEditActionData.SetData(stuffToPaste);
    731  if (!stuffToPaste.IsEmpty()) {
    732    nsContentUtils::PlatformToDOMLineBreaks(stuffToPaste);
    733  }
    734  rv = aEditActionData.MaybeDispatchBeforeInputEvent();
    735  if (NS_FAILED(rv)) {
    736    NS_WARNING_ASSERTION(rv == NS_ERROR_EDITOR_ACTION_CANCELED,
    737                         "MaybeDispatchBeforeInputEvent() failed");
    738    return rv;
    739  }
    740 
    741  AutoPlaceholderBatch treatAsOneTransaction(
    742      *this, ScrollSelectionIntoView::Yes, __FUNCTION__);
    743  rv = InsertWithQuotationsAsSubAction(stuffToPaste);
    744  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    745                       "TextEditor::InsertWithQuotationsAsSubAction() failed");
    746  return rv;
    747 }
    748 
    749 nsresult TextEditor::InsertWithQuotationsAsSubAction(
    750    const nsAString& aQuotedText) {
    751  MOZ_ASSERT(IsEditActionDataAvailable());
    752 
    753  if (IsReadonly()) {
    754    return NS_OK;
    755  }
    756 
    757  // Let the citer quote it for us:
    758  nsString quotedStuff;
    759  InternetCiter::GetCiteString(aQuotedText, quotedStuff);
    760 
    761  // It's best to put a blank line after the quoted text so that mails
    762  // written without thinking won't be so ugly.
    763  if (!aQuotedText.IsEmpty() && (aQuotedText.Last() != char16_t('\n'))) {
    764    quotedStuff.Append(char16_t('\n'));
    765  }
    766 
    767  IgnoredErrorResult ignoredError;
    768  AutoEditSubActionNotifier startToHandleEditSubAction(
    769      *this, EditSubAction::eInsertText, nsIEditor::eNext, ignoredError);
    770  if (NS_WARN_IF(ignoredError.ErrorCodeIs(NS_ERROR_EDITOR_DESTROYED))) {
    771    return ignoredError.StealNSResult();
    772  }
    773  NS_WARNING_ASSERTION(
    774      !ignoredError.Failed(),
    775      "TextEditor::OnStartToHandleTopLevelEditSubAction() failed, but ignored");
    776 
    777  // XXX Do we need to support paste-as-quotation in password editor (and
    778  //     also in single line editor)?
    779  MaybeDoAutoPasswordMasking();
    780 
    781  nsresult rv = InsertTextAsSubAction(quotedStuff, InsertTextFor::NormalText);
    782  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    783                       "EditorBase::InsertTextAsSubAction() failed");
    784  return rv;
    785 }
    786 
    787 nsresult TextEditor::SelectEntireDocument() {
    788  MOZ_ASSERT(IsEditActionDataAvailable());
    789 
    790  if (NS_WARN_IF(!mInitSucceeded)) {
    791    return NS_ERROR_NOT_INITIALIZED;
    792  }
    793 
    794  RefPtr<Element> anonymousDivElement = GetRoot();
    795  if (NS_WARN_IF(!anonymousDivElement)) {
    796    return NS_ERROR_NOT_INITIALIZED;
    797  }
    798 
    799  RefPtr<Text> text =
    800      Text::FromNodeOrNull(anonymousDivElement->GetFirstChild());
    801  MOZ_ASSERT(text);
    802 
    803  MOZ_TRY(SelectionRef().SetStartAndEndInLimiter(
    804      *text, 0, *text, text->TextDataLength(), eDirNext,
    805      nsISelectionListener::SELECTALL_REASON));
    806 
    807  return NS_OK;
    808 }
    809 
    810 EventTarget* TextEditor::GetDOMEventTarget() const { return mEventTarget; }
    811 
    812 void TextEditor::ReinitializeSelection(Element& aElement) {
    813  MOZ_LOG(gTextEditorLog, LogLevel::Info,
    814          ("%p: ReinitializeSelection(aElement=%s)", this,
    815           ToString(RefPtr{&aElement}).c_str()));
    816 
    817  if (MOZ_UNLIKELY(Destroyed())) {
    818    LogOrWarn(this, gTextEditorLog, LogLevel::Error, "Destroyed() failed");
    819    return;
    820  }
    821 
    822  AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
    823  if (MOZ_UNLIKELY(!editActionData.CanHandle())) {
    824    LogOrWarn(this, gTextEditorLog, LogLevel::Error,
    825              "AutoEditActionDataSetter::CanHandle() failed");
    826    return;
    827  }
    828 
    829  // We don't need to flush pending notifications here and we don't need to
    830  // handle spellcheck at first focus.  Therefore, we don't need to call
    831  // `TextEditor::OnFocus` here.
    832  EditorBase::OnFocus(aElement);
    833 
    834  // If previous focused editor turn on spellcheck and this editor doesn't
    835  // turn on it, spellcheck state is mismatched.  So we need to re-sync it.
    836  SyncRealTimeSpell();
    837 }
    838 
    839 nsresult TextEditor::OnFocus(const nsINode& aOriginalEventTargetNode) {
    840  MOZ_LOG(gTextEditorLog, LogLevel::Info,
    841          ("%p: OnFocus(aOriginalEventTargetNode=%s)", this,
    842           ToString(RefPtr{&aOriginalEventTargetNode}).c_str()));
    843 
    844  RefPtr<PresShell> presShell = GetPresShell();
    845  if (MOZ_UNLIKELY(!presShell)) {
    846    LogOrWarn(this, gTextEditorLog, LogLevel::Error, "!presShell");
    847    return NS_ERROR_FAILURE;
    848  }
    849  // Let's update the layout information right now because there are some
    850  // pending notifications and flushing them may cause destroying the editor.
    851  presShell->FlushPendingNotifications(FlushType::Layout);
    852  if (MOZ_UNLIKELY(!CanKeepHandlingFocusEvent(aOriginalEventTargetNode))) {
    853    MOZ_LOG(gTextEditorLog, LogLevel::Debug,
    854            ("%p: CanKeepHandlingFocusEvent() returned false", this));
    855    return NS_OK;
    856  }
    857 
    858  AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
    859  if (MOZ_UNLIKELY(!editActionData.CanHandle())) {
    860    LogOrWarn(this, gTextEditorLog, LogLevel::Error,
    861              "AutoEditActionDataSetter::CanHandle() failed");
    862    return NS_ERROR_FAILURE;
    863  }
    864 
    865  // Spell check a textarea the first time that it is focused.
    866  nsresult rv = FlushPendingSpellCheck();
    867  if (MOZ_UNLIKELY(rv == NS_ERROR_EDITOR_DESTROYED)) {
    868    LogOrWarn(this, gTextEditorLog, LogLevel::Error,
    869              "EditorBase::FlushPendingSpellCheck() failed");
    870    return NS_ERROR_EDITOR_DESTROYED;
    871  }
    872  NS_WARNING_ASSERTION(
    873      NS_SUCCEEDED(rv),
    874      "EditorBase::FlushPendingSpellCheck() failed, but ignored");
    875  if (MOZ_UNLIKELY(!CanKeepHandlingFocusEvent(aOriginalEventTargetNode))) {
    876    MOZ_LOG(gTextEditorLog, LogLevel::Debug,
    877            ("%p: CanKeepHandlingFocusEvent() returned false after "
    878             "FlushPendingSpellCheck()",
    879             this));
    880    return NS_OK;
    881  }
    882 
    883  return EditorBase::OnFocus(aOriginalEventTargetNode);
    884 }
    885 
    886 nsresult TextEditor::OnBlur(const EventTarget* aEventTarget) {
    887  MOZ_LOG(gTextEditorLog, LogLevel::Info,
    888          ("%p: OnBlur(aEventTarget=%s)", this,
    889           ToString(RefPtr{aEventTarget}).c_str()));
    890 
    891  // check if something else is focused. If another element is focused, then
    892  // we should not change the selection.  If another element already has focus,
    893  // we should not maintain the selection because we may not have the rights
    894  // doing it.
    895  if ([[maybe_unused]] Element* const focusedElement =
    896          nsFocusManager::GetFocusedElementStatic()) {
    897    MOZ_LOG(gTextEditorLog, LogLevel::Info,
    898            ("%p: OnBlur() is ignored because another element already has "
    899             "focus (%s)",
    900             this, ToString(RefPtr{focusedElement}).c_str()));
    901    return NS_OK;
    902  }
    903 
    904  nsresult rv = FinalizeSelection();
    905  if (NS_FAILED(rv)) {
    906    LogOrWarn(this, gTextEditorLog, LogLevel::Error,
    907              "EditorBase::FinalizeSelection() failed");
    908    return rv;
    909  }
    910  return NS_OK;
    911 }
    912 
    913 nsresult TextEditor::SetAttributeOrEquivalent(Element* aElement,
    914                                              nsAtom* aAttribute,
    915                                              const nsAString& aValue,
    916                                              bool aSuppressTransaction) {
    917  if (NS_WARN_IF(!aElement) || NS_WARN_IF(!aAttribute)) {
    918    return NS_ERROR_INVALID_ARG;
    919  }
    920 
    921  AutoEditActionDataSetter editActionData(*this, EditAction::eSetAttribute);
    922  nsresult rv = editActionData.CanHandleAndMaybeDispatchBeforeInputEvent();
    923  if (NS_FAILED(rv)) {
    924    NS_WARNING_ASSERTION(rv == NS_ERROR_EDITOR_ACTION_CANCELED,
    925                         "CanHandleAndMaybeDispatchBeforeInputEvent() failed");
    926    return EditorBase::ToGenericNSResult(rv);
    927  }
    928 
    929  rv = SetAttributeWithTransaction(*aElement, *aAttribute, aValue);
    930  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    931                       "EditorBase::SetAttributeWithTransaction() failed");
    932  return EditorBase::ToGenericNSResult(rv);
    933 }
    934 
    935 nsresult TextEditor::RemoveAttributeOrEquivalent(Element* aElement,
    936                                                 nsAtom* aAttribute,
    937                                                 bool aSuppressTransaction) {
    938  if (NS_WARN_IF(!aElement) || NS_WARN_IF(!aAttribute)) {
    939    return NS_ERROR_INVALID_ARG;
    940  }
    941 
    942  AutoEditActionDataSetter editActionData(*this, EditAction::eRemoveAttribute);
    943  nsresult rv = editActionData.CanHandleAndMaybeDispatchBeforeInputEvent();
    944  if (NS_FAILED(rv)) {
    945    NS_WARNING_ASSERTION(rv == NS_ERROR_EDITOR_ACTION_CANCELED,
    946                         "CanHandleAndMaybeDispatchBeforeInputEvent() failed");
    947    return EditorBase::ToGenericNSResult(rv);
    948  }
    949 
    950  rv = RemoveAttributeWithTransaction(*aElement, *aAttribute);
    951  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    952                       "EditorBase::RemoveAttributeWithTransaction() failed");
    953  return EditorBase::ToGenericNSResult(rv);
    954 }
    955 
    956 template <typename EditorDOMPointType>
    957 EditorDOMPointType TextEditor::FindBetterInsertionPoint(
    958    const EditorDOMPointType& aPoint) const {
    959  if (MOZ_UNLIKELY(NS_WARN_IF(!aPoint.IsInContentNode()))) {
    960    return aPoint;
    961  }
    962 
    963  MOZ_ASSERT(aPoint.IsSetAndValid());
    964 
    965  Element* const anonymousDivElement = GetRoot();
    966  if (aPoint.GetContainer() == anonymousDivElement) {
    967    // In some cases, aPoint points start of the anonymous <div>.  To avoid
    968    // injecting unneeded text nodes, we first look to see if we have one
    969    // available.  In that case, we'll just adjust node and offset accordingly.
    970    if (aPoint.IsStartOfContainer()) {
    971      if (aPoint.GetContainer()->HasChildren() &&
    972          aPoint.GetContainer()->GetFirstChild()->IsText()) {
    973        return EditorDOMPointType(aPoint.GetContainer()->GetFirstChild(), 0u);
    974      }
    975    }
    976    // In some other cases, aPoint points the terminating padding <br> element
    977    // for empty last line in the anonymous <div>.  In that case, we'll adjust
    978    // aInOutNode and aInOutOffset to the preceding text node, if any.
    979    else {
    980      nsIContent* child = aPoint.GetContainer()->GetLastChild();
    981      while (child) {
    982        if (child->IsText()) {
    983          return EditorDOMPointType::AtEndOf(*child);
    984        }
    985        child = child->GetPreviousSibling();
    986      }
    987    }
    988  }
    989 
    990  // Sometimes, aPoint points the padding <br> element.  In that case, we'll
    991  // adjust the insertion point to the previous text node, if one exists, or to
    992  // the parent anonymous DIV.
    993  if (EditorUtils::IsPaddingBRElementForEmptyLastLine(
    994          *aPoint.template ContainerAs<nsIContent>()) &&
    995      aPoint.IsStartOfContainer()) {
    996    nsIContent* previousSibling = aPoint.GetContainer()->GetPreviousSibling();
    997    if (previousSibling && previousSibling->IsText()) {
    998      return EditorDOMPointType::AtEndOf(*previousSibling);
    999    }
   1000 
   1001    nsINode* parentOfContainer = aPoint.GetContainerParent();
   1002    if (parentOfContainer && parentOfContainer == anonymousDivElement) {
   1003      return EditorDOMPointType(parentOfContainer,
   1004                                aPoint.template ContainerAs<nsIContent>(), 0u);
   1005    }
   1006  }
   1007 
   1008  return aPoint;
   1009 }
   1010 
   1011 // static
   1012 void TextEditor::MaskString(nsString& aString, const Text& aTextNode,
   1013                            uint32_t aStartOffsetInString,
   1014                            uint32_t aStartOffsetInText) {
   1015  MOZ_ASSERT(aTextNode.HasFlag(NS_MAYBE_MASKED));
   1016  MOZ_ASSERT(aStartOffsetInString == 0 || aStartOffsetInText == 0);
   1017 
   1018  uint32_t unmaskStart = UINT32_MAX, unmaskLength = 0;
   1019  const TextEditor* const textEditor =
   1020      nsContentUtils::GetExtantTextEditorFromAnonymousNode(&aTextNode);
   1021  if (textEditor && textEditor->UnmaskedLength() > 0) {
   1022    unmaskStart = textEditor->UnmaskedStart();
   1023    unmaskLength = textEditor->UnmaskedLength();
   1024    // If text is copied from after unmasked range, we can treat this case
   1025    // as mask all.
   1026    if (aStartOffsetInText >= unmaskStart + unmaskLength) {
   1027      unmaskLength = 0;
   1028      unmaskStart = UINT32_MAX;
   1029    } else {
   1030      // If text is copied from middle of unmasked range, reduce the length
   1031      // and adjust start offset.
   1032      if (aStartOffsetInText > unmaskStart) {
   1033        unmaskLength = unmaskStart + unmaskLength - aStartOffsetInText;
   1034        unmaskStart = 0;
   1035      }
   1036      // If text is copied from before start of unmasked range, just adjust
   1037      // the start offset.
   1038      else {
   1039        unmaskStart -= aStartOffsetInText;
   1040      }
   1041      // Make the range is in the string.
   1042      unmaskStart += aStartOffsetInString;
   1043    }
   1044  }
   1045 
   1046  const char16_t kPasswordMask = TextEditor::PasswordMask();
   1047  for (uint32_t i = aStartOffsetInString; i < aString.Length(); ++i) {
   1048    bool isSurrogatePair = NS_IS_HIGH_SURROGATE(aString.CharAt(i)) &&
   1049                           i < aString.Length() - 1 &&
   1050                           NS_IS_LOW_SURROGATE(aString.CharAt(i + 1));
   1051    if (i < unmaskStart || i >= unmaskStart + unmaskLength) {
   1052      if (isSurrogatePair) {
   1053        aString.SetCharAt(kPasswordMask, i);
   1054        aString.SetCharAt(kPasswordMask, i + 1);
   1055      } else {
   1056        aString.SetCharAt(kPasswordMask, i);
   1057      }
   1058    }
   1059 
   1060    // Skip the following low surrogate.
   1061    if (isSurrogatePair) {
   1062      ++i;
   1063    }
   1064  }
   1065 }
   1066 
   1067 nsresult TextEditor::SetUnmaskRangeInternal(uint32_t aStart, uint32_t aLength,
   1068                                            uint32_t aTimeout, bool aNotify,
   1069                                            bool aForceStartMasking) {
   1070  if (mPasswordMaskData) {
   1071    mPasswordMaskData->mIsMaskingPassword = aForceStartMasking || aTimeout != 0;
   1072 
   1073    // We cannot manage multiple unmasked ranges so that shrink the previous
   1074    // range first.
   1075    if (!IsAllMasked()) {
   1076      mPasswordMaskData->mUnmaskedLength = 0;
   1077      mPasswordMaskData->CancelTimer(PasswordMaskData::ReleaseTimer::No);
   1078    }
   1079  }
   1080 
   1081  // If we're not a password editor, return error since this call does not
   1082  // make sense.
   1083  if (!IsPasswordEditor() || NS_WARN_IF(!mPasswordMaskData)) {
   1084    mPasswordMaskData->CancelTimer(PasswordMaskData::ReleaseTimer::Yes);
   1085    return NS_ERROR_NOT_AVAILABLE;
   1086  }
   1087 
   1088  if (NS_WARN_IF(!GetRoot())) {
   1089    return NS_ERROR_NOT_INITIALIZED;
   1090  }
   1091  Text* const text = GetTextNode();
   1092  if (!text || !text->Length()) {
   1093    // There is no anonymous text node in the editor.
   1094    return aStart > 0 && aStart != UINT32_MAX ? NS_ERROR_INVALID_ARG : NS_OK;
   1095  }
   1096 
   1097  if (aStart < UINT32_MAX) {
   1098    uint32_t valueLength = text->Length();
   1099    if (aStart >= valueLength) {
   1100      return NS_ERROR_INVALID_ARG;  // There is no character can be masked.
   1101    }
   1102    // If aStart is middle of a surrogate pair, expand it to include the
   1103    // preceding high surrogate because the caller may want to show a
   1104    // character before the character at `aStart + 1`.
   1105    const CharacterDataBuffer& characterDataBuffer = text->DataBuffer();
   1106    if (characterDataBuffer.IsLowSurrogateFollowingHighSurrogateAt(aStart)) {
   1107      mPasswordMaskData->mUnmaskedStart = aStart - 1;
   1108      // If caller collapses the range, keep it.  Otherwise, expand the length.
   1109      if (aLength > 0) {
   1110        ++aLength;
   1111      }
   1112    } else {
   1113      mPasswordMaskData->mUnmaskedStart = aStart;
   1114    }
   1115    mPasswordMaskData->mUnmaskedLength =
   1116        std::min(valueLength - UnmaskedStart(), aLength);
   1117    // If unmasked end is middle of a surrogate pair, expand it to include
   1118    // the following low surrogate because the caller may want to show a
   1119    // character after the character at `aStart + aLength`.
   1120    if (UnmaskedEnd() < valueLength &&
   1121        characterDataBuffer.IsLowSurrogateFollowingHighSurrogateAt(
   1122            UnmaskedEnd())) {
   1123      mPasswordMaskData->mUnmaskedLength++;
   1124    }
   1125    // If it's first time to mask the unmasking characters with timer, create
   1126    // the timer now.  Then, we'll keep using it for saving the creation cost.
   1127    if (!HasAutoMaskingTimer() && aLength && aTimeout && UnmaskedLength()) {
   1128      mPasswordMaskData->mTimer = NS_NewTimer();
   1129    }
   1130  } else {
   1131    if (NS_WARN_IF(aLength != 0)) {
   1132      return NS_ERROR_INVALID_ARG;
   1133    }
   1134    mPasswordMaskData->MaskAll();
   1135  }
   1136 
   1137  // Notify nsTextFrame of this update if the caller wants this to do it.
   1138  // Only in this case, script may run.
   1139  if (aNotify) {
   1140    MOZ_ASSERT(IsEditActionDataAvailable());
   1141 
   1142    RefPtr<Document> document = GetDocument();
   1143    if (NS_WARN_IF(!document)) {
   1144      return NS_ERROR_NOT_INITIALIZED;
   1145    }
   1146    // Notify nsTextFrame of masking range change.
   1147    if (RefPtr<PresShell> presShell = document->GetObservingPresShell()) {
   1148      nsAutoScriptBlocker blockRunningScript;
   1149      uint32_t valueLength = text->Length();
   1150      CharacterDataChangeInfo changeInfo = {false, 0, valueLength, valueLength};
   1151      presShell->CharacterDataChanged(text, changeInfo);
   1152    }
   1153 
   1154    // Scroll caret into the view since masking or unmasking character may
   1155    // move caret to outside of the view.
   1156    nsresult rv = ScrollSelectionFocusIntoView();
   1157    if (NS_FAILED(rv)) {
   1158      NS_WARNING("EditorBase::ScrollSelectionFocusIntoView() failed");
   1159      return rv;
   1160    }
   1161  }
   1162 
   1163  if (!IsAllMasked() && aTimeout != 0) {
   1164    // Initialize the timer to mask the range automatically.
   1165    MOZ_ASSERT(HasAutoMaskingTimer());
   1166    DebugOnly<nsresult> rvIgnored = mPasswordMaskData->mTimer->InitWithCallback(
   1167        this, aTimeout, nsITimer::TYPE_ONE_SHOT);
   1168    NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
   1169                         "nsITimer::InitWithCallback() failed, but ignored");
   1170  }
   1171 
   1172  return NS_OK;
   1173 }
   1174 
   1175 // static
   1176 char16_t TextEditor::PasswordMask() {
   1177  char16_t ret = LookAndFeel::GetPasswordCharacter();
   1178  if (!ret) {
   1179    ret = '*';
   1180  }
   1181  return ret;
   1182 }
   1183 
   1184 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHODIMP TextEditor::Notify(nsITimer* aTimer) {
   1185  // Check whether our text editor's password flag was changed before this
   1186  // "hide password character" timer actually fires.
   1187  if (!IsPasswordEditor() || NS_WARN_IF(!mPasswordMaskData)) {
   1188    return NS_OK;
   1189  }
   1190 
   1191  if (IsAllMasked()) {
   1192    return NS_OK;
   1193  }
   1194 
   1195  AutoEditActionDataSetter editActionData(*this, EditAction::eHidePassword);
   1196  if (NS_WARN_IF(!editActionData.CanHandle())) {
   1197    return NS_ERROR_NOT_INITIALIZED;
   1198  }
   1199 
   1200  // Mask all characters.
   1201  nsresult rv = MaskAllCharactersAndNotify();
   1202  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   1203                       "TextEditor::MaskAllCharactersAndNotify() failed");
   1204 
   1205  if (StaticPrefs::editor_password_testing_mask_delay()) {
   1206    if (RefPtr<Element> target = GetInputEventTargetElement()) {
   1207      RefPtr<Document> document = target->OwnerDoc();
   1208      DebugOnly<nsresult> rvIgnored = nsContentUtils::DispatchTrustedEvent(
   1209          document, target, u"MozLastInputMasked"_ns, CanBubble::eYes,
   1210          Cancelable::eNo);
   1211      NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
   1212                           "nsContentUtils::DispatchTrustedEvent("
   1213                           "MozLastInputMasked) failed, but ignored");
   1214    }
   1215  }
   1216 
   1217  return EditorBase::ToGenericNSResult(rv);
   1218 }
   1219 
   1220 NS_IMETHODIMP TextEditor::GetName(nsACString& aName) {
   1221  aName.AssignLiteral("TextEditor");
   1222  return NS_OK;
   1223 }
   1224 
   1225 void TextEditor::WillDeleteText(uint32_t aCurrentLength,
   1226                                uint32_t aRemoveStartOffset,
   1227                                uint32_t aRemoveLength) {
   1228  MOZ_ASSERT(IsEditActionDataAvailable());
   1229 
   1230  if (!IsPasswordEditor() || NS_WARN_IF(!mPasswordMaskData) || IsAllMasked()) {
   1231    return;
   1232  }
   1233 
   1234  // Adjust unmasked range before deletion since DOM mutation may cause
   1235  // layout referring the range in old text.
   1236 
   1237  // If we need to mask automatically, mask all now.
   1238  if (IsMaskingPassword()) {
   1239    DebugOnly<nsresult> rvIgnored = MaskAllCharacters();
   1240    NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
   1241                         "TextEditor::MaskAllCharacters() failed, but ignored");
   1242    return;
   1243  }
   1244 
   1245  if (aRemoveStartOffset < UnmaskedStart()) {
   1246    // If removing range is before the unmasked range, move it.
   1247    if (aRemoveStartOffset + aRemoveLength <= UnmaskedStart()) {
   1248      DebugOnly<nsresult> rvIgnored =
   1249          SetUnmaskRange(UnmaskedStart() - aRemoveLength, UnmaskedLength());
   1250      NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
   1251                           "TextEditor::SetUnmaskRange() failed, but ignored");
   1252      return;
   1253    }
   1254 
   1255    // If removing range starts before unmasked range, and ends in unmasked
   1256    // range, move and shrink the range.
   1257    if (aRemoveStartOffset + aRemoveLength < UnmaskedEnd()) {
   1258      uint32_t unmaskedLengthInRemovingRange =
   1259          aRemoveStartOffset + aRemoveLength - UnmaskedStart();
   1260      DebugOnly<nsresult> rvIgnored = SetUnmaskRange(
   1261          aRemoveStartOffset, UnmaskedLength() - unmaskedLengthInRemovingRange);
   1262      NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
   1263                           "TextEditor::SetUnmaskRange() failed, but ignored");
   1264      return;
   1265    }
   1266 
   1267    // If removing range includes all unmasked range, collapse it to the
   1268    // remove offset.
   1269    DebugOnly<nsresult> rvIgnored = SetUnmaskRange(aRemoveStartOffset, 0);
   1270    NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
   1271                         "TextEditor::SetUnmaskRange() failed, but ignored");
   1272    return;
   1273  }
   1274 
   1275  if (aRemoveStartOffset < UnmaskedEnd()) {
   1276    // If removing range is in unmasked range, shrink the range.
   1277    if (aRemoveStartOffset + aRemoveLength <= UnmaskedEnd()) {
   1278      DebugOnly<nsresult> rvIgnored =
   1279          SetUnmaskRange(UnmaskedStart(), UnmaskedLength() - aRemoveLength);
   1280      NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
   1281                           "TextEditor::SetUnmaskRange() failed, but ignored");
   1282      return;
   1283    }
   1284 
   1285    // If removing range starts from unmasked range, and ends after it,
   1286    // shrink it.
   1287    DebugOnly<nsresult> rvIgnored =
   1288        SetUnmaskRange(UnmaskedStart(), aRemoveStartOffset - UnmaskedStart());
   1289    NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
   1290                         "TextEditor::SetUnmaskRange() failed, but ignored");
   1291    return;
   1292  }
   1293 
   1294  // If removing range is after the unmasked range, keep it.
   1295 }
   1296 
   1297 nsresult TextEditor::DidInsertText(uint32_t aNewLength,
   1298                                   uint32_t aInsertedOffset,
   1299                                   uint32_t aInsertedLength) {
   1300  MOZ_ASSERT(IsEditActionDataAvailable());
   1301 
   1302  if (!IsPasswordEditor() || NS_WARN_IF(!mPasswordMaskData) || IsAllMasked()) {
   1303    return NS_OK;
   1304  }
   1305 
   1306  if (IsMaskingPassword()) {
   1307    // If we need to mask password, mask all right now.
   1308    nsresult rv = MaskAllCharactersAndNotify();
   1309    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   1310                         "TextEditor::MaskAllCharacters() failed");
   1311    return rv;
   1312  }
   1313 
   1314  if (aInsertedOffset < UnmaskedStart()) {
   1315    // If insertion point is before unmasked range, expand the unmasked range
   1316    // to include the new text.
   1317    nsresult rv = SetUnmaskRangeAndNotify(
   1318        aInsertedOffset, UnmaskedEnd() + aInsertedLength - aInsertedOffset);
   1319    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   1320                         "TextEditor::SetUnmaskRangeAndNotify() failed");
   1321    return rv;
   1322  }
   1323 
   1324  if (aInsertedOffset <= UnmaskedEnd()) {
   1325    // If insertion point is in unmasked range, unmask new text.
   1326    nsresult rv = SetUnmaskRangeAndNotify(UnmaskedStart(),
   1327                                          UnmaskedLength() + aInsertedLength);
   1328    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   1329                         "TextEditor::SetUnmaskRangeAndNotify() failed");
   1330    return rv;
   1331  }
   1332 
   1333  // If insertion point is after unmasked range, extend the unmask range to
   1334  // include the new text.
   1335  nsresult rv = SetUnmaskRangeAndNotify(
   1336      UnmaskedStart(), aInsertedOffset + aInsertedLength - UnmaskedStart());
   1337  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   1338                       "TextEditor::SetUnmaskRangeAndNotify() failed");
   1339  return rv;
   1340 }
   1341 
   1342 }  // namespace mozilla