Line data Source code
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/. */
6 :
7 : #ifndef mozilla_IMEStateManager_h_
8 : #define mozilla_IMEStateManager_h_
9 :
10 : #include "mozilla/EventForwards.h"
11 : #include "mozilla/StaticPtr.h"
12 : #include "mozilla/dom/TabParent.h"
13 : #include "nsIWidget.h"
14 :
15 : class nsIContent;
16 : class nsINode;
17 : class nsPresContext;
18 : class nsISelection;
19 :
20 : namespace mozilla {
21 :
22 : class EditorBase;
23 : class EventDispatchingCallback;
24 : class IMEContentObserver;
25 : class TextCompositionArray;
26 : class TextComposition;
27 :
28 : /**
29 : * IMEStateManager manages InputContext (e.g., active editor type, IME enabled
30 : * state and IME open state) of nsIWidget instances, manages IMEContentObserver
31 : * and provides useful API for IME.
32 : */
33 :
34 : class IMEStateManager
35 : {
36 : typedef dom::TabParent TabParent;
37 : typedef widget::IMEMessage IMEMessage;
38 : typedef widget::IMENotification IMENotification;
39 : typedef widget::IMEState IMEState;
40 : typedef widget::InputContext InputContext;
41 : typedef widget::InputContextAction InputContextAction;
42 :
43 : public:
44 : static void Init();
45 : static void Shutdown();
46 :
47 : /**
48 : * GetActiveTabParent() returns a pointer to a TabParent instance which is
49 : * managed by the focused content (sContent). If the focused content isn't
50 : * managing another process, this returns nullptr.
51 : */
52 0 : static TabParent* GetActiveTabParent()
53 : {
54 : // If menu has pseudo focus, we should ignore active child process.
55 0 : if (sInstalledMenuKeyboardListener) {
56 0 : return nullptr;
57 : }
58 0 : return sActiveTabParent.get();
59 : }
60 :
61 : /**
62 : * DoesTabParentHaveIMEFocus() returns true when aTabParent has IME focus,
63 : * i.e., the TabParent sent "focus" notification but not yet sends "blur".
64 : * Note that this doesn't check if the remote processes are same because
65 : * if another TabParent has focus, committing composition causes firing
66 : * composition events in different TabParent. (Anyway, such case shouldn't
67 : * occur.)
68 : */
69 0 : static bool DoesTabParentHaveIMEFocus(const TabParent* aTabParent)
70 : {
71 0 : MOZ_ASSERT(aTabParent);
72 0 : return sFocusedIMETabParent == aTabParent;
73 : }
74 :
75 : /**
76 : * OnTabParentDestroying() is called when aTabParent is being destroyed.
77 : */
78 : static void OnTabParentDestroying(TabParent* aTabParent);
79 :
80 : /**
81 : * Called when aWidget is being deleted.
82 : */
83 : static void WidgetDestroyed(nsIWidget* aWidget);
84 :
85 : /**
86 : * GetWidgetForActiveInputContext() returns a widget which IMEStateManager
87 : * is managing input context with. If a widget instance needs to cache
88 : * the last input context for nsIWidget::GetInputContext() or something,
89 : * it should check if its cache is valid with this method before using it
90 : * because if this method returns another instance, it means that
91 : * IMEStateManager may have already changed shared input context via the
92 : * widget.
93 : */
94 1 : static nsIWidget* GetWidgetForActiveInputContext()
95 : {
96 1 : return sActiveInputContextWidget;
97 : }
98 :
99 : /**
100 : * SetIMEContextForChildProcess() is called when aTabParent receives
101 : * SetInputContext() from the remote process.
102 : */
103 : static void SetInputContextForChildProcess(TabParent* aTabParent,
104 : const InputContext& aInputContext,
105 : const InputContextAction& aAction);
106 :
107 : /**
108 : * StopIMEStateManagement() is called when the process should stop managing
109 : * IME state.
110 : */
111 : static void StopIMEStateManagement();
112 :
113 : /**
114 : * MaybeStartOffsetUpdatedInChild() is called when composition start offset
115 : * is maybe updated in the child process. I.e., even if it's not updated,
116 : * this is called and never called if the composition is in this process.
117 : * @param aWidget The widget whose native IME context has the
118 : * composition.
119 : * @param aStartOffset New composition start offset with native
120 : * linebreaks.
121 : */
122 : static void MaybeStartOffsetUpdatedInChild(nsIWidget* aWidget,
123 : uint32_t aStartOffset);
124 :
125 : static nsresult OnDestroyPresContext(nsPresContext* aPresContext);
126 : static nsresult OnRemoveContent(nsPresContext* aPresContext,
127 : nsIContent* aContent);
128 : /**
129 : * OnChangeFocus() should be called when focused content is changed or
130 : * IME enabled state is changed. If nobody has focus, set both aPresContext
131 : * and aContent nullptr. E.g., all windows are deactivated.
132 : */
133 : static nsresult OnChangeFocus(nsPresContext* aPresContext,
134 : nsIContent* aContent,
135 : InputContextAction::Cause aCause);
136 : static void OnInstalledMenuKeyboardListener(bool aInstalling);
137 :
138 : // These two methods manage focus and selection/text observers.
139 : // They are separate from OnChangeFocus above because this offers finer
140 : // control compared to having the two methods incorporated into OnChangeFocus
141 :
142 : // Get the focused editor's selection and root
143 : static nsresult GetFocusSelectionAndRoot(nsISelection** aSel,
144 : nsIContent** aRoot);
145 : // This method updates the current IME state. However, if the enabled state
146 : // isn't changed by the new state, this method does nothing.
147 : // Note that this method changes the IME state of the active element in the
148 : // widget. So, the caller must have focus.
149 : static void UpdateIMEState(const IMEState &aNewIMEState,
150 : nsIContent* aContent,
151 : EditorBase& aEditorBase);
152 :
153 : // This method is called when user operates mouse button in focused editor
154 : // and before the editor handles it.
155 : // Returns true if IME consumes the event. Otherwise, false.
156 : static bool OnMouseButtonEventInEditor(nsPresContext* aPresContext,
157 : nsIContent* aContent,
158 : WidgetMouseEvent* aMouseEvent);
159 :
160 : // This method is called when user clicked in an editor.
161 : // aContent must be:
162 : // If the editor is for <input> or <textarea>, the element.
163 : // If the editor is for contenteditable, the active editinghost.
164 : // If the editor is for designMode, nullptr.
165 : static void OnClickInEditor(nsPresContext* aPresContext,
166 : nsIContent* aContent,
167 : const WidgetMouseEvent* aMouseEvent);
168 :
169 : // This method is called when editor actually gets focus.
170 : // aContent must be:
171 : // If the editor is for <input> or <textarea>, the element.
172 : // If the editor is for contenteditable, the active editinghost.
173 : // If the editor is for designMode, nullptr.
174 : static void OnFocusInEditor(nsPresContext* aPresContext,
175 : nsIContent* aContent,
176 : EditorBase& aEditorBase);
177 :
178 : // This method is called when the editor is initialized.
179 : static void OnEditorInitialized(EditorBase& aEditorBase);
180 :
181 : // This method is called when the editor is (might be temporarily) being
182 : // destroyed.
183 : static void OnEditorDestroying(EditorBase& aEditorBase);
184 :
185 : /**
186 : * All composition events must be dispatched via DispatchCompositionEvent()
187 : * for storing the composition target and ensuring a set of composition
188 : * events must be fired the stored target. If the stored composition event
189 : * target is destroying, this removes the stored composition automatically.
190 : */
191 : static void DispatchCompositionEvent(
192 : nsINode* aEventTargetNode,
193 : nsPresContext* aPresContext,
194 : WidgetCompositionEvent* aCompositionEvent,
195 : nsEventStatus* aStatus,
196 : EventDispatchingCallback* aCallBack,
197 : bool aIsSynthesized = false);
198 :
199 : /**
200 : * All selection events must be handled via HandleSelectionEvent()
201 : * because they must be handled by same target as composition events when
202 : * there is a composition.
203 : */
204 : static void HandleSelectionEvent(nsPresContext* aPresContext,
205 : nsIContent* aEventTargetContent,
206 : WidgetSelectionEvent* aSelectionEvent);
207 :
208 : /**
209 : * This is called when PresShell ignores a composition event due to not safe
210 : * to dispatch events.
211 : */
212 : static void OnCompositionEventDiscarded(
213 : WidgetCompositionEvent* aCompositionEvent);
214 :
215 : /**
216 : * Get TextComposition from widget.
217 : */
218 : static already_AddRefed<TextComposition>
219 : GetTextCompositionFor(nsIWidget* aWidget);
220 :
221 : /**
222 : * Returns TextComposition instance for the event.
223 : */
224 : static already_AddRefed<TextComposition>
225 : GetTextCompositionFor(const WidgetCompositionEvent* aCompositionEvent);
226 :
227 : /**
228 : * Returns TextComposition instance for the pres context.
229 : * Be aware, even if another pres context which shares native IME context with
230 : * specified pres context has composition, this returns nullptr.
231 : */
232 : static already_AddRefed<TextComposition>
233 : GetTextCompositionFor(nsPresContext* aPresContext);
234 :
235 : /**
236 : * Send a notification to IME. It depends on the IME or platform spec what
237 : * will occur (or not occur).
238 : */
239 : static nsresult NotifyIME(const IMENotification& aNotification,
240 : nsIWidget* aWidget,
241 : TabParent* aTabParent = nullptr);
242 : static nsresult NotifyIME(IMEMessage aMessage,
243 : nsIWidget* aWidget,
244 : TabParent* aTabParent = nullptr);
245 : static nsresult NotifyIME(IMEMessage aMessage,
246 : nsPresContext* aPresContext,
247 : TabParent* aTabParent = nullptr);
248 :
249 : static nsINode* GetRootEditableNode(nsPresContext* aPresContext,
250 : nsIContent* aContent);
251 :
252 : /**
253 : * Returns active IMEContentObserver but may be nullptr if focused content
254 : * isn't editable or focus in a remote process.
255 : */
256 : static IMEContentObserver* GetActiveContentObserver();
257 :
258 : protected:
259 : static nsresult OnChangeFocusInternal(nsPresContext* aPresContext,
260 : nsIContent* aContent,
261 : InputContextAction aAction);
262 : static void SetIMEState(const IMEState &aState,
263 : nsIContent* aContent,
264 : nsIWidget* aWidget,
265 : InputContextAction aAction,
266 : InputContext::Origin aOrigin);
267 : static void SetInputContext(nsIWidget* aWidget,
268 : const InputContext& aInputContext,
269 : const InputContextAction& aAction);
270 : static IMEState GetNewIMEState(nsPresContext* aPresContext,
271 : nsIContent* aContent);
272 :
273 : static void EnsureTextCompositionArray();
274 : static void CreateIMEContentObserver(EditorBase* aEditorBase);
275 : static void DestroyIMEContentObserver();
276 :
277 : static bool IsEditable(nsINode* node);
278 :
279 : static bool IsIMEObserverNeeded(const IMEState& aState);
280 :
281 : static nsIContent* GetRootContent(nsPresContext* aPresContext);
282 :
283 : /**
284 : * CanHandleWith() returns false if aPresContext is nullptr or it's destroyed.
285 : */
286 : static bool CanHandleWith(nsPresContext* aPresContext);
287 :
288 : // sContent and sPresContext are the focused content and PresContext. If a
289 : // document has focus but there is no focused element, sContent may be
290 : // nullptr.
291 : static StaticRefPtr<nsIContent> sContent;
292 : static StaticRefPtr<nsPresContext> sPresContext;
293 : // sWidget is cache for the root widget of sPresContext. Even afer
294 : // sPresContext has gone, we need to clean up some IME state on the widget
295 : // if the widget is available.
296 : static nsIWidget* sWidget;
297 : // sFocusedIMETabParent is the tab parent, which send "focus" notification to
298 : // sFocusedIMEWidget (and didn't yet sent "blur" notification).
299 : static nsIWidget* sFocusedIMEWidget;
300 : static StaticRefPtr<TabParent> sFocusedIMETabParent;
301 : // sActiveInputContextWidget is the last widget whose SetInputContext() is
302 : // called. This is important to reduce sync IPC cost with parent process.
303 : // If IMEStateManager set input context to different widget, PuppetWidget can
304 : // return cached input context safely.
305 : static nsIWidget* sActiveInputContextWidget;
306 : static StaticRefPtr<TabParent> sActiveTabParent;
307 : // sActiveIMEContentObserver points to the currently active
308 : // IMEContentObserver. This is null if there is no focused editor.
309 : static StaticRefPtr<IMEContentObserver> sActiveIMEContentObserver;
310 :
311 : // All active compositions in the process are stored by this array.
312 : // When you get an item of this array and use it, please be careful.
313 : // The instances in this array can be destroyed automatically if you do
314 : // something to cause committing or canceling the composition.
315 : static TextCompositionArray* sTextCompositions;
316 :
317 : // Origin type of current process.
318 : static InputContext::Origin sOrigin;
319 :
320 : static bool sInstalledMenuKeyboardListener;
321 : static bool sIsGettingNewIMEState;
322 : static bool sCheckForIMEUnawareWebApps;
323 : static bool sInputModeSupported;
324 :
325 : class MOZ_STACK_CLASS GettingNewIMEStateBlocker final
326 : {
327 : public:
328 0 : GettingNewIMEStateBlocker()
329 0 : : mOldValue(IMEStateManager::sIsGettingNewIMEState)
330 : {
331 0 : IMEStateManager::sIsGettingNewIMEState = true;
332 0 : }
333 0 : ~GettingNewIMEStateBlocker()
334 0 : {
335 0 : IMEStateManager::sIsGettingNewIMEState = mOldValue;
336 0 : }
337 : private:
338 : bool mOldValue;
339 : };
340 : };
341 :
342 : } // namespace mozilla
343 :
344 : #endif // mozilla_IMEStateManager_h_
|