gecko-dev/dom/events/IMEStateManager.h

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

375 строки
15 KiB
C
Исходник Обычный вид История

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
2012-05-21 15:12:37 +04:00
/* 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/BrowserParent.h"
#include "nsIWidget.h"
class nsIContent;
class nsINode;
class nsPresContext;
namespace mozilla {
class EditorBase;
class EventDispatchingCallback;
class IMEContentObserver;
class TextCompositionArray;
class TextComposition;
namespace dom {
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 {
typedef dom::BrowserParent BrowserParent;
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();
/**
* GetActiveBrowserParent() returns a pointer to a BrowserParent instance
* which is managed by the focused content (sContent). 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;
}
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;
}
/**
* 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);
Bug 1251063 PuppetWidget should cache InputContext which is set with SetInputContext() and use it in GetInputContext() only when it is the widget which has active input context in the process r=smaug PuppetWidget::GetInputContext() needs to communicate with its parent process with synchronous IPC. This is very expensive for focus move. Currently, IMEStateManager uses nsIWidget::GetInputContext() only for checking the IME enabled state. Therefore, it's enough to cache input context when nsIWidget::SetInputContext() is called. Then, we can avoid to communicate with synchronous IPC with PuppetWidget::GetInputContext() in most cases. This patch makes IMEStateManager stores the last widget which sets input context. When PuppetWidget uses its input context cache, it should check if it is the last widget to set input context with IMEStateManager since an input context may be shared with other widgets and another one may have update the input context. I.e., PuppetWidget's input context cache may be already outdated after IMEStateManager sets input context with another widget. This patch gives up to support retrieving IME open state from child process. However, perhaps, this is not necessary for everybody including add-on developers because the only user of IME open state in child process is nsIDOMWindowUtils. So, add-ons can send IME open state from chrome process instead. If this decision is wrong, unfortunately, we should support it again in another bug. It's easy to support with creating another nsIWidget::GetInputContext() or adding additional argument to it. MozReview-Commit-ID: B2d2CCTsPKj --HG-- extra : rebase_source : 4117330ba7871753176da960063b612e96f11752
2016-05-28 05:27:56 +03:00
/**
* 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 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);
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);
Bug 1396725 - IMEStateManager doesn't need to manage whether menu keyboard listener is installed in different process r=smaug Currently, IMEStateManager::OnChangeFocusInternal() tries to sync the state whether menu keyboard listener is installed between itself and active remote process -- When menu keyboard listener is installed, it posts a message to _only_ active remote process. When menu keyboard listener is uninstalled, it posts a message to _only_ active remote process. So, it's not guaranteed that active remote process at installing and uninstalling may be different. If it's different, IMEStateManager in the old remote process believes that menu keyboard listener is still installed. This is what the cause of IME unavailable in a remote process. Current approach must be wrong. IMEStateManager should manage menu keyboard listener state only in the process which the listener is installed in. Then, when menu keyboard listener is uninstalled, IMEStateManager needs to restore the latest input context in the remote process without asking the remote process. Therefore, this patch does: * stops IMEStateManager::OnChangeFocusInternal() posting message when menu keyboard listener is installed and uninstalled. * removes the message sender and receiver from PBrowser. * cache the latest input context of active remote process in IMEStateManager::SetInputContextForChildProcess(). * make IMEStateManager::SetInputContextForChildProcess() not set input context when menu keyboard listener is installed in the process. * tries to restore latest input context in the remote process in IMEStateManager::OnChangeFocusInternal(). If there is no cached input context, it does nothing and waits next SetInputContextForChildProcess() call. * clears the cache when IMEStateManager::OnChangeFocusInternal() changes active remote process to different one or nullptr. So, this must improve performance at activating and inactivating memubar and opening and closing popup menu in the main process. MozReview-Commit-ID: EelKSPlaXdw --HG-- extra : rebase_source : db7334b3c0d3ce87868450ee3179692027975bd6
2017-09-07 05:46:08 +03:00
/**
* 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.
*/
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(dom::Selection** 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,
WidgetMouseEvent* 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,
const WidgetMouseEvent* 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,
EditorBase& aEditorBase);
// This method is called when the editor is initialized.
static void OnEditorInitialized(EditorBase& aEditorBase);
// This method is called when the editor is (might be temporarily) being
// destroyed.
static void OnEditorDestroying(EditorBase& aEditorBase);
/**
* 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,
BrowserParent* aBrowserParent, 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.
*/
MOZ_CAN_RUN_SCRIPT
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,
BrowserParent* aBrowserParent = nullptr);
static nsresult NotifyIME(IMEMessage aMessage, nsIWidget* aWidget,
BrowserParent* aBrowserParent = nullptr);
static nsresult NotifyIME(IMEMessage aMessage, nsPresContext* aPresContext,
BrowserParent* aBrowserParent = nullptr);
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,
InputContext::Origin aOrigin);
static void SetInputContext(nsIWidget* aWidget,
const InputContext& aInputContext,
const InputContextAction& aAction);
static IMEState GetNewIMEState(nsPresContext* aPresContext,
nsIContent* aContent);
static void EnsureTextCompositionArray();
static void CreateIMEContentObserver(EditorBase* aEditorBase);
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);
Bug 1396725 - IMEStateManager doesn't need to manage whether menu keyboard listener is installed in different process r=smaug Currently, IMEStateManager::OnChangeFocusInternal() tries to sync the state whether menu keyboard listener is installed between itself and active remote process -- When menu keyboard listener is installed, it posts a message to _only_ active remote process. When menu keyboard listener is uninstalled, it posts a message to _only_ active remote process. So, it's not guaranteed that active remote process at installing and uninstalling may be different. If it's different, IMEStateManager in the old remote process believes that menu keyboard listener is still installed. This is what the cause of IME unavailable in a remote process. Current approach must be wrong. IMEStateManager should manage menu keyboard listener state only in the process which the listener is installed in. Then, when menu keyboard listener is uninstalled, IMEStateManager needs to restore the latest input context in the remote process without asking the remote process. Therefore, this patch does: * stops IMEStateManager::OnChangeFocusInternal() posting message when menu keyboard listener is installed and uninstalled. * removes the message sender and receiver from PBrowser. * cache the latest input context of active remote process in IMEStateManager::SetInputContextForChildProcess(). * make IMEStateManager::SetInputContextForChildProcess() not set input context when menu keyboard listener is installed in the process. * tries to restore latest input context in the remote process in IMEStateManager::OnChangeFocusInternal(). If there is no cached input context, it does nothing and waits next SetInputContextForChildProcess() call. * clears the cache when IMEStateManager::OnChangeFocusInternal() changes active remote process to different one or nullptr. So, this must improve performance at activating and inactivating memubar and opening and closing popup menu in the main process. MozReview-Commit-ID: EelKSPlaXdw --HG-- extra : rebase_source : db7334b3c0d3ce87868450ee3179692027975bd6
2017-09-07 05:46:08 +03:00
/**
* ResetActiveChildInputContext() resets sActiveChildInputContext.
* So, HasActiveChildSetInputContext() will return false until a remote
* process gets focus and set input context.
*/
static void ResetActiveChildInputContext();
/**
* HasActiveChildSetInputContext() returns true if a remote tab has focus
* and it has already set input context. Otherwise, returns false.
*/
static bool HasActiveChildSetInputContext();
// 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;
// sFocusedIMEBrowserParent is the tab parent, which send "focus" notification
// to sFocusedIMEWidget (and didn't yet sent "blur" notification).
static nsIWidget* sFocusedIMEWidget;
static StaticRefPtr<BrowserParent> sFocusedIMEBrowserParent;
Bug 1251063 PuppetWidget should cache InputContext which is set with SetInputContext() and use it in GetInputContext() only when it is the widget which has active input context in the process r=smaug PuppetWidget::GetInputContext() needs to communicate with its parent process with synchronous IPC. This is very expensive for focus move. Currently, IMEStateManager uses nsIWidget::GetInputContext() only for checking the IME enabled state. Therefore, it's enough to cache input context when nsIWidget::SetInputContext() is called. Then, we can avoid to communicate with synchronous IPC with PuppetWidget::GetInputContext() in most cases. This patch makes IMEStateManager stores the last widget which sets input context. When PuppetWidget uses its input context cache, it should check if it is the last widget to set input context with IMEStateManager since an input context may be shared with other widgets and another one may have update the input context. I.e., PuppetWidget's input context cache may be already outdated after IMEStateManager sets input context with another widget. This patch gives up to support retrieving IME open state from child process. However, perhaps, this is not necessary for everybody including add-on developers because the only user of IME open state in child process is nsIDOMWindowUtils. So, add-ons can send IME open state from chrome process instead. If this decision is wrong, unfortunately, we should support it again in another bug. It's easy to support with creating another nsIWidget::GetInputContext() or adding additional argument to it. MozReview-Commit-ID: B2d2CCTsPKj --HG-- extra : rebase_source : 4117330ba7871753176da960063b612e96f11752
2016-05-28 05:27:56 +03:00
// 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.
Bug 1251063 PuppetWidget should cache InputContext which is set with SetInputContext() and use it in GetInputContext() only when it is the widget which has active input context in the process r=smaug PuppetWidget::GetInputContext() needs to communicate with its parent process with synchronous IPC. This is very expensive for focus move. Currently, IMEStateManager uses nsIWidget::GetInputContext() only for checking the IME enabled state. Therefore, it's enough to cache input context when nsIWidget::SetInputContext() is called. Then, we can avoid to communicate with synchronous IPC with PuppetWidget::GetInputContext() in most cases. This patch makes IMEStateManager stores the last widget which sets input context. When PuppetWidget uses its input context cache, it should check if it is the last widget to set input context with IMEStateManager since an input context may be shared with other widgets and another one may have update the input context. I.e., PuppetWidget's input context cache may be already outdated after IMEStateManager sets input context with another widget. This patch gives up to support retrieving IME open state from child process. However, perhaps, this is not necessary for everybody including add-on developers because the only user of IME open state in child process is nsIDOMWindowUtils. So, add-ons can send IME open state from chrome process instead. If this decision is wrong, unfortunately, we should support it again in another bug. It's easy to support with creating another nsIWidget::GetInputContext() or adding additional argument to it. MozReview-Commit-ID: B2d2CCTsPKj --HG-- extra : rebase_source : 4117330ba7871753176da960063b612e96f11752
2016-05-28 05:27:56 +03:00
static nsIWidget* sActiveInputContextWidget;
// 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;
// Origin type of current process.
static InputContext::Origin sOrigin;
// sActiveChildInputContext is valid only when BrowserParent::GetFocused() is
// not nullptr. This stores last information of input context in the remote
// process of BrowserParent::GetFocused(). I.e., they are set when
Bug 1396725 - IMEStateManager doesn't need to manage whether menu keyboard listener is installed in different process r=smaug Currently, IMEStateManager::OnChangeFocusInternal() tries to sync the state whether menu keyboard listener is installed between itself and active remote process -- When menu keyboard listener is installed, it posts a message to _only_ active remote process. When menu keyboard listener is uninstalled, it posts a message to _only_ active remote process. So, it's not guaranteed that active remote process at installing and uninstalling may be different. If it's different, IMEStateManager in the old remote process believes that menu keyboard listener is still installed. This is what the cause of IME unavailable in a remote process. Current approach must be wrong. IMEStateManager should manage menu keyboard listener state only in the process which the listener is installed in. Then, when menu keyboard listener is uninstalled, IMEStateManager needs to restore the latest input context in the remote process without asking the remote process. Therefore, this patch does: * stops IMEStateManager::OnChangeFocusInternal() posting message when menu keyboard listener is installed and uninstalled. * removes the message sender and receiver from PBrowser. * cache the latest input context of active remote process in IMEStateManager::SetInputContextForChildProcess(). * make IMEStateManager::SetInputContextForChildProcess() not set input context when menu keyboard listener is installed in the process. * tries to restore latest input context in the remote process in IMEStateManager::OnChangeFocusInternal(). If there is no cached input context, it does nothing and waits next SetInputContextForChildProcess() call. * clears the cache when IMEStateManager::OnChangeFocusInternal() changes active remote process to different one or nullptr. So, this must improve performance at activating and inactivating memubar and opening and closing popup menu in the main process. MozReview-Commit-ID: EelKSPlaXdw --HG-- extra : rebase_source : db7334b3c0d3ce87868450ee3179692027975bd6
2017-09-07 05:46:08 +03:00
// SetInputContextForChildProcess() is called. This is necessary for
// restoring IME state when menu keyboard listener is uninstalled.
static InputContext sActiveChildInputContext;
// sInstalledMenuKeyboardListener is true if menu keyboard listener is
// installed in the process.
static bool sInstalledMenuKeyboardListener;
static bool sIsGettingNewIMEState;
static bool sCheckForIMEUnawareWebApps;
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_