/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "ContentEventHandler.h" #include "IMEContentObserver.h" #include "mozilla/Assertions.h" #include "mozilla/AsyncEventDispatcher.h" #include "mozilla/AutoRestore.h" #include "mozilla/ErrorResult.h" #include "mozilla/EventStateManager.h" #include "mozilla/IMEStateManager.h" #include "mozilla/Logging.h" #include "mozilla/MouseEvents.h" #include "mozilla/PresShell.h" #include "mozilla/StaticPrefs_test.h" #include "mozilla/TextComposition.h" #include "mozilla/TextControlElement.h" #include "mozilla/TextEvents.h" #include "mozilla/dom/AncestorIterator.h" #include "mozilla/dom/Document.h" #include "mozilla/dom/Element.h" #include "mozilla/dom/Selection.h" #include "nsContentUtils.h" #include "nsAtom.h" #include "nsDocShell.h" #include "nsGkAtoms.h" #include "nsIContent.h" #include "nsIFrame.h" #include "nsINode.h" #include "nsISelectionController.h" #include "nsISupports.h" #include "nsIWeakReferenceUtils.h" #include "nsIWidget.h" #include "nsPresContext.h" #include "nsRange.h" #include "nsRefreshDriver.h" #include "WritingModes.h" #include "nsString.h" namespace mozilla { using RawNodePosition = ContentEventHandler::RawNodePosition; using namespace dom; using namespace widget; LazyLogModule sIMECOLog("IMEContentObserver"); LazyLogModule sCacheLog("IMEContentObserverCache"); static const char* ToChar(bool aBool) { return aBool ? "true" : "false"; } /****************************************************************************** * mozilla::IMEContentObserver ******************************************************************************/ NS_IMPL_CYCLE_COLLECTION_CLASS(IMEContentObserver) // Note that we don't need to add mFirstAddedContainer nor // mLastAddedContainer to cycle collection because they are non-null only // during short time and shouldn't be touched while they are non-null. NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IMEContentObserver) nsAutoScriptBlocker scriptBlocker; tmp->NotifyIMEOfBlur(); tmp->UnregisterObservers(); NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelection) NS_IMPL_CYCLE_COLLECTION_UNLINK(mRootElement) NS_IMPL_CYCLE_COLLECTION_UNLINK(mEditableNode) NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocShell) NS_IMPL_CYCLE_COLLECTION_UNLINK(mEditorBase) NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentObserver) NS_IMPL_CYCLE_COLLECTION_UNLINK(mEndOfAddedTextCache.mContainerNode) NS_IMPL_CYCLE_COLLECTION_UNLINK(mEndOfAddedTextCache.mContent) NS_IMPL_CYCLE_COLLECTION_UNLINK(mStartOfRemovingTextRangeCache.mContainerNode) NS_IMPL_CYCLE_COLLECTION_UNLINK(mStartOfRemovingTextRangeCache.mContent) NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE tmp->mIMENotificationRequests = nullptr; tmp->mESM = nullptr; NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IMEContentObserver) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWidget) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFocusedWidget) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelection) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRootElement) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEditableNode) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocShell) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEditorBase) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentObserver) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEndOfAddedTextCache.mContainerNode) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEndOfAddedTextCache.mContent) NS_IMPL_CYCLE_COLLECTION_TRAVERSE( mStartOfRemovingTextRangeCache.mContainerNode) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStartOfRemovingTextRangeCache.mContent) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IMEContentObserver) NS_INTERFACE_MAP_ENTRY(nsIMutationObserver) NS_INTERFACE_MAP_ENTRY(nsIReflowObserver) NS_INTERFACE_MAP_ENTRY(nsIScrollObserver) NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIReflowObserver) NS_INTERFACE_MAP_END NS_IMPL_CYCLE_COLLECTING_ADDREF(IMEContentObserver) NS_IMPL_CYCLE_COLLECTING_RELEASE(IMEContentObserver) IMEContentObserver::IMEContentObserver() { #ifdef DEBUG // TODO: Make this test as GTest. mTextChangeData.Test(); #endif } void IMEContentObserver::Init(nsIWidget& aWidget, nsPresContext& aPresContext, Element* aElement, EditorBase& aEditorBase) { State state = GetState(); if (NS_WARN_IF(state == eState_Observing)) { return; // Nothing to do. } bool firstInitialization = state != eState_StoppedObserving; if (!firstInitialization) { // If this is now trying to initialize with new contents, all observers // should be registered again for simpler implementation. UnregisterObservers(); Clear(); } mESM = aPresContext.EventStateManager(); mESM->OnStartToObserveContent(this); mWidget = &aWidget; mIMENotificationRequests = &mWidget->IMENotificationRequestsRef(); if (!InitWithEditor(aPresContext, aElement, aEditorBase)) { MOZ_LOG(sIMECOLog, LogLevel::Error, ("0x%p Init() FAILED, due to InitWithEditor() " "failure", this)); Clear(); return; } if (firstInitialization) { // Now, try to send NOTIFY_IME_OF_FOCUS to IME via the widget. MaybeNotifyIMEOfFocusSet(); // When this is called first time, IME has not received NOTIFY_IME_OF_FOCUS // yet since NOTIFY_IME_OF_FOCUS will be sent to widget asynchronously. // So, we need to do nothing here. After NOTIFY_IME_OF_FOCUS has been // sent, OnIMEReceivedFocus() will be called and content, selection and/or // position changes will be observed return; } // When this is called after editor reframing (i.e., the root editable node // is also recreated), IME has usually received NOTIFY_IME_OF_FOCUS. In this // case, we need to restart to observe content, selection and/or position // changes in new root editable node. ObserveEditableNode(); if (!NeedsToNotifyIMEOfSomething()) { return; } // Some change events may wait to notify IME because this was being // initialized. It is the time to flush them. FlushMergeableNotifications(); } void IMEContentObserver::OnIMEReceivedFocus() { // While Init() notifies IME of focus, pending layout may be flushed // because the notification may cause querying content. Then, recursive // call of Init() with the latest content may occur. In such case, we // shouldn't keep first initialization which notified IME of focus. if (GetState() != eState_Initializing) { MOZ_LOG(sIMECOLog, LogLevel::Warning, ("0x%p OnIMEReceivedFocus(), " "but the state is not \"initializing\", so does nothing", this)); return; } // NOTIFY_IME_OF_FOCUS might cause recreating IMEContentObserver // instance via IMEStateManager::UpdateIMEState(). So, this // instance might already have been destroyed, check it. if (!mRootElement) { MOZ_LOG(sIMECOLog, LogLevel::Warning, ("0x%p OnIMEReceivedFocus(), " "but mRootElement has already been cleared, so does nothing", this)); return; } // Start to observe which is needed by IME when IME actually has focus. ObserveEditableNode(); if (!NeedsToNotifyIMEOfSomething()) { return; } // Some change events may wait to notify IME because this was being // initialized. It is the time to flush them. FlushMergeableNotifications(); } bool IMEContentObserver::InitWithEditor(nsPresContext& aPresContext, Element* aElement, EditorBase& aEditorBase) { // mEditableNode is one of // - Anonymous
in or