LCOV - code coverage report
Current view: top level - dom/events - IMEContentObserver.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 1058 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 100 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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 "mozilla/Logging.h"
       8             : 
       9             : #include "ContentEventHandler.h"
      10             : #include "IMEContentObserver.h"
      11             : #include "mozilla/AsyncEventDispatcher.h"
      12             : #include "mozilla/AutoRestore.h"
      13             : #include "mozilla/EventStateManager.h"
      14             : #include "mozilla/IMEStateManager.h"
      15             : #include "mozilla/MouseEvents.h"
      16             : #include "mozilla/TextComposition.h"
      17             : #include "mozilla/TextEvents.h"
      18             : #include "mozilla/dom/Element.h"
      19             : #include "nsContentUtils.h"
      20             : #include "nsGkAtoms.h"
      21             : #include "nsIAtom.h"
      22             : #include "nsIContent.h"
      23             : #include "nsIDocument.h"
      24             : #include "nsIDOMDocument.h"
      25             : #include "nsIDOMRange.h"
      26             : #include "nsIFrame.h"
      27             : #include "nsINode.h"
      28             : #include "nsIPresShell.h"
      29             : #include "nsISelectionController.h"
      30             : #include "nsISelectionPrivate.h"
      31             : #include "nsISupports.h"
      32             : #include "nsIWidget.h"
      33             : #include "nsPresContext.h"
      34             : #include "nsRefreshDriver.h"
      35             : #include "nsWeakReference.h"
      36             : #include "WritingModes.h"
      37             : 
      38             : namespace mozilla {
      39             : 
      40             : using namespace widget;
      41             : 
      42             : LazyLogModule sIMECOLog("IMEContentObserver");
      43             : 
      44             : static const char*
      45           0 : ToChar(bool aBool)
      46             : {
      47           0 :   return aBool ? "true" : "false";
      48             : }
      49             : 
      50             : class WritingModeToString final : public nsAutoCString
      51             : {
      52             : public:
      53           0 :   explicit WritingModeToString(const WritingMode& aWritingMode)
      54           0 :   {
      55           0 :     if (!aWritingMode.IsVertical()) {
      56           0 :       AssignLiteral("Horizontal");
      57           0 :       return;
      58             :     }
      59           0 :     if (aWritingMode.IsVerticalLR()) {
      60           0 :       AssignLiteral("Vertical (LR)");
      61           0 :       return;
      62             :     }
      63           0 :     AssignLiteral("Vertical (RL)");
      64             :   }
      65           0 :   virtual ~WritingModeToString() {}
      66             : };
      67             : 
      68             : class SelectionChangeDataToString final : public nsAutoCString
      69             : {
      70             : public:
      71           0 :   explicit SelectionChangeDataToString(
      72             :              const IMENotification::SelectionChangeDataBase& aData)
      73           0 :   {
      74           0 :     if (!aData.IsValid()) {
      75           0 :       AppendLiteral("{ IsValid()=false }");
      76           0 :       return;
      77             :     }
      78           0 :     AppendPrintf("{ mOffset=%u, ", aData.mOffset);
      79           0 :     if (aData.mString->Length() > 20) {
      80           0 :       AppendPrintf("mString.Length()=%u, ", aData.mString->Length());
      81             :     } else {
      82           0 :       AppendPrintf("mString=\"%s\" (Length()=%u), ",
      83           0 :                    NS_ConvertUTF16toUTF8(*aData.mString).get(),
      84           0 :                    aData.mString->Length());
      85             :     }
      86           0 :     AppendPrintf("GetWritingMode()=%s, mReversed=%s, mCausedByComposition=%s, "
      87             :                  "mCausedBySelectionEvent=%s }",
      88           0 :                  WritingModeToString(aData.GetWritingMode()).get(),
      89           0 :                  ToChar(aData.mReversed),
      90           0 :                  ToChar(aData.mCausedByComposition),
      91           0 :                  ToChar(aData.mCausedBySelectionEvent));
      92             :   }
      93           0 :   virtual ~SelectionChangeDataToString() {}
      94             : };
      95             : 
      96             : class TextChangeDataToString final : public nsAutoCString
      97             : {
      98             : public:
      99           0 :   explicit TextChangeDataToString(
     100             :              const IMENotification::TextChangeDataBase& aData)
     101           0 :   {
     102           0 :     if (!aData.IsValid()) {
     103           0 :       AppendLiteral("{ IsValid()=false }");
     104           0 :       return;
     105             :     }
     106           0 :     AppendPrintf("{ mStartOffset=%u, mRemovedEndOffset=%u, mAddedEndOffset=%u, "
     107             :                  "mCausedOnlyByComposition=%s, "
     108             :                  "mIncludingChangesDuringComposition=%s, "
     109             :                  "mIncludingChangesWithoutComposition=%s }",
     110           0 :                  aData.mStartOffset, aData.mRemovedEndOffset,
     111           0 :                  aData.mAddedEndOffset,
     112           0 :                  ToChar(aData.mCausedOnlyByComposition),
     113           0 :                  ToChar(aData.mIncludingChangesDuringComposition),
     114           0 :                  ToChar(aData.mIncludingChangesWithoutComposition));
     115             :   }
     116           0 :   virtual ~TextChangeDataToString() {}
     117             : };
     118             : 
     119             : /******************************************************************************
     120             :  * mozilla::IMEContentObserver
     121             :  ******************************************************************************/
     122             : 
     123             : NS_IMPL_CYCLE_COLLECTION_CLASS(IMEContentObserver)
     124             : 
     125             : // Note that we don't need to add mFirstAddedNodeContainer nor
     126             : // mLastAddedNodeContainer to cycle collection because they are non-null only
     127             : // during short time and shouldn't be touched while they are non-null.
     128             : 
     129           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IMEContentObserver)
     130           0 :   nsAutoScriptBlocker scriptBlocker;
     131             : 
     132           0 :   tmp->NotifyIMEOfBlur();
     133           0 :   tmp->UnregisterObservers();
     134             : 
     135           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelection)
     136           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mRootContent)
     137           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mEditableNode)
     138           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocShell)
     139           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mEditorBase)
     140           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentObserver)
     141           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mEndOfAddedTextCache.mContainerNode)
     142           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mStartOfRemovingTextRangeCache.mContainerNode)
     143             : 
     144           0 :   tmp->mIMENotificationRequests = nullptr;
     145           0 :   tmp->mESM = nullptr;
     146           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     147             : 
     148           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IMEContentObserver)
     149           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWidget)
     150           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFocusedWidget)
     151           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelection)
     152           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRootContent)
     153           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEditableNode)
     154           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocShell)
     155           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEditorBase)
     156           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentObserver)
     157           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEndOfAddedTextCache.mContainerNode)
     158           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(
     159             :     mStartOfRemovingTextRangeCache.mContainerNode)
     160           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     161             : 
     162           0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IMEContentObserver)
     163           0 :  NS_INTERFACE_MAP_ENTRY(nsISelectionListener)
     164           0 :  NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
     165           0 :  NS_INTERFACE_MAP_ENTRY(nsIReflowObserver)
     166           0 :  NS_INTERFACE_MAP_ENTRY(nsIScrollObserver)
     167           0 :  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
     168           0 :  NS_INTERFACE_MAP_ENTRY(nsIEditorObserver)
     169           0 :  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISelectionListener)
     170           0 : NS_INTERFACE_MAP_END
     171             : 
     172           0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(IMEContentObserver)
     173           0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(IMEContentObserver)
     174             : 
     175           0 : IMEContentObserver::IMEContentObserver()
     176             :   : mFirstAddedNodeOffset(0)
     177             :   , mLastAddedNodeOffset(0)
     178             :   , mESM(nullptr)
     179             :   , mIMENotificationRequests(nullptr)
     180             :   , mSuppressNotifications(0)
     181             :   , mPreCharacterDataChangeLength(-1)
     182             :   , mSendingNotification(NOTIFY_IME_OF_NOTHING)
     183             :   , mIsObserving(false)
     184             :   , mIMEHasFocus(false)
     185             :   , mNeedsToNotifyIMEOfFocusSet(false)
     186             :   , mNeedsToNotifyIMEOfTextChange(false)
     187             :   , mNeedsToNotifyIMEOfSelectionChange(false)
     188             :   , mNeedsToNotifyIMEOfPositionChange(false)
     189             :   , mNeedsToNotifyIMEOfCompositionEventHandled(false)
     190           0 :   , mIsHandlingQueryContentEvent(false)
     191             : {
     192             : #ifdef DEBUG
     193           0 :   mTextChangeData.Test();
     194             : #endif
     195           0 : }
     196             : 
     197             : void
     198           0 : IMEContentObserver::Init(nsIWidget* aWidget,
     199             :                          nsPresContext* aPresContext,
     200             :                          nsIContent* aContent,
     201             :                          EditorBase* aEditorBase)
     202             : {
     203           0 :   State state = GetState();
     204           0 :   if (NS_WARN_IF(state == eState_Observing)) {
     205           0 :     return; // Nothing to do.
     206             :   }
     207             : 
     208           0 :   bool firstInitialization = state != eState_StoppedObserving;
     209           0 :   if (!firstInitialization) {
     210             :     // If this is now trying to initialize with new contents, all observers
     211             :     // should be registered again for simpler implementation.
     212           0 :     UnregisterObservers();
     213           0 :     Clear();
     214             :   }
     215             : 
     216           0 :   mESM = aPresContext->EventStateManager();
     217           0 :   mESM->OnStartToObserveContent(this);
     218             : 
     219           0 :   mWidget = aWidget;
     220           0 :   mIMENotificationRequests = &mWidget->IMENotificationRequestsRef();
     221             : 
     222           0 :   if (aWidget->GetInputContext().mIMEState.mEnabled == IMEState::PLUGIN) {
     223           0 :     if (!InitWithPlugin(aPresContext, aContent)) {
     224           0 :       Clear();
     225           0 :       return;
     226             :     }
     227             :   } else {
     228           0 :     if (!InitWithEditor(aPresContext, aContent, aEditorBase)) {
     229           0 :       Clear();
     230           0 :       return;
     231             :     }
     232             :   }
     233             : 
     234           0 :   if (firstInitialization) {
     235             :     // Now, try to send NOTIFY_IME_OF_FOCUS to IME via the widget.
     236           0 :     MaybeNotifyIMEOfFocusSet();
     237             :     // When this is called first time, IME has not received NOTIFY_IME_OF_FOCUS
     238             :     // yet since NOTIFY_IME_OF_FOCUS will be sent to widget asynchronously.
     239             :     // So, we need to do nothing here.  After NOTIFY_IME_OF_FOCUS has been
     240             :     // sent, OnIMEReceivedFocus() will be called and content, selection and/or
     241             :     // position changes will be observed
     242           0 :     return;
     243             :   }
     244             : 
     245             :   // When this is called after editor reframing (i.e., the root editable node
     246             :   // is also recreated), IME has usually received NOTIFY_IME_OF_FOCUS.  In this
     247             :   // case, we need to restart to observe content, selection and/or position
     248             :   // changes in new root editable node.
     249           0 :   ObserveEditableNode();
     250             : 
     251           0 :   if (!NeedsToNotifyIMEOfSomething()) {
     252           0 :     return;
     253             :   }
     254             : 
     255             :   // Some change events may wait to notify IME because this was being
     256             :   // initialized.  It is the time to flush them.
     257           0 :   FlushMergeableNotifications();
     258             : }
     259             : 
     260             : void
     261           0 : IMEContentObserver::OnIMEReceivedFocus()
     262             : {
     263             :   // While Init() notifies IME of focus, pending layout may be flushed
     264             :   // because the notification may cause querying content.  Then, recursive
     265             :   // call of Init() with the latest content may occur.  In such case, we
     266             :   // shouldn't keep first initialization which notified IME of focus.
     267           0 :   if (GetState() != eState_Initializing) {
     268           0 :     return;
     269             :   }
     270             : 
     271             :   // NOTIFY_IME_OF_FOCUS might cause recreating IMEContentObserver
     272             :   // instance via IMEStateManager::UpdateIMEState().  So, this
     273             :   // instance might already have been destroyed, check it.
     274           0 :   if (!mRootContent) {
     275           0 :     return;
     276             :   }
     277             : 
     278             :   // Start to observe which is needed by IME when IME actually has focus.
     279           0 :   ObserveEditableNode();
     280             : 
     281           0 :   if (!NeedsToNotifyIMEOfSomething()) {
     282           0 :     return;
     283             :   }
     284             : 
     285             :   // Some change events may wait to notify IME because this was being
     286             :   // initialized.  It is the time to flush them.
     287           0 :   FlushMergeableNotifications();
     288             : }
     289             : 
     290             : bool
     291           0 : IMEContentObserver::InitWithEditor(nsPresContext* aPresContext,
     292             :                                    nsIContent* aContent,
     293             :                                    EditorBase* aEditorBase)
     294             : {
     295           0 :   MOZ_ASSERT(aEditorBase);
     296             : 
     297             :   mEditableNode =
     298           0 :     IMEStateManager::GetRootEditableNode(aPresContext, aContent);
     299           0 :   if (NS_WARN_IF(!mEditableNode)) {
     300           0 :     return false;
     301             :   }
     302             : 
     303           0 :   mEditorBase = aEditorBase;
     304           0 :   if (NS_WARN_IF(!mEditorBase)) {
     305           0 :     return false;
     306             :   }
     307             : 
     308           0 :   nsIPresShell* presShell = aPresContext->PresShell();
     309             : 
     310             :   // get selection and root content
     311           0 :   nsCOMPtr<nsISelectionController> selCon;
     312           0 :   if (mEditableNode->IsNodeOfType(nsINode::eCONTENT)) {
     313             :     nsIFrame* frame =
     314           0 :       static_cast<nsIContent*>(mEditableNode.get())->GetPrimaryFrame();
     315           0 :     if (NS_WARN_IF(!frame)) {
     316           0 :       return false;
     317             :     }
     318             : 
     319           0 :     frame->GetSelectionController(aPresContext,
     320           0 :                                   getter_AddRefs(selCon));
     321             :   } else {
     322             :     // mEditableNode is a document
     323           0 :     selCon = do_QueryInterface(presShell);
     324             :   }
     325             : 
     326           0 :   if (NS_WARN_IF(!selCon)) {
     327           0 :     return false;
     328             :   }
     329             : 
     330           0 :   selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
     331           0 :                        getter_AddRefs(mSelection));
     332           0 :   if (NS_WARN_IF(!mSelection)) {
     333           0 :     return false;
     334             :   }
     335             : 
     336           0 :   nsCOMPtr<nsIDOMRange> selDomRange;
     337           0 :   if (NS_SUCCEEDED(mSelection->GetRangeAt(0, getter_AddRefs(selDomRange)))) {
     338           0 :     nsRange* selRange = static_cast<nsRange*>(selDomRange.get());
     339           0 :     if (NS_WARN_IF(!selRange) || NS_WARN_IF(!selRange->GetStartContainer())) {
     340           0 :       return false;
     341             :     }
     342             : 
     343             :     mRootContent = selRange->GetStartContainer()->
     344           0 :                      GetSelectionRootContent(presShell);
     345             :   } else {
     346           0 :     mRootContent = mEditableNode->GetSelectionRootContent(presShell);
     347             :   }
     348           0 :   if (!mRootContent && mEditableNode->IsNodeOfType(nsINode::eDOCUMENT)) {
     349             :     // The document node is editable, but there are no contents, this document
     350             :     // is not editable.
     351           0 :     return false;
     352             :   }
     353             : 
     354           0 :   if (NS_WARN_IF(!mRootContent)) {
     355           0 :     return false;
     356             :   }
     357             : 
     358           0 :   mDocShell = aPresContext->GetDocShell();
     359           0 :   if (NS_WARN_IF(!mDocShell)) {
     360           0 :     return false;
     361             :   }
     362             : 
     363           0 :   mDocumentObserver = new DocumentObserver(*this);
     364             : 
     365           0 :   MOZ_ASSERT(!WasInitializedWithPlugin());
     366             : 
     367           0 :   return true;
     368             : }
     369             : 
     370             : bool
     371           0 : IMEContentObserver::InitWithPlugin(nsPresContext* aPresContext,
     372             :                                    nsIContent* aContent)
     373             : {
     374           0 :   if (NS_WARN_IF(!aContent) ||
     375           0 :       NS_WARN_IF(aContent->GetDesiredIMEState().mEnabled != IMEState::PLUGIN)) {
     376           0 :     return false;
     377             :   }
     378           0 :   nsIFrame* frame = aContent->GetPrimaryFrame();
     379           0 :   if (NS_WARN_IF(!frame)) {
     380           0 :     return false;
     381             :   }
     382           0 :   nsCOMPtr<nsISelectionController> selCon;
     383           0 :   frame->GetSelectionController(aPresContext, getter_AddRefs(selCon));
     384           0 :   if (NS_WARN_IF(!selCon)) {
     385           0 :     return false;
     386             :   }
     387           0 :   selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
     388           0 :                        getter_AddRefs(mSelection));
     389           0 :   if (NS_WARN_IF(!mSelection)) {
     390           0 :     return false;
     391             :   }
     392             : 
     393           0 :   mEditorBase = nullptr;
     394           0 :   mEditableNode = aContent;
     395           0 :   mRootContent = aContent;
     396             :   // Should be safe to clear mDocumentObserver here even though it *might*
     397             :   // grab this instance because this is called by Init() and the callers of
     398             :   // it and MaybeReinitialize() grabs this instance with local RefPtr.
     399             :   // So, this won't cause refcount of this instance become 0.
     400           0 :   mDocumentObserver = nullptr;
     401             : 
     402           0 :   mDocShell = aPresContext->GetDocShell();
     403           0 :   if (NS_WARN_IF(!mDocShell)) {
     404           0 :     return false;
     405             :   }
     406             : 
     407           0 :   MOZ_ASSERT(WasInitializedWithPlugin());
     408             : 
     409           0 :   return true;
     410             : }
     411             : 
     412             : bool
     413           0 : IMEContentObserver::WasInitializedWithPlugin() const
     414             : {
     415           0 :   return mDocShell && !mEditorBase;
     416             : }
     417             : 
     418             : void
     419           0 : IMEContentObserver::Clear()
     420             : {
     421           0 :   mEditorBase = nullptr;
     422           0 :   mSelection = nullptr;
     423           0 :   mEditableNode = nullptr;
     424           0 :   mRootContent = nullptr;
     425           0 :   mDocShell = nullptr;
     426             :   // Should be safe to clear mDocumentObserver here even though it grabs
     427             :   // this instance in most cases because this is called by Init() or Destroy().
     428             :   // The callers of Init() grab this instance with local RefPtr.
     429             :   // The caller of Destroy() also grabs this instance with local RefPtr.
     430             :   // So, this won't cause refcount of this instance become 0.
     431           0 :   mDocumentObserver = nullptr;
     432           0 : }
     433             : 
     434             : void
     435           0 : IMEContentObserver::ObserveEditableNode()
     436             : {
     437           0 :   MOZ_RELEASE_ASSERT(mSelection);
     438           0 :   MOZ_RELEASE_ASSERT(mRootContent);
     439           0 :   MOZ_RELEASE_ASSERT(GetState() != eState_Observing);
     440             : 
     441             :   // If this is called before sending NOTIFY_IME_OF_FOCUS (it's possible when
     442             :   // the editor is reframed before sending NOTIFY_IME_OF_FOCUS asynchronously),
     443             :   // the notification requests of mWidget may be different from after the widget
     444             :   // receives NOTIFY_IME_OF_FOCUS.   So, this should be called again by
     445             :   // OnIMEReceivedFocus() which is called after sending NOTIFY_IME_OF_FOCUS.
     446           0 :   if (!mIMEHasFocus) {
     447           0 :     MOZ_ASSERT(!mWidget || mNeedsToNotifyIMEOfFocusSet ||
     448             :                mSendingNotification == NOTIFY_IME_OF_FOCUS,
     449             :                "Wow, OnIMEReceivedFocus() won't be called?");
     450           0 :     return;
     451             :   }
     452             : 
     453           0 :   mIsObserving = true;
     454           0 :   if (mEditorBase) {
     455           0 :     mEditorBase->AddEditorObserver(this);
     456             :   }
     457             : 
     458           0 :   if (!WasInitializedWithPlugin()) {
     459             :     // Add selection change listener only when this starts to observe
     460             :     // non-plugin content since we cannot detect selection changes in
     461             :     // plugins.
     462           0 :     nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(mSelection));
     463           0 :     NS_ENSURE_TRUE_VOID(selPrivate);
     464           0 :     nsresult rv = selPrivate->AddSelectionListener(this);
     465           0 :     NS_ENSURE_SUCCESS_VOID(rv);
     466             :     // Add text change observer only when this starts to observe
     467             :     // non-plugin content since we cannot detect text changes in
     468             :     // plugins.
     469           0 :     mRootContent->AddMutationObserver(this);
     470             :     // If it's in a document (should be so), we can use document observer to
     471             :     // reduce redundant computation of text change offsets.
     472           0 :     nsIDocument* doc = mRootContent->GetComposedDoc();
     473           0 :     if (doc) {
     474           0 :       RefPtr<DocumentObserver> documentObserver = mDocumentObserver;
     475           0 :       documentObserver->Observe(doc);
     476             :     }
     477             :   }
     478             : 
     479           0 :   if (mDocShell) {
     480             :     // Add scroll position listener and reflow observer to detect position
     481             :     // and size changes
     482           0 :     mDocShell->AddWeakScrollObserver(this);
     483           0 :     mDocShell->AddWeakReflowObserver(this);
     484             :   }
     485             : }
     486             : 
     487             : void
     488           0 : IMEContentObserver::NotifyIMEOfBlur()
     489             : {
     490             :   // Prevent any notifications to be sent IME.
     491           0 :   nsCOMPtr<nsIWidget> widget;
     492           0 :   mWidget.swap(widget);
     493           0 :   mIMENotificationRequests = nullptr;
     494             : 
     495             :   // If we hasn't been set focus, we shouldn't send blur notification to IME.
     496           0 :   if (!mIMEHasFocus) {
     497           0 :     return;
     498             :   }
     499             : 
     500             :   // mWidget must have been non-nullptr if IME has focus.
     501           0 :   MOZ_RELEASE_ASSERT(widget);
     502             : 
     503           0 :   RefPtr<IMEContentObserver> kungFuDeathGrip(this);
     504             : 
     505           0 :   MOZ_LOG(sIMECOLog, LogLevel::Info,
     506             :     ("0x%p IMEContentObserver::NotifyIMEOfBlur(), "
     507             :      "sending NOTIFY_IME_OF_BLUR", this));
     508             : 
     509             :   // For now, we need to send blur notification in any condition because
     510             :   // we don't have any simple ways to send blur notification asynchronously.
     511             :   // After this call, Destroy() or Unlink() will stop observing the content
     512             :   // and forget everything.  Therefore, if it's not safe to send notification
     513             :   // when script blocker is unlocked, we cannot send blur notification after
     514             :   // that and before next focus notification.
     515             :   // Anyway, as far as we know, IME doesn't try to query content when it loses
     516             :   // focus.  So, this may not cause any problem.
     517           0 :   mIMEHasFocus = false;
     518           0 :   IMEStateManager::NotifyIME(IMENotification(NOTIFY_IME_OF_BLUR), widget);
     519             : 
     520           0 :   MOZ_LOG(sIMECOLog, LogLevel::Debug,
     521             :     ("0x%p IMEContentObserver::NotifyIMEOfBlur(), "
     522             :      "sent NOTIFY_IME_OF_BLUR", this));
     523             : }
     524             : 
     525             : void
     526           0 : IMEContentObserver::UnregisterObservers()
     527             : {
     528           0 :   if (!mIsObserving) {
     529           0 :     return;
     530             :   }
     531           0 :   mIsObserving = false;
     532             : 
     533           0 :   if (mEditorBase) {
     534           0 :     mEditorBase->RemoveEditorObserver(this);
     535             :   }
     536             : 
     537           0 :   if (mSelection) {
     538           0 :     nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(mSelection));
     539           0 :     if (selPrivate) {
     540           0 :       selPrivate->RemoveSelectionListener(this);
     541             :     }
     542           0 :     mSelectionData.Clear();
     543           0 :     mFocusedWidget = nullptr;
     544             :   }
     545             : 
     546           0 :   if (mRootContent) {
     547           0 :     mRootContent->RemoveMutationObserver(this);
     548             :   }
     549             : 
     550           0 :   if (mDocumentObserver) {
     551           0 :     RefPtr<DocumentObserver> documentObserver = mDocumentObserver;
     552           0 :     documentObserver->StopObserving();
     553             :   }
     554             : 
     555           0 :   if (mDocShell) {
     556           0 :     mDocShell->RemoveWeakScrollObserver(this);
     557           0 :     mDocShell->RemoveWeakReflowObserver(this);
     558             :   }
     559             : }
     560             : 
     561             : nsPresContext*
     562           0 : IMEContentObserver::GetPresContext() const
     563             : {
     564           0 :   return mESM ? mESM->GetPresContext() : nullptr;
     565             : }
     566             : 
     567             : void
     568           0 : IMEContentObserver::Destroy()
     569             : {
     570             :   // WARNING: When you change this method, you have to check Unlink() too.
     571             : 
     572           0 :   NotifyIMEOfBlur();
     573           0 :   UnregisterObservers();
     574           0 :   Clear();
     575             : 
     576           0 :   mWidget = nullptr;
     577           0 :   mIMENotificationRequests = nullptr;
     578             : 
     579           0 :   if (mESM) {
     580           0 :     mESM->OnStopObservingContent(this);
     581           0 :     mESM = nullptr;
     582             :   }
     583           0 : }
     584             : 
     585             : bool
     586           0 : IMEContentObserver::Destroyed() const
     587             : {
     588           0 :   return !mWidget;
     589             : }
     590             : 
     591             : void
     592           0 : IMEContentObserver::DisconnectFromEventStateManager()
     593             : {
     594           0 :   mESM = nullptr;
     595           0 : }
     596             : 
     597             : bool
     598           0 : IMEContentObserver::MaybeReinitialize(nsIWidget* aWidget,
     599             :                                       nsPresContext* aPresContext,
     600             :                                       nsIContent* aContent,
     601             :                                       EditorBase* aEditorBase)
     602             : {
     603           0 :   if (!IsObservingContent(aPresContext, aContent)) {
     604           0 :     return false;
     605             :   }
     606             : 
     607           0 :   if (GetState() == eState_StoppedObserving) {
     608           0 :     Init(aWidget, aPresContext, aContent, aEditorBase);
     609             :   }
     610           0 :   return IsManaging(aPresContext, aContent);
     611             : }
     612             : 
     613             : bool
     614           0 : IMEContentObserver::IsManaging(nsPresContext* aPresContext,
     615             :                                nsIContent* aContent) const
     616             : {
     617           0 :   return GetState() == eState_Observing &&
     618           0 :          IsObservingContent(aPresContext, aContent);
     619             : }
     620             : 
     621             : bool
     622           0 : IMEContentObserver::IsManaging(const TextComposition* aComposition) const
     623             : {
     624           0 :   if (GetState() != eState_Observing) {
     625           0 :     return false;
     626             :   }
     627           0 :   nsPresContext* presContext = aComposition->GetPresContext();
     628           0 :   if (NS_WARN_IF(!presContext)) {
     629           0 :     return false;
     630             :   }
     631           0 :   if (presContext != GetPresContext()) {
     632           0 :     return false; // observing different document
     633             :   }
     634           0 :   nsINode* targetNode = aComposition->GetEventTargetNode();
     635             :   nsIContent* targetContent =
     636           0 :     targetNode && targetNode->IsContent() ? targetNode->AsContent() : nullptr;
     637           0 :   return IsObservingContent(presContext, targetContent);
     638             : }
     639             : 
     640             : IMEContentObserver::State
     641           0 : IMEContentObserver::GetState() const
     642             : {
     643           0 :   if (!mSelection || !mRootContent || !mEditableNode) {
     644           0 :     return eState_NotObserving; // failed to initialize or finalized.
     645             :   }
     646           0 :   if (!mRootContent->IsInComposedDoc()) {
     647             :     // the focused editor has already been reframed.
     648           0 :     return eState_StoppedObserving;
     649             :   }
     650           0 :   return mIsObserving ? eState_Observing : eState_Initializing;
     651             : }
     652             : 
     653             : bool
     654           0 : IMEContentObserver::IsObservingContent(nsPresContext* aPresContext,
     655             :                                        nsIContent* aContent) const
     656             : {
     657           0 :   return IsInitializedWithPlugin() ?
     658           0 :     mRootContent == aContent && mRootContent != nullptr :
     659           0 :     mEditableNode == IMEStateManager::GetRootEditableNode(aPresContext,
     660           0 :                                                           aContent);
     661             : }
     662             : 
     663             : bool
     664           0 : IMEContentObserver::IsEditorHandlingEventForComposition() const
     665             : {
     666           0 :   if (!mWidget) {
     667           0 :     return false;
     668             :   }
     669             :   RefPtr<TextComposition> composition =
     670           0 :     IMEStateManager::GetTextCompositionFor(mWidget);
     671           0 :   if (!composition) {
     672           0 :     return false;
     673             :   }
     674           0 :   return composition->IsEditorHandlingEvent();
     675             : }
     676             : 
     677             : bool
     678           0 : IMEContentObserver::IsEditorComposing() const
     679             : {
     680             :   // Note that don't use TextComposition here. The important thing is,
     681             :   // whether the editor already started to handle composition because
     682             :   // web contents can change selection, text content and/or something from
     683             :   // compositionstart event listener which is run before EditorBase handles it.
     684           0 :   if (NS_WARN_IF(!mEditorBase)) {
     685           0 :     return false;
     686             :   }
     687           0 :   return mEditorBase->IsIMEComposing();
     688             : }
     689             : 
     690             : nsresult
     691           0 : IMEContentObserver::GetSelectionAndRoot(nsISelection** aSelection,
     692             :                                         nsIContent** aRootContent) const
     693             : {
     694           0 :   if (!mEditableNode || !mSelection) {
     695           0 :     return NS_ERROR_NOT_AVAILABLE;
     696             :   }
     697             : 
     698           0 :   NS_ASSERTION(mSelection && mRootContent, "uninitialized content observer");
     699           0 :   NS_ADDREF(*aSelection = mSelection);
     700           0 :   NS_ADDREF(*aRootContent = mRootContent);
     701           0 :   return NS_OK;
     702             : }
     703             : 
     704             : nsresult
     705           0 : IMEContentObserver::NotifySelectionChanged(nsIDOMDocument* aDOMDocument,
     706             :                                            nsISelection* aSelection,
     707             :                                            int16_t aReason)
     708             : {
     709           0 :   int32_t count = 0;
     710           0 :   nsresult rv = aSelection->GetRangeCount(&count);
     711           0 :   NS_ENSURE_SUCCESS(rv, rv);
     712           0 :   if (count > 0 && mWidget) {
     713           0 :     bool causedByComposition = IsEditorHandlingEventForComposition();
     714           0 :     bool causedBySelectionEvent = TextComposition::IsHandlingSelectionEvent();
     715           0 :     bool duringComposition = IsEditorComposing();
     716           0 :     MaybeNotifyIMEOfSelectionChange(causedByComposition,
     717             :                                     causedBySelectionEvent,
     718           0 :                                     duringComposition);
     719             :   }
     720           0 :   return NS_OK;
     721             : }
     722             : 
     723             : void
     724           0 : IMEContentObserver::ScrollPositionChanged()
     725             : {
     726           0 :   if (!NeedsPositionChangeNotification()) {
     727           0 :     return;
     728             :   }
     729             : 
     730           0 :   MaybeNotifyIMEOfPositionChange();
     731             : }
     732             : 
     733             : NS_IMETHODIMP
     734           0 : IMEContentObserver::Reflow(DOMHighResTimeStamp aStart,
     735             :                            DOMHighResTimeStamp aEnd)
     736             : {
     737           0 :   if (!NeedsPositionChangeNotification()) {
     738           0 :     return NS_OK;
     739             :   }
     740             : 
     741           0 :   MaybeNotifyIMEOfPositionChange();
     742           0 :   return NS_OK;
     743             : }
     744             : 
     745             : NS_IMETHODIMP
     746           0 : IMEContentObserver::ReflowInterruptible(DOMHighResTimeStamp aStart,
     747             :                                         DOMHighResTimeStamp aEnd)
     748             : {
     749           0 :   if (!NeedsPositionChangeNotification()) {
     750           0 :     return NS_OK;
     751             :   }
     752             : 
     753           0 :   MaybeNotifyIMEOfPositionChange();
     754           0 :   return NS_OK;
     755             : }
     756             : 
     757             : nsresult
     758           0 : IMEContentObserver::HandleQueryContentEvent(WidgetQueryContentEvent* aEvent)
     759             : {
     760             :   // If the instance has normal selection cache and the query event queries
     761             :   // normal selection's range, it should use the cached selection which was
     762             :   // sent to the widget.  However, if this instance has already received new
     763             :   // selection change notification but hasn't updated the cache yet (i.e.,
     764             :   // not sending selection change notification to IME, don't use the cached
     765             :   // value.  Note that don't update selection cache here since if you update
     766             :   // selection cache here, IMENotificationSender won't notify IME of selection
     767             :   // change because it looks like that the selection isn't actually changed.
     768             :   bool isSelectionCacheAvailable =
     769           0 :     aEvent->mUseNativeLineBreak && mSelectionData.IsValid() &&
     770           0 :     !mNeedsToNotifyIMEOfSelectionChange;
     771           0 :   if (isSelectionCacheAvailable &&
     772           0 :       aEvent->mMessage == eQuerySelectedText &&
     773           0 :       aEvent->mInput.mSelectionType == SelectionType::eNormal) {
     774           0 :     aEvent->mReply.mContentsRoot = mRootContent;
     775           0 :     aEvent->mReply.mHasSelection = !mSelectionData.IsCollapsed();
     776           0 :     aEvent->mReply.mOffset = mSelectionData.mOffset;
     777           0 :     aEvent->mReply.mString = mSelectionData.String();
     778           0 :     aEvent->mReply.mWritingMode = mSelectionData.GetWritingMode();
     779           0 :     aEvent->mReply.mReversed = mSelectionData.mReversed;
     780           0 :     aEvent->mSucceeded = true;
     781           0 :     MOZ_LOG(sIMECOLog, LogLevel::Debug,
     782             :       ("0x%p IMEContentObserver::HandleQueryContentEvent(aEvent={ "
     783             :        "mMessage=%s })", this, ToChar(aEvent->mMessage)));
     784           0 :     return NS_OK;
     785             :   }
     786             : 
     787           0 :   MOZ_LOG(sIMECOLog, LogLevel::Debug,
     788             :     ("0x%p IMEContentObserver::HandleQueryContentEvent(aEvent={ "
     789             :      "mMessage=%s })", this, ToChar(aEvent->mMessage)));
     790             : 
     791             :   // If we can make the event's input offset absolute with TextComposition or
     792             :   // mSelection, we should set it here for reducing the cost of computing
     793             :   // selection start offset.  If ContentEventHandler receives a
     794             :   // WidgetQueryContentEvent whose input offset is relative to insertion point,
     795             :   // it computes current selection start offset (this may be expensive) and
     796             :   // make the offset absolute value itself.
     797             :   // Note that calling MakeOffsetAbsolute() makes the event a query event with
     798             :   // absolute offset.  So, ContentEventHandler doesn't pay any additional cost
     799             :   // after calling MakeOffsetAbsolute() here.
     800           0 :   if (aEvent->mInput.mRelativeToInsertionPoint &&
     801           0 :       aEvent->mInput.IsValidEventMessage(aEvent->mMessage)) {
     802             :     RefPtr<TextComposition> composition =
     803           0 :       IMEStateManager::GetTextCompositionFor(aEvent->mWidget);
     804           0 :     if (composition) {
     805           0 :       uint32_t compositionStart = composition->NativeOffsetOfStartComposition();
     806           0 :       if (NS_WARN_IF(!aEvent->mInput.MakeOffsetAbsolute(compositionStart))) {
     807           0 :         return NS_ERROR_FAILURE;
     808             :       }
     809           0 :     } else if (isSelectionCacheAvailable) {
     810           0 :       uint32_t selectionStart = mSelectionData.mOffset;
     811           0 :       if (NS_WARN_IF(!aEvent->mInput.MakeOffsetAbsolute(selectionStart))) {
     812           0 :         return NS_ERROR_FAILURE;
     813             :       }
     814             :     }
     815             :   }
     816             : 
     817           0 :   AutoRestore<bool> handling(mIsHandlingQueryContentEvent);
     818           0 :   mIsHandlingQueryContentEvent = true;
     819           0 :   ContentEventHandler handler(GetPresContext());
     820           0 :   nsresult rv = handler.HandleQueryContentEvent(aEvent);
     821           0 :   if (NS_WARN_IF(Destroyed())) {
     822             :     // If this has already destroyed during querying the content, the query
     823             :     // is outdated even if it's succeeded.  So, make the query fail.
     824           0 :     aEvent->mSucceeded = false;
     825           0 :     MOZ_LOG(sIMECOLog, LogLevel::Warning,
     826             :       ("0x%p IMEContentObserver::HandleQueryContentEvent(), WARNING, "
     827             :        "IMEContentObserver has been destroyed during the query, "
     828             :        "making the query fail", this));
     829           0 :     return rv;
     830             :   }
     831             : 
     832           0 :   if (!IsInitializedWithPlugin() &&
     833           0 :       NS_WARN_IF(aEvent->mReply.mContentsRoot != mRootContent)) {
     834             :     // Focus has changed unexpectedly, so make the query fail.
     835           0 :     aEvent->mSucceeded = false;
     836             :   }
     837           0 :   return rv;
     838             : }
     839             : 
     840             : bool
     841           0 : IMEContentObserver::OnMouseButtonEvent(nsPresContext* aPresContext,
     842             :                                        WidgetMouseEvent* aMouseEvent)
     843             : {
     844           0 :   if (!mIMENotificationRequests ||
     845           0 :       !mIMENotificationRequests->WantMouseButtonEventOnChar()) {
     846           0 :     return false;
     847             :   }
     848           0 :   if (!aMouseEvent->IsTrusted() ||
     849           0 :       aMouseEvent->DefaultPrevented() ||
     850           0 :       !aMouseEvent->mWidget) {
     851           0 :     return false;
     852             :   }
     853             :   // Now, we need to notify only mouse down and mouse up event.
     854           0 :   switch (aMouseEvent->mMessage) {
     855             :     case eMouseUp:
     856             :     case eMouseDown:
     857           0 :       break;
     858             :     default:
     859           0 :       return false;
     860             :   }
     861           0 :   if (NS_WARN_IF(!mWidget) || NS_WARN_IF(mWidget->Destroyed())) {
     862           0 :     return false;
     863             :   }
     864             : 
     865           0 :   RefPtr<IMEContentObserver> kungFuDeathGrip(this);
     866             : 
     867             :   WidgetQueryContentEvent charAtPt(true, eQueryCharacterAtPoint,
     868           0 :                                    aMouseEvent->mWidget);
     869           0 :   charAtPt.mRefPoint = aMouseEvent->mRefPoint;
     870           0 :   ContentEventHandler handler(aPresContext);
     871           0 :   handler.OnQueryCharacterAtPoint(&charAtPt);
     872           0 :   if (NS_WARN_IF(!charAtPt.mSucceeded) ||
     873           0 :       charAtPt.mReply.mOffset == WidgetQueryContentEvent::NOT_FOUND) {
     874           0 :     return false;
     875             :   }
     876             : 
     877             :   // The widget might be destroyed during querying the content since it
     878             :   // causes flushing layout.
     879           0 :   if (!mWidget || NS_WARN_IF(mWidget->Destroyed())) {
     880           0 :     return false;
     881             :   }
     882             : 
     883             :   // The result character rect is relative to the top level widget.
     884             :   // We should notify it with offset in the widget.
     885           0 :   nsIWidget* topLevelWidget = mWidget->GetTopLevelWidget();
     886           0 :   if (topLevelWidget && topLevelWidget != mWidget) {
     887             :     charAtPt.mReply.mRect.MoveBy(
     888           0 :       topLevelWidget->WidgetToScreenOffset() -
     889           0 :         mWidget->WidgetToScreenOffset());
     890             :   }
     891             :   // The refPt is relative to its widget.
     892             :   // We should notify it with offset in the widget.
     893           0 :   if (aMouseEvent->mWidget != mWidget) {
     894           0 :     charAtPt.mRefPoint += aMouseEvent->mWidget->WidgetToScreenOffset() -
     895           0 :       mWidget->WidgetToScreenOffset();
     896             :   }
     897             : 
     898           0 :   IMENotification notification(NOTIFY_IME_OF_MOUSE_BUTTON_EVENT);
     899           0 :   notification.mMouseButtonEventData.mEventMessage = aMouseEvent->mMessage;
     900           0 :   notification.mMouseButtonEventData.mOffset = charAtPt.mReply.mOffset;
     901             :   notification.mMouseButtonEventData.mCursorPos.Set(
     902           0 :     charAtPt.mRefPoint.ToUnknownPoint());
     903             :   notification.mMouseButtonEventData.mCharRect.Set(
     904           0 :     charAtPt.mReply.mRect.ToUnknownRect());
     905           0 :   notification.mMouseButtonEventData.mButton = aMouseEvent->button;
     906           0 :   notification.mMouseButtonEventData.mButtons = aMouseEvent->buttons;
     907           0 :   notification.mMouseButtonEventData.mModifiers = aMouseEvent->mModifiers;
     908             : 
     909           0 :   nsresult rv = IMEStateManager::NotifyIME(notification, mWidget);
     910           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     911           0 :     return false;
     912             :   }
     913             : 
     914           0 :   bool consumed = (rv == NS_SUCCESS_EVENT_CONSUMED);
     915           0 :   if (consumed) {
     916           0 :     aMouseEvent->PreventDefault();
     917             :   }
     918           0 :   return consumed;
     919             : }
     920             : 
     921             : void
     922           0 : IMEContentObserver::CharacterDataWillChange(nsIDocument* aDocument,
     923             :                                             nsIContent* aContent,
     924             :                                             CharacterDataChangeInfo* aInfo)
     925             : {
     926           0 :   NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT),
     927             :                "character data changed for non-text node");
     928           0 :   MOZ_ASSERT(mPreCharacterDataChangeLength < 0,
     929             :              "CharacterDataChanged() should've reset "
     930             :              "mPreCharacterDataChangeLength");
     931             : 
     932           0 :   if (!NeedsTextChangeNotification()) {
     933           0 :     return;
     934             :   }
     935             : 
     936           0 :   mEndOfAddedTextCache.Clear();
     937           0 :   mStartOfRemovingTextRangeCache.Clear();
     938             : 
     939             :   // Although we don't assume this change occurs while this is storing
     940             :   // the range of added consecutive nodes, if it actually happens, we need to
     941             :   // flush them since this change may occur before or in the range.  So, it's
     942             :   // safe to flush pending computation of mTextChangeData before handling this.
     943           0 :   MaybeNotifyIMEOfAddedTextDuringDocumentChange();
     944             : 
     945           0 :   mPreCharacterDataChangeLength =
     946           0 :     ContentEventHandler::GetNativeTextLength(aContent, aInfo->mChangeStart,
     947             :                                              aInfo->mChangeEnd);
     948           0 :   MOZ_ASSERT(mPreCharacterDataChangeLength >=
     949             :                aInfo->mChangeEnd - aInfo->mChangeStart,
     950             :              "The computed length must be same as or larger than XP length");
     951             : }
     952             : 
     953             : void
     954           0 : IMEContentObserver::CharacterDataChanged(nsIDocument* aDocument,
     955             :                                          nsIContent* aContent,
     956             :                                          CharacterDataChangeInfo* aInfo)
     957             : {
     958           0 :   NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT),
     959             :                "character data changed for non-text node");
     960             : 
     961           0 :   if (!NeedsTextChangeNotification()) {
     962           0 :     return;
     963             :   }
     964             : 
     965           0 :   mEndOfAddedTextCache.Clear();
     966           0 :   mStartOfRemovingTextRangeCache.Clear();
     967           0 :   MOZ_ASSERT(!HasAddedNodesDuringDocumentChange(),
     968             :     "The stored range should be flushed before actually the data is changed");
     969             : 
     970           0 :   int64_t removedLength = mPreCharacterDataChangeLength;
     971           0 :   mPreCharacterDataChangeLength = -1;
     972             : 
     973           0 :   MOZ_ASSERT(removedLength >= 0,
     974             :              "mPreCharacterDataChangeLength should've been set by "
     975             :              "CharacterDataWillChange()");
     976             : 
     977           0 :   uint32_t offset = 0;
     978             :   // get offsets of change and fire notification
     979             :   nsresult rv =
     980           0 :     ContentEventHandler::GetFlatTextLengthInRange(
     981           0 :                            NodePosition(mRootContent, 0),
     982           0 :                            NodePosition(aContent, aInfo->mChangeStart),
     983           0 :                            mRootContent, &offset, LINE_BREAK_TYPE_NATIVE);
     984           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     985           0 :     return;
     986             :   }
     987             : 
     988             :   uint32_t newLength =
     989           0 :     ContentEventHandler::GetNativeTextLength(aContent, aInfo->mChangeStart,
     990           0 :                                              aInfo->mChangeStart +
     991           0 :                                                aInfo->mReplaceLength);
     992             : 
     993           0 :   uint32_t oldEnd = offset + static_cast<uint32_t>(removedLength);
     994           0 :   uint32_t newEnd = offset + newLength;
     995             : 
     996             :   TextChangeData data(offset, oldEnd, newEnd,
     997           0 :                       IsEditorHandlingEventForComposition(),
     998           0 :                       IsEditorComposing());
     999           0 :   MaybeNotifyIMEOfTextChange(data);
    1000             : }
    1001             : 
    1002             : void
    1003           0 : IMEContentObserver::NotifyContentAdded(nsINode* aContainer,
    1004             :                                        int32_t aStartIndex,
    1005             :                                        int32_t aEndIndex)
    1006             : {
    1007           0 :   if (!NeedsTextChangeNotification()) {
    1008           0 :     return;
    1009             :   }
    1010             : 
    1011           0 :   mStartOfRemovingTextRangeCache.Clear();
    1012             : 
    1013             :   // If it's in a document change, nodes are added consecutively.  Therefore,
    1014             :   // if we cache the first node and the last node, we need to compute the
    1015             :   // range once.
    1016             :   // FYI: This is not true if the change caused by an operation in the editor.
    1017           0 :   if (IsInDocumentChange()) {
    1018             :     // Now, mEndOfAddedTextCache may be invalid if node is added before
    1019             :     // the last node in mEndOfAddedTextCache.  Clear it.
    1020           0 :     mEndOfAddedTextCache.Clear();
    1021           0 :     if (!HasAddedNodesDuringDocumentChange()) {
    1022           0 :       mFirstAddedNodeContainer = mLastAddedNodeContainer = aContainer;
    1023           0 :       mFirstAddedNodeOffset = aStartIndex;
    1024           0 :       mLastAddedNodeOffset = aEndIndex;
    1025           0 :       MOZ_LOG(sIMECOLog, LogLevel::Debug,
    1026             :         ("0x%p IMEContentObserver::NotifyContentAdded(), starts to store "
    1027             :          "consecutive added nodes", this));
    1028           0 :       return;
    1029             :     }
    1030             :     // If first node being added is not next node of the last node,
    1031             :     // notify IME of the previous range first, then, restart to cache the
    1032             :     // range.
    1033           0 :     if (NS_WARN_IF(!IsNextNodeOfLastAddedNode(aContainer, aStartIndex))) {
    1034             :       // Flush the old range first.
    1035           0 :       MaybeNotifyIMEOfAddedTextDuringDocumentChange();
    1036           0 :       mFirstAddedNodeContainer = aContainer;
    1037           0 :       mFirstAddedNodeOffset = aStartIndex;
    1038           0 :       MOZ_LOG(sIMECOLog, LogLevel::Debug,
    1039             :         ("0x%p IMEContentObserver::NotifyContentAdded(), starts to store "
    1040             :          "consecutive added nodes", this));
    1041             :     }
    1042           0 :     mLastAddedNodeContainer = aContainer;
    1043           0 :     mLastAddedNodeOffset = aEndIndex;
    1044           0 :     return;
    1045             :   }
    1046           0 :   MOZ_ASSERT(!HasAddedNodesDuringDocumentChange(),
    1047             :     "The cache should be cleared when document change finished");
    1048             : 
    1049           0 :   uint32_t offset = 0;
    1050           0 :   nsresult rv = NS_OK;
    1051           0 :   if (!mEndOfAddedTextCache.Match(aContainer, aStartIndex)) {
    1052           0 :     mEndOfAddedTextCache.Clear();
    1053           0 :     rv = ContentEventHandler::GetFlatTextLengthInRange(
    1054           0 :                                 NodePosition(mRootContent, 0),
    1055           0 :                                 NodePositionBefore(aContainer, aStartIndex),
    1056           0 :                                 mRootContent, &offset, LINE_BREAK_TYPE_NATIVE);
    1057           0 :     if (NS_WARN_IF(NS_FAILED((rv)))) {
    1058           0 :       return;
    1059             :     }
    1060             :   } else {
    1061           0 :     offset = mEndOfAddedTextCache.mFlatTextLength;
    1062             :   }
    1063             : 
    1064             :   // get offset at the end of the last added node
    1065           0 :   uint32_t addingLength = 0;
    1066           0 :   rv = ContentEventHandler::GetFlatTextLengthInRange(
    1067           0 :                               NodePositionBefore(aContainer, aStartIndex),
    1068           0 :                               NodePosition(aContainer, aEndIndex),
    1069             :                               mRootContent, &addingLength,
    1070           0 :                               LINE_BREAK_TYPE_NATIVE);
    1071           0 :   if (NS_WARN_IF(NS_FAILED((rv)))) {
    1072           0 :     mEndOfAddedTextCache.Clear();
    1073           0 :     return;
    1074             :   }
    1075             : 
    1076             :   // If multiple lines are being inserted in an HTML editor, next call of
    1077             :   // NotifyContentAdded() is for adding next node.  Therefore, caching the text
    1078             :   // length can skip to compute the text length before the adding node and
    1079             :   // before of it.
    1080           0 :   mEndOfAddedTextCache.Cache(aContainer, aEndIndex, offset + addingLength);
    1081             : 
    1082           0 :   if (!addingLength) {
    1083           0 :     return;
    1084             :   }
    1085             : 
    1086             :   TextChangeData data(offset, offset, offset + addingLength,
    1087           0 :                       IsEditorHandlingEventForComposition(),
    1088           0 :                       IsEditorComposing());
    1089           0 :   MaybeNotifyIMEOfTextChange(data);
    1090             : }
    1091             : 
    1092             : void
    1093           0 : IMEContentObserver::ContentAppended(nsIDocument* aDocument,
    1094             :                                     nsIContent* aContainer,
    1095             :                                     nsIContent* aFirstNewContent,
    1096             :                                     int32_t aNewIndexInContainer)
    1097             : {
    1098           0 :   NotifyContentAdded(aContainer, aNewIndexInContainer,
    1099           0 :                      aContainer->GetChildCount());
    1100           0 : }
    1101             : 
    1102             : void
    1103           0 : IMEContentObserver::ContentInserted(nsIDocument* aDocument,
    1104             :                                     nsIContent* aContainer,
    1105             :                                     nsIContent* aChild,
    1106             :                                     int32_t aIndexInContainer)
    1107             : {
    1108           0 :   NotifyContentAdded(NODE_FROM(aContainer, aDocument),
    1109           0 :                      aIndexInContainer, aIndexInContainer + 1);
    1110           0 : }
    1111             : 
    1112             : void
    1113           0 : IMEContentObserver::ContentRemoved(nsIDocument* aDocument,
    1114             :                                    nsIContent* aContainer,
    1115             :                                    nsIContent* aChild,
    1116             :                                    int32_t aIndexInContainer,
    1117             :                                    nsIContent* aPreviousSibling)
    1118             : {
    1119           0 :   if (!NeedsTextChangeNotification()) {
    1120           0 :     return;
    1121             :   }
    1122             : 
    1123           0 :   mEndOfAddedTextCache.Clear();
    1124           0 :   MaybeNotifyIMEOfAddedTextDuringDocumentChange();
    1125             : 
    1126           0 :   nsINode* containerNode = NODE_FROM(aContainer, aDocument);
    1127             : 
    1128           0 :   uint32_t offset = 0;
    1129           0 :   nsresult rv = NS_OK;
    1130           0 :   if (!mStartOfRemovingTextRangeCache.Match(containerNode, aIndexInContainer)) {
    1131             :     // At removing a child node of aContainer, we need the line break caused
    1132             :     // by open tag of aContainer.  Be careful when aIndexInContainer is 0.
    1133           0 :     rv = ContentEventHandler::GetFlatTextLengthInRange(
    1134           0 :                                 NodePosition(mRootContent, 0),
    1135           0 :                                 NodePosition(containerNode, aIndexInContainer),
    1136           0 :                                 mRootContent, &offset, LINE_BREAK_TYPE_NATIVE);
    1137           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    1138           0 :       mStartOfRemovingTextRangeCache.Clear();
    1139           0 :       return;
    1140             :     }
    1141           0 :     mStartOfRemovingTextRangeCache.Cache(containerNode, aIndexInContainer,
    1142           0 :                                          offset);
    1143             :   } else {
    1144           0 :     offset = mStartOfRemovingTextRangeCache.mFlatTextLength;
    1145             :   }
    1146             : 
    1147             :   // get offset at the end of the deleted node
    1148           0 :   uint32_t textLength = 0;
    1149           0 :   if (aChild->IsNodeOfType(nsINode::eTEXT)) {
    1150           0 :     textLength = ContentEventHandler::GetNativeTextLength(aChild);
    1151             :   } else {
    1152           0 :     uint32_t nodeLength = static_cast<int32_t>(aChild->GetChildCount());
    1153           0 :     rv = ContentEventHandler::GetFlatTextLengthInRange(
    1154           0 :                                 NodePositionBefore(aChild, 0),
    1155           0 :                                 NodePosition(aChild, nodeLength),
    1156             :                                 mRootContent, &textLength,
    1157           0 :                                 LINE_BREAK_TYPE_NATIVE, true);
    1158           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    1159           0 :       mStartOfRemovingTextRangeCache.Clear();
    1160           0 :       return;
    1161             :     }
    1162             :   }
    1163             : 
    1164           0 :   if (!textLength) {
    1165           0 :     return;
    1166             :   }
    1167             : 
    1168             :   TextChangeData data(offset, offset + textLength, offset,
    1169           0 :                       IsEditorHandlingEventForComposition(),
    1170           0 :                       IsEditorComposing());
    1171           0 :   MaybeNotifyIMEOfTextChange(data);
    1172             : }
    1173             : 
    1174             : void
    1175           0 : IMEContentObserver::AttributeWillChange(nsIDocument* aDocument,
    1176             :                                         dom::Element* aElement,
    1177             :                                         int32_t aNameSpaceID,
    1178             :                                         nsIAtom* aAttribute,
    1179             :                                         int32_t aModType,
    1180             :                                         const nsAttrValue* aNewValue)
    1181             : {
    1182           0 :   if (!NeedsTextChangeNotification()) {
    1183           0 :     return;
    1184             :   }
    1185             : 
    1186           0 :   mPreAttrChangeLength =
    1187           0 :     ContentEventHandler::GetNativeTextLengthBefore(aElement, mRootContent);
    1188             : }
    1189             : 
    1190             : void
    1191           0 : IMEContentObserver::AttributeChanged(nsIDocument* aDocument,
    1192             :                                      dom::Element* aElement,
    1193             :                                      int32_t aNameSpaceID,
    1194             :                                      nsIAtom* aAttribute,
    1195             :                                      int32_t aModType,
    1196             :                                      const nsAttrValue* aOldValue)
    1197             : {
    1198           0 :   if (!NeedsTextChangeNotification()) {
    1199           0 :     return;
    1200             :   }
    1201             : 
    1202           0 :   mEndOfAddedTextCache.Clear();
    1203           0 :   mStartOfRemovingTextRangeCache.Clear();
    1204             : 
    1205             :   uint32_t postAttrChangeLength =
    1206           0 :     ContentEventHandler::GetNativeTextLengthBefore(aElement, mRootContent);
    1207           0 :   if (postAttrChangeLength == mPreAttrChangeLength) {
    1208           0 :     return;
    1209             :   }
    1210             :   // First, compute text range which were added during a document change.
    1211           0 :   MaybeNotifyIMEOfAddedTextDuringDocumentChange();
    1212             :   // Then, compute the new text changed caused by this attribute change.
    1213             :   uint32_t start;
    1214             :   nsresult rv =
    1215           0 :     ContentEventHandler::GetFlatTextLengthInRange(
    1216           0 :                            NodePosition(mRootContent, 0),
    1217           0 :                            NodePositionBefore(aElement, 0),
    1218           0 :                            mRootContent, &start, LINE_BREAK_TYPE_NATIVE);
    1219           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    1220           0 :     return;
    1221             :   }
    1222             : 
    1223           0 :   TextChangeData data(start, start + mPreAttrChangeLength,
    1224             :                       start + postAttrChangeLength,
    1225           0 :                       IsEditorHandlingEventForComposition(),
    1226           0 :                       IsEditorComposing());
    1227           0 :   MaybeNotifyIMEOfTextChange(data);
    1228             : }
    1229             : 
    1230             : void
    1231           0 : IMEContentObserver::ClearAddedNodesDuringDocumentChange()
    1232             : {
    1233           0 :   mFirstAddedNodeContainer = mLastAddedNodeContainer = nullptr;
    1234           0 :   mFirstAddedNodeOffset = mLastAddedNodeOffset = 0;
    1235           0 :   MOZ_LOG(sIMECOLog, LogLevel::Debug,
    1236             :     ("0x%p IMEContentObserver::ClearAddedNodesDuringDocumentChange()"
    1237             :      ", finished storing consecutive nodes", this));
    1238           0 : }
    1239             : 
    1240             : // static
    1241             : nsIContent*
    1242           0 : IMEContentObserver::GetChildNode(nsINode* aParent, int32_t aOffset)
    1243             : {
    1244           0 :   if (!aParent->HasChildren() || aOffset < 0 ||
    1245           0 :       aOffset >= static_cast<int32_t>(aParent->Length())) {
    1246           0 :     return nullptr;
    1247             :   }
    1248           0 :   if (!aOffset) {
    1249           0 :     return aParent->GetFirstChild();
    1250             :   }
    1251           0 :   if (aOffset == static_cast<int32_t>(aParent->Length() - 1)) {
    1252           0 :     return aParent->GetLastChild();
    1253             :   }
    1254           0 :   return aParent->GetChildAt(aOffset);
    1255             : }
    1256             : 
    1257             : bool
    1258           0 : IMEContentObserver::IsNextNodeOfLastAddedNode(nsINode* aParent,
    1259             :                                               int32_t aOffset) const
    1260             : {
    1261           0 :   MOZ_ASSERT(aParent);
    1262           0 :   MOZ_ASSERT(aOffset >= 0 &&
    1263             :              aOffset <= static_cast<int32_t>(aParent->Length()));
    1264           0 :   MOZ_ASSERT(mRootContent);
    1265           0 :   MOZ_ASSERT(HasAddedNodesDuringDocumentChange());
    1266             : 
    1267             :   // If the parent node isn't changed, we can check it only with offset.
    1268           0 :   if (aParent == mLastAddedNodeContainer) {
    1269           0 :     if (NS_WARN_IF(mLastAddedNodeOffset != aOffset)) {
    1270           0 :       return false;
    1271             :     }
    1272           0 :     return true;
    1273             :   }
    1274             : 
    1275             :   // If the parent node is changed, that means that given offset should be the
    1276             :   // last added node not having next sibling.
    1277           0 :   if (NS_WARN_IF(mLastAddedNodeOffset !=
    1278             :                    static_cast<int32_t>(mLastAddedNodeContainer->Length()))) {
    1279           0 :     return false;
    1280             :   }
    1281             : 
    1282             :   // If the node is aParent is a descendant of mLastAddedNodeContainer,
    1283             :   // aOffset should be 0.
    1284           0 :   if (mLastAddedNodeContainer == aParent->GetParent()) {
    1285           0 :     if (NS_WARN_IF(aOffset)) {
    1286           0 :       return false;
    1287             :     }
    1288           0 :     return true;
    1289             :   }
    1290             : 
    1291             :   // Otherwise, we need to check it even with slow path.
    1292             :   nsIContent* lastAddedContent =
    1293           0 :     GetChildNode(mLastAddedNodeContainer, mLastAddedNodeOffset - 1);
    1294           0 :   if (NS_WARN_IF(!lastAddedContent)) {
    1295           0 :     return false;
    1296             :   }
    1297             : 
    1298             :   nsIContent* nextContentOfLastAddedContent =
    1299           0 :     lastAddedContent->GetNextNode(mRootContent->GetParentNode());
    1300           0 :   if (NS_WARN_IF(!nextContentOfLastAddedContent)) {
    1301           0 :     return false;
    1302             :   }
    1303             : 
    1304           0 :   nsIContent* startContent = GetChildNode(aParent, aOffset);
    1305           0 :   if (NS_WARN_IF(!startContent) ||
    1306           0 :       NS_WARN_IF(nextContentOfLastAddedContent != startContent)) {
    1307           0 :     return false;
    1308             :   }
    1309             : #ifdef DEBUG
    1310           0 :   NS_WARNING_ASSERTION(
    1311             :     !aOffset || aOffset == static_cast<int32_t>(aParent->Length() - 1),
    1312             :     "Used slow path for aParent");
    1313           0 :   NS_WARNING_ASSERTION(
    1314             :     !(mLastAddedNodeOffset - 1) ||
    1315             :     mLastAddedNodeOffset ==
    1316             :       static_cast<int32_t>(mLastAddedNodeContainer->Length()),
    1317             :     "Used slow path for mLastAddedNodeContainer");
    1318             : #endif // #ifdef DEBUG
    1319           0 :   return true;
    1320             : }
    1321             : 
    1322             : void
    1323           0 : IMEContentObserver::MaybeNotifyIMEOfAddedTextDuringDocumentChange()
    1324             : {
    1325           0 :   if (!HasAddedNodesDuringDocumentChange()) {
    1326           0 :     return;
    1327             :   }
    1328             : 
    1329           0 :   MOZ_LOG(sIMECOLog, LogLevel::Debug,
    1330             :     ("0x%p IMEContentObserver::MaybeNotifyIMEOfAddedTextDuringDocumentChange()"
    1331             :      ", flushing stored consecutive nodes", this));
    1332             : 
    1333             :   // Notify IME of text change which is caused by added nodes now.
    1334             : 
    1335             :   // First, compute offset of start of first added node from start of the
    1336             :   // editor.
    1337             :   uint32_t offset;
    1338             :   nsresult rv =
    1339           0 :     ContentEventHandler::GetFlatTextLengthInRange(
    1340           0 :                             NodePosition(mRootContent, 0),
    1341           0 :                             NodePosition(mFirstAddedNodeContainer,
    1342             :                                          mFirstAddedNodeOffset),
    1343           0 :                             mRootContent, &offset, LINE_BREAK_TYPE_NATIVE);
    1344           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    1345           0 :     ClearAddedNodesDuringDocumentChange();
    1346           0 :     return;
    1347             :   }
    1348             : 
    1349             :   // Next, compute the text length of added nodes.
    1350             :   uint32_t length;
    1351             :   rv =
    1352           0 :     ContentEventHandler::GetFlatTextLengthInRange(
    1353           0 :                            NodePosition(mFirstAddedNodeContainer,
    1354             :                                         mFirstAddedNodeOffset),
    1355           0 :                            NodePosition(mLastAddedNodeContainer,
    1356             :                                         mLastAddedNodeOffset),
    1357           0 :                            mRootContent, &length, LINE_BREAK_TYPE_NATIVE);
    1358           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    1359           0 :     ClearAddedNodesDuringDocumentChange();
    1360           0 :     return;
    1361             :   }
    1362             : 
    1363             :   // Finally, try to notify IME of the range.
    1364             :   TextChangeData data(offset, offset, offset + length,
    1365           0 :                       IsEditorHandlingEventForComposition(),
    1366           0 :                       IsEditorComposing());
    1367           0 :   MaybeNotifyIMEOfTextChange(data);
    1368           0 :   ClearAddedNodesDuringDocumentChange();
    1369             : }
    1370             : 
    1371             : void
    1372           0 : IMEContentObserver::BeginDocumentUpdate()
    1373             : {
    1374           0 :   MOZ_LOG(sIMECOLog, LogLevel::Debug,
    1375             :     ("0x%p IMEContentObserver::BeginDocumentUpdate(), "
    1376             :      "HasAddedNodesDuringDocumentChange()=%s",
    1377             :      this, ToChar(HasAddedNodesDuringDocumentChange())));
    1378             : 
    1379           0 :   MOZ_ASSERT(!HasAddedNodesDuringDocumentChange());
    1380           0 : }
    1381             : 
    1382             : void
    1383           0 : IMEContentObserver::EndDocumentUpdate()
    1384             : {
    1385           0 :   MOZ_LOG(sIMECOLog, LogLevel::Debug,
    1386             :     ("0x%p IMEContentObserver::EndDocumentUpdate(), "
    1387             :      "HasAddedNodesDuringDocumentChange()=%s",
    1388             :      this, ToChar(HasAddedNodesDuringDocumentChange())));
    1389             : 
    1390           0 :   MaybeNotifyIMEOfAddedTextDuringDocumentChange();
    1391           0 : }
    1392             : 
    1393             : void
    1394           0 : IMEContentObserver::SuppressNotifyingIME()
    1395             : {
    1396           0 :   mSuppressNotifications++;
    1397             : 
    1398           0 :   MOZ_LOG(sIMECOLog, LogLevel::Debug,
    1399             :     ("0x%p IMEContentObserver::SuppressNotifyingIME(), "
    1400             :      "mSuppressNotifications=%u", this, mSuppressNotifications));
    1401           0 : }
    1402             : 
    1403             : void
    1404           0 : IMEContentObserver::UnsuppressNotifyingIME()
    1405             : {
    1406           0 :   MOZ_LOG(sIMECOLog, LogLevel::Debug,
    1407             :     ("0x%p IMEContentObserver::UnsuppressNotifyingIME(), "
    1408             :      "mSuppressNotifications=%u", this, mSuppressNotifications));
    1409             : 
    1410           0 :   if (!mSuppressNotifications || --mSuppressNotifications) {
    1411           0 :     return;
    1412             :   }
    1413           0 :   FlushMergeableNotifications();
    1414             : }
    1415             : 
    1416             : NS_IMETHODIMP
    1417           0 : IMEContentObserver::EditAction()
    1418             : {
    1419           0 :   MOZ_LOG(sIMECOLog, LogLevel::Debug,
    1420             :     ("0x%p IMEContentObserver::EditAction()", this));
    1421             : 
    1422           0 :   mEndOfAddedTextCache.Clear();
    1423           0 :   mStartOfRemovingTextRangeCache.Clear();
    1424           0 :   FlushMergeableNotifications();
    1425           0 :   return NS_OK;
    1426             : }
    1427             : 
    1428             : NS_IMETHODIMP
    1429           0 : IMEContentObserver::BeforeEditAction()
    1430             : {
    1431           0 :   MOZ_LOG(sIMECOLog, LogLevel::Debug,
    1432             :     ("0x%p IMEContentObserver::BeforeEditAction()", this));
    1433             : 
    1434           0 :   mEndOfAddedTextCache.Clear();
    1435           0 :   mStartOfRemovingTextRangeCache.Clear();
    1436           0 :   return NS_OK;
    1437             : }
    1438             : 
    1439             : NS_IMETHODIMP
    1440           0 : IMEContentObserver::CancelEditAction()
    1441             : {
    1442           0 :   MOZ_LOG(sIMECOLog, LogLevel::Debug,
    1443             :     ("0x%p IMEContentObserver::CancelEditAction()", this));
    1444             : 
    1445           0 :   mEndOfAddedTextCache.Clear();
    1446           0 :   mStartOfRemovingTextRangeCache.Clear();
    1447           0 :   FlushMergeableNotifications();
    1448           0 :   return NS_OK;
    1449             : }
    1450             : 
    1451             : void
    1452           0 : IMEContentObserver::PostFocusSetNotification()
    1453             : {
    1454           0 :   MOZ_LOG(sIMECOLog, LogLevel::Debug,
    1455             :     ("0x%p IMEContentObserver::PostFocusSetNotification()", this));
    1456             : 
    1457           0 :   mNeedsToNotifyIMEOfFocusSet = true;
    1458           0 : }
    1459             : 
    1460             : void
    1461           0 : IMEContentObserver::PostTextChangeNotification()
    1462             : {
    1463           0 :   MOZ_LOG(sIMECOLog, LogLevel::Debug,
    1464             :     ("0x%p IMEContentObserver::PostTextChangeNotification("
    1465             :      "mTextChangeData=%s)",
    1466             :      this, TextChangeDataToString(mTextChangeData).get()));
    1467             : 
    1468           0 :   MOZ_ASSERT(mTextChangeData.IsValid(),
    1469             :              "mTextChangeData must have text change data");
    1470           0 :   mNeedsToNotifyIMEOfTextChange = true;
    1471           0 : }
    1472             : 
    1473             : void
    1474           0 : IMEContentObserver::PostSelectionChangeNotification()
    1475             : {
    1476           0 :   MOZ_LOG(sIMECOLog, LogLevel::Debug,
    1477             :     ("0x%p IMEContentObserver::PostSelectionChangeNotification(), "
    1478             :      "mSelectionData={ mCausedByComposition=%s, mCausedBySelectionEvent=%s }",
    1479             :      this, ToChar(mSelectionData.mCausedByComposition),
    1480             :      ToChar(mSelectionData.mCausedBySelectionEvent)));
    1481             : 
    1482           0 :   mNeedsToNotifyIMEOfSelectionChange = true;
    1483           0 : }
    1484             : 
    1485             : void
    1486           0 : IMEContentObserver::MaybeNotifyIMEOfFocusSet()
    1487             : {
    1488           0 :   MOZ_LOG(sIMECOLog, LogLevel::Debug,
    1489             :     ("0x%p IMEContentObserver::MaybeNotifyIMEOfFocusSet()", this));
    1490             : 
    1491           0 :   PostFocusSetNotification();
    1492           0 :   FlushMergeableNotifications();
    1493           0 : }
    1494             : 
    1495             : void
    1496           0 : IMEContentObserver::MaybeNotifyIMEOfTextChange(
    1497             :                       const TextChangeDataBase& aTextChangeData)
    1498             : {
    1499           0 :   MOZ_LOG(sIMECOLog, LogLevel::Debug,
    1500             :     ("0x%p IMEContentObserver::MaybeNotifyIMEOfTextChange("
    1501             :      "aTextChangeData=%s)",
    1502             :      this, TextChangeDataToString(aTextChangeData).get()));
    1503             : 
    1504           0 :   mTextChangeData += aTextChangeData;
    1505           0 :   PostTextChangeNotification();
    1506           0 :   FlushMergeableNotifications();
    1507           0 : }
    1508             : 
    1509             : void
    1510           0 : IMEContentObserver::CancelNotifyingIMEOfTextChange()
    1511             : {
    1512           0 :   MOZ_LOG(sIMECOLog, LogLevel::Debug,
    1513             :     ("0x%p IMEContentObserver::CancelNotifyingIMEOfTextChange()", this));
    1514           0 :   mTextChangeData.Clear();
    1515           0 :   mNeedsToNotifyIMEOfTextChange = false;
    1516           0 : }
    1517             : 
    1518             : void
    1519           0 : IMEContentObserver::MaybeNotifyIMEOfSelectionChange(
    1520             :                       bool aCausedByComposition,
    1521             :                       bool aCausedBySelectionEvent,
    1522             :                       bool aOccurredDuringComposition)
    1523             : {
    1524           0 :   MOZ_LOG(sIMECOLog, LogLevel::Debug,
    1525             :     ("0x%p IMEContentObserver::MaybeNotifyIMEOfSelectionChange("
    1526             :      "aCausedByComposition=%s, aCausedBySelectionEvent=%s, "
    1527             :      "aOccurredDuringComposition)",
    1528             :      this, ToChar(aCausedByComposition), ToChar(aCausedBySelectionEvent)));
    1529             : 
    1530           0 :   mSelectionData.AssignReason(aCausedByComposition,
    1531             :                               aCausedBySelectionEvent,
    1532           0 :                               aOccurredDuringComposition);
    1533           0 :   PostSelectionChangeNotification();
    1534           0 :   FlushMergeableNotifications();
    1535           0 : }
    1536             : 
    1537             : void
    1538           0 : IMEContentObserver::MaybeNotifyIMEOfPositionChange()
    1539             : {
    1540           0 :   MOZ_LOG(sIMECOLog, LogLevel::Debug,
    1541             :     ("0x%p IMEContentObserver::MaybeNotifyIMEOfPositionChange()", this));
    1542             :   // If reflow is caused by ContentEventHandler during PositionChangeEvent
    1543             :   // sending NOTIFY_IME_OF_POSITION_CHANGE, we don't need to notify IME of it
    1544             :   // again since ContentEventHandler returns the result including this reflow's
    1545             :   // result.
    1546           0 :   if (mIsHandlingQueryContentEvent &&
    1547           0 :       mSendingNotification == NOTIFY_IME_OF_POSITION_CHANGE) {
    1548           0 :     MOZ_LOG(sIMECOLog, LogLevel::Debug,
    1549             :       ("0x%p   IMEContentObserver::MaybeNotifyIMEOfPositionChange(), "
    1550             :        "ignored since caused by ContentEventHandler during sending "
    1551             :        "NOTIY_IME_OF_POSITION_CHANGE", this));
    1552           0 :     return;
    1553             :   }
    1554           0 :   PostPositionChangeNotification();
    1555           0 :   FlushMergeableNotifications();
    1556             : }
    1557             : 
    1558             : void
    1559           0 : IMEContentObserver::CancelNotifyingIMEOfPositionChange()
    1560             : {
    1561           0 :   MOZ_LOG(sIMECOLog, LogLevel::Debug,
    1562             :     ("0x%p IMEContentObserver::CancelNotifyIMEOfPositionChange()", this));
    1563           0 :   mNeedsToNotifyIMEOfPositionChange = false;
    1564           0 : }
    1565             : 
    1566             : void
    1567           0 : IMEContentObserver::MaybeNotifyCompositionEventHandled()
    1568             : {
    1569           0 :   MOZ_LOG(sIMECOLog, LogLevel::Debug,
    1570             :     ("0x%p IMEContentObserver::MaybeNotifyCompositionEventHandled()",
    1571             :      this));
    1572             : 
    1573           0 :   PostCompositionEventHandledNotification();
    1574           0 :   FlushMergeableNotifications();
    1575           0 : }
    1576             : 
    1577             : bool
    1578           0 : IMEContentObserver::UpdateSelectionCache()
    1579             : {
    1580           0 :   MOZ_ASSERT(IsSafeToNotifyIME());
    1581             : 
    1582           0 :   if (WasInitializedWithPlugin()) {
    1583           0 :     return false;
    1584             :   }
    1585             : 
    1586           0 :   mSelectionData.ClearSelectionData();
    1587             : 
    1588             :   // XXX Cannot we cache some information for reducing the cost to compute
    1589             :   //     selection offset and writing mode?
    1590           0 :   WidgetQueryContentEvent selection(true, eQuerySelectedText, mWidget);
    1591           0 :   ContentEventHandler handler(GetPresContext());
    1592           0 :   handler.OnQuerySelectedText(&selection);
    1593           0 :   if (NS_WARN_IF(!selection.mSucceeded) ||
    1594           0 :       NS_WARN_IF(selection.mReply.mContentsRoot != mRootContent)) {
    1595           0 :     return false;
    1596             :   }
    1597             : 
    1598           0 :   mFocusedWidget = selection.mReply.mFocusedWidget;
    1599           0 :   mSelectionData.mOffset = selection.mReply.mOffset;
    1600           0 :   *mSelectionData.mString = selection.mReply.mString;
    1601           0 :   mSelectionData.SetWritingMode(selection.GetWritingMode());
    1602           0 :   mSelectionData.mReversed = selection.mReply.mReversed;
    1603             : 
    1604             :   // WARNING: Don't modify the reason of selection change here.
    1605             : 
    1606           0 :   MOZ_LOG(sIMECOLog, LogLevel::Debug,
    1607             :     ("0x%p IMEContentObserver::UpdateSelectionCache(), "
    1608             :      "mSelectionData=%s",
    1609             :      this, SelectionChangeDataToString(mSelectionData).get()));
    1610             : 
    1611           0 :   return mSelectionData.IsValid();
    1612             : }
    1613             : 
    1614             : void
    1615           0 : IMEContentObserver::PostPositionChangeNotification()
    1616             : {
    1617           0 :   MOZ_LOG(sIMECOLog, LogLevel::Debug,
    1618             :     ("0x%p IMEContentObserver::PostPositionChangeNotification()", this));
    1619             : 
    1620           0 :   mNeedsToNotifyIMEOfPositionChange = true;
    1621           0 : }
    1622             : 
    1623             : void
    1624           0 : IMEContentObserver::PostCompositionEventHandledNotification()
    1625             : {
    1626           0 :   MOZ_LOG(sIMECOLog, LogLevel::Debug,
    1627             :     ("0x%p IMEContentObserver::"
    1628             :      "PostCompositionEventHandledNotification()", this));
    1629             : 
    1630           0 :   mNeedsToNotifyIMEOfCompositionEventHandled = true;
    1631           0 : }
    1632             : 
    1633             : bool
    1634           0 : IMEContentObserver::IsReflowLocked() const
    1635             : {
    1636           0 :   nsPresContext* presContext = GetPresContext();
    1637           0 :   if (NS_WARN_IF(!presContext)) {
    1638           0 :     return false;
    1639             :   }
    1640           0 :   nsIPresShell* presShell = presContext->GetPresShell();
    1641           0 :   if (NS_WARN_IF(!presShell)) {
    1642           0 :     return false;
    1643             :   }
    1644             :   // During reflow, we shouldn't notify IME because IME may query content
    1645             :   // synchronously.  Then, it causes ContentEventHandler will try to flush
    1646             :   // pending notifications during reflow.
    1647           0 :   return presShell->IsReflowLocked();
    1648             : }
    1649             : 
    1650             : bool
    1651           0 : IMEContentObserver::IsSafeToNotifyIME() const
    1652             : {
    1653             :   // If this is already detached from the widget, this doesn't need to notify
    1654             :   // anything.
    1655           0 :   if (!mWidget) {
    1656           0 :     return false;
    1657             :   }
    1658             : 
    1659             :   // Don't notify IME of anything if it's not good time to do it.
    1660           0 :   if (mSuppressNotifications) {
    1661           0 :     return false;
    1662             :   }
    1663             : 
    1664           0 :   if (!mESM || NS_WARN_IF(!GetPresContext())) {
    1665           0 :     return false;
    1666             :   }
    1667             : 
    1668             :   // If it's in reflow, we should wait to finish the reflow.
    1669             :   // FYI: This should be called again from Reflow() or ReflowInterruptible().
    1670           0 :   if (IsReflowLocked()) {
    1671           0 :     return false;
    1672             :   }
    1673             : 
    1674             :   // If we're in handling an edit action, this method will be called later.
    1675           0 :   if (mEditorBase && mEditorBase->IsInEditAction()) {
    1676           0 :     return false;
    1677             :   }
    1678             : 
    1679           0 :   return true;
    1680             : }
    1681             : 
    1682             : void
    1683           0 : IMEContentObserver::FlushMergeableNotifications()
    1684             : {
    1685           0 :   if (!IsSafeToNotifyIME()) {
    1686             :     // So, if this is already called, this should do nothing.
    1687           0 :     MOZ_LOG(sIMECOLog, LogLevel::Debug,
    1688             :       ("0x%p IMEContentObserver::FlushMergeableNotifications(), "
    1689             :        "FAILED, due to unsafe to notify IME", this));
    1690           0 :     return;
    1691             :   }
    1692             : 
    1693             :   // Notifying something may cause nested call of this method.  For example,
    1694             :   // when somebody notified one of the notifications may dispatch query content
    1695             :   // event. Then, it causes flushing layout which may cause another layout
    1696             :   // change notification.
    1697             : 
    1698           0 :   if (mQueuedSender) {
    1699             :     // So, if this is already called, this should do nothing.
    1700           0 :     MOZ_LOG(sIMECOLog, LogLevel::Debug,
    1701             :       ("0x%p   IMEContentObserver::FlushMergeableNotifications(), "
    1702             :        "FAILED, due to already flushing pending notifications", this));
    1703           0 :     return;
    1704             :   }
    1705             : 
    1706             :   // If text change notification and/or position change notification becomes
    1707             :   // unnecessary, let's cancel them.
    1708           0 :   if (mNeedsToNotifyIMEOfTextChange && !NeedsTextChangeNotification()) {
    1709           0 :     CancelNotifyingIMEOfTextChange();
    1710             :   }
    1711           0 :   if (mNeedsToNotifyIMEOfPositionChange && !NeedsPositionChangeNotification()) {
    1712           0 :     CancelNotifyingIMEOfPositionChange();
    1713             :   }
    1714             : 
    1715           0 :   if (!NeedsToNotifyIMEOfSomething()) {
    1716           0 :     MOZ_LOG(sIMECOLog, LogLevel::Debug,
    1717             :       ("0x%p   IMEContentObserver::FlushMergeableNotifications(), "
    1718             :        "FAILED, due to no pending notifications", this));
    1719           0 :     return;
    1720             :   }
    1721             : 
    1722             :   // NOTE: Reset each pending flag because sending notification may cause
    1723             :   //       another change.
    1724             : 
    1725           0 :   MOZ_LOG(sIMECOLog, LogLevel::Debug,
    1726             :     ("0x%p IMEContentObserver::FlushMergeableNotifications(), "
    1727             :      "creating IMENotificationSender...", this));
    1728             : 
    1729             :   // If contents in selection range is modified, the selection range still
    1730             :   // has removed node from the tree.  In such case, nsContentIterator won't
    1731             :   // work well.  Therefore, we shouldn't use AddScriptRunnder() here since
    1732             :   // it may kick runnable event immediately after DOM tree is changed but
    1733             :   // the selection range isn't modified yet.
    1734           0 :   mQueuedSender = new IMENotificationSender(this);
    1735           0 :   mQueuedSender->Dispatch(mDocShell);
    1736           0 :   MOZ_LOG(sIMECOLog, LogLevel::Debug,
    1737             :     ("0x%p IMEContentObserver::FlushMergeableNotifications(), "
    1738             :      "finished", this));
    1739             : }
    1740             : 
    1741             : void
    1742           0 : IMEContentObserver::TryToFlushPendingNotifications()
    1743             : {
    1744           0 :   if (!mQueuedSender || mSendingNotification != NOTIFY_IME_OF_NOTHING ||
    1745           0 :       XRE_IsContentProcess()) {
    1746           0 :     return;
    1747             :   }
    1748             : 
    1749           0 :   MOZ_LOG(sIMECOLog, LogLevel::Debug,
    1750             :     ("0x%p IMEContentObserver::TryToFlushPendingNotifications(), "
    1751             :      "performing queued IMENotificationSender forcibly", this));
    1752           0 :   RefPtr<IMENotificationSender> queuedSender = mQueuedSender;
    1753           0 :   queuedSender->Run();
    1754             : }
    1755             : 
    1756             : /******************************************************************************
    1757             :  * mozilla::IMEContentObserver::AChangeEvent
    1758             :  ******************************************************************************/
    1759             : 
    1760             : bool
    1761           0 : IMEContentObserver::AChangeEvent::CanNotifyIME(
    1762             :                                     ChangeEventType aChangeEventType) const
    1763             : {
    1764           0 :   RefPtr<IMEContentObserver> observer = GetObserver();
    1765           0 :   if (NS_WARN_IF(!observer)) {
    1766           0 :     return false;
    1767             :   }
    1768             : 
    1769           0 :   if (aChangeEventType == eChangeEventType_CompositionEventHandled) {
    1770           0 :     return observer->mWidget != nullptr;
    1771             :   }
    1772           0 :   State state = observer->GetState();
    1773             :   // If it's not initialized, we should do nothing.
    1774           0 :   if (state == eState_NotObserving) {
    1775           0 :     return false;
    1776             :   }
    1777             :   // If setting focus, just check the state.
    1778           0 :   if (aChangeEventType == eChangeEventType_Focus) {
    1779           0 :     return !NS_WARN_IF(observer->mIMEHasFocus);
    1780             :   }
    1781             :   // If we've not notified IME of focus yet, we shouldn't notify anything.
    1782           0 :   if (!observer->mIMEHasFocus) {
    1783           0 :     return false;
    1784             :   }
    1785             : 
    1786             :   // If IME has focus, IMEContentObserver must hold the widget.
    1787           0 :   MOZ_ASSERT(observer->mWidget);
    1788             : 
    1789           0 :   return true;
    1790             : }
    1791             : 
    1792             : bool
    1793           0 : IMEContentObserver::AChangeEvent::IsSafeToNotifyIME(
    1794             :                                     ChangeEventType aChangeEventType) const
    1795             : {
    1796           0 :   if (NS_WARN_IF(!nsContentUtils::IsSafeToRunScript())) {
    1797           0 :     return false;
    1798             :   }
    1799             : 
    1800           0 :   RefPtr<IMEContentObserver> observer = GetObserver();
    1801           0 :   if (!observer) {
    1802           0 :     return false;
    1803             :   }
    1804             : 
    1805             :   // While we're sending a notification, we shouldn't send another notification
    1806             :   // recursively.
    1807           0 :   if (observer->mSendingNotification != NOTIFY_IME_OF_NOTHING) {
    1808           0 :     MOZ_LOG(sIMECOLog, LogLevel::Debug,
    1809             :       ("0x%p   IMEContentObserver::AChangeEvent::IsSafeToNotifyIME(), "
    1810             :        "putting off sending notification due to detecting recursive call, "
    1811             :        "mIMEContentObserver={ mSendingNotification=%s }",
    1812             :        this, ToChar(observer->mSendingNotification)));
    1813           0 :     return false;
    1814             :   }
    1815           0 :   State state = observer->GetState();
    1816           0 :   if (aChangeEventType == eChangeEventType_Focus) {
    1817           0 :     if (NS_WARN_IF(state != eState_Initializing && state != eState_Observing)) {
    1818           0 :       return false;
    1819             :     }
    1820           0 :   } else if (aChangeEventType == eChangeEventType_CompositionEventHandled) {
    1821             :     // It doesn't need to check the observing status.
    1822           0 :   } else if (state != eState_Observing) {
    1823           0 :     return false;
    1824             :   }
    1825           0 :   return observer->IsSafeToNotifyIME();
    1826             : }
    1827             : 
    1828             : /******************************************************************************
    1829             :  * mozilla::IMEContentObserver::IMENotificationSender
    1830             :  ******************************************************************************/
    1831             : 
    1832             : void
    1833           0 : IMEContentObserver::IMENotificationSender::Dispatch(nsIDocShell* aDocShell)
    1834             : {
    1835           0 :   if (XRE_IsContentProcess() && aDocShell) {
    1836           0 :     RefPtr<nsPresContext> presContext;
    1837           0 :     aDocShell->GetPresContext(getter_AddRefs(presContext));
    1838           0 :     if (presContext) {
    1839           0 :       nsRefreshDriver* refreshDriver = presContext->RefreshDriver();
    1840           0 :       if (refreshDriver) {
    1841           0 :         refreshDriver->AddEarlyRunner(this);
    1842           0 :         return;
    1843             :       }
    1844             :     }
    1845             :   }
    1846             : 
    1847             :   nsIScriptGlobalObject* globalObject =
    1848           0 :     aDocShell ? aDocShell->GetScriptGlobalObject() : nullptr;
    1849           0 :   if (globalObject) {
    1850           0 :     RefPtr<IMENotificationSender> queuedSender = this;
    1851           0 :     globalObject->Dispatch(nullptr, TaskCategory::Other,
    1852           0 :                            queuedSender.forget());
    1853             :   } else {
    1854           0 :     NS_DispatchToCurrentThread(this);
    1855             :   }
    1856             : }
    1857             : 
    1858             : NS_IMETHODIMP
    1859           0 : IMEContentObserver::IMENotificationSender::Run()
    1860             : {
    1861           0 :   if (NS_WARN_IF(mIsRunning)) {
    1862           0 :     MOZ_LOG(sIMECOLog, LogLevel::Error,
    1863             :       ("0x%p IMEContentObserver::IMENotificationSender::Run(), FAILED, "
    1864             :        "called recursively", this));
    1865           0 :     return NS_OK;
    1866             :   }
    1867             : 
    1868           0 :   RefPtr<IMEContentObserver> observer = GetObserver();
    1869           0 :   if (!observer) {
    1870           0 :     return NS_OK;
    1871             :   }
    1872             : 
    1873           0 :   AutoRestore<bool> running(mIsRunning);
    1874           0 :   mIsRunning = true;
    1875             : 
    1876             :   // This instance was already performed forcibly.
    1877           0 :   if (observer->mQueuedSender != this) {
    1878           0 :     return NS_OK;
    1879             :   }
    1880             : 
    1881             :   // NOTE: Reset each pending flag because sending notification may cause
    1882             :   //       another change.
    1883             : 
    1884           0 :   if (observer->mNeedsToNotifyIMEOfFocusSet) {
    1885           0 :     observer->mNeedsToNotifyIMEOfFocusSet = false;
    1886           0 :     SendFocusSet();
    1887           0 :     observer->mQueuedSender = nullptr;
    1888             :     // If it's not safe to notify IME of focus, SendFocusSet() sets
    1889             :     // mNeedsToNotifyIMEOfFocusSet true again.  For guaranteeing to send the
    1890             :     // focus notification later,  we should put a new sender into the queue but
    1891             :     // this case must be rare.  Note that if mIMEContentObserver is already
    1892             :     // destroyed, mNeedsToNotifyIMEOfFocusSet is never set true again.
    1893           0 :     if (observer->mNeedsToNotifyIMEOfFocusSet) {
    1894           0 :       MOZ_ASSERT(!observer->mIMEHasFocus);
    1895           0 :       MOZ_LOG(sIMECOLog, LogLevel::Debug,
    1896             :         ("0x%p IMEContentObserver::IMENotificationSender::Run(), "
    1897             :          "posting IMENotificationSender to current thread", this));
    1898           0 :       observer->mQueuedSender = new IMENotificationSender(observer);
    1899           0 :       observer->mQueuedSender->Dispatch(observer->mDocShell);
    1900           0 :       return NS_OK;
    1901             :     }
    1902             :     // This is the first notification to IME. So, we don't need to notify
    1903             :     // anymore since IME starts to query content after it gets focus.
    1904           0 :     observer->ClearPendingNotifications();
    1905           0 :     return NS_OK;
    1906             :   }
    1907             : 
    1908           0 :   if (observer->mNeedsToNotifyIMEOfTextChange) {
    1909           0 :     observer->mNeedsToNotifyIMEOfTextChange = false;
    1910           0 :     SendTextChange();
    1911             :   }
    1912             : 
    1913             :   // If a text change notification causes another text change again, we should
    1914             :   // notify IME of that before sending a selection change notification.
    1915           0 :   if (!observer->mNeedsToNotifyIMEOfTextChange) {
    1916             :     // Be aware, PuppetWidget depends on the order of this. A selection change
    1917             :     // notification should not be sent before a text change notification because
    1918             :     // PuppetWidget shouldn't query new text content every selection change.
    1919           0 :     if (observer->mNeedsToNotifyIMEOfSelectionChange) {
    1920           0 :       observer->mNeedsToNotifyIMEOfSelectionChange = false;
    1921           0 :       SendSelectionChange();
    1922             :     }
    1923             :   }
    1924             : 
    1925             :   // If a text change notification causes another text change again or a
    1926             :   // selection change notification causes either a text change or another
    1927             :   // selection change, we should notify IME of those before sending a position
    1928             :   // change notification.
    1929           0 :   if (!observer->mNeedsToNotifyIMEOfTextChange &&
    1930           0 :       !observer->mNeedsToNotifyIMEOfSelectionChange) {
    1931           0 :     if (observer->mNeedsToNotifyIMEOfPositionChange) {
    1932           0 :       observer->mNeedsToNotifyIMEOfPositionChange = false;
    1933           0 :       SendPositionChange();
    1934             :     }
    1935             :   }
    1936             : 
    1937             :   // Composition event handled notification should be sent after all the
    1938             :   // other notifications because this notifies widget of finishing all pending
    1939             :   // events are handled completely.
    1940           0 :   if (!observer->mNeedsToNotifyIMEOfTextChange &&
    1941           0 :       !observer->mNeedsToNotifyIMEOfSelectionChange &&
    1942           0 :       !observer->mNeedsToNotifyIMEOfPositionChange) {
    1943           0 :     if (observer->mNeedsToNotifyIMEOfCompositionEventHandled) {
    1944           0 :       observer->mNeedsToNotifyIMEOfCompositionEventHandled = false;
    1945           0 :       SendCompositionEventHandled();
    1946             :     }
    1947             :   }
    1948             : 
    1949           0 :   observer->mQueuedSender = nullptr;
    1950             : 
    1951             :   // If notifications caused some new change, we should notify them now.
    1952           0 :   if (observer->NeedsToNotifyIMEOfSomething()) {
    1953           0 :     if (observer->GetState() == eState_StoppedObserving) {
    1954           0 :       MOZ_LOG(sIMECOLog, LogLevel::Debug,
    1955             :         ("0x%p IMEContentObserver::IMENotificationSender::Run(), "
    1956             :          "waiting IMENotificationSender to be reinitialized", this));
    1957             :     } else {
    1958           0 :       MOZ_LOG(sIMECOLog, LogLevel::Debug,
    1959             :         ("0x%p IMEContentObserver::IMENotificationSender::Run(), "
    1960             :          "posting IMENotificationSender to current thread", this));
    1961           0 :       observer->mQueuedSender = new IMENotificationSender(observer);
    1962           0 :       observer->mQueuedSender->Dispatch(observer->mDocShell);
    1963             :     }
    1964             :   }
    1965           0 :   return NS_OK;
    1966             : }
    1967             : 
    1968             : void
    1969           0 : IMEContentObserver::IMENotificationSender::SendFocusSet()
    1970             : {
    1971           0 :   RefPtr<IMEContentObserver> observer = GetObserver();
    1972           0 :   if (!observer) {
    1973           0 :     return;
    1974             :   }
    1975             : 
    1976           0 :   if (!CanNotifyIME(eChangeEventType_Focus)) {
    1977             :     // If IMEContentObserver has already gone, we don't need to notify IME of
    1978             :     // focus.
    1979           0 :     MOZ_LOG(sIMECOLog, LogLevel::Debug,
    1980             :       ("0x%p IMEContentObserver::IMENotificationSender::"
    1981             :        "SendFocusSet(), FAILED, due to impossible to notify IME of focus",
    1982             :        this));
    1983           0 :     observer->ClearPendingNotifications();
    1984           0 :     return;
    1985             :   }
    1986             : 
    1987           0 :   if (!IsSafeToNotifyIME(eChangeEventType_Focus)) {
    1988           0 :     MOZ_LOG(sIMECOLog, LogLevel::Debug,
    1989             :       ("0x%p   IMEContentObserver::IMENotificationSender::"
    1990             :        "SendFocusSet(), retrying to send NOTIFY_IME_OF_FOCUS...", this));
    1991           0 :     observer->PostFocusSetNotification();
    1992           0 :     return;
    1993             :   }
    1994             : 
    1995           0 :   observer->mIMEHasFocus = true;
    1996             :   // Initialize selection cache with the first selection data.
    1997           0 :   observer->UpdateSelectionCache();
    1998             : 
    1999           0 :   MOZ_LOG(sIMECOLog, LogLevel::Info,
    2000             :     ("0x%p IMEContentObserver::IMENotificationSender::"
    2001             :      "SendFocusSet(), sending NOTIFY_IME_OF_FOCUS...", this));
    2002             : 
    2003           0 :   MOZ_RELEASE_ASSERT(observer->mSendingNotification ==
    2004             :                        NOTIFY_IME_OF_NOTHING);
    2005           0 :   observer->mSendingNotification = NOTIFY_IME_OF_FOCUS;
    2006           0 :   IMEStateManager::NotifyIME(IMENotification(NOTIFY_IME_OF_FOCUS),
    2007           0 :                              observer->mWidget);
    2008           0 :   observer->mSendingNotification = NOTIFY_IME_OF_NOTHING;
    2009             : 
    2010             :   // IMENotificationRequests referred by ObserveEditableNode() may be different
    2011             :   // before or after widget receives NOTIFY_IME_OF_FOCUS.  Therefore, we need
    2012             :   // to guarantee to call ObserveEditableNode() after sending
    2013             :   // NOTIFY_IME_OF_FOCUS.
    2014           0 :   observer->OnIMEReceivedFocus();
    2015             : 
    2016           0 :   MOZ_LOG(sIMECOLog, LogLevel::Debug,
    2017             :     ("0x%p IMEContentObserver::IMENotificationSender::"
    2018             :      "SendFocusSet(), sent NOTIFY_IME_OF_FOCUS", this));
    2019             : }
    2020             : 
    2021             : void
    2022           0 : IMEContentObserver::IMENotificationSender::SendSelectionChange()
    2023             : {
    2024           0 :   RefPtr<IMEContentObserver> observer = GetObserver();
    2025           0 :   if (!observer) {
    2026           0 :     return;
    2027             :   }
    2028             : 
    2029           0 :   if (!CanNotifyIME(eChangeEventType_Selection)) {
    2030           0 :     MOZ_LOG(sIMECOLog, LogLevel::Debug,
    2031             :       ("0x%p IMEContentObserver::IMENotificationSender::"
    2032             :        "SendSelectionChange(), FAILED, due to impossible to notify IME of "
    2033             :        "selection change", this));
    2034           0 :     return;
    2035             :   }
    2036             : 
    2037           0 :   if (!IsSafeToNotifyIME(eChangeEventType_Selection)) {
    2038           0 :     MOZ_LOG(sIMECOLog, LogLevel::Debug,
    2039             :       ("0x%p   IMEContentObserver::IMENotificationSender::"
    2040             :        "SendSelectionChange(), retrying to send "
    2041             :        "NOTIFY_IME_OF_SELECTION_CHANGE...", this));
    2042           0 :     observer->PostSelectionChangeNotification();
    2043           0 :     return;
    2044             :   }
    2045             : 
    2046           0 :   SelectionChangeData lastSelChangeData = observer->mSelectionData;
    2047           0 :   if (NS_WARN_IF(!observer->UpdateSelectionCache())) {
    2048           0 :     MOZ_LOG(sIMECOLog, LogLevel::Error,
    2049             :       ("0x%p IMEContentObserver::IMENotificationSender::"
    2050             :        "SendSelectionChange(), FAILED, due to UpdateSelectionCache() failure",
    2051             :        this));
    2052           0 :     return;
    2053             :   }
    2054             : 
    2055             :   // The state may be changed since querying content causes flushing layout.
    2056           0 :   if (!CanNotifyIME(eChangeEventType_Selection)) {
    2057           0 :     MOZ_LOG(sIMECOLog, LogLevel::Debug,
    2058             :       ("0x%p IMEContentObserver::IMENotificationSender::"
    2059             :        "SendSelectionChange(), FAILED, due to flushing layout having changed "
    2060             :        "something", this));
    2061           0 :     return;
    2062             :   }
    2063             : 
    2064             :   // If the selection isn't changed actually, we shouldn't notify IME of
    2065             :   // selection change.
    2066           0 :   SelectionChangeData& newSelChangeData = observer->mSelectionData;
    2067           0 :   if (lastSelChangeData.IsValid() &&
    2068           0 :       lastSelChangeData.mOffset == newSelChangeData.mOffset &&
    2069           0 :       lastSelChangeData.String() == newSelChangeData.String() &&
    2070           0 :       lastSelChangeData.GetWritingMode() == newSelChangeData.GetWritingMode() &&
    2071           0 :       lastSelChangeData.mReversed == newSelChangeData.mReversed) {
    2072           0 :     MOZ_LOG(sIMECOLog, LogLevel::Debug,
    2073             :       ("0x%p IMEContentObserver::IMENotificationSender::"
    2074             :        "SendSelectionChange(), not notifying IME of "
    2075             :        "NOTIFY_IME_OF_SELECTION_CHANGE due to not changed actually", this));
    2076           0 :     return;
    2077             :   }
    2078             : 
    2079           0 :   MOZ_LOG(sIMECOLog, LogLevel::Info,
    2080             :     ("0x%p IMEContentObserver::IMENotificationSender::"
    2081             :      "SendSelectionChange(), sending NOTIFY_IME_OF_SELECTION_CHANGE... "
    2082             :      "newSelChangeData=%s",
    2083             :      this, SelectionChangeDataToString(newSelChangeData).get()));
    2084             : 
    2085           0 :   IMENotification notification(NOTIFY_IME_OF_SELECTION_CHANGE);
    2086           0 :   notification.SetData(observer->mSelectionData);
    2087             : 
    2088           0 :   MOZ_RELEASE_ASSERT(observer->mSendingNotification ==
    2089             :                        NOTIFY_IME_OF_NOTHING);
    2090           0 :   observer->mSendingNotification = NOTIFY_IME_OF_SELECTION_CHANGE;
    2091           0 :   IMEStateManager::NotifyIME(notification, observer->mWidget);
    2092           0 :   observer->mSendingNotification = NOTIFY_IME_OF_NOTHING;
    2093             : 
    2094           0 :   MOZ_LOG(sIMECOLog, LogLevel::Debug,
    2095             :     ("0x%p IMEContentObserver::IMENotificationSender::"
    2096             :      "SendSelectionChange(), sent NOTIFY_IME_OF_SELECTION_CHANGE", this));
    2097             : }
    2098             : 
    2099             : void
    2100           0 : IMEContentObserver::IMENotificationSender::SendTextChange()
    2101             : {
    2102           0 :   RefPtr<IMEContentObserver> observer = GetObserver();
    2103           0 :   if (!observer) {
    2104           0 :     return;
    2105             :   }
    2106             : 
    2107           0 :   if (!CanNotifyIME(eChangeEventType_Text)) {
    2108           0 :     MOZ_LOG(sIMECOLog, LogLevel::Debug,
    2109             :       ("0x%p IMEContentObserver::IMENotificationSender::"
    2110             :        "SendTextChange(), FAILED, due to impossible to notify IME of text "
    2111             :        "change", this));
    2112           0 :     return;
    2113             :   }
    2114             : 
    2115           0 :   if (!IsSafeToNotifyIME(eChangeEventType_Text)) {
    2116           0 :     MOZ_LOG(sIMECOLog, LogLevel::Debug,
    2117             :       ("0x%p   IMEContentObserver::IMENotificationSender::"
    2118             :        "SendTextChange(), retrying to send NOTIFY_IME_OF_TEXT_CHANGE...",
    2119             :        this));
    2120           0 :     observer->PostTextChangeNotification();
    2121           0 :     return;
    2122             :   }
    2123             : 
    2124             :   // If text change notification is unnecessary anymore, just cancel it.
    2125           0 :   if (!observer->NeedsTextChangeNotification()) {
    2126           0 :     MOZ_LOG(sIMECOLog, LogLevel::Warning,
    2127             :       ("0x%p   IMEContentObserver::IMENotificationSender::"
    2128             :        "SendTextChange(), canceling sending NOTIFY_IME_OF_TEXT_CHANGE",
    2129             :        this));
    2130           0 :     observer->CancelNotifyingIMEOfTextChange();
    2131           0 :     return;
    2132             :   }
    2133             : 
    2134           0 :   MOZ_LOG(sIMECOLog, LogLevel::Info,
    2135             :     ("0x%p IMEContentObserver::IMENotificationSender::"
    2136             :      "SendTextChange(), sending NOTIFY_IME_OF_TEXT_CHANGE... "
    2137             :      "mIMEContentObserver={ mTextChangeData=%s }",
    2138             :      this, TextChangeDataToString(observer->mTextChangeData).get()));
    2139             : 
    2140           0 :   IMENotification notification(NOTIFY_IME_OF_TEXT_CHANGE);
    2141           0 :   notification.SetData(observer->mTextChangeData);
    2142           0 :   observer->mTextChangeData.Clear();
    2143             : 
    2144           0 :   MOZ_RELEASE_ASSERT(observer->mSendingNotification ==
    2145             :                        NOTIFY_IME_OF_NOTHING);
    2146           0 :   observer->mSendingNotification = NOTIFY_IME_OF_TEXT_CHANGE;
    2147           0 :   IMEStateManager::NotifyIME(notification, observer->mWidget);
    2148           0 :   observer->mSendingNotification = NOTIFY_IME_OF_NOTHING;
    2149             : 
    2150           0 :   MOZ_LOG(sIMECOLog, LogLevel::Debug,
    2151             :     ("0x%p IMEContentObserver::IMENotificationSender::"
    2152             :      "SendTextChange(), sent NOTIFY_IME_OF_TEXT_CHANGE", this));
    2153             : }
    2154             : 
    2155             : void
    2156           0 : IMEContentObserver::IMENotificationSender::SendPositionChange()
    2157             : {
    2158           0 :   RefPtr<IMEContentObserver> observer = GetObserver();
    2159           0 :   if (!observer) {
    2160           0 :     return;
    2161             :   }
    2162             : 
    2163           0 :   if (!CanNotifyIME(eChangeEventType_Position)) {
    2164           0 :     MOZ_LOG(sIMECOLog, LogLevel::Debug,
    2165             :       ("0x%p IMEContentObserver::IMENotificationSender::"
    2166             :        "SendPositionChange(), FAILED, due to impossible to notify IME of "
    2167             :        "position change", this));
    2168           0 :     return;
    2169             :   }
    2170             : 
    2171           0 :   if (!IsSafeToNotifyIME(eChangeEventType_Position)) {
    2172           0 :     MOZ_LOG(sIMECOLog, LogLevel::Debug,
    2173             :       ("0x%p   IMEContentObserver::IMENotificationSender::"
    2174             :        "SendPositionChange(), retrying to send "
    2175             :        "NOTIFY_IME_OF_POSITION_CHANGE...", this));
    2176           0 :     observer->PostPositionChangeNotification();
    2177           0 :     return;
    2178             :   }
    2179             : 
    2180             :   // If position change notification is unnecessary anymore, just cancel it.
    2181           0 :   if (!observer->NeedsPositionChangeNotification()) {
    2182           0 :     MOZ_LOG(sIMECOLog, LogLevel::Warning,
    2183             :       ("0x%p   IMEContentObserver::IMENotificationSender::"
    2184             :        "SendPositionChange(), canceling sending NOTIFY_IME_OF_POSITION_CHANGE",
    2185             :        this));
    2186           0 :     observer->CancelNotifyingIMEOfPositionChange();
    2187           0 :     return;
    2188             :   }
    2189             : 
    2190           0 :   MOZ_LOG(sIMECOLog, LogLevel::Info,
    2191             :     ("0x%p IMEContentObserver::IMENotificationSender::"
    2192             :      "SendPositionChange(), sending NOTIFY_IME_OF_POSITION_CHANGE...", this));
    2193             : 
    2194           0 :   MOZ_RELEASE_ASSERT(observer->mSendingNotification ==
    2195             :                        NOTIFY_IME_OF_NOTHING);
    2196           0 :   observer->mSendingNotification = NOTIFY_IME_OF_POSITION_CHANGE;
    2197           0 :   IMEStateManager::NotifyIME(IMENotification(NOTIFY_IME_OF_POSITION_CHANGE),
    2198           0 :                              observer->mWidget);
    2199           0 :   observer->mSendingNotification = NOTIFY_IME_OF_NOTHING;
    2200             : 
    2201           0 :   MOZ_LOG(sIMECOLog, LogLevel::Debug,
    2202             :     ("0x%p IMEContentObserver::IMENotificationSender::"
    2203             :      "SendPositionChange(), sent NOTIFY_IME_OF_POSITION_CHANGE", this));
    2204             : }
    2205             : 
    2206             : void
    2207           0 : IMEContentObserver::IMENotificationSender::SendCompositionEventHandled()
    2208             : {
    2209           0 :   RefPtr<IMEContentObserver> observer = GetObserver();
    2210           0 :   if (!observer) {
    2211           0 :     return;
    2212             :   }
    2213             : 
    2214           0 :   if (!CanNotifyIME(eChangeEventType_CompositionEventHandled)) {
    2215           0 :     MOZ_LOG(sIMECOLog, LogLevel::Debug,
    2216             :       ("0x%p IMEContentObserver::IMENotificationSender::"
    2217             :        "SendCompositionEventHandled(), FAILED, due to impossible to notify "
    2218             :        "IME of composition event handled", this));
    2219           0 :     return;
    2220             :   }
    2221             : 
    2222           0 :   if (!IsSafeToNotifyIME(eChangeEventType_CompositionEventHandled)) {
    2223           0 :     MOZ_LOG(sIMECOLog, LogLevel::Debug,
    2224             :       ("0x%p   IMEContentObserver::IMENotificationSender::"
    2225             :        "SendCompositionEventHandled(), retrying to send "
    2226             :        "NOTIFY_IME_OF_POSITION_CHANGE...", this));
    2227           0 :     observer->PostCompositionEventHandledNotification();
    2228           0 :     return;
    2229             :   }
    2230             : 
    2231           0 :   MOZ_LOG(sIMECOLog, LogLevel::Info,
    2232             :     ("0x%p IMEContentObserver::IMENotificationSender::"
    2233             :      "SendCompositionEventHandled(), sending "
    2234             :      "NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED...", this));
    2235             : 
    2236           0 :   MOZ_RELEASE_ASSERT(observer->mSendingNotification ==
    2237             :                        NOTIFY_IME_OF_NOTHING);
    2238           0 :   observer->mSendingNotification =
    2239             :                          NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED;
    2240           0 :   IMEStateManager::NotifyIME(
    2241           0 :                      IMENotification(NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED),
    2242           0 :                      observer->mWidget);
    2243           0 :   observer->mSendingNotification = NOTIFY_IME_OF_NOTHING;
    2244             : 
    2245           0 :   MOZ_LOG(sIMECOLog, LogLevel::Debug,
    2246             :     ("0x%p IMEContentObserver::IMENotificationSender::"
    2247             :      "SendCompositionEventHandled(), sent "
    2248             :      "NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED", this));
    2249             : }
    2250             : 
    2251             : /******************************************************************************
    2252             :  * mozilla::IMEContentObserver::DocumentObservingHelper
    2253             :  ******************************************************************************/
    2254             : 
    2255             : NS_IMPL_CYCLE_COLLECTION_CLASS(IMEContentObserver::DocumentObserver)
    2256             : 
    2257           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IMEContentObserver::DocumentObserver)
    2258             :   // StopObserving() releases mIMEContentObserver and mDocument.
    2259           0 :   tmp->StopObserving();
    2260           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    2261             : 
    2262           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IMEContentObserver::DocumentObserver)
    2263           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIMEContentObserver)
    2264           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
    2265           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    2266             : 
    2267           0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IMEContentObserver::DocumentObserver)
    2268           0 :  NS_INTERFACE_MAP_ENTRY(nsIDocumentObserver)
    2269           0 :  NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
    2270           0 :  NS_INTERFACE_MAP_ENTRY(nsISupports)
    2271           0 : NS_INTERFACE_MAP_END
    2272             : 
    2273           0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(IMEContentObserver::DocumentObserver)
    2274           0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(IMEContentObserver::DocumentObserver)
    2275             : 
    2276             : void
    2277           0 : IMEContentObserver::DocumentObserver::Observe(nsIDocument* aDocument)
    2278             : {
    2279           0 :   MOZ_ASSERT(aDocument);
    2280             : 
    2281             :   // Guarantee that aDocument won't be destroyed during a call of
    2282             :   // StopObserving().
    2283           0 :   RefPtr<nsIDocument> newDocument = aDocument;
    2284             : 
    2285           0 :   StopObserving();
    2286             : 
    2287           0 :   mDocument = newDocument.forget();
    2288           0 :   mDocument->AddObserver(this);
    2289           0 : }
    2290             : 
    2291             : void
    2292           0 : IMEContentObserver::DocumentObserver::StopObserving()
    2293             : {
    2294           0 :   if (!IsObserving()) {
    2295           0 :     return;
    2296             :   }
    2297             : 
    2298             :   // Grab IMEContentObserver which could be destroyed during method calls.
    2299           0 :   RefPtr<IMEContentObserver> observer = mIMEContentObserver.forget();
    2300             : 
    2301             :   // Stop observing the document first.
    2302           0 :   RefPtr<nsIDocument> document = mDocument.forget();
    2303           0 :   document->RemoveObserver(this);
    2304             : 
    2305             :   // Notify IMEContentObserver of ending of document updates if this already
    2306             :   // notified it of beginning of document updates.
    2307           0 :   for (; IsUpdating(); --mDocumentUpdating) {
    2308             :     // FYI: IsUpdating() returns true until mDocumentUpdating becomes 0.
    2309             :     //      However, IsObserving() returns false now because mDocument was
    2310             :     //      already cleared above.  Therefore, this method won't be called
    2311             :     //      recursively.
    2312           0 :     observer->EndDocumentUpdate();
    2313             :   }
    2314             : }
    2315             : 
    2316             : void
    2317           0 : IMEContentObserver::DocumentObserver::Destroy()
    2318             : {
    2319           0 :   StopObserving();
    2320           0 :   mIMEContentObserver = nullptr;
    2321           0 : }
    2322             : 
    2323             : void
    2324           0 : IMEContentObserver::DocumentObserver::BeginUpdate(nsIDocument* aDocument,
    2325             :                                                   nsUpdateType aUpdateType)
    2326             : {
    2327           0 :   if (NS_WARN_IF(Destroyed()) || NS_WARN_IF(!IsObserving())) {
    2328           0 :     return;
    2329             :   }
    2330           0 :   if (!(aUpdateType & UPDATE_CONTENT_MODEL)) {
    2331           0 :     return;
    2332             :   }
    2333           0 :   mDocumentUpdating++;
    2334           0 :   mIMEContentObserver->BeginDocumentUpdate();
    2335             : }
    2336             : 
    2337             : void
    2338           0 : IMEContentObserver::DocumentObserver::EndUpdate(nsIDocument* aDocument,
    2339             :                                                 nsUpdateType aUpdateType)
    2340             : {
    2341           0 :   if (NS_WARN_IF(Destroyed()) || NS_WARN_IF(!IsObserving()) ||
    2342           0 :       NS_WARN_IF(!IsUpdating())) {
    2343           0 :     return;
    2344             :   }
    2345           0 :   if (!(aUpdateType & UPDATE_CONTENT_MODEL)) {
    2346           0 :     return;
    2347             :   }
    2348           0 :   mDocumentUpdating--;
    2349           0 :   mIMEContentObserver->EndDocumentUpdate();
    2350             : }
    2351             : 
    2352             : } // namespace mozilla

Generated by: LCOV version 1.13