/* -*- 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/. */ #ifndef mozilla_IMEStateManager_h_ #define mozilla_IMEStateManager_h_ #include "mozilla/EventForwards.h" #include "mozilla/Maybe.h" #include "mozilla/StaticPtr.h" #include "mozilla/dom/BrowserParent.h" #include "nsIWidget.h" class nsIContent; class nsINode; class nsPresContext; namespace mozilla { class EditorBase; class EventDispatchingCallback; class IMEContentObserver; class PseudoFocusChangeRunnable; class TextCompositionArray; class TextComposition; namespace dom { class Element; class Selection; } // namespace dom /** * IMEStateManager manages InputContext (e.g., active editor type, IME enabled * state and IME open state) of nsIWidget instances, manages IMEContentObserver * and provides useful API for IME. */ class IMEStateManager { using BrowserParent = dom::BrowserParent; using IMEMessage = widget::IMEMessage; using IMENotification = widget::IMENotification; using IMEState = widget::IMEState; using InputContext = widget::InputContext; using InputContextAction = widget::InputContextAction; public: static void Init(); static void Shutdown(); /** * GetActiveBrowserParent() returns a pointer to a BrowserParent instance * which is managed by the focused content (sFocusedElement). If the focused * content isn't managing another process, this returns nullptr. */ static BrowserParent* GetActiveBrowserParent() { // If menu has pseudo focus, we should ignore active child process. if (sInstalledMenuKeyboardListener) { return nullptr; } // If we know focused browser parent, use it for making any events related // to composition go to same content process. if (sFocusedIMEBrowserParent) { return sFocusedIMEBrowserParent; } return BrowserParent::GetFocused(); } /** * DoesBrowserParentHaveIMEFocus() returns true when aBrowserParent has IME * focus, i.e., the BrowserParent sent "focus" notification but not yet sends * "blur". Note that this doesn't check if the remote processes are same * because if another BrowserParent has focus, committing composition causes * firing composition events in different BrowserParent. (Anyway, such case * shouldn't occur.) */ static bool DoesBrowserParentHaveIMEFocus( const BrowserParent* aBrowserParent) { MOZ_ASSERT(aBrowserParent); return sFocusedIMEBrowserParent == aBrowserParent; } /** * If CanSendNotificationToWidget() returns false (it should occur * only in a content process), we shouldn't notify the widget of * any focused editor changes since the content process was blurred. * Also, even if content process, widget has native text event dispatcher such * as Android, it still notify it. */ static bool CanSendNotificationToWidget() { #ifdef MOZ_WIDGET_ANDROID return true; #else return !sCleaningUpForStoppingIMEStateManagement; #endif } /** * Focus moved between browsers from aBlur to aFocus. (nullptr means the * chrome process.) */ static void OnFocusMovedBetweenBrowsers(BrowserParent* aBlur, BrowserParent* aFocus); /** * Called when aWidget is being deleted. */ static void WidgetDestroyed(nsIWidget* aWidget); /** * Called when a widget exists when the app is quitting */ static void WidgetOnQuit(nsIWidget* aWidget); /** * GetWidgetForActiveInputContext() returns a widget which IMEStateManager * is managing input context with. If a widget instance needs to cache * the last input context for nsIWidget::GetInputContext() or something, * it should check if its cache is valid with this method before using it * because if this method returns another instance, it means that * IMEStateManager may have already changed shared input context via the * widget. */ static nsIWidget* GetWidgetForActiveInputContext() { return sActiveInputContextWidget; } /** * Return a widget which is for handling text input. This should be valid * while an editable element has focus or an editable document has focus. */ static nsIWidget* GetWidgetForTextInputHandling() { return sTextInputHandlingWidget; } /** * SetIMEContextForChildProcess() is called when aBrowserParent receives * SetInputContext() from the remote process. */ static void SetInputContextForChildProcess(BrowserParent* aBrowserParent, const InputContext& aInputContext, const InputContextAction& aAction); /** * StopIMEStateManagement() is called when the process should stop managing * IME state. */ static void StopIMEStateManagement(); /** * MaybeStartOffsetUpdatedInChild() is called when composition start offset * is maybe updated in the child process. I.e., even if it's not updated, * this is called and never called if the composition is in this process. * @param aWidget The widget whose native IME context has the * composition. * @param aStartOffset New composition start offset with native * linebreaks. */ static void MaybeStartOffsetUpdatedInChild(nsIWidget* aWidget, uint32_t aStartOffset); MOZ_CAN_RUN_SCRIPT static nsresult OnDestroyPresContext( nsPresContext& aPresContext); MOZ_CAN_RUN_SCRIPT static nsresult OnRemoveContent( nsPresContext& aPresContext, dom::Element& aElement); /** * OnChangeFocus() should be called when focused content is changed or * IME enabled state is changed. If nobody has focus, set both aPresContext * and aContent nullptr. E.g., all windows are deactivated. Otherwise, * set focused element (even if it won't receive `focus`event) and * corresponding nsPresContext for it. Then, IMEStateManager can avoid * handling delayed notifications from the others with verifying the * focused element. */ MOZ_CAN_RUN_SCRIPT static nsresult OnChangeFocus( nsPresContext* aPresContext, dom::Element* aElement, InputContextAction::Cause aCause); /** * OnInstalledMenuKeyboardListener() is called when menu keyboard listener * is installed or uninstalled in the process. So, even if menu keyboard * listener was installed in chrome process, this won't be called in content * processes. * * @param aInstalling true if menu keyboard listener is installed. * Otherwise, i.e., menu keyboard listener is * uninstalled, false. */ MOZ_CAN_RUN_SCRIPT static void OnInstalledMenuKeyboardListener( bool aInstalling); // These two methods manage focus and selection/text observers. // They are separate from OnChangeFocus above because this offers finer // control compared to having the two methods incorporated into OnChangeFocus // Get the focused editor's selection and root static nsresult GetFocusSelectionAndRootElement(dom::Selection** aSel, dom::Element** aRootElement); // This method updates the current IME state. However, if the enabled state // isn't changed by the new state, this method does nothing. // Note that this method changes the IME state of the active element in the // widget. So, the caller must have focus. // XXX Changing this to MOZ_CAN_RUN_SCRIPT requires too many callers to be // marked too. Probably, we should initialize IMEContentObserver // asynchronously. enum class UpdateIMEStateOption { ForceUpdate, DontCommitComposition, }; using UpdateIMEStateOptions = EnumSet; MOZ_CAN_RUN_SCRIPT static void UpdateIMEState( const IMEState& aNewIMEState, dom::Element* aElement, EditorBase& aEditorBase, const UpdateIMEStateOptions& aOptions = {}); // This method is called when user operates mouse button in focused editor // and before the editor handles it. // Returns true if IME consumes the event. Otherwise, false. MOZ_CAN_RUN_SCRIPT static bool OnMouseButtonEventInEditor( nsPresContext& aPresContext, dom::Element* aElement, WidgetMouseEvent& aMouseEvent); // This method is called when user clicked in an editor. // aElement must be: // If the editor is for or