diff options
Diffstat (limited to 'dom/events/IMEStateManager.h')
-rw-r--r-- | dom/events/IMEStateManager.h | 328 |
1 files changed, 328 insertions, 0 deletions
diff --git a/dom/events/IMEStateManager.h b/dom/events/IMEStateManager.h new file mode 100644 index 000000000..7dfc48aa5 --- /dev/null +++ b/dom/events/IMEStateManager.h @@ -0,0 +1,328 @@ +/* -*- 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, + 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_ |