Bug 1852740: add tests for the `fetchpriority` attribute in Link headers. r=necko...
[gecko.git] / dom / events / IMEStateManager.h
blob2bf7775ca0bd2cc9853dd04696410eb5f7793e4f
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef mozilla_IMEStateManager_h_
8 #define mozilla_IMEStateManager_h_
10 #include "mozilla/EventForwards.h"
11 #include "mozilla/Maybe.h"
12 #include "mozilla/StaticPtr.h"
13 #include "mozilla/dom/BrowserParent.h"
14 #include "nsIWidget.h"
16 class nsIContent;
17 class nsINode;
18 class nsPresContext;
20 namespace mozilla {
22 class EditorBase;
23 class EventDispatchingCallback;
24 class IMEContentObserver;
25 class TextCompositionArray;
26 class TextComposition;
28 namespace dom {
29 class Element;
30 class Selection;
31 } // namespace dom
33 /**
34 * IMEStateManager manages InputContext (e.g., active editor type, IME enabled
35 * state and IME open state) of nsIWidget instances, manages IMEContentObserver
36 * and provides useful API for IME.
39 class IMEStateManager {
40 using BrowserParent = dom::BrowserParent;
41 using IMEMessage = widget::IMEMessage;
42 using IMENotification = widget::IMENotification;
43 using IMEState = widget::IMEState;
44 using InputContext = widget::InputContext;
45 using InputContextAction = widget::InputContextAction;
47 public:
48 static void Init();
49 static void Shutdown();
51 /**
52 * GetActiveBrowserParent() returns a pointer to a BrowserParent instance
53 * which is managed by the focused content (sFocusedElement). If the focused
54 * content isn't managing another process, this returns nullptr.
56 static BrowserParent* GetActiveBrowserParent() {
57 // If menu has pseudo focus, we should ignore active child process.
58 if (sInstalledMenuKeyboardListener) {
59 return nullptr;
61 // If we know focused browser parent, use it for making any events related
62 // to composition go to same content process.
63 if (sFocusedIMEBrowserParent) {
64 return sFocusedIMEBrowserParent;
66 return BrowserParent::GetFocused();
69 /**
70 * DoesBrowserParentHaveIMEFocus() returns true when aBrowserParent has IME
71 * focus, i.e., the BrowserParent sent "focus" notification but not yet sends
72 * "blur". Note that this doesn't check if the remote processes are same
73 * because if another BrowserParent has focus, committing composition causes
74 * firing composition events in different BrowserParent. (Anyway, such case
75 * shouldn't occur.)
77 static bool DoesBrowserParentHaveIMEFocus(
78 const BrowserParent* aBrowserParent) {
79 MOZ_ASSERT(aBrowserParent);
80 return sFocusedIMEBrowserParent == aBrowserParent;
83 /**
84 * If CanSendNotificationToWidget() returns false (it should occur
85 * only in a content process), we shouldn't notify the widget of
86 * any focused editor changes since the content process was blurred.
87 * Also, even if content process, widget has native text event dispatcher such
88 * as Android, it still notify it.
90 static bool CanSendNotificationToWidget() {
91 #ifdef MOZ_WIDGET_ANDROID
92 return true;
93 #else
94 return !sCleaningUpForStoppingIMEStateManagement;
95 #endif
98 /**
99 * Focus moved between browsers from aBlur to aFocus. (nullptr means the
100 * chrome process.)
102 static void OnFocusMovedBetweenBrowsers(BrowserParent* aBlur,
103 BrowserParent* aFocus);
106 * Called when aWidget is being deleted.
108 static void WidgetDestroyed(nsIWidget* aWidget);
111 * Called when a widget exists when the app is quitting
113 static void WidgetOnQuit(nsIWidget* aWidget);
116 * GetWidgetForActiveInputContext() returns a widget which IMEStateManager
117 * is managing input context with. If a widget instance needs to cache
118 * the last input context for nsIWidget::GetInputContext() or something,
119 * it should check if its cache is valid with this method before using it
120 * because if this method returns another instance, it means that
121 * IMEStateManager may have already changed shared input context via the
122 * widget.
124 static nsIWidget* GetWidgetForActiveInputContext() {
125 return sActiveInputContextWidget;
129 * SetIMEContextForChildProcess() is called when aBrowserParent receives
130 * SetInputContext() from the remote process.
132 static void SetInputContextForChildProcess(BrowserParent* aBrowserParent,
133 const InputContext& aInputContext,
134 const InputContextAction& aAction);
137 * StopIMEStateManagement() is called when the process should stop managing
138 * IME state.
140 static void StopIMEStateManagement();
143 * MaybeStartOffsetUpdatedInChild() is called when composition start offset
144 * is maybe updated in the child process. I.e., even if it's not updated,
145 * this is called and never called if the composition is in this process.
146 * @param aWidget The widget whose native IME context has the
147 * composition.
148 * @param aStartOffset New composition start offset with native
149 * linebreaks.
151 static void MaybeStartOffsetUpdatedInChild(nsIWidget* aWidget,
152 uint32_t aStartOffset);
154 MOZ_CAN_RUN_SCRIPT static nsresult OnDestroyPresContext(
155 nsPresContext& aPresContext);
156 MOZ_CAN_RUN_SCRIPT static nsresult OnRemoveContent(
157 nsPresContext& aPresContext, dom::Element& aElement);
159 * OnChangeFocus() should be called when focused content is changed or
160 * IME enabled state is changed. If nobody has focus, set both aPresContext
161 * and aContent nullptr. E.g., all windows are deactivated. Otherwise,
162 * set focused element (even if it won't receive `focus`event) and
163 * corresponding nsPresContext for it. Then, IMEStateManager can avoid
164 * handling delayed notifications from the others with verifying the
165 * focused element.
167 MOZ_CAN_RUN_SCRIPT static nsresult OnChangeFocus(
168 nsPresContext* aPresContext, dom::Element* aElement,
169 InputContextAction::Cause aCause);
172 * OnInstalledMenuKeyboardListener() is called when menu keyboard listener
173 * is installed or uninstalled in the process. So, even if menu keyboard
174 * listener was installed in chrome process, this won't be called in content
175 * processes.
177 * @param aInstalling true if menu keyboard listener is installed.
178 * Otherwise, i.e., menu keyboard listener is
179 * uninstalled, false.
181 MOZ_CAN_RUN_SCRIPT static void OnInstalledMenuKeyboardListener(
182 bool aInstalling);
184 // These two methods manage focus and selection/text observers.
185 // They are separate from OnChangeFocus above because this offers finer
186 // control compared to having the two methods incorporated into OnChangeFocus
188 // Get the focused editor's selection and root
189 static nsresult GetFocusSelectionAndRootElement(dom::Selection** aSel,
190 dom::Element** aRootElement);
191 // This method updates the current IME state. However, if the enabled state
192 // isn't changed by the new state, this method does nothing.
193 // Note that this method changes the IME state of the active element in the
194 // widget. So, the caller must have focus.
195 // XXX Changing this to MOZ_CAN_RUN_SCRIPT requires too many callers to be
196 // marked too. Probably, we should initialize IMEContentObserver
197 // asynchronously.
198 enum class UpdateIMEStateOption {
199 ForceUpdate,
200 DontCommitComposition,
202 using UpdateIMEStateOptions = EnumSet<UpdateIMEStateOption, uint32_t>;
203 MOZ_CAN_RUN_SCRIPT static void UpdateIMEState(
204 const IMEState& aNewIMEState, dom::Element* aElement,
205 EditorBase& aEditorBase, const UpdateIMEStateOptions& aOptions = {});
207 // This method is called when user operates mouse button in focused editor
208 // and before the editor handles it.
209 // Returns true if IME consumes the event. Otherwise, false.
210 MOZ_CAN_RUN_SCRIPT static bool OnMouseButtonEventInEditor(
211 nsPresContext& aPresContext, dom::Element* aElement,
212 WidgetMouseEvent& aMouseEvent);
214 // This method is called when user clicked in an editor.
215 // aElement must be:
216 // If the editor is for <input> or <textarea>, the element.
217 // If the editor is for contenteditable, the active editinghost.
218 // If the editor is for designMode, nullptr.
219 MOZ_CAN_RUN_SCRIPT static void OnClickInEditor(
220 nsPresContext& aPresContext, dom::Element* aElement,
221 const WidgetMouseEvent& aMouseEvent);
223 // This method is called when editor actually gets focus.
224 // aContent must be:
225 // If the editor is for <input> or <textarea>, the element.
226 // If the editor is for contenteditable, the active editinghost.
227 // If the editor is for designMode, nullptr.
228 static void OnFocusInEditor(nsPresContext& aPresContext,
229 dom::Element* aElement, EditorBase& aEditorBase);
231 // This method is called when the editor is initialized.
232 static void OnEditorInitialized(EditorBase& aEditorBase);
234 // This method is called when the editor is (might be temporarily) being
235 // destroyed.
236 static void OnEditorDestroying(EditorBase& aEditorBase);
238 // This method is called when focus is set to same content again.
239 MOZ_CAN_RUN_SCRIPT static void OnReFocus(nsPresContext& aPresContext,
240 dom::Element& aElement);
242 // This method is called when designMode is set to "off" or an editing host
243 // becomes not editable due to removing `contenteditable` attribute or setting
244 // it to "false".
245 MOZ_CAN_RUN_SCRIPT static void MaybeOnEditableStateDisabled(
246 nsPresContext& aPresContext, dom::Element* aElement);
249 * All composition events must be dispatched via DispatchCompositionEvent()
250 * for storing the composition target and ensuring a set of composition
251 * events must be fired the stored target. If the stored composition event
252 * target is destroying, this removes the stored composition automatically.
254 MOZ_CAN_RUN_SCRIPT static void DispatchCompositionEvent(
255 nsINode* aEventTargetNode, nsPresContext* aPresContext,
256 BrowserParent* aBrowserParent, WidgetCompositionEvent* aCompositionEvent,
257 nsEventStatus* aStatus, EventDispatchingCallback* aCallBack,
258 bool aIsSynthesized = false);
261 * All selection events must be handled via HandleSelectionEvent()
262 * because they must be handled by same target as composition events when
263 * there is a composition.
265 MOZ_CAN_RUN_SCRIPT
266 static void HandleSelectionEvent(nsPresContext* aPresContext,
267 nsIContent* aEventTargetContent,
268 WidgetSelectionEvent* aSelectionEvent);
271 * This is called when PresShell ignores a composition event due to not safe
272 * to dispatch events.
274 static void OnCompositionEventDiscarded(
275 WidgetCompositionEvent* aCompositionEvent);
278 * Get TextComposition from widget.
280 static TextComposition* GetTextCompositionFor(nsIWidget* aWidget);
283 * Returns TextComposition instance for the event.
285 static TextComposition* GetTextCompositionFor(
286 const WidgetCompositionEvent* aCompositionEvent);
289 * Returns TextComposition instance for the pres context.
290 * Be aware, even if another pres context which shares native IME context with
291 * specified pres context has composition, this returns nullptr.
293 static TextComposition* GetTextCompositionFor(nsPresContext* aPresContext);
296 * Send a notification to IME. It depends on the IME or platform spec what
297 * will occur (or not occur).
299 static nsresult NotifyIME(const IMENotification& aNotification,
300 nsIWidget* aWidget,
301 BrowserParent* aBrowserParent = nullptr);
302 static nsresult NotifyIME(IMEMessage aMessage, nsIWidget* aWidget,
303 BrowserParent* aBrowserParent = nullptr);
304 static nsresult NotifyIME(IMEMessage aMessage, nsPresContext* aPresContext,
305 BrowserParent* aBrowserParent = nullptr);
307 static nsINode* GetRootEditableNode(const nsPresContext& aPresContext,
308 const dom::Element* aElement);
311 * Returns active IMEContentObserver but may be nullptr if focused content
312 * isn't editable or focus in a remote process.
314 static IMEContentObserver* GetActiveContentObserver();
317 * Return focused element which was notified by a OnChangeFocus() call.
319 static dom::Element* GetFocusedElement();
321 protected:
322 MOZ_CAN_RUN_SCRIPT static nsresult OnChangeFocusInternal(
323 nsPresContext* aPresContext, dom::Element* aElement,
324 InputContextAction aAction);
325 MOZ_CAN_RUN_SCRIPT static void SetIMEState(const IMEState& aState,
326 const nsPresContext* aPresContext,
327 dom::Element* aElement,
328 nsIWidget& aWidget,
329 InputContextAction aAction,
330 InputContext::Origin aOrigin);
331 static void SetInputContext(nsIWidget& aWidget,
332 const InputContext& aInputContext,
333 const InputContextAction& aAction);
334 static IMEState GetNewIMEState(const nsPresContext& aPresContext,
335 dom::Element* aElement);
337 static void EnsureTextCompositionArray();
339 // XXX Changing this to MOZ_CAN_RUN_SCRIPT requires too many callers to be
340 // marked too. Probably, we should initialize IMEContentObserver
341 // asynchronously.
342 MOZ_CAN_RUN_SCRIPT_BOUNDARY static void CreateIMEContentObserver(
343 EditorBase& aEditorBase, dom::Element* aFocusedElement);
346 * Check whether the content matches or does not match with focus information
347 * which is previously notified via OnChangeFocus();
349 [[nodiscard]] static bool IsFocusedElement(
350 const nsPresContext& aPresContext, const dom::Element* aFocusedElement);
352 static void DestroyIMEContentObserver();
354 [[nodiscard]] static bool IsEditable(nsINode* node);
356 [[nodiscard]] static bool IsIMEObserverNeeded(const IMEState& aState);
358 [[nodiscard]] static nsIContent* GetRootContent(nsPresContext* aPresContext);
361 * CanHandleWith() returns false if it's destroyed.
363 [[nodiscard]] static bool CanHandleWith(const nsPresContext* aPresContext);
366 * ResetActiveChildInputContext() resets sActiveChildInputContext.
367 * So, HasActiveChildSetInputContext() will return false until a remote
368 * process gets focus and set input context.
370 static void ResetActiveChildInputContext();
373 * HasActiveChildSetInputContext() returns true if a remote tab has focus
374 * and it has already set input context. Otherwise, returns false.
376 static bool HasActiveChildSetInputContext();
378 // sFocusedElement and sFocusedPresContext are the focused content and
379 // PresContext. If a document has focus but there is no focused element,
380 // sFocusedElement may be nullptr.
381 static StaticRefPtr<dom::Element> sFocusedElement;
382 static StaticRefPtr<nsPresContext> sFocusedPresContext;
383 // sTextInputHandlingWidget is cache for the result of
384 // sFocusedPresContext->GetTextInputHandlingWidget(). Even after
385 // sFocusedPresContext has gone, we need to clean up some IME state on the
386 // widget if the widget is available.
387 // Note that this is cleared when the widget is being destroyed.
388 static nsIWidget* sTextInputHandlingWidget;
389 // sFocusedIMEBrowserParent is the tab parent, which send "focus" notification
390 // to sFocusedIMEWidget (and didn't yet sent "blur" notification).
391 // Note that this is cleared when the widget is being destroyed.
392 static nsIWidget* sFocusedIMEWidget;
393 static StaticRefPtr<BrowserParent> sFocusedIMEBrowserParent;
394 // sActiveInputContextWidget is the last widget whose SetInputContext() is
395 // called. This is important to reduce sync IPC cost with parent process.
396 // If IMEStateManager set input context to different widget, PuppetWidget can
397 // return cached input context safely.
398 // Note that this is cleared when the widget is being destroyed.
399 static nsIWidget* sActiveInputContextWidget;
400 // sActiveIMEContentObserver points to the currently active
401 // IMEContentObserver. This is null if there is no focused editor.
402 static StaticRefPtr<IMEContentObserver> sActiveIMEContentObserver;
404 // All active compositions in the process are stored by this array.
405 // When you get an item of this array and use it, please be careful.
406 // The instances in this array can be destroyed automatically if you do
407 // something to cause committing or canceling the composition.
408 static TextCompositionArray* sTextCompositions;
410 // Origin type of current process.
411 static InputContext::Origin sOrigin;
413 // sActiveChildInputContext is valid only when BrowserParent::GetFocused() is
414 // not nullptr. This stores last information of input context in the remote
415 // process of BrowserParent::GetFocused(). I.e., they are set when
416 // SetInputContextForChildProcess() is called. This is necessary for
417 // restoring IME state when menu keyboard listener is uninstalled.
418 static InputContext sActiveChildInputContext;
420 // sInstalledMenuKeyboardListener is true if menu keyboard listener is
421 // installed in the process.
422 static bool sInstalledMenuKeyboardListener;
424 static bool sIsGettingNewIMEState;
425 static bool sCheckForIMEUnawareWebApps;
427 // Set to true only if this is an instance in a content process and
428 // only while `IMEStateManager::StopIMEStateManagement()`.
429 static bool sCleaningUpForStoppingIMEStateManagement;
431 // Set to true when:
432 // - In the main process, a window belonging to this app is active in the
433 // desktop.
434 // - In a content process, the process has focus.
436 // This is updated by `OnChangeFocusInternal()` is called in the main
437 // process. Therefore, this indicates the active state which
438 // `IMEStateManager` notified the focus change, there is timelag from
439 // the `nsFocusManager`'s status update. This allows that all methods
440 // to handle something specially when they are called while the process
441 // is being activated or inactivated. E.g., `OnFocusMovedBetweenBrowsers()`
442 // is called twice before `OnChangeFocusInternal()` when the main process
443 // becomes active. In this case, it wants to wait a following call of
444 // `OnChangeFocusInternal()` to keep active composition. See also below.
445 static bool sIsActive;
447 // While the application is being activated, `OnFocusMovedBetweenBrowsers()`
448 // are called twice before `OnChangeFocusInternal()`. First time, aBlur is
449 // the last focused `BrowserParent` at deactivating and aFocus is always
450 // `nullptr`. Then, it'll be called again with actually focused
451 // `BrowserParent` when a content in a remote process has focus. If we need
452 // to keep active composition while all windows are deactivated, we shouldn't
453 // commit it at the first call since usually, the second call's aFocus
454 // and the first call's aBlur are same `BrowserParent`. For solving this
455 // issue, we need to merge the given `BrowserParent`s of multiple calls of
456 // `OnFocusMovedBetweenBrowsers()`. The following struct is the data for
457 // calling `OnFocusMovedBetweenBrowsers()` later from
458 // `OnChangeFocusInternal()`. Note that focus can be moved even while the
459 // main process is not active because JS can change focus. In such case,
460 // composition is committed at that time. Therefore, this is required only
461 // when the main process is activated and there is a composition in a remote
462 // process.
463 struct PendingFocusedBrowserSwitchingData final {
464 RefPtr<BrowserParent> mBrowserParentBlurred;
465 RefPtr<BrowserParent> mBrowserParentFocused;
467 PendingFocusedBrowserSwitchingData() = delete;
468 explicit PendingFocusedBrowserSwitchingData(BrowserParent* aBlur,
469 BrowserParent* aFocus)
470 : mBrowserParentBlurred(aBlur), mBrowserParentFocused(aFocus) {}
472 static Maybe<PendingFocusedBrowserSwitchingData>
473 sPendingFocusedBrowserSwitchingData;
475 class MOZ_STACK_CLASS GettingNewIMEStateBlocker final {
476 public:
477 GettingNewIMEStateBlocker()
478 : mOldValue(IMEStateManager::sIsGettingNewIMEState) {
479 IMEStateManager::sIsGettingNewIMEState = true;
481 ~GettingNewIMEStateBlocker() {
482 IMEStateManager::sIsGettingNewIMEState = mOldValue;
485 private:
486 bool mOldValue;
490 } // namespace mozilla
492 #endif // mozilla_IMEStateManager_h_