tor-browser

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

IMEContentObserver.cpp (120253B)


      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 "IMEContentObserver.h"
      8 
      9 #include "ContentEventHandler.h"
     10 #include "WritingModes.h"
     11 #include "mozilla/Assertions.h"
     12 #include "mozilla/AsyncEventDispatcher.h"
     13 #include "mozilla/AutoRestore.h"
     14 #include "mozilla/ErrorResult.h"
     15 #include "mozilla/EventStateManager.h"
     16 #include "mozilla/IMEStateManager.h"
     17 #include "mozilla/Logging.h"
     18 #include "mozilla/MouseEvents.h"
     19 #include "mozilla/PresShell.h"
     20 #include "mozilla/StaticPrefs_test.h"
     21 #include "mozilla/TextComposition.h"
     22 #include "mozilla/TextControlElement.h"
     23 #include "mozilla/TextEvents.h"
     24 #include "mozilla/dom/AncestorIterator.h"
     25 #include "mozilla/dom/Document.h"
     26 #include "mozilla/dom/Element.h"
     27 #include "mozilla/dom/Selection.h"
     28 #include "nsAtom.h"
     29 #include "nsContentUtils.h"
     30 #include "nsDocShell.h"
     31 #include "nsGkAtoms.h"
     32 #include "nsIContent.h"
     33 #include "nsIFrame.h"
     34 #include "nsINode.h"
     35 #include "nsISelectionController.h"
     36 #include "nsISupports.h"
     37 #include "nsIWeakReferenceUtils.h"
     38 #include "nsIWidget.h"
     39 #include "nsPresContext.h"
     40 #include "nsRange.h"
     41 #include "nsRefreshDriver.h"
     42 #include "nsString.h"
     43 
     44 namespace mozilla {
     45 
     46 using RawNodePosition = ContentEventHandler::RawNodePosition;
     47 
     48 using namespace dom;
     49 using namespace widget;
     50 
     51 LazyLogModule sIMECOLog("IMEContentObserver");
     52 LazyLogModule sCacheLog("IMEContentObserverCache");
     53 
     54 static const char* ToChar(bool aBool) { return aBool ? "true" : "false"; }
     55 
     56 static const char* ShortenFunctionName(const char* aFunctionName) {
     57  const nsDependentCString name(aFunctionName);
     58  const int32_t startIndexOfIMEContentObserverPrefix =
     59      name.Find("IMEContentObserver::", 0);
     60  if (startIndexOfIMEContentObserverPrefix >= 0) {
     61    return aFunctionName + startIndexOfIMEContentObserverPrefix +
     62           strlen("IMEContentObserver::");
     63  }
     64  return aFunctionName;
     65 }
     66 
     67 /******************************************************************************
     68 * mozilla::IMEContentObserver
     69 ******************************************************************************/
     70 
     71 NS_IMPL_CYCLE_COLLECTION_CLASS(IMEContentObserver)
     72 
     73 // Note that we don't need to add mFirstAddedContainer nor
     74 // mLastAddedContainer to cycle collection because they are non-null only
     75 // during short time and shouldn't be touched while they are non-null.
     76 
     77 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IMEContentObserver)
     78  nsAutoScriptBlocker scriptBlocker;
     79 
     80  tmp->NotifyIMEOfBlur();
     81  tmp->UnregisterObservers();
     82 
     83  NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelection)
     84  NS_IMPL_CYCLE_COLLECTION_UNLINK(mRootElement)
     85  NS_IMPL_CYCLE_COLLECTION_UNLINK(mRootEditableNodeOrTextControlElement)
     86  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocShell)
     87  NS_IMPL_CYCLE_COLLECTION_UNLINK(mEditorBase)
     88  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentObserver)
     89  NS_IMPL_CYCLE_COLLECTION_UNLINK(mEndOfAddedTextCache.mContainerNode)
     90  NS_IMPL_CYCLE_COLLECTION_UNLINK(mEndOfAddedTextCache.mContent)
     91  NS_IMPL_CYCLE_COLLECTION_UNLINK(mStartOfRemovingTextRangeCache.mContainerNode)
     92  NS_IMPL_CYCLE_COLLECTION_UNLINK(mStartOfRemovingTextRangeCache.mContent)
     93  NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE
     94 
     95  tmp->mIMENotificationRequests = nullptr;
     96  tmp->mESM = nullptr;
     97 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     98 
     99 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IMEContentObserver)
    100  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWidget)
    101  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFocusedWidget)
    102  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelection)
    103  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRootElement)
    104  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRootEditableNodeOrTextControlElement)
    105  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocShell)
    106  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEditorBase)
    107  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentObserver)
    108  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEndOfAddedTextCache.mContainerNode)
    109  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEndOfAddedTextCache.mContent)
    110  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(
    111      mStartOfRemovingTextRangeCache.mContainerNode)
    112  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStartOfRemovingTextRangeCache.mContent)
    113 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    114 
    115 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IMEContentObserver)
    116  NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
    117  NS_INTERFACE_MAP_ENTRY(nsIReflowObserver)
    118  NS_INTERFACE_MAP_ENTRY(nsIScrollObserver)
    119  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
    120  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIReflowObserver)
    121 NS_INTERFACE_MAP_END
    122 
    123 NS_IMPL_CYCLE_COLLECTING_ADDREF(IMEContentObserver)
    124 NS_IMPL_CYCLE_COLLECTING_RELEASE(IMEContentObserver)
    125 
    126 IMEContentObserver::IMEContentObserver() {
    127 #ifdef DEBUG
    128  // TODO: Make this test as GTest.
    129  mTextChangeData.Test();
    130 #endif
    131 }
    132 
    133 void IMEContentObserver::Init(nsIWidget& aWidget, nsPresContext& aPresContext,
    134                              Element* aElement, EditorBase& aEditorBase) {
    135  State state = GetState();
    136  if (NS_WARN_IF(state == eState_Observing)) {
    137    return;  // Nothing to do.
    138  }
    139 
    140  bool firstInitialization = state != eState_StoppedObserving;
    141  if (!firstInitialization) {
    142    // If this is now trying to initialize with new contents, all observers
    143    // should be registered again for simpler implementation.
    144    UnregisterObservers();
    145    Clear();
    146  }
    147 
    148  mESM = aPresContext.EventStateManager();
    149  mESM->OnStartToObserveContent(this);
    150 
    151  mWidget = &aWidget;
    152  mIMENotificationRequests = &mWidget->IMENotificationRequestsRef();
    153 
    154  if (!InitWithEditor(aPresContext, aElement, aEditorBase)) {
    155    MOZ_LOG(sIMECOLog, LogLevel::Error,
    156            ("0x%p   Init() FAILED, due to InitWithEditor() "
    157             "failure",
    158             this));
    159    Clear();
    160    return;
    161  }
    162 
    163  if (firstInitialization) {
    164    // Now, try to send NOTIFY_IME_OF_FOCUS to IME via the widget.
    165    MaybeNotifyIMEOfFocusSet();
    166    // When this is called first time, IME has not received NOTIFY_IME_OF_FOCUS
    167    // yet since NOTIFY_IME_OF_FOCUS will be sent to widget asynchronously.
    168    // So, we need to do nothing here.  After NOTIFY_IME_OF_FOCUS has been
    169    // sent, OnIMEReceivedFocus() will be called and content, selection and/or
    170    // position changes will be observed
    171    return;
    172  }
    173 
    174  // When this is called after editor reframing (i.e., the root editable node
    175  // is also recreated), IME has usually received NOTIFY_IME_OF_FOCUS.  In this
    176  // case, we need to restart to observe content, selection and/or position
    177  // changes in new root editable node.
    178  ObserveEditableNode();
    179 
    180  if (!NeedsToNotifyIMEOfSomething()) {
    181    return;
    182  }
    183 
    184  // Some change events may wait to notify IME because this was being
    185  // initialized.  It is the time to flush them.
    186  FlushMergeableNotifications();
    187 }
    188 
    189 void IMEContentObserver::OnIMEReceivedFocus() {
    190  // While Init() notifies IME of focus, pending layout may be flushed
    191  // because the notification may cause querying content.  Then, recursive
    192  // call of Init() with the latest content may occur.  In such case, we
    193  // shouldn't keep first initialization which notified IME of focus.
    194  if (GetState() != eState_Initializing) {
    195    MOZ_LOG(sIMECOLog, LogLevel::Warning,
    196            ("0x%p   OnIMEReceivedFocus(), "
    197             "but the state is not \"initializing\", so does nothing",
    198             this));
    199    return;
    200  }
    201 
    202  // NOTIFY_IME_OF_FOCUS might cause recreating IMEContentObserver
    203  // instance via IMEStateManager::UpdateIMEState().  So, this
    204  // instance might already have been destroyed, check it.
    205  if (!mRootElement) {
    206    MOZ_LOG(sIMECOLog, LogLevel::Warning,
    207            ("0x%p   OnIMEReceivedFocus(), "
    208             "but mRootElement has already been cleared, so does nothing",
    209             this));
    210    return;
    211  }
    212 
    213  // Start to observe which is needed by IME when IME actually has focus.
    214  ObserveEditableNode();
    215 
    216  if (!NeedsToNotifyIMEOfSomething()) {
    217    return;
    218  }
    219 
    220  // Some change events may wait to notify IME because this was being
    221  // initialized.  It is the time to flush them.
    222  FlushMergeableNotifications();
    223 }
    224 
    225 bool IMEContentObserver::InitWithEditor(nsPresContext& aPresContext,
    226                                        Element* aElement,
    227                                        EditorBase& aEditorBase) {
    228  mRootEditableNodeOrTextControlElement =
    229      aEditorBase.IsTextEditor()
    230          ? aEditorBase.GetExposedRoot()
    231          : IMEContentObserver::GetMostDistantInclusiveEditableAncestorNode(
    232                aPresContext, aElement);
    233  if (NS_WARN_IF(!mRootEditableNodeOrTextControlElement)) {
    234    return false;
    235  }
    236 
    237  mEditorBase = &aEditorBase;
    238 
    239  RefPtr<PresShell> presShell = aPresContext.GetPresShell();
    240 
    241  // get selection and root content
    242  nsCOMPtr<nsISelectionController> selCon;
    243  if (mRootEditableNodeOrTextControlElement->IsElement()) {
    244    selCon = aEditorBase.GetSelectionController();
    245    if (NS_WARN_IF(!selCon)) {
    246      return false;
    247    }
    248  } else {
    249    MOZ_ASSERT(mRootEditableNodeOrTextControlElement->IsDocument());
    250    selCon = presShell;
    251    if (NS_WARN_IF(!selCon)) {
    252      return false;
    253    }
    254  }
    255 
    256  mSelection = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL);
    257  if (NS_WARN_IF(!mSelection)) {
    258    return false;
    259  }
    260 
    261  if (mEditorBase->IsTextEditor()) {
    262    mRootElement = mEditorBase->GetRoot();  // The anonymous <div>
    263    MOZ_ASSERT(mRootElement);
    264    MOZ_ASSERT(mRootElement->GetFirstChild());
    265    if (auto* text = Text::FromNodeOrNull(
    266            mRootElement ? mRootElement->GetFirstChild() : nullptr)) {
    267      mTextControlValueLength = ContentEventHandler::GetNativeTextLength(*text);
    268    }
    269    mIsTextControl = true;
    270  } else if (const nsRange* selRange = mSelection->GetRangeAt(0)) {
    271    MOZ_ASSERT(!mIsTextControl);
    272    if (NS_WARN_IF(!selRange->GetStartContainer())) {
    273      return false;
    274    }
    275 
    276    // If an editing host has focus, mRootElement is it.
    277    // Otherwise, if we're in the design mode, mRootElement is the <body> if
    278    // there is and startContainer is not outside of the <body>.  Otherwise, the
    279    // document element is used instead.
    280    nsCOMPtr<nsINode> startContainer = selRange->GetStartContainer();
    281    mRootElement =
    282        Element::FromNodeOrNull(startContainer->GetSelectionRootContent(
    283            presShell, nsINode::IgnoreOwnIndependentSelection::Yes,
    284            nsINode::AllowCrossShadowBoundary::No));
    285  } else {
    286    MOZ_ASSERT(!mIsTextControl);
    287    // If an editing host has focus, mRootElement is it.
    288    // Otherwise, if we're in the design mode, mRootElement is the <body> if
    289    // there is.  Otherwise, the document element is used instead.
    290    const OwningNonNull<nsINode> rootEditableNode(
    291        *mRootEditableNodeOrTextControlElement);
    292    mRootElement =
    293        Element::FromNodeOrNull(rootEditableNode->GetSelectionRootContent(
    294            presShell, nsINode::IgnoreOwnIndependentSelection::Yes,
    295            nsINode::AllowCrossShadowBoundary::No));
    296  }
    297  if (!mRootElement && mRootEditableNodeOrTextControlElement->IsDocument()) {
    298    // The document node is editable, but there are no contents, this document
    299    // is not editable.
    300    return false;
    301  }
    302 
    303  if (NS_WARN_IF(!mRootElement)) {
    304    return false;
    305  }
    306 
    307  mDocShell = aPresContext.GetDocShell();
    308  if (NS_WARN_IF(!mDocShell)) {
    309    return false;
    310  }
    311 
    312  mDocumentObserver = new DocumentObserver(*this);
    313 
    314  return true;
    315 }
    316 
    317 void IMEContentObserver::Clear() {
    318  mEditorBase = nullptr;
    319  mSelection = nullptr;
    320  mRootEditableNodeOrTextControlElement = nullptr;
    321  mRootElement = nullptr;
    322  mDocShell = nullptr;
    323  // Should be safe to clear mDocumentObserver here even though it grabs
    324  // this instance in most cases because this is called by Init() or Destroy().
    325  // The callers of Init() grab this instance with local RefPtr.
    326  // The caller of Destroy() also grabs this instance with local RefPtr.
    327  // So, this won't cause refcount of this instance become 0.
    328  mDocumentObserver = nullptr;
    329 }
    330 
    331 void IMEContentObserver::ObserveEditableNode() {
    332  MOZ_RELEASE_ASSERT(mSelection);
    333  MOZ_RELEASE_ASSERT(mRootElement);
    334  MOZ_RELEASE_ASSERT(GetState() != eState_Observing);
    335 
    336  // If this is called before sending NOTIFY_IME_OF_FOCUS (it's possible when
    337  // the editor is reframed before sending NOTIFY_IME_OF_FOCUS asynchronously),
    338  // the notification requests of mWidget may be different from after the widget
    339  // receives NOTIFY_IME_OF_FOCUS.   So, this should be called again by
    340  // OnIMEReceivedFocus() which is called after sending NOTIFY_IME_OF_FOCUS.
    341  if (!mIMEHasFocus) {
    342    MOZ_ASSERT(!mWidget || mNeedsToNotifyIMEOfFocusSet ||
    343                   mSendingNotification == NOTIFY_IME_OF_FOCUS,
    344               "Wow, OnIMEReceivedFocus() won't be called?");
    345    return;
    346  }
    347 
    348  mIsObserving = true;
    349  if (mEditorBase) {
    350    mEditorBase->SetIMEContentObserver(this);
    351  }
    352 
    353  MOZ_LOG(sIMECOLog, LogLevel::Info,
    354          ("0x%p ObserveEditableNode(), starting to observe 0x%p (%s)", this,
    355           mRootElement.get(), ToString(*mRootElement).c_str()));
    356 
    357  mRootElement->AddMutationObserver(this);
    358  // If it's in a document (should be so), we can use document observer to
    359  // reduce redundant computation of text change offsets.
    360  Document* doc = mRootElement->GetComposedDoc();
    361  if (doc) {
    362    RefPtr<DocumentObserver> documentObserver = mDocumentObserver;
    363    documentObserver->Observe(doc);
    364  }
    365 
    366  if (mDocShell) {
    367    // Add scroll position listener and reflow observer to detect position
    368    // and size changes
    369    mDocShell->AddWeakScrollObserver(this);
    370    mDocShell->AddWeakReflowObserver(this);
    371  }
    372 }
    373 
    374 void IMEContentObserver::NotifyIMEOfBlur() {
    375  // Prevent any notifications to be sent IME.
    376  nsCOMPtr<nsIWidget> widget;
    377  mWidget.swap(widget);
    378  mIMENotificationRequests = nullptr;
    379 
    380  // If we hasn't been set focus, we shouldn't send blur notification to IME.
    381  if (!mIMEHasFocus) {
    382    return;
    383  }
    384 
    385  // mWidget must have been non-nullptr if IME has focus.
    386  MOZ_RELEASE_ASSERT(widget);
    387 
    388  RefPtr<IMEContentObserver> kungFuDeathGrip(this);
    389 
    390  MOZ_LOG(sIMECOLog, LogLevel::Info,
    391          ("0x%p NotifyIMEOfBlur(), sending NOTIFY_IME_OF_BLUR", this));
    392 
    393  // For now, we need to send blur notification in any condition because
    394  // we don't have any simple ways to send blur notification asynchronously.
    395  // After this call, Destroy() or Unlink() will stop observing the content
    396  // and forget everything.  Therefore, if it's not safe to send notification
    397  // when script blocker is unlocked, we cannot send blur notification after
    398  // that and before next focus notification.
    399  // Anyway, as far as we know, IME doesn't try to query content when it loses
    400  // focus.  So, this may not cause any problem.
    401  mIMEHasFocus = false;
    402  IMEStateManager::NotifyIME(IMENotification(NOTIFY_IME_OF_BLUR), widget);
    403 
    404  MOZ_LOG(sIMECOLog, LogLevel::Debug,
    405          ("0x%p   NotifyIMEOfBlur(), sent NOTIFY_IME_OF_BLUR", this));
    406 }
    407 
    408 void IMEContentObserver::UnregisterObservers() {
    409  if (!mIsObserving) {
    410    return;
    411  }
    412 
    413  MOZ_LOG(sIMECOLog, LogLevel::Info,
    414          ("0x%p UnregisterObservers(), stop observing 0x%p (%s)", this,
    415           mRootElement.get(),
    416           mRootElement ? ToString(*mRootElement).c_str() : "nullptr"));
    417 
    418  mIsObserving = false;
    419 
    420  if (mEditorBase) {
    421    mEditorBase->SetIMEContentObserver(nullptr);
    422  }
    423 
    424  if (mSelection) {
    425    mSelectionData.Clear();
    426    mFocusedWidget = nullptr;
    427  }
    428 
    429  if (mRootElement) {
    430    mRootElement->RemoveMutationObserver(this);
    431  }
    432 
    433  if (mDocumentObserver) {
    434    RefPtr<DocumentObserver> documentObserver = mDocumentObserver;
    435    documentObserver->StopObserving();
    436  }
    437 
    438  if (mDocShell) {
    439    mDocShell->RemoveWeakScrollObserver(this);
    440    mDocShell->RemoveWeakReflowObserver(this);
    441  }
    442 }
    443 
    444 nsPresContext* IMEContentObserver::GetPresContext() const {
    445  return mESM ? mESM->GetPresContext() : nullptr;
    446 }
    447 
    448 void IMEContentObserver::Destroy() {
    449  // WARNING: When you change this method, you have to check Unlink() too.
    450 
    451  // Note that don't send any notifications later from here.  I.e., notify
    452  // IMEStateManager of the blur synchronously because IMEStateManager needs to
    453  // stop notifying the main process if this is requested by the main process.
    454  NotifyIMEOfBlur();
    455  UnregisterObservers();
    456  Clear();
    457 
    458  mWidget = nullptr;
    459  mIMENotificationRequests = nullptr;
    460 
    461  if (mESM) {
    462    mESM->OnStopObservingContent(this);
    463    mESM = nullptr;
    464  }
    465 }
    466 
    467 bool IMEContentObserver::Destroyed() const { return !mWidget; }
    468 
    469 void IMEContentObserver::DisconnectFromEventStateManager() { mESM = nullptr; }
    470 
    471 bool IMEContentObserver::MaybeReinitialize(nsIWidget& aWidget,
    472                                           nsPresContext& aPresContext,
    473                                           Element* aElement,
    474                                           EditorBase& aEditorBase) {
    475  if (!IsObservingElement(aPresContext, aElement)) {
    476    return false;
    477  }
    478 
    479  if (GetState() == eState_StoppedObserving) {
    480    Init(aWidget, aPresContext, aElement, aEditorBase);
    481  }
    482  return IsObserving(aPresContext, aElement);
    483 }
    484 
    485 bool IMEContentObserver::IsObserving(const nsPresContext& aPresContext,
    486                                     const Element* aElement) const {
    487  if (GetState() != eState_Observing) {
    488    return false;
    489  }
    490  // If aElement is not a text control, aElement is an editing host or entire
    491  // the document is editable in the design mode.  Therefore, return false if
    492  // we're observing an anonymous subtree of a text control.
    493  const auto* const textControlElement =
    494      TextControlElement::FromNodeOrNull(aElement);
    495  if (!textControlElement ||
    496      !textControlElement->IsSingleLineTextControlOrTextArea()) {
    497    if (mIsTextControl) {
    498      return false;
    499    }
    500  }
    501  // If aElement is a text control, return true if we're observing the anonymous
    502  // subtree of aElement.  Therefore, return false if we're observing with
    503  // HTMLEditor.
    504  else if (!mIsTextControl) {
    505    return false;
    506  }
    507  return IsObservingElement(aPresContext, aElement);
    508 }
    509 
    510 bool IMEContentObserver::IsBeingInitializedFor(
    511    const nsPresContext& aPresContext, const Element* aElement,
    512    const EditorBase& aEditorBase) const {
    513  return GetState() == eState_Initializing && mEditorBase == &aEditorBase &&
    514         IsObservingElement(aPresContext, aElement);
    515 }
    516 
    517 bool IMEContentObserver::IsObserving(
    518    const TextComposition& aTextComposition) const {
    519  if (GetState() != eState_Observing) {
    520    return false;
    521  }
    522  nsPresContext* const presContext = aTextComposition.GetPresContext();
    523  if (NS_WARN_IF(!presContext)) {
    524    return false;
    525  }
    526  if (presContext != GetPresContext()) {
    527    return false;  // observing different document
    528  }
    529  auto* const elementHavingComposition =
    530      Element::FromNodeOrNull(aTextComposition.GetEventTargetNode());
    531  bool isObserving = IsObservingElement(*presContext, elementHavingComposition);
    532 #ifdef DEBUG
    533  if (isObserving) {
    534    if (mIsTextControl) {
    535      MOZ_ASSERT(elementHavingComposition);
    536      MOZ_ASSERT(elementHavingComposition->IsTextControlElement(),
    537                 "Should've never started to observe non-text-control element");
    538      // XXX Our fake focus move has not been implemented properly. So, the
    539      // following assertions may fail, but I don't like to make the failures
    540      // cause crash even in debug builds because it may block developers to
    541      // debug web-compat issues.  On the other hand, it'd be nice if we can
    542      // detect the bug with automated tests.  Therefore, the following
    543      // assertions are NS_ASSERTION.
    544      NS_ASSERTION(static_cast<TextControlElement*>(elementHavingComposition)
    545                       ->IsSingleLineTextControlOrTextArea(),
    546                   "Should've stopped observing when the type is changed");
    547      NS_ASSERTION(!elementHavingComposition->IsInDesignMode(),
    548                   "Should've stopped observing when the design mode started");
    549    } else if (elementHavingComposition) {
    550      NS_ASSERTION(
    551          !elementHavingComposition->IsTextControlElement() ||
    552              !static_cast<TextControlElement*>(elementHavingComposition)
    553                   ->IsSingleLineTextControlOrTextArea(),
    554          "Should've never started to observe text-control element or "
    555          "stopped observing it when the type is changed");
    556    } else {
    557      MOZ_ASSERT(presContext->GetPresShell());
    558      MOZ_ASSERT(presContext->GetPresShell()->GetDocument());
    559      NS_ASSERTION(
    560          presContext->GetPresShell()->GetDocument()->IsInDesignMode(),
    561          "Should be observing entire the document only in the design mode");
    562    }
    563  }
    564 #endif  // #ifdef DEBUG
    565  return isObserving;
    566 }
    567 
    568 IMEContentObserver::State IMEContentObserver::GetState() const {
    569  if (!mSelection || !mRootElement || !mRootEditableNodeOrTextControlElement) {
    570    return eState_NotObserving;  // failed to initialize or finalized.
    571  }
    572  if (!mRootElement->IsInComposedDoc()) {
    573    // the focused editor has already been reframed.
    574    return eState_StoppedObserving;
    575  }
    576  return mIsObserving ? eState_Observing : eState_Initializing;
    577 }
    578 
    579 bool IMEContentObserver::IsObservingElement(const nsPresContext& aPresContext,
    580                                            const Element* aElement) const {
    581  MOZ_ASSERT_IF(aElement,
    582                aElement->GetPresContext(
    583                    Element::PresContextFor::eForComposedDoc) == &aPresContext);
    584 
    585  if (GetPresContext() != &aPresContext) {
    586    return false;
    587  }
    588  // If this is initialized with a TextEditor,
    589  // mRootEditableNodeOrTextControlElement is a text control element. Therefore,
    590  // aElement should be aElement.
    591  if (mIsTextControl) {
    592    return !aElement->IsInDesignMode() &&
    593           aElement == mRootEditableNodeOrTextControlElement;
    594  }
    595  // If this is initialized with an HTMLEditor,
    596  // mRootEditableNodeOrTextControlElement is an editing host when this is
    597  // initialized.  However, its ancestor may become editable.  Therefore, we
    598  // need to check whether it's still an editing host.
    599  return mRootEditableNodeOrTextControlElement ==
    600         IMEContentObserver::GetMostDistantInclusiveEditableAncestorNode(
    601             aPresContext, aElement);
    602 }
    603 
    604 // static
    605 nsINode* IMEContentObserver::GetMostDistantInclusiveEditableAncestorNode(
    606    const nsPresContext& aPresContext, const Element* aElement) {
    607  if (aElement) {
    608    // If the focused content is in design mode, return is composed document
    609    // because aElement may be in UA widget shadow tree.
    610    if (aElement->IsInDesignMode()) {
    611      return aElement->GetComposedDoc();
    612    }
    613    // Otherwise, return the editing host.
    614    return aElement->GetEditingHost();
    615  }
    616 
    617  return aPresContext.Document() && aPresContext.Document()->IsInDesignMode()
    618             ? aPresContext.Document()
    619             : nullptr;
    620 }
    621 
    622 bool IMEContentObserver::IsEditorHandlingEventForComposition() const {
    623  if (!mWidget) {
    624    return false;
    625  }
    626  RefPtr<TextComposition> composition =
    627      IMEStateManager::GetTextCompositionFor(mWidget);
    628  if (!composition) {
    629    return false;
    630  }
    631  return composition->EditorIsHandlingLatestChange();
    632 }
    633 
    634 bool IMEContentObserver::IsEditorComposing() const {
    635  // Note that don't use TextComposition here. The important thing is,
    636  // whether the editor already started to handle composition because
    637  // web contents can change selection, text content and/or something from
    638  // compositionstart event listener which is run before EditorBase handles it.
    639  if (NS_WARN_IF(!mEditorBase)) {
    640    return false;
    641  }
    642  return mEditorBase->IsIMEComposing();
    643 }
    644 
    645 nsresult IMEContentObserver::GetSelectionAndRoot(Selection** aSelection,
    646                                                 Element** aRootElement) const {
    647  if (!mRootEditableNodeOrTextControlElement || !mSelection) {
    648    return NS_ERROR_NOT_AVAILABLE;
    649  }
    650 
    651  NS_ASSERTION(mSelection && mRootElement, "uninitialized content observer");
    652  NS_ADDREF(*aSelection = mSelection);
    653  NS_ADDREF(*aRootElement = mRootElement);
    654  return NS_OK;
    655 }
    656 
    657 void IMEContentObserver::OnSelectionChange(Selection& aSelection) {
    658  if (!mIsObserving) {
    659    return;
    660  }
    661 
    662  if (mWidget) {
    663    bool causedByComposition = IsEditorHandlingEventForComposition();
    664    bool causedBySelectionEvent = TextComposition::IsHandlingSelectionEvent();
    665    bool duringComposition = IsEditorComposing();
    666    MaybeNotifyIMEOfSelectionChange(causedByComposition, causedBySelectionEvent,
    667                                    duringComposition);
    668  }
    669 }
    670 
    671 void IMEContentObserver::ScrollPositionChanged() {
    672  if (!NeedsPositionChangeNotification()) {
    673    return;
    674  }
    675 
    676  MaybeNotifyIMEOfPositionChange();
    677 }
    678 
    679 NS_IMETHODIMP
    680 IMEContentObserver::Reflow(DOMHighResTimeStamp aStart,
    681                           DOMHighResTimeStamp aEnd) {
    682  if (!NeedsPositionChangeNotification()) {
    683    return NS_OK;
    684  }
    685 
    686  MaybeNotifyIMEOfPositionChange();
    687  return NS_OK;
    688 }
    689 
    690 NS_IMETHODIMP
    691 IMEContentObserver::ReflowInterruptible(DOMHighResTimeStamp aStart,
    692                                        DOMHighResTimeStamp aEnd) {
    693  if (!NeedsPositionChangeNotification()) {
    694    return NS_OK;
    695  }
    696 
    697  MaybeNotifyIMEOfPositionChange();
    698  return NS_OK;
    699 }
    700 
    701 nsresult IMEContentObserver::HandleQueryContentEvent(
    702    WidgetQueryContentEvent* aEvent) {
    703  // If the instance has normal selection cache and the query event queries
    704  // normal selection's range, it should use the cached selection which was
    705  // sent to the widget.  However, if this instance has already received new
    706  // selection change notification but hasn't updated the cache yet (i.e.,
    707  // not sending selection change notification to IME, don't use the cached
    708  // value.  Note that don't update selection cache here since if you update
    709  // selection cache here, IMENotificationSender won't notify IME of selection
    710  // change because it looks like that the selection isn't actually changed.
    711  const bool isSelectionCacheAvailable = aEvent->mUseNativeLineBreak &&
    712                                         mSelectionData.IsInitialized() &&
    713                                         !mNeedsToNotifyIMEOfSelectionChange;
    714  if (isSelectionCacheAvailable && aEvent->mMessage == eQuerySelectedText &&
    715      aEvent->mInput.mSelectionType == SelectionType::eNormal) {
    716    aEvent->EmplaceReply();
    717    if (mSelectionData.HasRange()) {
    718      aEvent->mReply->mOffsetAndData.emplace(mSelectionData.mOffset,
    719                                             mSelectionData.String(),
    720                                             OffsetAndDataFor::SelectedString);
    721      aEvent->mReply->mReversed = mSelectionData.mReversed;
    722    }
    723    aEvent->mReply->mContentsRoot = mRootElement;
    724    aEvent->mReply->mWritingMode = mSelectionData.GetWritingMode();
    725    // The selection cache in IMEContentObserver must always have been in
    726    // an editing host (or an editable anonymous <div> element).  Therefore,
    727    // we set mIsEditableContent to true here even though it's already been
    728    // blurred or changed its editable state but the selection cache has not
    729    // been invalidated yet.
    730    aEvent->mReply->mIsEditableContent = true;
    731    MOZ_LOG(sIMECOLog, LogLevel::Debug,
    732            ("0x%p HandleQueryContentEvent(aEvent={ "
    733             "mMessage=%s, mReply=%s })",
    734             this, ToChar(aEvent->mMessage), ToString(aEvent->mReply).c_str()));
    735    return NS_OK;
    736  }
    737 
    738  MOZ_LOG(sIMECOLog, LogLevel::Info,
    739          ("0x%p HandleQueryContentEvent(aEvent={ mMessage=%s })", this,
    740           ToChar(aEvent->mMessage)));
    741 
    742  // If we can make the event's input offset absolute with TextComposition or
    743  // mSelection, we should set it here for reducing the cost of computing
    744  // selection start offset.  If ContentEventHandler receives a
    745  // WidgetQueryContentEvent whose input offset is relative to insertion point,
    746  // it computes current selection start offset (this may be expensive) and
    747  // make the offset absolute value itself.
    748  // Note that calling MakeOffsetAbsolute() makes the event a query event with
    749  // absolute offset.  So, ContentEventHandler doesn't pay any additional cost
    750  // after calling MakeOffsetAbsolute() here.
    751  if (aEvent->mInput.mRelativeToInsertionPoint &&
    752      aEvent->mInput.IsValidEventMessage(aEvent->mMessage)) {
    753    RefPtr<TextComposition> composition =
    754        IMEStateManager::GetTextCompositionFor(aEvent->mWidget);
    755    if (composition) {
    756      uint32_t compositionStart = composition->NativeOffsetOfStartComposition();
    757      if (NS_WARN_IF(!aEvent->mInput.MakeOffsetAbsolute(compositionStart))) {
    758        return NS_ERROR_FAILURE;
    759      }
    760    } else if (isSelectionCacheAvailable && mSelectionData.HasRange()) {
    761      const uint32_t selectionStart = mSelectionData.mOffset;
    762      if (NS_WARN_IF(!aEvent->mInput.MakeOffsetAbsolute(selectionStart))) {
    763        return NS_ERROR_FAILURE;
    764      }
    765    }
    766  }
    767 
    768  AutoRestore<bool> handling(mIsHandlingQueryContentEvent);
    769  mIsHandlingQueryContentEvent = true;
    770  ContentEventHandler handler(GetPresContext());
    771  nsresult rv = handler.HandleQueryContentEvent(aEvent);
    772  if (NS_WARN_IF(Destroyed())) {
    773    // If this has already destroyed during querying the content, the query
    774    // is outdated even if it's succeeded.  So, make the query fail.
    775    aEvent->mReply.reset();
    776    MOZ_LOG(sIMECOLog, LogLevel::Warning,
    777            ("0x%p   HandleQueryContentEvent(), WARNING, "
    778             "IMEContentObserver has been destroyed during the query, "
    779             "making the query fail",
    780             this));
    781    return rv;
    782  }
    783 
    784  if (aEvent->Succeeded() &&
    785      NS_WARN_IF(aEvent->mReply->mContentsRoot != mRootElement)) {
    786    // Focus has changed unexpectedly, so make the query fail.
    787    aEvent->mReply.reset();
    788  }
    789  return rv;
    790 }
    791 
    792 nsresult IMEContentObserver::MaybeHandleSelectionEvent(
    793    nsPresContext* aPresContext, WidgetSelectionEvent* aEvent) {
    794  MOZ_ASSERT(aEvent);
    795  MOZ_ASSERT(aEvent->mMessage == eSetSelection);
    796  NS_ASSERTION(!mNeedsToNotifyIMEOfSelectionChange,
    797               "Selection cache has not been updated yet");
    798 
    799  MOZ_LOG(
    800      sIMECOLog, LogLevel::Debug,
    801      ("0x%p MaybeHandleSelectionEvent(aEvent={ "
    802       "mMessage=%s, mOffset=%u, mLength=%u, mReversed=%s, "
    803       "mExpandToClusterBoundary=%s, mUseNativeLineBreak=%s }), "
    804       "mSelectionData=%s",
    805       this, ToChar(aEvent->mMessage), aEvent->mOffset, aEvent->mLength,
    806       ToChar(aEvent->mReversed), ToChar(aEvent->mExpandToClusterBoundary),
    807       ToChar(aEvent->mUseNativeLineBreak), ToString(mSelectionData).c_str()));
    808 
    809  // When we have Selection cache, and the caller wants to set same selection
    810  // range, we shouldn't try to compute same range because it may be impossible
    811  // if the range boundary is around element boundaries which won't be
    812  // serialized with line breaks like close tags of inline elements.  In that
    813  // case, inserting new text at different point may be different from intention
    814  // of users or web apps which set current selection.
    815  // FIXME: We cache only selection data computed with native line breaker
    816  // lengths.  Perhaps, we should improve the struct to have both data of
    817  // offset and length.  E.g., adding line break counts for both offset and
    818  // length.
    819  if (!mNeedsToNotifyIMEOfSelectionChange && aEvent->mUseNativeLineBreak &&
    820      mSelectionData.IsInitialized() && mSelectionData.HasRange() &&
    821      mSelectionData.StartOffset() == aEvent->mOffset &&
    822      mSelectionData.Length() == aEvent->mLength) {
    823    if (RefPtr<Selection> selection = mSelection) {
    824      selection->ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION);
    825    }
    826    aEvent->mSucceeded = true;
    827    return NS_OK;
    828  }
    829 
    830  ContentEventHandler handler(aPresContext);
    831  return handler.OnSelectionEvent(aEvent);
    832 }
    833 
    834 bool IMEContentObserver::OnMouseButtonEvent(nsPresContext& aPresContext,
    835                                            WidgetMouseEvent& aMouseEvent) {
    836  if (!mIMENotificationRequests ||
    837      !mIMENotificationRequests->WantMouseButtonEventOnChar()) {
    838    return false;
    839  }
    840  if (!aMouseEvent.IsTrusted() || aMouseEvent.DefaultPrevented() ||
    841      !aMouseEvent.mWidget) {
    842    return false;
    843  }
    844  // Now, we need to notify only mouse down and mouse up event.
    845  switch (aMouseEvent.mMessage) {
    846    case eMouseUp:
    847    case eMouseDown:
    848      break;
    849    default:
    850      return false;
    851  }
    852  if (NS_WARN_IF(!mWidget) || NS_WARN_IF(mWidget->Destroyed())) {
    853    return false;
    854  }
    855 
    856  WidgetQueryContentEvent queryCharAtPointEvent(true, eQueryCharacterAtPoint,
    857                                                aMouseEvent.mWidget);
    858  queryCharAtPointEvent.mRefPoint = aMouseEvent.mRefPoint;
    859  ContentEventHandler handler(&aPresContext);
    860  handler.OnQueryCharacterAtPoint(&queryCharAtPointEvent);
    861  if (NS_WARN_IF(queryCharAtPointEvent.Failed()) ||
    862      queryCharAtPointEvent.DidNotFindChar()) {
    863    return false;
    864  }
    865 
    866  // The widget might be destroyed during querying the content since it
    867  // causes flushing layout.
    868  if (!mWidget || NS_WARN_IF(mWidget->Destroyed())) {
    869    return false;
    870  }
    871 
    872  // The result character rect is relative to the top level widget.
    873  // We should notify it with offset in the widget.
    874  nsIWidget* topLevelWidget = mWidget->GetTopLevelWidget();
    875  if (topLevelWidget && topLevelWidget != mWidget) {
    876    queryCharAtPointEvent.mReply->mRect.MoveBy(
    877        topLevelWidget->WidgetToScreenOffset() -
    878        mWidget->WidgetToScreenOffset());
    879  }
    880  // The refPt is relative to its widget.
    881  // We should notify it with offset in the widget.
    882  if (aMouseEvent.mWidget != mWidget) {
    883    queryCharAtPointEvent.mRefPoint +=
    884        aMouseEvent.mWidget->WidgetToScreenOffset() -
    885        mWidget->WidgetToScreenOffset();
    886  }
    887 
    888  IMENotification notification(NOTIFY_IME_OF_MOUSE_BUTTON_EVENT);
    889  notification.mMouseButtonEventData.mEventMessage = aMouseEvent.mMessage;
    890  notification.mMouseButtonEventData.mOffset =
    891      queryCharAtPointEvent.mReply->StartOffset();
    892  notification.mMouseButtonEventData.mCursorPos =
    893      queryCharAtPointEvent.mRefPoint;
    894  notification.mMouseButtonEventData.mCharRect =
    895      queryCharAtPointEvent.mReply->mRect;
    896  notification.mMouseButtonEventData.mButton = aMouseEvent.mButton;
    897  notification.mMouseButtonEventData.mButtons = aMouseEvent.mButtons;
    898  notification.mMouseButtonEventData.mModifiers = aMouseEvent.mModifiers;
    899 
    900  nsresult rv = IMEStateManager::NotifyIME(notification, mWidget);
    901  if (NS_WARN_IF(NS_FAILED(rv))) {
    902    return false;
    903  }
    904 
    905  bool consumed = (rv == NS_SUCCESS_EVENT_CONSUMED);
    906  if (consumed) {
    907    aMouseEvent.PreventDefault();
    908  }
    909  return consumed;
    910 }
    911 
    912 void IMEContentObserver::CharacterDataWillChange(
    913    nsIContent* aContent, const CharacterDataChangeInfo& aInfo) {
    914  if (!aContent->IsText()) {
    915    return;  // Ignore if it's a comment node or something other invisible data
    916             // node.
    917  }
    918  MOZ_ASSERT(mPreCharacterDataChangeLength < 0,
    919             "CharacterDataChanged() should've reset "
    920             "mPreCharacterDataChangeLength");
    921 
    922  if (!NeedsTextChangeNotification() ||
    923      !nsContentUtils::IsInSameAnonymousTree(mRootElement, aContent)) {
    924    return;
    925  }
    926 
    927  mEndOfAddedTextCache.Clear(__FUNCTION__);
    928  mStartOfRemovingTextRangeCache.Clear(__FUNCTION__);
    929 
    930  // Although we don't assume this change occurs while this is storing
    931  // the range of added consecutive nodes, if it actually happens, we need to
    932  // flush them since this change may occur before or in the range.  So, it's
    933  // safe to flush pending computation of mTextChangeData before handling this.
    934  if (mAddedContentCache.HasCache()) {
    935    NotifyIMEOfCachedConsecutiveNewNodes(__FUNCTION__);
    936  }
    937 
    938  mPreCharacterDataChangeLength = ContentEventHandler::GetNativeTextLength(
    939      *aContent->AsText(), aInfo.mChangeStart, aInfo.mChangeEnd);
    940  MOZ_ASSERT(
    941      mPreCharacterDataChangeLength >= aInfo.mChangeEnd - aInfo.mChangeStart,
    942      "The computed length must be same as or larger than XP length");
    943 }
    944 
    945 void IMEContentObserver::CharacterDataChanged(
    946    nsIContent* aContent, const CharacterDataChangeInfo& aInfo) {
    947  if (!aContent->IsText()) {
    948    return;  // Ignore if it's a comment node or something other invisible data
    949             // node.
    950  }
    951 
    952  // Let TextComposition have a change to update composition string range in
    953  // the text node if the change is caused by the web apps.
    954  if (mWidget && !IsEditorHandlingEventForComposition()) {
    955    if (RefPtr<TextComposition> composition =
    956            IMEStateManager::GetTextCompositionFor(mWidget)) {
    957      composition->OnCharacterDataChanged(*aContent->AsText(), aInfo);
    958    }
    959  }
    960 
    961  if (!NeedsTextChangeNotification() ||
    962      !nsContentUtils::IsInSameAnonymousTree(mRootElement, aContent)) {
    963    return;
    964  }
    965 
    966  if (mAddedContentCache.HasCache()) {
    967    NotifyIMEOfCachedConsecutiveNewNodes(__FUNCTION__);
    968  }
    969  mEndOfAddedTextCache.Clear(__FUNCTION__);
    970  mStartOfRemovingTextRangeCache.Clear(__FUNCTION__);
    971  MOZ_ASSERT(
    972      !mAddedContentCache.HasCache(),
    973      "The stored range should be flushed before actually the data is changed");
    974 
    975  int64_t removedLength = mPreCharacterDataChangeLength;
    976  mPreCharacterDataChangeLength = -1;
    977 
    978  MOZ_ASSERT(removedLength >= 0,
    979             "mPreCharacterDataChangeLength should've been set by "
    980             "CharacterDataWillChange()");
    981 
    982  uint32_t offset = 0;
    983  if (mIsTextControl) {
    984    // If we're observing a text control, mRootElement is the anonymous <div>
    985    // element which has only one text node and/or invisible <br> element.
    986    // TextEditor assumes this structure when it handles editing commands.
    987    // Therefore, it's safe to assume same things here.
    988    MOZ_ASSERT(mRootElement->GetFirstChild() == aContent);
    989    if (aInfo.mChangeStart) {
    990      offset = ContentEventHandler::GetNativeTextLength(*aContent->AsText(), 0,
    991                                                        aInfo.mChangeStart);
    992    }
    993  } else {
    994    nsresult rv = ContentEventHandler::GetFlatTextLengthInRange(
    995        RawNodePosition::BeforeFirstContentOf(*mRootElement),
    996        RawNodePosition(aContent, aInfo.mChangeStart), mRootElement, &offset,
    997        LINE_BREAK_TYPE_NATIVE);
    998    if (NS_WARN_IF(NS_FAILED(rv))) {
    999      return;
   1000    }
   1001  }
   1002 
   1003  uint32_t newLength = ContentEventHandler::GetNativeTextLength(
   1004      *aContent->AsText(), aInfo.mChangeStart,
   1005      aInfo.mChangeStart + aInfo.mReplaceLength);
   1006 
   1007  uint32_t oldEnd = offset + static_cast<uint32_t>(removedLength);
   1008  uint32_t newEnd = offset + newLength;
   1009 
   1010  TextChangeData data(offset, oldEnd, newEnd,
   1011                      IsEditorHandlingEventForComposition(),
   1012                      IsEditorComposing());
   1013  MaybeNotifyIMEOfTextChange(data);
   1014 }
   1015 
   1016 void IMEContentObserver::ContentAdded(nsINode* aContainer,
   1017                                      nsIContent* aFirstContent,
   1018                                      nsIContent* aLastContent) {
   1019  if (!NeedsTextChangeNotification() ||
   1020      !nsContentUtils::IsInSameAnonymousTree(mRootElement, aFirstContent)) {
   1021    return;
   1022  }
   1023 
   1024  // We can skip everything when a padding <br> element is added since its text
   1025  // length is 0.
   1026  if (aFirstContent == aLastContent) {
   1027    if (const auto* brElement = HTMLBRElement::FromNode(aFirstContent)) {
   1028      if (MOZ_LIKELY(!brElement->HasChildNodes()) &&
   1029          (brElement->IsPaddingForEmptyEditor() ||
   1030           brElement->IsPaddingForEmptyLastLine())) {
   1031        return;
   1032      }
   1033    }
   1034  }
   1035 
   1036  MOZ_ASSERT(IsInDocumentChange());
   1037  MOZ_ASSERT_IF(aFirstContent, aFirstContent->GetParentNode() == aContainer);
   1038  MOZ_ASSERT_IF(aLastContent, aLastContent->GetParentNode() == aContainer);
   1039 
   1040  // While a document change, new nodes should be added consecutively in a
   1041  // container node.  Therefore, we can cache the first added node and the last
   1042  // added node until ending the document change at least.  Then, we can avoid
   1043  // to compute first added node offset in the flattened text repeatedly.
   1044  bool needToCache = true;
   1045  if (mAddedContentCache.HasCache()) {
   1046    MOZ_DIAGNOSTIC_ASSERT(aFirstContent->GetParentNode() ==
   1047                          aLastContent->GetParentNode());
   1048    if (mAddedContentCache.IsInRange(*aFirstContent, mRootElement)) {
   1049      // The new content nodes are in the range, we can include their text
   1050      // length when we flush the cached range later.  Therefore, we need to
   1051      // do nothing in this case.
   1052      needToCache = false;
   1053      MOZ_LOG(sCacheLog, LogLevel::Info,
   1054              ("ContentAdded: mAddedContentCache already caches the give "
   1055               "content nodes"));
   1056      MOZ_ASSERT(mAddedContentCache.IsInRange(*aLastContent, mRootElement));
   1057    }
   1058    // When new nodes are inserted in a different container, let's flush the
   1059    // preceding content first.  Then, we should restart to cache the new
   1060    // inserted nodes.
   1061    else if (!mAddedContentCache.CanMergeWith(*aFirstContent, *aLastContent,
   1062                                              mRootElement)) {
   1063      MOZ_LOG(sCacheLog, LogLevel::Info,
   1064              ("ContentAdded: mAddedContentCache was cached not in current "
   1065               "document change and new content nodes cannot be merged"));
   1066      mEndOfAddedTextCache.Clear(__FUNCTION__);
   1067      mStartOfRemovingTextRangeCache.Clear(__FUNCTION__);
   1068      OffsetAndLengthAdjustments differences;
   1069      Result<std::pair<uint32_t, uint32_t>, nsresult> offsetAndLength =
   1070          mAddedContentCache.ComputeFlatTextRangeBeforeInsertingNewContent(
   1071              *aFirstContent, *aLastContent, mRootElement, differences);
   1072      if (NS_WARN_IF(offsetAndLength.isErr())) {
   1073        MOZ_LOG(sCacheLog, LogLevel::Error,
   1074                ("ContentAdded: "
   1075                 "AddedContentCache::"
   1076                 "ComputeFlatTextRangeExcludingInsertingNewContent() failed"));
   1077        mAddedContentCache.Clear(__FUNCTION__);
   1078        return;
   1079      }
   1080      NotifyIMEOfCachedConsecutiveNewNodes(
   1081          __FUNCTION__, Some(offsetAndLength.inspect().first),
   1082          Some(offsetAndLength.inspect().second), differences);
   1083      mAddedContentCache.Clear(__FUNCTION__);
   1084    }
   1085  }
   1086 
   1087  mEndOfAddedTextCache.ContentAdded(__FUNCTION__, *aFirstContent, *aLastContent,
   1088                                    Nothing(), mRootElement);
   1089  mStartOfRemovingTextRangeCache.ContentAdded(
   1090      __FUNCTION__, *aFirstContent, *aLastContent, Nothing(), mRootElement);
   1091 
   1092  if (!needToCache) {
   1093    return;
   1094  }
   1095 
   1096  // Okay, now, we can start to cache new nodes or merge the range of new
   1097  // nodes with the cached range.
   1098  if (!mAddedContentCache.TryToCache(*aFirstContent, *aLastContent,
   1099                                     mRootElement)) {
   1100    // Flush the old range first.
   1101    MOZ_LOG(sCacheLog, LogLevel::Info,
   1102            ("ContentAdded: called during a document change flushed "
   1103             "previous added nodes (aFirstContent=%s, aLastContent=%s)",
   1104             ToString(RefPtr<nsINode>(aFirstContent)).c_str(),
   1105             ToString(RefPtr<nsINode>(aLastContent)).c_str()));
   1106    NotifyIMEOfCachedConsecutiveNewNodes(__FUNCTION__);
   1107    MOZ_ASSERT(!mAddedContentCache.HasCache());
   1108    MOZ_ALWAYS_TRUE(mAddedContentCache.TryToCache(*aFirstContent, *aLastContent,
   1109                                                  mRootElement));
   1110  }
   1111 }
   1112 
   1113 void IMEContentObserver::NotifyIMEOfCachedConsecutiveNewNodes(
   1114    const char* aCallerName,
   1115    const Maybe<uint32_t>& aOffsetOfFirstContent /* = Nothing() */,
   1116    const Maybe<uint32_t>& aLengthOfContentNNodes /* = Nothing() */,
   1117    const OffsetAndLengthAdjustments& aAdjustments /* = Nothing() */) {
   1118  MOZ_ASSERT(mAddedContentCache.HasCache());
   1119 
   1120  MOZ_LOG(sIMECOLog, LogLevel::Debug,
   1121          ("0x%p "
   1122           "IMEContentObserver::NotifyIMEOfCachedConsecutiveNewNodes(), "
   1123           "flushing stored consecutive nodes",
   1124           this));
   1125  MOZ_LOG(
   1126      sCacheLog, LogLevel::Info,
   1127      ("NotifyIMEOfCachedConsecutiveNewNodes: called by %s "
   1128       "(mAddedContentCache=%s)",
   1129       ShortenFunctionName(aCallerName), ToString(mAddedContentCache).c_str()));
   1130 
   1131  // If 2 <div> elements are inserted into the DOM, we wan't the text length
   1132  // from start of the first <div> (including line break caused by its open
   1133  // tag) to end of the second <div>.  I.e., we want to compute:
   1134  // ...{<div>.....</div><div>......</div>}...
   1135  //    ^  ^               ^              ^
   1136  //    |  mFirst          |              |
   1137  //    |                  mLast          |
   1138  //    offset                            (offset + length)
   1139  Maybe<uint32_t> offset =
   1140      aOffsetOfFirstContent.isSome()
   1141          ? aOffsetOfFirstContent
   1142          : mEndOfAddedTextCache.GetFlatTextLengthBeforeContent(
   1143                *mAddedContentCache.mFirst, mRootElement);
   1144  if (offset.isNothing()) {
   1145    Result<uint32_t, nsresult> textLengthBeforeFirstContentOrError =
   1146        FlatTextCache::ComputeTextLengthBeforeContent(
   1147            *mAddedContentCache.mFirst, mRootElement);
   1148    if (NS_WARN_IF(textLengthBeforeFirstContentOrError.isErr())) {
   1149      mEndOfAddedTextCache.Clear(__FUNCTION__);
   1150      mStartOfRemovingTextRangeCache.Clear(__FUNCTION__);
   1151      MOZ_LOG(
   1152          sCacheLog, LogLevel::Error,
   1153          ("NotifyContentAdded: failed to compute text length before mFirst"));
   1154      mAddedContentCache.Clear(__FUNCTION__);
   1155      return;
   1156    }
   1157    offset = Some(textLengthBeforeFirstContentOrError.unwrap());
   1158  }
   1159  Maybe<uint32_t> length = aLengthOfContentNNodes;
   1160  if (aLengthOfContentNNodes.isNothing()) {
   1161    Result<uint32_t, nsresult> addingLengthOrError =
   1162        FlatTextCache::ComputeTextLengthStartOfContentToEndOfContent(
   1163            *mAddedContentCache.mFirst, *mAddedContentCache.mLast,
   1164            mRootElement);
   1165    if (NS_WARN_IF(addingLengthOrError.isErr())) {
   1166      mEndOfAddedTextCache.Clear(__FUNCTION__);
   1167      mStartOfRemovingTextRangeCache.Clear(__FUNCTION__);
   1168      MOZ_LOG(sCacheLog, LogLevel::Error,
   1169              ("NotifyContentAdded: failed to compute text length of added"));
   1170      mAddedContentCache.Clear(__FUNCTION__);
   1171      return;
   1172    }
   1173    length = Some(addingLengthOrError.inspect());
   1174  }
   1175 
   1176  // If multiple lines are being inserted in an HTML editor, next call of
   1177  // NotifyContentAdded() is for adding next node.  Therefore, caching the text
   1178  // length can skip to compute the text length before the adding node and
   1179  // before of it.
   1180  mEndOfAddedTextCache.CacheFlatTextLengthBeforeEndOfContent(
   1181      __FUNCTION__, *mAddedContentCache.mLast,
   1182      aAdjustments.AdjustedEndOffset(*offset + *length), mRootElement);
   1183  mStartOfRemovingTextRangeCache.ContentAdded(
   1184      __FUNCTION__, *mAddedContentCache.mFirst, *mAddedContentCache.mLast,
   1185      Some(aAdjustments.AdjustedEndOffset(*offset + *length)), mRootElement);
   1186 
   1187  mAddedContentCache.Clear(__FUNCTION__);
   1188 
   1189  if (*length == 0u) {
   1190    return;
   1191  }
   1192 
   1193  TextChangeData data(*offset, *offset, *offset + *length,
   1194                      IsEditorHandlingEventForComposition(),
   1195                      IsEditorComposing());
   1196  MaybeNotifyIMEOfTextChange(data);
   1197 }
   1198 
   1199 void IMEContentObserver::ContentAppended(nsIContent* aFirstNewContent,
   1200                                         const ContentAppendInfo&) {
   1201  nsIContent* parent = aFirstNewContent->GetParent();
   1202  MOZ_ASSERT(parent);
   1203  ContentAdded(parent, aFirstNewContent, parent->GetLastChild());
   1204 }
   1205 
   1206 void IMEContentObserver::ContentInserted(nsIContent* aChild,
   1207                                         const ContentInsertInfo&) {
   1208  MOZ_ASSERT(aChild);
   1209  ContentAdded(aChild->GetParentNode(), aChild, aChild);
   1210 }
   1211 
   1212 void IMEContentObserver::ContentWillBeRemoved(nsIContent* aChild,
   1213                                              const ContentRemoveInfo&) {
   1214  if (!NeedsTextChangeNotification() ||
   1215      !nsContentUtils::IsInSameAnonymousTree(mRootElement, aChild)) {
   1216    return;
   1217  }
   1218 
   1219  // We can skip everything when padding <br> element is removed since its text
   1220  // length is 0.
   1221  if (const auto* brElement = HTMLBRElement::FromNode(aChild)) {
   1222    if (MOZ_LIKELY(!brElement->HasChildNodes()) &&
   1223        (brElement->IsPaddingForEmptyEditor() ||
   1224         brElement->IsPaddingForEmptyLastLine())) {
   1225      return;
   1226    }
   1227  }
   1228 
   1229  const Result<uint32_t, nsresult> textLengthOrError =
   1230      FlatTextCache::ComputeTextLengthOfContent(*aChild, mRootElement,
   1231                                                ForRemoval::Yes);
   1232  if (NS_WARN_IF(textLengthOrError.isErr())) {
   1233    mEndOfAddedTextCache.Clear(__FUNCTION__);
   1234    mStartOfRemovingTextRangeCache.Clear(__FUNCTION__);
   1235    mAddedContentCache.Clear(__FUNCTION__);
   1236    return;
   1237  }
   1238 
   1239  if (mAddedContentCache.HasCache()) {
   1240    mEndOfAddedTextCache.ContentWillBeRemoved(
   1241        *aChild, textLengthOrError.inspect(), mRootElement);
   1242    mStartOfRemovingTextRangeCache.ContentWillBeRemoved(
   1243        *aChild, textLengthOrError.inspect(), mRootElement);
   1244    NotifyIMEOfCachedConsecutiveNewNodes(__FUNCTION__);
   1245    MOZ_DIAGNOSTIC_ASSERT(!mAddedContentCache.HasCache());
   1246  }
   1247 
   1248  nsINode* containerNode = aChild->GetParentNode();
   1249  MOZ_ASSERT(containerNode);
   1250 
   1251  mEndOfAddedTextCache.ContentWillBeRemoved(
   1252      *aChild, textLengthOrError.inspect(), mRootElement);
   1253 
   1254  Maybe<uint32_t> offset =
   1255      mStartOfRemovingTextRangeCache.GetFlatTextLengthBeforeContent(
   1256          *aChild, mRootElement, ForRemoval::Yes);
   1257  nsIContent* const prevSibling = aChild->GetPreviousSibling();
   1258  if (offset.isSome()) {
   1259    // Update the cache because next remove may be the previous or the next
   1260    // sibling removal.  So, caching offset of currently removing content node
   1261    // makes us skip computing offset of next removal.
   1262    if (prevSibling) {
   1263      mStartOfRemovingTextRangeCache.CacheFlatTextLengthBeforeEndOfContent(
   1264          __FUNCTION__, *prevSibling, *offset, mRootElement);
   1265    } else {
   1266      mStartOfRemovingTextRangeCache.CacheFlatTextLengthBeforeFirstContent(
   1267          __FUNCTION__, *containerNode, *offset, mRootElement);
   1268    }
   1269  } else {
   1270    if (prevSibling) {
   1271      // When we compute preceding text length of the removing content node, we
   1272      // cannot make the range cross the removing node boundary because
   1273      // containerNode->ComputeIndexOf(aChild) returns Nothing so that
   1274      // ContentEventHandler fails to compute the length.  Therefore, if a <div>
   1275      // is being removed, we want to compute the length of `...}<div>`.
   1276      if (NS_WARN_IF(
   1277              NS_FAILED(mStartOfRemovingTextRangeCache
   1278                            .ComputeAndCacheFlatTextLengthBeforeEndOfContent(
   1279                                __FUNCTION__, *prevSibling, mRootElement)))) {
   1280        return;
   1281      }
   1282    } else {
   1283      // At removing a child node of containerNode, we need the line break
   1284      // caused by open tag of containerNode.
   1285      if (NS_WARN_IF(
   1286              NS_FAILED(mStartOfRemovingTextRangeCache
   1287                            .ComputeAndCacheFlatTextLengthBeforeFirstContent(
   1288                                __FUNCTION__, *containerNode, mRootElement)))) {
   1289        return;
   1290      }
   1291    }
   1292    offset = Some(mStartOfRemovingTextRangeCache.GetFlatTextLength());
   1293  }
   1294 
   1295  // We do not need a text change notification since removing aChild does not
   1296  // change flattened text and no pending added length.
   1297  if (textLengthOrError.inspect() == 0u) {
   1298    return;
   1299  }
   1300 
   1301  TextChangeData data(*offset, *offset + textLengthOrError.inspect(), *offset,
   1302                      IsEditorHandlingEventForComposition(),
   1303                      IsEditorComposing());
   1304  MaybeNotifyIMEOfTextChange(data);
   1305 }
   1306 
   1307 MOZ_CAN_RUN_SCRIPT_BOUNDARY void IMEContentObserver::ParentChainChanged(
   1308    nsIContent* aContent) {
   1309  MOZ_ASSERT(aContent);
   1310  // When the observing element itself is directly removed from the document
   1311  // without a focus move, i.e., it's the root of the removed document fragment
   1312  // and the editor was handling the design mode, we have already stopped
   1313  // observing the element because IMEStateManager::OnRemoveContent() should
   1314  // have already been called for it and the instance which was observing the
   1315  // node has already been destroyed.  Therefore, this is called only when
   1316  // this is observing the <body> in the design mode and it's disconnected from
   1317  // the tree by an <html> element removal.  Even in this case, IMEStateManager
   1318  // never gets a focus change notification, but we need to notify IME of focus
   1319  // change because we cannot interact with IME anymore due to no editable
   1320  // content.  Therefore, this method notifies IMEStateManager of the
   1321  // disconnection of the observing node to emulate a blur from the editable
   1322  // content.
   1323  MOZ_ASSERT(mIsObserving);
   1324  OwningNonNull<IMEContentObserver> observer(*this);
   1325  IMEStateManager::OnParentChainChangedOfObservingElement(observer, *aContent);
   1326 }
   1327 
   1328 void IMEContentObserver::OnTextControlValueChangedWhileNotObservable(
   1329    const nsAString& aNewValue) {
   1330  MOZ_ASSERT(mEditorBase);
   1331  MOZ_ASSERT(mEditorBase->IsTextEditor());
   1332  if (!mTextControlValueLength && aNewValue.IsEmpty()) {
   1333    return;
   1334  }
   1335  MOZ_LOG(sIMECOLog, LogLevel::Debug,
   1336          ("0x%p OnTextControlValueChangedWhileNotObservable()", this));
   1337  uint32_t newLength = ContentEventHandler::GetNativeTextLength(aNewValue);
   1338  TextChangeData data(0, mTextControlValueLength, newLength, false, false);
   1339  MaybeNotifyIMEOfTextChange(data);
   1340 }
   1341 
   1342 void IMEContentObserver::BeginDocumentUpdate() {
   1343  MOZ_LOG(sIMECOLog, LogLevel::Debug, ("0x%p BeginDocumentUpdate()", this));
   1344 }
   1345 
   1346 void IMEContentObserver::EndDocumentUpdate() {
   1347  MOZ_LOG(sIMECOLog, LogLevel::Debug, ("0x%p EndDocumentUpdate()", this));
   1348 
   1349  if (mAddedContentCache.HasCache() && !EditorIsHandlingEditSubAction()) {
   1350    NotifyIMEOfCachedConsecutiveNewNodes(__FUNCTION__);
   1351  }
   1352 }
   1353 
   1354 void IMEContentObserver::SuppressNotifyingIME() {
   1355  mSuppressNotifications++;
   1356 
   1357  MOZ_LOG(sIMECOLog, LogLevel::Debug,
   1358          ("0x%p SuppressNotifyingIME(), mSuppressNotifications=%u", this,
   1359           mSuppressNotifications));
   1360 }
   1361 
   1362 void IMEContentObserver::UnsuppressNotifyingIME() {
   1363  MOZ_LOG(sIMECOLog, LogLevel::Debug,
   1364          ("0x%p UnsuppressNotifyingIME(), mSuppressNotifications=%u", this,
   1365           mSuppressNotifications));
   1366  if (!mSuppressNotifications || --mSuppressNotifications) {
   1367    return;
   1368  }
   1369  FlushMergeableNotifications();
   1370 }
   1371 
   1372 void IMEContentObserver::OnEditActionHandled() {
   1373  MOZ_LOG(sIMECOLog, LogLevel::Debug, ("0x%p OnEditActionHandled()", this));
   1374 
   1375  if (mAddedContentCache.HasCache()) {
   1376    NotifyIMEOfCachedConsecutiveNewNodes(__FUNCTION__);
   1377  }
   1378  mEndOfAddedTextCache.Clear(__FUNCTION__);
   1379  mStartOfRemovingTextRangeCache.Clear(__FUNCTION__);
   1380  FlushMergeableNotifications();
   1381 }
   1382 
   1383 void IMEContentObserver::BeforeEditAction() {
   1384  MOZ_LOG(sIMECOLog, LogLevel::Debug, ("0x%p BeforeEditAction()", this));
   1385 
   1386  if (mAddedContentCache.HasCache()) {
   1387    NotifyIMEOfCachedConsecutiveNewNodes(__FUNCTION__);
   1388  }
   1389  mEndOfAddedTextCache.Clear(__FUNCTION__);
   1390  mStartOfRemovingTextRangeCache.Clear(__FUNCTION__);
   1391 }
   1392 
   1393 void IMEContentObserver::CancelEditAction() {
   1394  MOZ_LOG(sIMECOLog, LogLevel::Debug, ("0x%p CancelEditAction()", this));
   1395 
   1396  if (mAddedContentCache.HasCache()) {
   1397    NotifyIMEOfCachedConsecutiveNewNodes(__FUNCTION__);
   1398  }
   1399  mEndOfAddedTextCache.Clear(__FUNCTION__);
   1400  mStartOfRemovingTextRangeCache.Clear(__FUNCTION__);
   1401  FlushMergeableNotifications();
   1402 }
   1403 
   1404 bool IMEContentObserver::EditorIsHandlingEditSubAction() const {
   1405  return mEditorBase && mEditorBase->IsInEditSubAction();
   1406 }
   1407 
   1408 void IMEContentObserver::PostFocusSetNotification() {
   1409  MOZ_LOG(sIMECOLog, LogLevel::Debug,
   1410          ("0x%p PostFocusSetNotification()", this));
   1411 
   1412  mNeedsToNotifyIMEOfFocusSet = true;
   1413 }
   1414 
   1415 void IMEContentObserver::PostTextChangeNotification() {
   1416  MOZ_LOG(sIMECOLog, LogLevel::Debug,
   1417          ("0x%p PostTextChangeNotification(mTextChangeData=%s)", this,
   1418           ToString(mTextChangeData).c_str()));
   1419 
   1420  MOZ_ASSERT(mTextChangeData.IsValid(),
   1421             "mTextChangeData must have text change data");
   1422  mNeedsToNotifyIMEOfTextChange = true;
   1423  // Even if the observer hasn't received selection change, selection in the
   1424  // flat text may have already been changed.  For example, when previous `<p>`
   1425  // element of another `<p>` element which contains caret is removed by a DOM
   1426  // mutation, selection change event won't be fired, but selection start offset
   1427  // should be decreased by the length of removed `<p>` element.
   1428  // In such case, HandleQueryContentEvent shouldn't use the selection cache
   1429  // anymore.  Therefore, we also need to post selection change notification
   1430  // too.  eQuerySelectedText event may be dispatched at sending a text change
   1431  // notification.
   1432  mNeedsToNotifyIMEOfSelectionChange = true;
   1433 }
   1434 
   1435 void IMEContentObserver::PostSelectionChangeNotification() {
   1436  MOZ_LOG(sIMECOLog, LogLevel::Debug,
   1437          ("0x%p PostSelectionChangeNotification(), mSelectionData={ "
   1438           "mCausedByComposition=%s, mCausedBySelectionEvent=%s }",
   1439           this, ToChar(mSelectionData.mCausedByComposition),
   1440           ToChar(mSelectionData.mCausedBySelectionEvent)));
   1441 
   1442  mNeedsToNotifyIMEOfSelectionChange = true;
   1443 }
   1444 
   1445 void IMEContentObserver::MaybeNotifyIMEOfFocusSet() {
   1446  MOZ_LOG(sIMECOLog, LogLevel::Debug,
   1447          ("0x%p MaybeNotifyIMEOfFocusSet()", this));
   1448 
   1449  PostFocusSetNotification();
   1450  FlushMergeableNotifications();
   1451 }
   1452 
   1453 void IMEContentObserver::MaybeNotifyIMEOfTextChange(
   1454    const TextChangeDataBase& aTextChangeData) {
   1455  MOZ_LOG(sIMECOLog, LogLevel::Debug,
   1456          ("0x%p MaybeNotifyIMEOfTextChange(aTextChangeData=%s)", this,
   1457           ToString(aTextChangeData).c_str()));
   1458 
   1459  if (mEditorBase && mEditorBase->IsTextEditor()) {
   1460    MOZ_DIAGNOSTIC_ASSERT(static_cast<int64_t>(mTextControlValueLength) +
   1461                              aTextChangeData.Difference() >=
   1462                          0);
   1463    mTextControlValueLength += aTextChangeData.Difference();
   1464  }
   1465 
   1466  mTextChangeData += aTextChangeData;
   1467  PostTextChangeNotification();
   1468  FlushMergeableNotifications();
   1469 }
   1470 
   1471 void IMEContentObserver::CancelNotifyingIMEOfTextChange() {
   1472  MOZ_LOG(sIMECOLog, LogLevel::Debug,
   1473          ("0x%p CancelNotifyingIMEOfTextChange()", this));
   1474  mTextChangeData.Clear();
   1475  mNeedsToNotifyIMEOfTextChange = false;
   1476 }
   1477 
   1478 void IMEContentObserver::MaybeNotifyIMEOfSelectionChange(
   1479    bool aCausedByComposition, bool aCausedBySelectionEvent,
   1480    bool aOccurredDuringComposition) {
   1481  MOZ_LOG(
   1482      sIMECOLog, LogLevel::Debug,
   1483      ("0x%p MaybeNotifyIMEOfSelectionChange(aCausedByComposition=%s, "
   1484       "aCausedBySelectionEvent=%s, aOccurredDuringComposition)",
   1485       this, ToChar(aCausedByComposition), ToChar(aCausedBySelectionEvent)));
   1486 
   1487  mSelectionData.AssignReason(aCausedByComposition, aCausedBySelectionEvent,
   1488                              aOccurredDuringComposition);
   1489  PostSelectionChangeNotification();
   1490  FlushMergeableNotifications();
   1491 }
   1492 
   1493 void IMEContentObserver::MaybeNotifyIMEOfPositionChange() {
   1494  MOZ_LOG(sIMECOLog, LogLevel::Verbose,
   1495          ("0x%p MaybeNotifyIMEOfPositionChange()", this));
   1496  // If reflow is caused by ContentEventHandler during PositionChangeEvent
   1497  // sending NOTIFY_IME_OF_POSITION_CHANGE, we don't need to notify IME of it
   1498  // again since ContentEventHandler returns the result including this reflow's
   1499  // result.
   1500  if (mIsHandlingQueryContentEvent &&
   1501      mSendingNotification == NOTIFY_IME_OF_POSITION_CHANGE) {
   1502    MOZ_LOG(sIMECOLog, LogLevel::Verbose,
   1503            ("0x%p   MaybeNotifyIMEOfPositionChange(), ignored since caused by "
   1504             "ContentEventHandler during sending NOTIFY_IME_OF_POSITION_CHANGE",
   1505             this));
   1506    return;
   1507  }
   1508  PostPositionChangeNotification();
   1509  FlushMergeableNotifications();
   1510 }
   1511 
   1512 void IMEContentObserver::CancelNotifyingIMEOfPositionChange() {
   1513  MOZ_LOG(sIMECOLog, LogLevel::Debug,
   1514          ("0x%p CancelNotifyIMEOfPositionChange()", this));
   1515  mNeedsToNotifyIMEOfPositionChange = false;
   1516 }
   1517 
   1518 void IMEContentObserver::MaybeNotifyCompositionEventHandled() {
   1519  MOZ_LOG(sIMECOLog, LogLevel::Debug,
   1520          ("0x%p MaybeNotifyCompositionEventHandled()", this));
   1521 
   1522  PostCompositionEventHandledNotification();
   1523  FlushMergeableNotifications();
   1524 }
   1525 
   1526 bool IMEContentObserver::UpdateSelectionCache(bool aRequireFlush /* = true */) {
   1527  MOZ_ASSERT(IsSafeToNotifyIME());
   1528 
   1529  mSelectionData.ClearSelectionData();
   1530 
   1531  // XXX Cannot we cache some information for reducing the cost to compute
   1532  //     selection offset and writing mode?
   1533  WidgetQueryContentEvent querySelectedTextEvent(true, eQuerySelectedText,
   1534                                                 mWidget);
   1535  querySelectedTextEvent.mNeedsToFlushLayout = aRequireFlush;
   1536  ContentEventHandler handler(GetPresContext());
   1537  handler.OnQuerySelectedText(&querySelectedTextEvent);
   1538  if (NS_WARN_IF(querySelectedTextEvent.Failed()) ||
   1539      NS_WARN_IF(querySelectedTextEvent.mReply->mContentsRoot !=
   1540                 mRootElement)) {
   1541    return false;
   1542  }
   1543 
   1544  mFocusedWidget = querySelectedTextEvent.mReply->mFocusedWidget;
   1545  mSelectionData.Assign(querySelectedTextEvent);
   1546 
   1547  // WARNING: Don't set the reason of selection change here because it should be
   1548  //          set the reason at sending the notification.
   1549 
   1550  MOZ_LOG(sIMECOLog, LogLevel::Debug,
   1551          ("0x%p UpdateSelectionCache(), mSelectionData=%s", this,
   1552           ToString(mSelectionData).c_str()));
   1553 
   1554  return true;
   1555 }
   1556 
   1557 void IMEContentObserver::PostPositionChangeNotification() {
   1558  MOZ_LOG(sIMECOLog, LogLevel::Debug,
   1559          ("0x%p PostPositionChangeNotification()", this));
   1560 
   1561  mNeedsToNotifyIMEOfPositionChange = true;
   1562 }
   1563 
   1564 void IMEContentObserver::PostCompositionEventHandledNotification() {
   1565  MOZ_LOG(sIMECOLog, LogLevel::Debug,
   1566          ("0x%p PostCompositionEventHandledNotification()", this));
   1567 
   1568  mNeedsToNotifyIMEOfCompositionEventHandled = true;
   1569 }
   1570 
   1571 bool IMEContentObserver::IsReflowLocked() const {
   1572  nsPresContext* presContext = GetPresContext();
   1573  if (NS_WARN_IF(!presContext)) {
   1574    return false;
   1575  }
   1576  PresShell* presShell = presContext->GetPresShell();
   1577  if (NS_WARN_IF(!presShell)) {
   1578    return false;
   1579  }
   1580  // During reflow, we shouldn't notify IME because IME may query content
   1581  // synchronously.  Then, it causes ContentEventHandler will try to flush
   1582  // pending notifications during reflow.
   1583  return presShell->IsReflowLocked();
   1584 }
   1585 
   1586 bool IMEContentObserver::IsSafeToNotifyIME() const {
   1587  // If this is already detached from the widget, this doesn't need to notify
   1588  // anything.
   1589  if (!mWidget) {
   1590    MOZ_LOG(sIMECOLog, LogLevel::Debug,
   1591            ("0x%p   IsSafeToNotifyIME(), it's not safe because of no widget",
   1592             this));
   1593    return false;
   1594  }
   1595 
   1596  // Don't notify IME of anything if it's not good time to do it.
   1597  if (mSuppressNotifications) {
   1598    MOZ_LOG(sIMECOLog, LogLevel::Debug,
   1599            ("0x%p   IsSafeToNotifyIME(), it's not safe because of no widget",
   1600             this));
   1601    return false;
   1602  }
   1603 
   1604  if (!mESM || NS_WARN_IF(!GetPresContext())) {
   1605    MOZ_LOG(sIMECOLog, LogLevel::Debug,
   1606            ("0x%p   IsSafeToNotifyIME(), it's not safe because of no "
   1607             "EventStateManager and/or PresContext",
   1608             this));
   1609    return false;
   1610  }
   1611 
   1612  // If it's in reflow, we should wait to finish the reflow.
   1613  // FYI: This should be called again from Reflow() or ReflowInterruptible().
   1614  if (IsReflowLocked()) {
   1615    MOZ_LOG(
   1616        sIMECOLog, LogLevel::Debug,
   1617        ("0x%p   IsSafeToNotifyIME(), it's not safe because of reflow locked",
   1618         this));
   1619    return false;
   1620  }
   1621 
   1622  // If we're in handling an edit action, this method will be called later.
   1623  if (EditorIsHandlingEditSubAction()) {
   1624    MOZ_LOG(sIMECOLog, LogLevel::Debug,
   1625            ("0x%p   IsSafeToNotifyIME(), it's not safe because of focused "
   1626             "editor handling somethings",
   1627             this));
   1628    return false;
   1629  }
   1630 
   1631  return true;
   1632 }
   1633 
   1634 void IMEContentObserver::FlushMergeableNotifications() {
   1635  if (!IsSafeToNotifyIME()) {
   1636    // So, if this is already called, this should do nothing.
   1637    MOZ_LOG(sIMECOLog, LogLevel::Warning,
   1638            ("0x%p   FlushMergeableNotifications(), Warning, do nothing due to "
   1639             "unsafe to notify IME",
   1640             this));
   1641    return;
   1642  }
   1643 
   1644  // Notifying something may cause nested call of this method.  For example,
   1645  // when somebody notified one of the notifications may dispatch query content
   1646  // event. Then, it causes flushing layout which may cause another layout
   1647  // change notification.
   1648 
   1649  if (mQueuedSender) {
   1650    // So, if this is already called, this should do nothing.
   1651    MOZ_LOG(sIMECOLog, LogLevel::Warning,
   1652            ("0x%p   FlushMergeableNotifications(), Warning, do nothing due to "
   1653             "already flushing pending notifications",
   1654             this));
   1655    return;
   1656  }
   1657 
   1658  // If text change notification and/or position change notification becomes
   1659  // unnecessary, let's cancel them.
   1660  if (mNeedsToNotifyIMEOfTextChange && !NeedsTextChangeNotification()) {
   1661    CancelNotifyingIMEOfTextChange();
   1662  }
   1663  if (mNeedsToNotifyIMEOfPositionChange && !NeedsPositionChangeNotification()) {
   1664    CancelNotifyingIMEOfPositionChange();
   1665  }
   1666 
   1667  if (!NeedsToNotifyIMEOfSomething()) {
   1668    MOZ_LOG(sIMECOLog, LogLevel::Warning,
   1669            ("0x%p   FlushMergeableNotifications(), Warning, due to no pending "
   1670             "notifications",
   1671             this));
   1672    return;
   1673  }
   1674 
   1675  // NOTE: Reset each pending flag because sending notification may cause
   1676  //       another change.
   1677 
   1678  MOZ_LOG(
   1679      sIMECOLog, LogLevel::Info,
   1680      ("0x%p FlushMergeableNotifications(), creating IMENotificationSender...",
   1681       this));
   1682 
   1683  // If contents in selection range is modified, the selection range still
   1684  // has removed node from the tree.  In such case, ContentIterator won't
   1685  // work well.  Therefore, we shouldn't use AddScriptRunner() here since
   1686  // it may kick runnable event immediately after DOM tree is changed but
   1687  // the selection range isn't modified yet.
   1688  mQueuedSender = new IMENotificationSender(this);
   1689  mQueuedSender->Dispatch(mDocShell);
   1690  MOZ_LOG(sIMECOLog, LogLevel::Debug,
   1691          ("0x%p   FlushMergeableNotifications(), finished", this));
   1692 }
   1693 
   1694 void IMEContentObserver::TryToFlushPendingNotifications(bool aAllowAsync) {
   1695  // If a sender instance is sending notifications, we shouldn't try to create
   1696  // a new sender again because the sender will recreate by itself if there are
   1697  // new pending notifications.
   1698  if (mSendingNotification != NOTIFY_IME_OF_NOTHING) {
   1699    return;
   1700  }
   1701 
   1702  // When the caller allows to put off notifying IME, we can wait the next
   1703  // call of this method or to run the queued sender.
   1704  if (mQueuedSender && XRE_IsContentProcess() && aAllowAsync) {
   1705    return;
   1706  }
   1707 
   1708  if (!mQueuedSender) {
   1709    // If it was not safe to dispatch notifications when the pending
   1710    // notifications are posted, this may not have IMENotificationSender
   1711    // instance because it couldn't dispatch it, e.g., when an edit sub-action
   1712    // is being handled in the editor, we shouldn't do it even if it's safe to
   1713    // run script.  Therefore, we need to create the sender instance here in the
   1714    // case.
   1715    if (!NeedsToNotifyIMEOfSomething()) {
   1716      return;
   1717    }
   1718    mQueuedSender = new IMENotificationSender(this);
   1719  }
   1720 
   1721  MOZ_LOG(sIMECOLog, LogLevel::Debug,
   1722          ("0x%p TryToFlushPendingNotifications(), performing queued "
   1723           "IMENotificationSender forcibly",
   1724           this));
   1725  RefPtr<IMENotificationSender> queuedSender = mQueuedSender;
   1726  queuedSender->Run();
   1727 }
   1728 
   1729 /******************************************************************************
   1730 * mozilla::IMEContentObserver::AChangeEvent
   1731 ******************************************************************************/
   1732 
   1733 bool IMEContentObserver::AChangeEvent::CanNotifyIME(
   1734    ChangeEventType aChangeEventType) const {
   1735  RefPtr<IMEContentObserver> observer = GetObserver();
   1736  if (NS_WARN_IF(!observer)) {
   1737    return false;
   1738  }
   1739 
   1740  const LogLevel debugOrVerbose =
   1741      aChangeEventType == ChangeEventType::eChangeEventType_Position
   1742          ? LogLevel::Verbose
   1743          : LogLevel::Debug;
   1744 
   1745  if (aChangeEventType == eChangeEventType_CompositionEventHandled) {
   1746    if (observer->mWidget) {
   1747      return true;
   1748    }
   1749    MOZ_LOG(sIMECOLog, debugOrVerbose,
   1750            ("0x%p   AChangeEvent::CanNotifyIME(), Cannot notify IME of "
   1751             "composition event handled because of no widget",
   1752             this));
   1753    return false;
   1754  }
   1755  State state = observer->GetState();
   1756  // If it's not initialized, we should do nothing.
   1757  if (state == eState_NotObserving) {
   1758    MOZ_LOG(sIMECOLog, debugOrVerbose,
   1759            ("0x%p   AChangeEvent::CanNotifyIME(), Cannot notify IME because "
   1760             "of not observing",
   1761             this));
   1762    return false;
   1763  }
   1764  // If setting focus, just check the state.
   1765  if (aChangeEventType == eChangeEventType_Focus) {
   1766    if (!observer->mIMEHasFocus) {
   1767      return true;
   1768    }
   1769    MOZ_LOG(sIMECOLog, debugOrVerbose,
   1770            ("0x%p   AChangeEvent::CanNotifyIME(), Cannot notify IME of focus "
   1771             "change because of already focused",
   1772             this));
   1773    NS_WARNING("IME already has focus");
   1774    return false;
   1775  }
   1776  // If we've not notified IME of focus yet, we shouldn't notify anything.
   1777  if (!observer->mIMEHasFocus) {
   1778    MOZ_LOG(sIMECOLog, debugOrVerbose,
   1779            ("0x%p   AChangeEvent::CanNotifyIME(), Cannot notify IME because "
   1780             "of not focused",
   1781             this));
   1782    return false;
   1783  }
   1784 
   1785  // If IME has focus, IMEContentObserver must hold the widget.
   1786  MOZ_ASSERT(observer->mWidget);
   1787 
   1788  return true;
   1789 }
   1790 
   1791 bool IMEContentObserver::AChangeEvent::IsSafeToNotifyIME(
   1792    ChangeEventType aChangeEventType) const {
   1793  const LogLevel warningOrVerbose =
   1794      aChangeEventType == ChangeEventType::eChangeEventType_Position
   1795          ? LogLevel::Verbose
   1796          : LogLevel::Warning;
   1797 
   1798  if (NS_WARN_IF(!nsContentUtils::IsSafeToRunScript())) {
   1799    MOZ_LOG(sIMECOLog, warningOrVerbose,
   1800            ("0x%p   AChangeEvent::IsSafeToNotifyIME(), Warning, Cannot notify "
   1801             "IME because of not safe to run script",
   1802             this));
   1803    return false;
   1804  }
   1805 
   1806  RefPtr<IMEContentObserver> observer = GetObserver();
   1807  if (!observer) {
   1808    MOZ_LOG(sIMECOLog, warningOrVerbose,
   1809            ("0x%p   AChangeEvent::IsSafeToNotifyIME(), Warning, Cannot notify "
   1810             "IME because of no observer",
   1811             this));
   1812    return false;
   1813  }
   1814 
   1815  // While we're sending a notification, we shouldn't send another notification
   1816  // recursively.
   1817  if (observer->mSendingNotification != NOTIFY_IME_OF_NOTHING) {
   1818    MOZ_LOG(sIMECOLog, warningOrVerbose,
   1819            ("0x%p   AChangeEvent::IsSafeToNotifyIME(), Warning, Cannot notify "
   1820             "IME because of the observer sending another notification",
   1821             this));
   1822    return false;
   1823  }
   1824  State state = observer->GetState();
   1825  if (aChangeEventType == eChangeEventType_Focus) {
   1826    if (NS_WARN_IF(state != eState_Initializing && state != eState_Observing)) {
   1827      MOZ_LOG(sIMECOLog, warningOrVerbose,
   1828              ("0x%p   AChangeEvent::IsSafeToNotifyIME(), Warning, Cannot "
   1829               "notify IME of focus because of not observing",
   1830               this));
   1831      return false;
   1832    }
   1833  } else if (aChangeEventType == eChangeEventType_CompositionEventHandled) {
   1834    // It doesn't need to check the observing status.
   1835  } else if (state != eState_Observing) {
   1836    MOZ_LOG(sIMECOLog, warningOrVerbose,
   1837            ("0x%p   AChangeEvent::IsSafeToNotifyIME(), Warning, Cannot notify "
   1838             "IME because of not observing",
   1839             this));
   1840    return false;
   1841  }
   1842  return observer->IsSafeToNotifyIME();
   1843 }
   1844 
   1845 /******************************************************************************
   1846 * mozilla::IMEContentObserver::IMENotificationSender
   1847 ******************************************************************************/
   1848 
   1849 void IMEContentObserver::IMENotificationSender::Dispatch(
   1850    nsIDocShell* aDocShell) {
   1851  if (XRE_IsContentProcess() && aDocShell) {
   1852    if (RefPtr<nsPresContext> presContext = aDocShell->GetPresContext()) {
   1853      if (nsRefreshDriver* refreshDriver = presContext->RefreshDriver()) {
   1854        refreshDriver->AddEarlyRunner(this);
   1855        return;
   1856      }
   1857    }
   1858  }
   1859  NS_DispatchToCurrentThread(this);
   1860 }
   1861 
   1862 NS_IMETHODIMP
   1863 IMEContentObserver::IMENotificationSender::Run() {
   1864  if (NS_WARN_IF(mIsRunning)) {
   1865    MOZ_LOG(
   1866        sIMECOLog, LogLevel::Error,
   1867        ("0x%p IMENotificationSender::Run(), FAILED, due to called recursively",
   1868         this));
   1869    return NS_OK;
   1870  }
   1871 
   1872  RefPtr<IMEContentObserver> observer = GetObserver();
   1873  if (!observer) {
   1874    return NS_OK;
   1875  }
   1876 
   1877  AutoRestore<bool> running(mIsRunning);
   1878  mIsRunning = true;
   1879 
   1880  // This instance was already performed forcibly.
   1881  if (observer->mQueuedSender != this) {
   1882    return NS_OK;
   1883  }
   1884 
   1885  // NOTE: Reset each pending flag because sending notification may cause
   1886  //       another change.
   1887 
   1888  if (observer->mNeedsToNotifyIMEOfFocusSet) {
   1889    observer->mNeedsToNotifyIMEOfFocusSet = false;
   1890    SendFocusSet();
   1891    observer->mQueuedSender = nullptr;
   1892    // If it's not safe to notify IME of focus, SendFocusSet() sets
   1893    // mNeedsToNotifyIMEOfFocusSet true again.  For guaranteeing to send the
   1894    // focus notification later,  we should put a new sender into the queue but
   1895    // this case must be rare.  Note that if mIMEContentObserver is already
   1896    // destroyed, mNeedsToNotifyIMEOfFocusSet is never set true again.
   1897    if (observer->mNeedsToNotifyIMEOfFocusSet) {
   1898      MOZ_ASSERT(!observer->mIMEHasFocus);
   1899      MOZ_LOG(sIMECOLog, LogLevel::Debug,
   1900              ("0x%p IMENotificationSender::Run(), posting "
   1901               "IMENotificationSender to current thread",
   1902               this));
   1903      observer->mQueuedSender = new IMENotificationSender(observer);
   1904      observer->mQueuedSender->Dispatch(observer->mDocShell);
   1905      return NS_OK;
   1906    }
   1907    // This is the first notification to IME. So, we don't need to notify
   1908    // anymore since IME starts to query content after it gets focus.
   1909    observer->ClearPendingNotifications();
   1910    return NS_OK;
   1911  }
   1912 
   1913  if (observer->mNeedsToNotifyIMEOfTextChange) {
   1914    observer->mNeedsToNotifyIMEOfTextChange = false;
   1915    SendTextChange();
   1916  }
   1917 
   1918  // If a text change notification causes another text change again, we should
   1919  // notify IME of that before sending a selection change notification.
   1920  if (!observer->mNeedsToNotifyIMEOfTextChange) {
   1921    // Be aware, PuppetWidget depends on the order of this. A selection change
   1922    // notification should not be sent before a text change notification because
   1923    // PuppetWidget shouldn't query new text content every selection change.
   1924    if (observer->mNeedsToNotifyIMEOfSelectionChange) {
   1925      observer->mNeedsToNotifyIMEOfSelectionChange = false;
   1926      SendSelectionChange();
   1927    }
   1928  }
   1929 
   1930  // If a text change notification causes another text change again or a
   1931  // selection change notification causes either a text change or another
   1932  // selection change, we should notify IME of those before sending a position
   1933  // change notification.
   1934  if (!observer->mNeedsToNotifyIMEOfTextChange &&
   1935      !observer->mNeedsToNotifyIMEOfSelectionChange) {
   1936    if (observer->mNeedsToNotifyIMEOfPositionChange) {
   1937      observer->mNeedsToNotifyIMEOfPositionChange = false;
   1938      SendPositionChange();
   1939    }
   1940  }
   1941 
   1942  // Composition event handled notification should be sent after all the
   1943  // other notifications because this notifies widget of finishing all pending
   1944  // events are handled completely.
   1945  if (!observer->mNeedsToNotifyIMEOfTextChange &&
   1946      !observer->mNeedsToNotifyIMEOfSelectionChange &&
   1947      !observer->mNeedsToNotifyIMEOfPositionChange) {
   1948    if (observer->mNeedsToNotifyIMEOfCompositionEventHandled) {
   1949      observer->mNeedsToNotifyIMEOfCompositionEventHandled = false;
   1950      SendCompositionEventHandled();
   1951    }
   1952  }
   1953 
   1954  observer->mQueuedSender = nullptr;
   1955 
   1956  // If notifications caused some new change, we should notify them now.
   1957  if (observer->NeedsToNotifyIMEOfSomething()) {
   1958    if (observer->GetState() == eState_StoppedObserving) {
   1959      MOZ_LOG(sIMECOLog, LogLevel::Debug,
   1960              ("0x%p IMENotificationSender::Run(), waiting "
   1961               "IMENotificationSender to be reinitialized",
   1962               this));
   1963    } else {
   1964      MOZ_LOG(sIMECOLog, LogLevel::Debug,
   1965              ("0x%p IMENotificationSender::Run(), posting "
   1966               "IMENotificationSender to current thread",
   1967               this));
   1968      observer->mQueuedSender = new IMENotificationSender(observer);
   1969      observer->mQueuedSender->Dispatch(observer->mDocShell);
   1970    }
   1971  }
   1972  return NS_OK;
   1973 }
   1974 
   1975 void IMEContentObserver::IMENotificationSender::SendFocusSet() {
   1976  RefPtr<IMEContentObserver> observer = GetObserver();
   1977  if (!observer) {
   1978    return;
   1979  }
   1980 
   1981  if (!CanNotifyIME(eChangeEventType_Focus)) {
   1982    // If IMEContentObserver has already gone, we don't need to notify IME of
   1983    // focus.
   1984    MOZ_LOG(sIMECOLog, LogLevel::Warning,
   1985            ("0x%p   IMENotificationSender::SendFocusSet(), Warning, does not "
   1986             "send notification due to impossible to notify IME of focus",
   1987             this));
   1988    observer->ClearPendingNotifications();
   1989    return;
   1990  }
   1991 
   1992  if (!IsSafeToNotifyIME(eChangeEventType_Focus)) {
   1993    MOZ_LOG(
   1994        sIMECOLog, LogLevel::Warning,
   1995        ("0x%p   IMENotificationSender::SendFocusSet(), Warning, does not send "
   1996         "notification due to unsafe, retrying to send NOTIFY_IME_OF_FOCUS...",
   1997         this));
   1998    observer->PostFocusSetNotification();
   1999    return;
   2000  }
   2001 
   2002  observer->mIMEHasFocus = true;
   2003  // Initialize selection cache with the first selection data.  However, this
   2004  // may be handled synchronously when the editor gets focus.  In that case,
   2005  // some frames may be dirty and they may be required to get caret frame in
   2006  // ContentEventHandler::Init() to get the nearest widget from the selection.
   2007  // Therefore, we need to update selection cache with flushing the pending
   2008  // notifications.
   2009  observer->UpdateSelectionCache(true);
   2010  MOZ_LOG(sIMECOLog, LogLevel::Info,
   2011          ("0x%p IMENotificationSender::SendFocusSet(), sending "
   2012           "NOTIFY_IME_OF_FOCUS...",
   2013           this));
   2014 
   2015  MOZ_RELEASE_ASSERT(observer->mSendingNotification == NOTIFY_IME_OF_NOTHING);
   2016  observer->mSendingNotification = NOTIFY_IME_OF_FOCUS;
   2017  IMEStateManager::NotifyIME(IMENotification(NOTIFY_IME_OF_FOCUS),
   2018                             observer->mWidget);
   2019  observer->mSendingNotification = NOTIFY_IME_OF_NOTHING;
   2020 
   2021  // IMENotificationRequests referred by ObserveEditableNode() may be different
   2022  // before or after widget receives NOTIFY_IME_OF_FOCUS.  Therefore, we need
   2023  // to guarantee to call ObserveEditableNode() after sending
   2024  // NOTIFY_IME_OF_FOCUS.
   2025  observer->OnIMEReceivedFocus();
   2026 
   2027  MOZ_LOG(
   2028      sIMECOLog, LogLevel::Debug,
   2029      ("0x%p   IMENotificationSender::SendFocusSet(), sent NOTIFY_IME_OF_FOCUS",
   2030       this));
   2031 }
   2032 
   2033 void IMEContentObserver::IMENotificationSender::SendSelectionChange() {
   2034  RefPtr<IMEContentObserver> observer = GetObserver();
   2035  if (!observer) {
   2036    return;
   2037  }
   2038 
   2039  if (!CanNotifyIME(eChangeEventType_Selection)) {
   2040    MOZ_LOG(sIMECOLog, LogLevel::Warning,
   2041            ("0x%p   IMENotificationSender::SendSelectionChange(), Warning, "
   2042             "does not send notification due to impossible to notify IME of "
   2043             "selection change",
   2044             this));
   2045    return;
   2046  }
   2047 
   2048  if (!IsSafeToNotifyIME(eChangeEventType_Selection)) {
   2049    MOZ_LOG(sIMECOLog, LogLevel::Warning,
   2050            ("0x%p   IMENotificationSender::SendSelectionChange(), Warning, "
   2051             "does not send notification due to unsafe, retrying to send "
   2052             "NOTIFY_IME_OF_SELECTION_CHANGE...",
   2053             this));
   2054    observer->PostSelectionChangeNotification();
   2055    return;
   2056  }
   2057 
   2058  SelectionChangeData lastSelChangeData = observer->mSelectionData;
   2059  if (NS_WARN_IF(!observer->UpdateSelectionCache())) {
   2060    MOZ_LOG(sIMECOLog, LogLevel::Error,
   2061            ("0x%p   IMENotificationSender::SendSelectionChange(), FAILED, due "
   2062             "to UpdateSelectionCache() failure",
   2063             this));
   2064    return;
   2065  }
   2066 
   2067  // The state may be changed since querying content causes flushing layout.
   2068  if (!CanNotifyIME(eChangeEventType_Selection)) {
   2069    MOZ_LOG(sIMECOLog, LogLevel::Error,
   2070            ("0x%p   IMENotificationSender::SendSelectionChange(), FAILED, due "
   2071             "to flushing layout having changed something",
   2072             this));
   2073    return;
   2074  }
   2075 
   2076  // If the selection isn't changed actually, we shouldn't notify IME of
   2077  // selection change.
   2078  SelectionChangeData& newSelChangeData = observer->mSelectionData;
   2079  if (lastSelChangeData.IsInitialized() &&
   2080      lastSelChangeData.EqualsRangeAndDirectionAndWritingMode(
   2081          newSelChangeData)) {
   2082    MOZ_LOG(
   2083        sIMECOLog, LogLevel::Debug,
   2084        ("0x%p IMENotificationSender::SendSelectionChange(), not notifying IME "
   2085         "of NOTIFY_IME_OF_SELECTION_CHANGE due to not changed actually",
   2086         this));
   2087    return;
   2088  }
   2089 
   2090  MOZ_LOG(sIMECOLog, LogLevel::Info,
   2091          ("0x%p IMENotificationSender::SendSelectionChange(), sending "
   2092           "NOTIFY_IME_OF_SELECTION_CHANGE... newSelChangeData=%s",
   2093           this, ToString(newSelChangeData).c_str()));
   2094 
   2095  IMENotification notification(NOTIFY_IME_OF_SELECTION_CHANGE);
   2096  notification.SetData(observer->mSelectionData);
   2097 
   2098  MOZ_RELEASE_ASSERT(observer->mSendingNotification == NOTIFY_IME_OF_NOTHING);
   2099  observer->mSendingNotification = NOTIFY_IME_OF_SELECTION_CHANGE;
   2100  IMEStateManager::NotifyIME(notification, observer->mWidget);
   2101  observer->mSendingNotification = NOTIFY_IME_OF_NOTHING;
   2102 
   2103  MOZ_LOG(sIMECOLog, LogLevel::Debug,
   2104          ("0x%p   IMENotificationSender::SendSelectionChange(), sent "
   2105           "NOTIFY_IME_OF_SELECTION_CHANGE",
   2106           this));
   2107 }
   2108 
   2109 void IMEContentObserver::IMENotificationSender::SendTextChange() {
   2110  RefPtr<IMEContentObserver> observer = GetObserver();
   2111  if (!observer) {
   2112    return;
   2113  }
   2114 
   2115  if (!CanNotifyIME(eChangeEventType_Text)) {
   2116    MOZ_LOG(
   2117        sIMECOLog, LogLevel::Warning,
   2118        ("0x%p   IMENotificationSender::SendTextChange(), Warning, does not "
   2119         "send notification due to impossible to notify IME of text change",
   2120         this));
   2121    return;
   2122  }
   2123 
   2124  if (!IsSafeToNotifyIME(eChangeEventType_Text)) {
   2125    MOZ_LOG(sIMECOLog, LogLevel::Warning,
   2126            ("0x%p   IMENotificationSender::SendTextChange(), Warning, does "
   2127             "not send notification due to unsafe, retrying to send "
   2128             "NOTIFY_IME_OF_TEXT_CHANGE...",
   2129             this));
   2130    observer->PostTextChangeNotification();
   2131    return;
   2132  }
   2133 
   2134  // If text change notification is unnecessary anymore, just cancel it.
   2135  if (!observer->NeedsTextChangeNotification()) {
   2136    MOZ_LOG(sIMECOLog, LogLevel::Warning,
   2137            ("0x%p   IMENotificationSender::SendTextChange(), Warning, "
   2138             "canceling sending NOTIFY_IME_OF_TEXT_CHANGE",
   2139             this));
   2140    observer->CancelNotifyingIMEOfTextChange();
   2141    return;
   2142  }
   2143 
   2144  MOZ_LOG(sIMECOLog, LogLevel::Info,
   2145          ("0x%p IMENotificationSender::SendTextChange(), sending "
   2146           "NOTIFY_IME_OF_TEXT_CHANGE... mIMEContentObserver={ "
   2147           "mTextChangeData=%s }",
   2148           this, ToString(observer->mTextChangeData).c_str()));
   2149 
   2150  IMENotification notification(NOTIFY_IME_OF_TEXT_CHANGE);
   2151  notification.SetData(observer->mTextChangeData);
   2152  observer->mTextChangeData.Clear();
   2153 
   2154  MOZ_RELEASE_ASSERT(observer->mSendingNotification == NOTIFY_IME_OF_NOTHING);
   2155  observer->mSendingNotification = NOTIFY_IME_OF_TEXT_CHANGE;
   2156  IMEStateManager::NotifyIME(notification, observer->mWidget);
   2157  observer->mSendingNotification = NOTIFY_IME_OF_NOTHING;
   2158 
   2159  MOZ_LOG(sIMECOLog, LogLevel::Debug,
   2160          ("0x%p   IMENotificationSender::SendTextChange(), sent "
   2161           "NOTIFY_IME_OF_TEXT_CHANGE",
   2162           this));
   2163 }
   2164 
   2165 void IMEContentObserver::IMENotificationSender::SendPositionChange() {
   2166  RefPtr<IMEContentObserver> observer = GetObserver();
   2167  if (!observer) {
   2168    return;
   2169  }
   2170 
   2171  if (!CanNotifyIME(eChangeEventType_Position)) {
   2172    MOZ_LOG(sIMECOLog, LogLevel::Verbose,
   2173            ("0x%p   IMENotificationSender::SendPositionChange(), Warning, "
   2174             "does not send notification due to impossible to notify IME of "
   2175             "position change",
   2176             this));
   2177    return;
   2178  }
   2179 
   2180  if (!IsSafeToNotifyIME(eChangeEventType_Position)) {
   2181    MOZ_LOG(sIMECOLog, LogLevel::Verbose,
   2182            ("0x%p   IMENotificationSender::SendPositionChange(), Warning, "
   2183             "does not send notification due to unsafe, retrying to send "
   2184             "NOTIFY_IME_OF_POSITION_CHANGE...",
   2185             this));
   2186    observer->PostPositionChangeNotification();
   2187    return;
   2188  }
   2189 
   2190  // If position change notification is unnecessary anymore, just cancel it.
   2191  if (!observer->NeedsPositionChangeNotification()) {
   2192    MOZ_LOG(sIMECOLog, LogLevel::Verbose,
   2193            ("0x%p   IMENotificationSender::SendPositionChange(), Warning, "
   2194             "canceling sending NOTIFY_IME_OF_POSITION_CHANGE",
   2195             this));
   2196    observer->CancelNotifyingIMEOfPositionChange();
   2197    return;
   2198  }
   2199 
   2200  MOZ_LOG(sIMECOLog, LogLevel::Info,
   2201          ("0x%p IMENotificationSender::SendPositionChange(), sending "
   2202           "NOTIFY_IME_OF_POSITION_CHANGE...",
   2203           this));
   2204 
   2205  MOZ_RELEASE_ASSERT(observer->mSendingNotification == NOTIFY_IME_OF_NOTHING);
   2206  observer->mSendingNotification = NOTIFY_IME_OF_POSITION_CHANGE;
   2207  IMEStateManager::NotifyIME(IMENotification(NOTIFY_IME_OF_POSITION_CHANGE),
   2208                             observer->mWidget);
   2209  observer->mSendingNotification = NOTIFY_IME_OF_NOTHING;
   2210 
   2211  MOZ_LOG(sIMECOLog, LogLevel::Debug,
   2212          ("0x%p   IMENotificationSender::SendPositionChange(), sent "
   2213           "NOTIFY_IME_OF_POSITION_CHANGE",
   2214           this));
   2215 }
   2216 
   2217 void IMEContentObserver::IMENotificationSender::SendCompositionEventHandled() {
   2218  RefPtr<IMEContentObserver> observer = GetObserver();
   2219  if (!observer) {
   2220    return;
   2221  }
   2222 
   2223  if (!CanNotifyIME(eChangeEventType_CompositionEventHandled)) {
   2224    MOZ_LOG(sIMECOLog, LogLevel::Warning,
   2225            ("0x%p   IMENotificationSender::SendCompositionEventHandled(), "
   2226             "Warning, does not send notification due to impossible to notify "
   2227             "IME of composition event handled",
   2228             this));
   2229    return;
   2230  }
   2231 
   2232  if (!IsSafeToNotifyIME(eChangeEventType_CompositionEventHandled)) {
   2233    MOZ_LOG(sIMECOLog, LogLevel::Warning,
   2234            ("0x%p   IMENotificationSender::SendCompositionEventHandled(), "
   2235             "Warning, does not send notification due to unsafe, retrying to "
   2236             "send NOTIFY_IME_OF_POSITION_CHANGE...",
   2237             this));
   2238    observer->PostCompositionEventHandledNotification();
   2239    return;
   2240  }
   2241 
   2242  MOZ_LOG(sIMECOLog, LogLevel::Info,
   2243          ("0x%p IMENotificationSender::SendCompositionEventHandled(), sending "
   2244           "NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED...",
   2245           this));
   2246 
   2247  MOZ_RELEASE_ASSERT(observer->mSendingNotification == NOTIFY_IME_OF_NOTHING);
   2248  observer->mSendingNotification = NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED;
   2249  IMEStateManager::NotifyIME(
   2250      IMENotification(NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED),
   2251      observer->mWidget);
   2252  observer->mSendingNotification = NOTIFY_IME_OF_NOTHING;
   2253 
   2254  MOZ_LOG(sIMECOLog, LogLevel::Debug,
   2255          ("0x%p   IMENotificationSender::SendCompositionEventHandled(), sent "
   2256           "NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED",
   2257           this));
   2258 }
   2259 
   2260 /******************************************************************************
   2261 * mozilla::IMEContentObserver::DocumentObservingHelper
   2262 ******************************************************************************/
   2263 
   2264 NS_IMPL_CYCLE_COLLECTION_CLASS(IMEContentObserver::DocumentObserver)
   2265 
   2266 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IMEContentObserver::DocumentObserver)
   2267  // StopObserving() releases mIMEContentObserver and mDocument.
   2268  tmp->StopObserving();
   2269 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
   2270 
   2271 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IMEContentObserver::DocumentObserver)
   2272  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIMEContentObserver)
   2273  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
   2274 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
   2275 
   2276 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IMEContentObserver::DocumentObserver)
   2277  NS_INTERFACE_MAP_ENTRY(nsIDocumentObserver)
   2278  NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
   2279  NS_INTERFACE_MAP_ENTRY(nsISupports)
   2280 NS_INTERFACE_MAP_END
   2281 
   2282 NS_IMPL_CYCLE_COLLECTING_ADDREF(IMEContentObserver::DocumentObserver)
   2283 NS_IMPL_CYCLE_COLLECTING_RELEASE(IMEContentObserver::DocumentObserver)
   2284 
   2285 void IMEContentObserver::DocumentObserver::Observe(Document* aDocument) {
   2286  MOZ_ASSERT(aDocument);
   2287 
   2288  // Guarantee that aDocument won't be destroyed during a call of
   2289  // StopObserving().
   2290  RefPtr<Document> newDocument = aDocument;
   2291 
   2292  StopObserving();
   2293 
   2294  mDocument = std::move(newDocument);
   2295  mDocument->AddObserver(this);
   2296 }
   2297 
   2298 void IMEContentObserver::DocumentObserver::StopObserving() {
   2299  if (!IsObserving()) {
   2300    return;
   2301  }
   2302 
   2303  // Grab IMEContentObserver which could be destroyed during method calls.
   2304  RefPtr<IMEContentObserver> observer = std::move(mIMEContentObserver);
   2305 
   2306  // Stop observing the document first.
   2307  RefPtr<Document> document = std::move(mDocument);
   2308  document->RemoveObserver(this);
   2309 
   2310  // Notify IMEContentObserver of ending of document updates if this already
   2311  // notified it of beginning of document updates.
   2312  for (; IsUpdating(); --mDocumentUpdating) {
   2313    // FYI: IsUpdating() returns true until mDocumentUpdating becomes 0.
   2314    //      However, IsObserving() returns false now because mDocument was
   2315    //      already cleared above.  Therefore, this method won't be called
   2316    //      recursively.
   2317    observer->EndDocumentUpdate();
   2318  }
   2319 }
   2320 
   2321 void IMEContentObserver::DocumentObserver::Destroy() {
   2322  StopObserving();
   2323  mIMEContentObserver = nullptr;
   2324 }
   2325 
   2326 void IMEContentObserver::DocumentObserver::BeginUpdate(Document* aDocument) {
   2327  if (NS_WARN_IF(Destroyed()) || NS_WARN_IF(!IsObserving())) {
   2328    return;
   2329  }
   2330  mIMEContentObserver->BeginDocumentUpdate();
   2331  mDocumentUpdating++;
   2332 }
   2333 
   2334 void IMEContentObserver::DocumentObserver::EndUpdate(Document* aDocument) {
   2335  if (NS_WARN_IF(Destroyed()) || NS_WARN_IF(!IsObserving()) ||
   2336      NS_WARN_IF(!IsUpdating())) {
   2337    return;
   2338  }
   2339  mDocumentUpdating--;
   2340  mIMEContentObserver->EndDocumentUpdate();
   2341 }
   2342 
   2343 /******************************************************************************
   2344 * mozilla::IMEContentObserver::FlatTextCache
   2345 ******************************************************************************/
   2346 
   2347 void IMEContentObserver::FlatTextCache::Clear(const char* aCallerName) {
   2348  if (!HasCache()) {
   2349    return;
   2350  }
   2351  MOZ_LOG(sCacheLog, LogLevel::Info,
   2352          ("%s.Clear: called by %s", mInstanceName, aCallerName));
   2353  mContainerNode = nullptr;
   2354  mContent = nullptr;
   2355  mFlatTextLength = 0;
   2356 }
   2357 
   2358 nsresult IMEContentObserver::FlatTextCache::
   2359    ComputeAndCacheFlatTextLengthBeforeEndOfContent(
   2360        const char* aCallerName, const nsIContent& aContent,
   2361        const Element* aRootElement) {
   2362  MOZ_ASSERT(aRootElement);
   2363  MOZ_ASSERT(aContent.GetParentNode());
   2364 
   2365  uint32_t length = 0;
   2366  nsresult rv = ContentEventHandler::GetFlatTextLengthInRange(
   2367      RawNodePosition::BeforeFirstContentOf(*aRootElement),
   2368      RawNodePosition::After(aContent), aRootElement, &length,
   2369      LineBreakType::LINE_BREAK_TYPE_NATIVE);
   2370  if (NS_FAILED(rv)) {
   2371    Clear(aCallerName);
   2372    return rv;
   2373  }
   2374 
   2375  CacheFlatTextLengthBeforeEndOfContent(aCallerName, aContent, length,
   2376                                        aRootElement);
   2377  return NS_OK;
   2378 }
   2379 
   2380 void IMEContentObserver::FlatTextCache::CacheFlatTextLengthBeforeEndOfContent(
   2381    const char* aCallerName, const nsIContent& aContent,
   2382    uint32_t aFlatTextLength, const dom::Element* aRootElement) {
   2383  mContainerNode = aContent.GetParentNode();
   2384  mContent = const_cast<nsIContent*>(&aContent);
   2385  mFlatTextLength = aFlatTextLength;
   2386  MOZ_ASSERT(IsCachingToEndOfContent());
   2387  MOZ_LOG(sCacheLog, LogLevel::Info,
   2388          ("%s.%s: called by %s -> %s", mInstanceName, __func__,
   2389           ShortenFunctionName(aCallerName), ToString(*this).c_str()));
   2390  AssertValidCache(aRootElement);
   2391 }
   2392 
   2393 nsresult IMEContentObserver::FlatTextCache::
   2394    ComputeAndCacheFlatTextLengthBeforeFirstContent(
   2395        const char* aCallerName, const nsINode& aContainer,
   2396        const Element* aRootElement) {
   2397  MOZ_ASSERT(aRootElement);
   2398 
   2399  const Result<uint32_t, nsresult>
   2400      lengthIncludingLineBreakCausedByOpenTagOfContainer =
   2401          FlatTextCache::ComputeTextLengthBeforeFirstContentOf(aContainer,
   2402                                                               aRootElement);
   2403  if (MOZ_UNLIKELY(
   2404          lengthIncludingLineBreakCausedByOpenTagOfContainer.isErr())) {
   2405    Clear(__FUNCTION__);
   2406    return lengthIncludingLineBreakCausedByOpenTagOfContainer.inspectErr();
   2407  }
   2408 
   2409  CacheFlatTextLengthBeforeFirstContent(
   2410      aCallerName, aContainer,
   2411      lengthIncludingLineBreakCausedByOpenTagOfContainer.inspect(),
   2412      aRootElement);
   2413  return NS_OK;
   2414 }
   2415 
   2416 void IMEContentObserver::FlatTextCache::CacheFlatTextLengthBeforeFirstContent(
   2417    const char* aCallerName, const nsINode& aContainer,
   2418    uint32_t aFlatTextLength, const dom::Element* aRootElement) {
   2419  mContainerNode = const_cast<nsINode*>(&aContainer);
   2420  mContent = nullptr;
   2421  mFlatTextLength = aFlatTextLength;
   2422  MOZ_ASSERT(IsCachingToStartOfContainer());
   2423  MOZ_LOG(sCacheLog, LogLevel::Info,
   2424          ("%s.%s: called by %s -> %s", mInstanceName, __func__,
   2425           ShortenFunctionName(aCallerName), ToString(*this).c_str()));
   2426  AssertValidCache(aRootElement);
   2427 }
   2428 
   2429 Maybe<uint32_t>
   2430 IMEContentObserver::FlatTextCache::GetFlatTextLengthBeforeContent(
   2431    const nsIContent& aContent, const dom::Element* aRootElement,
   2432    ForRemoval aForRemoval) const {
   2433  MOZ_ASSERT(aRootElement);
   2434  if (!mContainerNode) {
   2435    return Nothing();
   2436  }
   2437 
   2438  nsIContent* const prevSibling = aContent.GetPreviousSibling();
   2439  if (IsCachingToStartOfContainer()) {
   2440    MOZ_ASSERT(!mContent);
   2441    // If aContent is the first child of mContainerNode and we're caching text
   2442    // length before first child of mContainerNode, we're caching the result
   2443    // as-is..
   2444    if (!prevSibling && mContainerNode == aContent.GetParentNode()) {
   2445      return Some(mFlatTextLength);
   2446    }
   2447    return Nothing();
   2448  }
   2449 
   2450  MOZ_ASSERT(IsCachingToEndOfContent());
   2451  MOZ_ASSERT(mContent);
   2452 
   2453  // If we're caching text length before end of previous sibling of aContent,
   2454  // the cached length is the result of this call.
   2455  if (mContent == prevSibling) {
   2456    return Some(mFlatTextLength);
   2457  }
   2458 
   2459  // If we're caching text length before end of aContent, aContent siblings
   2460  // may be being removed backward because aContent is the previous sibling of
   2461  // previously removed node.  We should return the length with computing the
   2462  // text length of aContent because it's much faster than computing the length
   2463  // starting from the root element especially when there are a lot of preceding
   2464  // content.
   2465  if (mContent == &aContent) {
   2466    const Result<uint32_t, nsresult> textLength =
   2467        FlatTextCache::ComputeTextLengthOfContent(aContent, aRootElement,
   2468                                                  aForRemoval);
   2469    if (NS_WARN_IF(textLength.isErr()) ||
   2470        NS_WARN_IF(mFlatTextLength < textLength.inspect())) {
   2471      return Nothing();
   2472    }
   2473    return Some(mFlatTextLength - textLength.inspect());
   2474  }
   2475  return Nothing();
   2476 }
   2477 
   2478 Maybe<uint32_t> IMEContentObserver::FlatTextCache::GetFlatTextOffsetOnInsertion(
   2479    const nsIContent& aFirstContent, const nsIContent& aLastContent,
   2480    const dom::Element* aRootElement) const {
   2481  MOZ_ASSERT(aRootElement);
   2482  MOZ_ASSERT(aFirstContent.GetParentNode() == aLastContent.GetParentNode());
   2483  MOZ_ASSERT(!aFirstContent.IsBeingRemoved());
   2484  MOZ_ASSERT(!aLastContent.IsBeingRemoved());
   2485 
   2486  if (!mContainerNode || mContainerNode != aFirstContent.GetParentNode()) {
   2487    return Nothing();
   2488  }
   2489 
   2490  if (IsCachingToStartOfContainer()) {
   2491    MOZ_ASSERT(!mContent);
   2492    // If aFirstContent is the first child of mContainerNode, we're caching the
   2493    // result as-is.
   2494    if (mContainerNode->GetFirstChild() == &aFirstContent) {
   2495      return Some(mFlatTextLength);
   2496    }
   2497    return Nothing();
   2498  }
   2499 
   2500  MOZ_ASSERT(IsCachingToEndOfContent());
   2501  MOZ_ASSERT(mContent);
   2502  MOZ_ASSERT(mContent != &aFirstContent);
   2503  MOZ_ASSERT(mContent != &aLastContent);
   2504 
   2505  // When the content nodes are inserted forward, we may cache text length
   2506  // before end of last inserted content.  If so, mContent should be the
   2507  // previous sibling of aFirstContent.  Then, we can return the cached length
   2508  // simply.
   2509  if (mContent == aFirstContent.GetPreviousSibling()) {
   2510    return Some(mFlatTextLength);
   2511  }
   2512  // When the content nodes inserted backward, we may cache text length before
   2513  // the end of the last inserted content which is next or latter sibling of
   2514  // aLastContent.  In this case, we can compute the length with the cache with
   2515  // computing text length starting from the next sibling of aLastContent to
   2516  // mContent which were previously inserted.  That must be faster than
   2517  // computing the length starting from the root element.
   2518  if (mContent == aLastContent.GetNextSibling() ||
   2519      aLastContent.ComputeIndexInParentNode().valueOr(UINT32_MAX) <
   2520          mContent->ComputeIndexInParentNode().valueOr(0u)) {
   2521    Result<uint32_t, nsresult> previouslyInsertedTextLengthOrError =
   2522        FlatTextCache::ComputeTextLengthStartOfContentToEndOfContent(
   2523            *aLastContent.GetNextSibling(), *mContent, aRootElement);
   2524    if (NS_WARN_IF(previouslyInsertedTextLengthOrError.isErr()) ||
   2525        NS_WARN_IF(mFlatTextLength <
   2526                   previouslyInsertedTextLengthOrError.inspect())) {
   2527      return Nothing();
   2528    }
   2529    // mFlatTextLength contains the last inserted text length, but it does not
   2530    // contain text length starting from aFirstContent to aLastContent.
   2531    // Therefore, subtracting the last inserted text length from mFlatTextLength
   2532    // equals the text length before aFirstContent.
   2533    return Some(mFlatTextLength - previouslyInsertedTextLengthOrError.unwrap());
   2534  }
   2535  return Nothing();
   2536 }
   2537 
   2538 /* static */
   2539 Result<uint32_t, nsresult>
   2540 IMEContentObserver::FlatTextCache::ComputeTextLengthOfContent(
   2541    const nsIContent& aContent, const dom::Element* aRootElement,
   2542    ForRemoval aForRemoval) {
   2543  MOZ_ASSERT(aRootElement);
   2544 
   2545  if (const Text* textNode = Text::FromNode(aContent)) {
   2546    return ContentEventHandler::GetNativeTextLength(*textNode);
   2547  }
   2548 
   2549  if (aForRemoval == ForRemoval::Yes) {
   2550    // When we compute the text length of the removing content node, we need to
   2551    // select all children in the removing node because of the same reason
   2552    // above.  Therefore, if a <div> is being removed, we want to compute
   2553    // `{<div>...}</div>`.  In this case, we want to include the open tag of
   2554    // aRemovingContent if it's an element to add the line break if it's caused
   2555    // by the open tag.  However, we have no way to specify it with
   2556    // RawNodePosition, but ContentEventHandler::GetFlatTextLengthInRange()
   2557    // treats the range as the start container is selected.  Therefore, we
   2558    // should use a RawNodePosition setting its container to the removed node.
   2559    uint32_t textLength = 0;
   2560    RawNodePosition start(const_cast<nsIContent*>(&aContent), 0u);
   2561    start.mAfterOpenTag = false;
   2562    nsresult rv = ContentEventHandler::GetFlatTextLengthInRange(
   2563        start, RawNodePosition::AtEndOf(aContent), aRootElement, &textLength,
   2564        LineBreakType::LINE_BREAK_TYPE_NATIVE, /* aIsRemovingNode = */ true);
   2565    if (NS_FAILED(rv)) {
   2566      return Err(rv);
   2567    }
   2568    return textLength;
   2569  }
   2570 
   2571  return ComputeTextLengthStartOfContentToEndOfContent(aContent, aContent,
   2572                                                       aRootElement);
   2573 }
   2574 
   2575 /* static */
   2576 Result<uint32_t, nsresult>
   2577 IMEContentObserver::FlatTextCache::ComputeTextLengthBeforeContent(
   2578    const nsIContent& aContent, const dom::Element* aRootElement) {
   2579  uint32_t textLengthBeforeContent = 0;
   2580  nsresult rv = ContentEventHandler::GetFlatTextLengthInRange(
   2581      RawNodePosition::BeforeFirstContentOf(*aRootElement),
   2582      RawNodePosition::Before(aContent), aRootElement, &textLengthBeforeContent,
   2583      LineBreakType::LINE_BREAK_TYPE_NATIVE);
   2584  if (NS_FAILED(rv)) {
   2585    return Err(rv);
   2586  }
   2587  return textLengthBeforeContent;
   2588 }
   2589 
   2590 /* static */
   2591 Result<uint32_t, nsresult> IMEContentObserver::FlatTextCache::
   2592    ComputeTextLengthStartOfContentToEndOfContent(
   2593        const nsIContent& aStartContent, const nsIContent& aEndContent,
   2594        const dom::Element* aRootElement) {
   2595  uint32_t textLength = 0;
   2596  nsresult rv = ContentEventHandler::GetFlatTextLengthInRange(
   2597      RawNodePosition::Before(aStartContent),
   2598      RawNodePosition::After(aEndContent), aRootElement, &textLength,
   2599      LineBreakType::LINE_BREAK_TYPE_NATIVE);
   2600  if (NS_FAILED(rv)) {
   2601    return Err(rv);
   2602  }
   2603  return textLength;
   2604 }
   2605 
   2606 /* static */
   2607 Result<uint32_t, nsresult>
   2608 IMEContentObserver::FlatTextCache::ComputeTextLengthBeforeFirstContentOf(
   2609    const nsINode& aContainer, const dom::Element* aRootElement) {
   2610  uint32_t lengthIncludingLineBreakCausedByOpenTagOfContent = 0;
   2611  nsresult rv = ContentEventHandler::GetFlatTextLengthInRange(
   2612      RawNodePosition::BeforeFirstContentOf(*aRootElement),
   2613      // Include the line break caused by open tag of aContainer if it's an
   2614      // element when we cache text length before first content of aContainer.
   2615      RawNodePosition(const_cast<nsINode*>(&aContainer), nullptr), aRootElement,
   2616      &lengthIncludingLineBreakCausedByOpenTagOfContent,
   2617      LineBreakType::LINE_BREAK_TYPE_NATIVE);
   2618  if (NS_FAILED(rv)) {
   2619    return Err(rv);
   2620  }
   2621  return lengthIncludingLineBreakCausedByOpenTagOfContent;
   2622 }
   2623 
   2624 void IMEContentObserver::FlatTextCache::AssertValidCache(
   2625    const Element* aRootElement) const {
   2626 #ifdef DEBUG
   2627  if (MOZ_LIKELY(
   2628          !StaticPrefs::test_ime_content_observer_assert_valid_cache())) {
   2629    return;
   2630  }
   2631  MOZ_ASSERT(aRootElement);
   2632  if (!mContainerNode) {
   2633    return;
   2634  }
   2635  MOZ_ASSERT(mContainerNode->IsInclusiveDescendantOf(aRootElement));
   2636  MOZ_ASSERT_IF(mContent, mContent->IsInclusiveDescendantOf(aRootElement));
   2637 
   2638  if (IsCachingToEndOfContent()) {
   2639    MOZ_ASSERT(mContent);
   2640    Result<uint32_t, nsresult> offset =
   2641        FlatTextCache::ComputeTextLengthBeforeContent(*mContent, aRootElement);
   2642    MOZ_ASSERT(offset.isOk());
   2643    Result<uint32_t, nsresult> length =
   2644        FlatTextCache::ComputeTextLengthStartOfContentToEndOfContent(
   2645            *mContent, *mContent, aRootElement);
   2646    MOZ_ASSERT(length.isOk());
   2647    if (mFlatTextLength != offset.inspect() + length.inspect()) {
   2648      nsAutoString innerHTMLOfEditable;
   2649      const_cast<Element*>(aRootElement)
   2650          ->GetInnerHTML(innerHTMLOfEditable, IgnoreErrors());
   2651      NS_WARNING(
   2652          nsPrintfCString(
   2653              "mFlatTextLength=%u, offset: %u, length: %u, mContainerNode:%s, "
   2654              "mContent=%s (%s)",
   2655              mFlatTextLength, offset.inspect(), length.inspect(),
   2656              ToString(mContainerNode).c_str(), ToString(*mContent).c_str(),
   2657              NS_ConvertUTF16toUTF8(innerHTMLOfEditable).get())
   2658              .get());
   2659    }
   2660    MOZ_ASSERT(mFlatTextLength == offset.inspect() + length.inspect());
   2661    return;
   2662  }
   2663 
   2664  MOZ_ASSERT(!mContent);
   2665  MOZ_ASSERT(mContainerNode->IsContent());
   2666  Result<uint32_t, nsresult> offset =
   2667      ComputeTextLengthBeforeFirstContentOf(*mContainerNode, aRootElement);
   2668  MOZ_ASSERT(offset.isOk());
   2669  if (mFlatTextLength != offset.inspect()) {
   2670    nsAutoString innerHTMLOfEditable;
   2671    const_cast<Element*>(aRootElement)
   2672        ->GetInnerHTML(innerHTMLOfEditable, IgnoreErrors());
   2673    NS_WARNING(nsPrintfCString(
   2674                   "mFlatTextLength=%u, offset: %u, mContainerNode:%s (%s)",
   2675                   mFlatTextLength, offset.inspect(),
   2676                   ToString(mContainerNode).c_str(),
   2677                   NS_ConvertUTF16toUTF8(innerHTMLOfEditable).get())
   2678                   .get());
   2679  }
   2680  MOZ_ASSERT(mFlatTextLength == offset.inspect());
   2681 #endif  // #ifdef DEBUG
   2682 }
   2683 
   2684 void IMEContentObserver::FlatTextCache::ContentAdded(
   2685    const char* aCallerName, const nsIContent& aFirstContent,
   2686    const nsIContent& aLastContent, const Maybe<uint32_t>& aAddedFlatTextLength,
   2687    const Element* aRootElement) {
   2688  MOZ_ASSERT(nsContentUtils::ComparePoints(
   2689                 RawRangeBoundary(aFirstContent.GetParentNode(),
   2690                                  aFirstContent.GetPreviousSibling()),
   2691                 RawRangeBoundary(aLastContent.GetParentNode(),
   2692                                  aLastContent.GetPreviousSibling()))
   2693                 .value() <= 0);
   2694  if (!mContainerNode) {
   2695    return;  // No cache.
   2696  }
   2697 
   2698  // We can keep cache without anything if the next sibling is the first added
   2699  // content.
   2700  if (mContent && &aFirstContent == mContent->GetNextSibling()) {
   2701    return;
   2702  }
   2703 
   2704  if (IsCachingToStartOfContainer()) {
   2705    MOZ_ASSERT(!mContent);
   2706    // We can keep the cache if added nodes are children of mContainerNode since
   2707    // we cache the text length before its first child.
   2708    if (mContainerNode == aFirstContent.GetParentNode()) {
   2709      AssertValidCache(aRootElement);
   2710      return;
   2711    }
   2712 
   2713    // Let's clear the cache for avoiding to do anything expensive for a hot
   2714    // path only for not frequent cases.  Be aware, this is a hot code path
   2715    // here.  Therefore, expensive computation would make the DOM mutation
   2716    // slower.
   2717    Clear(aCallerName);
   2718    return;
   2719  }
   2720 
   2721  MOZ_ASSERT(IsCachingToEndOfContent());
   2722  MOZ_ASSERT(mContent);
   2723  if (aAddedFlatTextLength.isSome() &&
   2724      aLastContent.GetNextSibling() == mContent) {
   2725    // If we cache test length before end of next sibling of the last added
   2726    // content node, we can update the cached text simply.
   2727    CacheFlatTextLengthBeforeEndOfContent(
   2728        aCallerName, *mContent, mFlatTextLength + *aAddedFlatTextLength,
   2729        aRootElement);
   2730    return;
   2731  }
   2732 
   2733  // If empty nodes are appended, we can avoid to clear the cache because the
   2734  // new nodes won't affect to the flattened text content.
   2735  const bool addingEmptyNode = [&]() {
   2736    if (aAddedFlatTextLength.isSome()) {
   2737      return !aAddedFlatTextLength.value();
   2738    }
   2739    if (&aFirstContent != &aLastContent) {
   2740      return false;  // If it's better to check here strictly, please do that.
   2741    }
   2742    if (aFirstContent.IsText()) {
   2743      return !aFirstContent.AsText()->TextDataLength();
   2744    }
   2745    if (aFirstContent.IsCharacterData()) {
   2746      return true;  // Should be invisible.
   2747    }
   2748    if (aFirstContent.HasChildren()) {
   2749      return false;  // If it's better to check here strictly, please do that.
   2750    }
   2751    Result<uint32_t, nsresult> lengthOrError =
   2752        ComputeTextLengthOfContent(aFirstContent, aRootElement, ForRemoval::No);
   2753    return lengthOrError.isOk() && !lengthOrError.unwrap();
   2754  }();
   2755  if (addingEmptyNode) {
   2756    return;
   2757  }
   2758 
   2759  // Let's clear the cache for avoiding to do anything expensive for a hot
   2760  // path only for not frequent cases.  Be aware, this is a hot code path here.
   2761  // Therefore, expensive computation would make the DOM mutation slower.
   2762  Clear(aCallerName);
   2763 }
   2764 
   2765 void IMEContentObserver::FlatTextCache::ContentWillBeRemoved(
   2766    const nsIContent& aContent, uint32_t aFlatTextLengthOfContent,
   2767    const Element* aRootElement) {
   2768  if (!mContainerNode) {
   2769    return;  // No cache.
   2770  }
   2771 
   2772  // We can keep the cache without anything if the next sibling is removed.
   2773  if (mContent && mContent == aContent.GetPreviousSibling()) {
   2774    return;
   2775  }
   2776 
   2777  if (IsCachingToStartOfContainer()) {
   2778    MOZ_ASSERT(!mContent);
   2779    // We're caching text length before first child of mContainerNode.
   2780    // Therefore, if a child of mContainerNode is being removed, we can keep the
   2781    // cache.
   2782    if (mContainerNode == aContent.GetParentNode()) {
   2783      AssertValidCache(aRootElement);
   2784      return;
   2785    }
   2786 
   2787    // Let's clear the cache for avoiding to do anything expensive for a hot
   2788    // path only for not frequent cases.  Be aware, this is a hot code path
   2789    // here.  Therefore, expensive computation would make the DOM mutation
   2790    // slower.
   2791    Clear(__FUNCTION__);
   2792    return;
   2793  }
   2794 
   2795  MOZ_ASSERT(IsCachingToEndOfContent());
   2796  if (&aContent == mContent) {
   2797    MOZ_ASSERT(mFlatTextLength >= aFlatTextLengthOfContent);
   2798    if (NS_WARN_IF(mFlatTextLength < aFlatTextLengthOfContent)) {
   2799      Clear(__FUNCTION__);
   2800      return;
   2801    }
   2802    // We're caching text length before end of aContent.  So, if there is a
   2803    // previous sibling, we can cache text length before aContent with
   2804    // subtracting the text length caused by aContent from the cached value.
   2805    if (nsIContent* prevSibling = aContent.GetPreviousSibling()) {
   2806      CacheFlatTextLengthBeforeEndOfContent(
   2807          __FUNCTION__, *prevSibling,
   2808          mFlatTextLength - aFlatTextLengthOfContent, aRootElement);
   2809      return;
   2810    }
   2811    // Otherwise, i.e., if aContent is first child of mContainerNode, we can
   2812    // cache text length before first content of mContainerNode with subtracting
   2813    // the text length caused by aContent from the cached value.
   2814    CacheFlatTextLengthBeforeFirstContent(
   2815        __FUNCTION__, *mContainerNode,
   2816        mFlatTextLength - aFlatTextLengthOfContent, aRootElement);
   2817    return;
   2818  }
   2819 
   2820  // If the removing content is empty, removing that won't affect to the
   2821  // flattened text.  Therefore, we can avoid to clear the cache.
   2822  if (!aFlatTextLengthOfContent) {
   2823    return;
   2824  }
   2825 
   2826  // Let's clear the cache for avoiding to do anything expensive for a hot
   2827  // path only for not frequent cases.  Be aware, this is a hot code path here.
   2828  // Therefore, expensive computation would make the DOM mutation slower.
   2829  Clear(__FUNCTION__);
   2830 }
   2831 
   2832 /******************************************************************************
   2833 * mozilla::IMEContentObserver::AddedContentCache
   2834 ******************************************************************************/
   2835 
   2836 void IMEContentObserver::AddedContentCache::Clear(const char* aCallerName) {
   2837  mFirst = nullptr;
   2838  mLast = nullptr;
   2839  MOZ_LOG(sCacheLog, LogLevel::Info,
   2840          ("AddedContentCache::Clear: called by %s",
   2841           ShortenFunctionName(aCallerName)));
   2842 }
   2843 
   2844 bool IMEContentObserver::AddedContentCache::IsInRange(
   2845    const nsIContent& aContent, const dom::Element* aRootElement) const {
   2846  MOZ_ASSERT(HasCache());
   2847 
   2848  // First, try to find sibling of mFirst from the ancestor chain of aContent.
   2849  const nsIContent* sibling = [&]() -> const nsIContent* {
   2850    const nsIContent* maybeSibling = &aContent;
   2851    const nsIContent* const container = mFirst->GetParent();
   2852    for (const nsIContent* ancestor : aContent.AncestorsOfType<nsIContent>()) {
   2853      if (ancestor == container) {
   2854        return maybeSibling;
   2855      }
   2856      if (ancestor == aRootElement) {
   2857        return nullptr;
   2858      }
   2859      maybeSibling = ancestor;
   2860    }
   2861    return nullptr;
   2862  }();
   2863  if (!sibling) {
   2864    return false;  // Not in same container node
   2865  }
   2866  // Let's avoid to compute indices...
   2867  if (mFirst == sibling || mLast == sibling ||
   2868      (mFirst != mLast && (mFirst->GetNextSibling() == sibling ||
   2869                           sibling->GetNextSibling() == mLast))) {
   2870    return true;
   2871  }
   2872  if (mFirst == mLast || sibling->GetNextSibling() == mFirst ||
   2873      mLast->GetNextSibling() == sibling || !sibling->GetPreviousSibling() ||
   2874      !sibling->GetNextSibling()) {
   2875    return false;
   2876  }
   2877  const Maybe<uint32_t> index = aContent.ComputeIndexInParentNode();
   2878  MOZ_ASSERT(index.isSome());
   2879  const Maybe<uint32_t> firstIndex = mFirst->ComputeIndexInParentNode();
   2880  MOZ_ASSERT(firstIndex.isSome());
   2881  const Maybe<uint32_t> lastIndex = mLast->ComputeIndexInParentNode();
   2882  MOZ_ASSERT(lastIndex.isSome());
   2883  return firstIndex.value() < index.value() &&
   2884         index.value() < lastIndex.value();
   2885 }
   2886 
   2887 bool IMEContentObserver::AddedContentCache::CanMergeWith(
   2888    const nsIContent& aFirstContent, const nsIContent& aLastContent,
   2889    const dom::Element* aRootElement) const {
   2890  MOZ_ASSERT(HasCache());
   2891  if (aLastContent.GetNextSibling() == mFirst ||
   2892      mLast->GetNextSibling() == &aFirstContent) {
   2893    return true;
   2894  }
   2895  MOZ_DIAGNOSTIC_ASSERT(aFirstContent.GetParentNode() ==
   2896                        aLastContent.GetParentNode());
   2897  if (mFirst->GetParentNode() != aFirstContent.GetParentNode()) {
   2898    return false;
   2899  }
   2900  const Maybe<uint32_t> newFirstIndex =
   2901      aFirstContent.ComputeIndexInParentNode();
   2902  MOZ_RELEASE_ASSERT(newFirstIndex.isSome());
   2903  const Maybe<uint32_t> newLastIndex =
   2904      &aFirstContent == &aLastContent ? newFirstIndex
   2905                                      : aLastContent.ComputeIndexInParentNode();
   2906  MOZ_RELEASE_ASSERT(newLastIndex.isSome());
   2907  const Maybe<uint32_t> currentFirstIndex = mFirst->ComputeIndexInParentNode();
   2908  MOZ_RELEASE_ASSERT(currentFirstIndex.isSome());
   2909  const Maybe<uint32_t> currentLastIndex =
   2910      mFirst == mLast ? currentFirstIndex : mLast->ComputeIndexInParentNode();
   2911  MOZ_RELEASE_ASSERT(currentLastIndex.isSome());
   2912  MOZ_ASSERT(!(newFirstIndex.value() < currentFirstIndex.value() &&
   2913               newLastIndex.value() > currentLastIndex.value()),
   2914             "New content nodes shouldn't contain mFirst nor mLast");
   2915  MOZ_ASSERT(!(newFirstIndex.value() < currentFirstIndex.value() &&
   2916               newLastIndex.value() > currentFirstIndex.value()),
   2917             "New content nodes shouldn't contain mFirst");
   2918  MOZ_ASSERT(!(newFirstIndex.value() < currentLastIndex.value() &&
   2919               newLastIndex.value() > currentLastIndex.value()),
   2920             "New content nodes shouldn't contain mLast");
   2921  return *newFirstIndex > *currentFirstIndex &&
   2922         *newLastIndex < *currentLastIndex;
   2923 }
   2924 
   2925 bool IMEContentObserver::AddedContentCache::TryToCache(
   2926    const nsIContent& aFirstContent, const nsIContent& aLastContent,
   2927    const dom::Element* aRootElement) {
   2928  if (!HasCache()) {
   2929    mFirst = const_cast<nsIContent*>(&aFirstContent);
   2930    mLast = const_cast<nsIContent*>(&aLastContent);
   2931    MOZ_LOG(
   2932        sCacheLog, LogLevel::Info,
   2933        ("AddedContentCache::TryToCache: Starting to cache the range: %s - %s",
   2934         ToString(mFirst).c_str(), ToString(mLast).c_str()));
   2935    return true;
   2936  }
   2937  MOZ_ASSERT(mFirst != &aFirstContent);
   2938  MOZ_ASSERT(mLast != &aLastContent);
   2939  if (aLastContent.GetNextSibling() == mFirst) {
   2940    MOZ_ASSERT(CanMergeWith(aFirstContent, aLastContent, aRootElement));
   2941    mFirst = const_cast<nsIContent*>(&aFirstContent);
   2942    MOZ_LOG(
   2943        sCacheLog, LogLevel::Info,
   2944        ("AddedContentCache::TryToCache: Extending the range backward (to %s)",
   2945         ToString(mFirst).c_str()));
   2946    return true;
   2947  }
   2948  if (mLast->GetNextSibling() == &aFirstContent) {
   2949    MOZ_ASSERT(CanMergeWith(aFirstContent, aLastContent, aRootElement));
   2950    mLast = const_cast<nsIContent*>(&aLastContent);
   2951    MOZ_LOG(
   2952        sCacheLog, LogLevel::Info,
   2953        ("AddedContentCache::TryToCache: Extending the range forward (to %s)",
   2954         ToString(mLast).c_str()));
   2955    return true;
   2956  }
   2957 
   2958  MOZ_DIAGNOSTIC_ASSERT(aFirstContent.GetParentNode() ==
   2959                        aLastContent.GetParentNode());
   2960  if (mFirst->GetParentNode() != aFirstContent.GetParentNode()) {
   2961    MOZ_ASSERT(!CanMergeWith(aFirstContent, aLastContent, aRootElement));
   2962    return false;
   2963  }
   2964  const Maybe<uint32_t> newFirstIndex =
   2965      aFirstContent.ComputeIndexInParentNode();
   2966  MOZ_RELEASE_ASSERT(newFirstIndex.isSome());
   2967  const Maybe<uint32_t> newLastIndex =
   2968      &aFirstContent == &aLastContent ? newFirstIndex
   2969                                      : aLastContent.ComputeIndexInParentNode();
   2970  MOZ_RELEASE_ASSERT(newLastIndex.isSome());
   2971  const Maybe<uint32_t> currentFirstIndex = mFirst->ComputeIndexInParentNode();
   2972  MOZ_RELEASE_ASSERT(currentFirstIndex.isSome());
   2973  const Maybe<uint32_t> currentLastIndex =
   2974      mFirst == mLast ? currentFirstIndex : mLast->ComputeIndexInParentNode();
   2975  MOZ_RELEASE_ASSERT(currentLastIndex.isSome());
   2976  MOZ_ASSERT(!(newFirstIndex.value() < currentFirstIndex.value() &&
   2977               newLastIndex.value() > currentLastIndex.value()),
   2978             "New content nodes shouldn't contain mFirst nor mLast");
   2979  MOZ_ASSERT(!(newFirstIndex.value() < currentFirstIndex.value() &&
   2980               newLastIndex.value() > currentFirstIndex.value()),
   2981             "New content nodes shouldn't contain mFirst");
   2982  MOZ_ASSERT(!(newFirstIndex.value() < currentLastIndex.value() &&
   2983               newLastIndex.value() > currentLastIndex.value()),
   2984             "New content nodes shouldn't contain mLast");
   2985  if (*newFirstIndex > *currentFirstIndex &&
   2986      *newLastIndex < *currentLastIndex) {
   2987    MOZ_ASSERT(CanMergeWith(aFirstContent, aLastContent, aRootElement));
   2988    MOZ_LOG(sCacheLog, LogLevel::Info,
   2989            ("AddedContentCache::TryToCache: New nodes in the range"));
   2990    return true;
   2991  }
   2992  MOZ_ASSERT(!CanMergeWith(aFirstContent, aLastContent, aRootElement));
   2993  return false;
   2994 }
   2995 
   2996 Result<std::pair<uint32_t, uint32_t>, nsresult> IMEContentObserver::
   2997    AddedContentCache::ComputeFlatTextRangeBeforeInsertingNewContent(
   2998        const nsIContent& aNewFirstContent, const nsIContent& aNewLastContent,
   2999        const dom::Element* aRootElement,
   3000        OffsetAndLengthAdjustments& aDifferences) const {
   3001  MOZ_ASSERT(HasCache());
   3002  const Maybe<int32_t> newLastContentComparedWithCachedFirstContent =
   3003      nsContentUtils::ComparePoints(
   3004          RawRangeBoundary(aNewLastContent.GetParentNode(),
   3005                           aNewLastContent.GetPreviousSibling()),
   3006          RawRangeBoundary(mFirst->GetParentNode(),
   3007                           mFirst->GetPreviousSibling()));
   3008  MOZ_RELEASE_ASSERT(newLastContentComparedWithCachedFirstContent.isSome());
   3009  MOZ_ASSERT(*newLastContentComparedWithCachedFirstContent != 0);
   3010  MOZ_ASSERT((*nsContentUtils::ComparePoints(
   3011                  RawRangeBoundary(aNewFirstContent.GetParentNode(),
   3012                                   aNewFirstContent.GetPreviousSibling()),
   3013                  RawRangeBoundary(mFirst->GetParentNode(),
   3014                                   mFirst->GetPreviousSibling())) > 0) ==
   3015                 (*newLastContentComparedWithCachedFirstContent > 0),
   3016             "New nodes shouldn't contain mFirst");
   3017  const Maybe<int32_t> newFirstContentComparedWithCachedLastContent =
   3018      mLast->GetNextSibling() == &aNewFirstContent
   3019          ? Some(1)
   3020          : nsContentUtils::ComparePoints(
   3021                RawRangeBoundary(aNewFirstContent.GetParentNode(),
   3022                                 aNewFirstContent.GetPreviousSibling()),
   3023                // aNewFirstContent and aNewLastContent may be descendants of
   3024                // mLast. Then, we need to ignore the new length.  Therefore,
   3025                // we need to compare aNewFirstContent position with next
   3026                // sibling of mLast.
   3027                RawRangeBoundary(mLast->GetParentNode(), mLast));
   3028  MOZ_RELEASE_ASSERT(newFirstContentComparedWithCachedLastContent.isSome());
   3029  MOZ_ASSERT(*newFirstContentComparedWithCachedLastContent != 0);
   3030  MOZ_ASSERT((*newFirstContentComparedWithCachedLastContent > 0) ==
   3031                 (*nsContentUtils::ComparePoints(
   3032                      RawRangeBoundary(aNewLastContent.GetParentNode(),
   3033                                       aNewLastContent.GetPreviousSibling()),
   3034                      RawRangeBoundary(mLast->GetParentNode(), mLast)) > 0),
   3035             "New nodes shouldn't contain mLast");
   3036 
   3037  Result<uint32_t, nsresult> length =
   3038      FlatTextCache::ComputeTextLengthStartOfContentToEndOfContent(
   3039          *mFirst, *mLast, aRootElement);
   3040  if (NS_WARN_IF(length.isErr())) {
   3041    return length.propagateErr();
   3042  }
   3043  Result<uint32_t, nsresult> offset =
   3044      FlatTextCache::ComputeTextLengthBeforeContent(*mFirst, aRootElement);
   3045  if (NS_WARN_IF(offset.isErr())) {
   3046    return offset.propagateErr();
   3047  }
   3048 
   3049  // If new content nodes are after the cached range, we can just ignore the
   3050  // new content nodes.
   3051  if (*newFirstContentComparedWithCachedLastContent == 1u) {
   3052    aDifferences = OffsetAndLengthAdjustments{0, 0};
   3053    return std::make_pair(offset.inspect(), length.inspect());
   3054  }
   3055 
   3056  Result<uint32_t, nsresult> newLength =
   3057      FlatTextCache::ComputeTextLengthStartOfContentToEndOfContent(
   3058          aNewFirstContent, aNewLastContent, aRootElement);
   3059  if (NS_WARN_IF(newLength.isErr())) {
   3060    return newLength.propagateErr();
   3061  }
   3062 
   3063  // If new content nodes are in the cached range, we need to subtract the new
   3064  // content length from cached content length.
   3065  if (*newLastContentComparedWithCachedFirstContent == 1u) {
   3066    MOZ_RELEASE_ASSERT(length.inspect() >= newLength.inspect());
   3067    aDifferences = OffsetAndLengthAdjustments{0, newLength.inspect()};
   3068    return std::make_pair(offset.inspect(),
   3069                          length.inspect() - newLength.inspect());
   3070  }
   3071 
   3072  // If new content nodes are before the cached range, we need to subtract the
   3073  // new content length from cached offset.
   3074  MOZ_RELEASE_ASSERT(offset.inspect() >= newLength.inspect());
   3075  aDifferences = OffsetAndLengthAdjustments{newLength.inspect(), 0};
   3076  return std::make_pair(offset.inspect() - newLength.inspect(),
   3077                        length.inspect());
   3078 }
   3079 
   3080 }  // namespace mozilla