/* -*- 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 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::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();
}
/**
* DoesTabParentHaveIMEFocus() returns true when aTabParent has IME focus,
* i.e., the TabParent sent "focus" notification but not yet sends "blur".
* Note that this doesn't check if the remote processes are same because
* if another TabParent has focus, committing composition causes firing
* composition events in different TabParent. (Anyway, such case shouldn't
* occur.)
*/
static bool DoesTabParentHaveIMEFocus(const TabParent* aTabParent) {
MOZ_ASSERT(aTabParent);
return sFocusedIMETabParent == aTabParent;
}
/**
* 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);
/**
* 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 or