/* -*- 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 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;
}
// 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;
}
/**
* 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);
/**
* 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.
// XXX Changing this to MOZ_CAN_RUN_SCRIPT requires too many callers to be
// marked too. Probably, we should initialize IMEContentObserver
// asynchronously.
MOZ_CAN_RUN_SCRIPT_BOUNDARY 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.
MOZ_CAN_RUN_SCRIPT 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 or