/* -*- 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/StaticPtr.h" #include "mozilla/dom/TabParent.h" #include "nsIWidget.h" class nsIContent; class nsIDOMMouseEvent; class nsINode; class nsPresContext; class nsISelection; namespace mozilla { class EditorBase; class EventDispatchingCallback; class IMEContentObserver; class TextCompositionArray; class TextComposition; /** * 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 { typedef dom::TabParent TabParent; typedef widget::IMEMessage IMEMessage; typedef widget::IMENotification IMENotification; typedef widget::IMEState IMEState; typedef widget::InputContext InputContext; typedef widget::InputContextAction InputContextAction; public: static void Init(); static void Shutdown(); /** * GetActiveTabParent() returns a pointer to a TabParent instance which is * managed by the focused content (sContent). If the focused content isn't * managing another process, this returns nullptr. */ static TabParent* GetActiveTabParent() { // If menu has pseudo focus, we should ignore active child process. if (sInstalledMenuKeyboardListener) { return nullptr; } return sActiveTabParent.get(); } /** * OnTabParentDestroying() is called when aTabParent is being destroyed. */ static void OnTabParentDestroying(TabParent* aTabParent); /** * Called when aWidget is being deleted. */ static void WidgetDestroyed(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; } /** * SetIMEContextForChildProcess() is called when aTabParent receives * SetInputContext() from the remote process. */ static void SetInputContextForChildProcess(TabParent* aTabParent, 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); static nsresult OnDestroyPresContext(nsPresContext* aPresContext); static nsresult OnRemoveContent(nsPresContext* aPresContext, nsIContent* aContent); /** * 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. */ static nsresult OnChangeFocus(nsPresContext* aPresContext, nsIContent* aContent, InputContextAction::Cause aCause); 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 GetFocusSelectionAndRoot(nsISelection** aSel, nsIContent** aRoot); // 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. static void UpdateIMEState(const IMEState &aNewIMEState, nsIContent* aContent, EditorBase& aEditorBase); // 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. static bool OnMouseButtonEventInEditor(nsPresContext* aPresContext, nsIContent* aContent, nsIDOMMouseEvent* aMouseEvent); // This method is called when user clicked in an editor. // aContent must be: // If the editor is for <input> or <textarea>, the element. // If the editor is for contenteditable, the active editinghost. // If the editor is for designMode, nullptr. static void OnClickInEditor(nsPresContext* aPresContext, nsIContent* aContent, nsIDOMMouseEvent* aMouseEvent); // This method is called when editor actually gets focus. // aContent must be: // If the editor is for <input> or <textarea>, the element. // If the editor is for contenteditable, the active editinghost. // If the editor is for designMode, nullptr. static void OnFocusInEditor(nsPresContext* aPresContext, nsIContent* aContent, nsIEditor* aEditor); // This method is called when the editor is initialized. static void OnEditorInitialized(nsIEditor* aEditor); // This method is called when the editor is (might be temporarily) being // destroyed. static void OnEditorDestroying(nsIEditor* aEditor); /** * All composition events must be dispatched via DispatchCompositionEvent() * for storing the composition target and ensuring a set of composition * events must be fired the stored target. If the stored composition event * target is destroying, this removes the stored composition automatically. */ static void DispatchCompositionEvent( nsINode* aEventTargetNode, nsPresContext* aPresContext, WidgetCompositionEvent* aCompositionEvent, nsEventStatus* aStatus, EventDispatchingCallback* aCallBack, bool aIsSynthesized = false); /** * All selection events must be handled via HandleSelectionEvent() * because they must be handled by same target as composition events when * there is a composition. */ static void HandleSelectionEvent(nsPresContext* aPresContext, nsIContent* aEventTargetContent, WidgetSelectionEvent* aSelectionEvent); /** * This is called when PresShell ignores a composition event due to not safe * to dispatch events. */ static void OnCompositionEventDiscarded( WidgetCompositionEvent* aCompositionEvent); /** * Get TextComposition from widget. */ static already_AddRefed<TextComposition> GetTextCompositionFor(nsIWidget* aWidget); /** * Returns TextComposition instance for the event. */ static already_AddRefed<TextComposition> GetTextCompositionFor(const WidgetCompositionEvent* aCompositionEvent); /** * Returns TextComposition instance for the pres context. * Be aware, even if another pres context which shares native IME context with * specified pres context has composition, this returns nullptr. */ static already_AddRefed<TextComposition> GetTextCompositionFor(nsPresContext* aPresContext); /** * Send a notification to IME. It depends on the IME or platform spec what * will occur (or not occur). */ static nsresult NotifyIME(const IMENotification& aNotification, nsIWidget* aWidget, bool aOriginIsRemote = false); static nsresult NotifyIME(IMEMessage aMessage, nsIWidget* aWidget, bool aOriginIsRemote = false); static nsresult NotifyIME(IMEMessage aMessage, nsPresContext* aPresContext, bool aOriginIsRemote = false); static nsINode* GetRootEditableNode(nsPresContext* aPresContext, nsIContent* aContent); /** * Returns active IMEContentObserver but may be nullptr if focused content * isn't editable or focus in a remote process. */ static IMEContentObserver* GetActiveContentObserver(); protected: static nsresult OnChangeFocusInternal(nsPresContext* aPresContext, nsIContent* aContent, InputContextAction aAction); static void SetIMEState(const IMEState &aState, nsPresContext* aPresContext, nsIContent* aContent, nsIWidget* aWidget, InputContextAction aAction); static void SetInputContext(nsIWidget* aWidget, const InputContext& aInputContext, const InputContextAction& aAction); static IMEState GetNewIMEState(nsPresContext* aPresContext, nsIContent* aContent); static void EnsureTextCompositionArray(); static void CreateIMEContentObserver(nsIEditor* aEditor); static void DestroyIMEContentObserver(); static bool IsEditable(nsINode* node); static bool IsIMEObserverNeeded(const IMEState& aState); static nsIContent* GetRootContent(nsPresContext* aPresContext); /** * CanHandleWith() returns false if aPresContext is nullptr or it's destroyed. */ static bool CanHandleWith(nsPresContext* aPresContext); // sContent and sPresContext are the focused content and PresContext. If a // document has focus but there is no focused element, sContent may be // nullptr. static StaticRefPtr<nsIContent> sContent; static StaticRefPtr<nsPresContext> sPresContext; // sWidget is cache for the root widget of sPresContext. Even afer // sPresContext has gone, we need to clean up some IME state on the widget // if the widget is available. static nsIWidget* sWidget; // sFocusedIMEWidget is, the widget which was sent to "focus" notification // from IMEContentObserver and not yet sent "blur" notification. // So, if this is not nullptr, the widget needs to receive "blur" // notification. static nsIWidget* sFocusedIMEWidget; // sActiveInputContextWidget is the last widget whose SetInputContext() is // called. This is important to reduce sync IPC cost with parent process. // If IMEStateManager set input context to different widget, PuppetWidget can // return cached input context safely. static nsIWidget* sActiveInputContextWidget; static StaticRefPtr<TabParent> sActiveTabParent; // sActiveIMEContentObserver points to the currently active // IMEContentObserver. This is null if there is no focused editor. static StaticRefPtr<IMEContentObserver> sActiveIMEContentObserver; // All active compositions in the process are stored by this array. // When you get an item of this array and use it, please be careful. // The instances in this array can be destroyed automatically if you do // something to cause committing or canceling the composition. static TextCompositionArray* sTextCompositions; static bool sInstalledMenuKeyboardListener; static bool sIsGettingNewIMEState; static bool sCheckForIMEUnawareWebApps; static bool sRemoteHasFocus; class MOZ_STACK_CLASS GettingNewIMEStateBlocker final { public: GettingNewIMEStateBlocker() : mOldValue(IMEStateManager::sIsGettingNewIMEState) { IMEStateManager::sIsGettingNewIMEState = true; } ~GettingNewIMEStateBlocker() { IMEStateManager::sIsGettingNewIMEState = mOldValue; } private: bool mOldValue; }; }; } // namespace mozilla #endif // mozilla_IMEStateManager_h_