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 : #include "mozilla/Logging.h"
8 :
9 : #include "mozilla/IMEStateManager.h"
10 :
11 : #include "mozilla/Attributes.h"
12 : #include "mozilla/EditorBase.h"
13 : #include "mozilla/EventListenerManager.h"
14 : #include "mozilla/EventStates.h"
15 : #include "mozilla/MouseEvents.h"
16 : #include "mozilla/Preferences.h"
17 : #include "mozilla/Services.h"
18 : #include "mozilla/SizePrintfMacros.h"
19 : #include "mozilla/TextComposition.h"
20 : #include "mozilla/TextEvents.h"
21 : #include "mozilla/Unused.h"
22 : #include "mozilla/dom/HTMLFormElement.h"
23 : #include "mozilla/dom/TabParent.h"
24 :
25 : #include "HTMLInputElement.h"
26 : #include "IMEContentObserver.h"
27 :
28 : #include "nsCOMPtr.h"
29 : #include "nsContentUtils.h"
30 : #include "nsIContent.h"
31 : #include "nsIDocument.h"
32 : #include "nsIDOMMouseEvent.h"
33 : #include "nsIForm.h"
34 : #include "nsIFormControl.h"
35 : #include "nsINode.h"
36 : #include "nsIObserverService.h"
37 : #include "nsIPresShell.h"
38 : #include "nsISelection.h"
39 : #include "nsISupports.h"
40 : #include "nsPresContext.h"
41 :
42 : namespace mozilla {
43 :
44 : using namespace dom;
45 : using namespace widget;
46 :
47 : /**
48 : * When a method is called, log its arguments and/or related static variables
49 : * with LogLevel::Info. However, if it puts too many logs like
50 : * OnDestroyPresContext(), should long only when the method actually does
51 : * something. In this case, the log should start with "<method name>".
52 : *
53 : * When a method quits due to unexpected situation, log the reason with
54 : * LogLevel::Error. In this case, the log should start with
55 : * "<method name>(), FAILED". The indent makes the log look easier.
56 : *
57 : * When a method does something only in some situations and it may be important
58 : * for debug, log the information with LogLevel::Debug. In this case, the log
59 : * should start with " <method name>(),".
60 : */
61 : LazyLogModule sISMLog("IMEStateManager");
62 :
63 : static const char*
64 0 : GetBoolName(bool aBool)
65 : {
66 0 : return aBool ? "true" : "false";
67 : }
68 :
69 : static const char*
70 0 : GetActionCauseName(InputContextAction::Cause aCause)
71 : {
72 0 : switch (aCause) {
73 : case InputContextAction::CAUSE_UNKNOWN:
74 0 : return "CAUSE_UNKNOWN";
75 : case InputContextAction::CAUSE_UNKNOWN_CHROME:
76 0 : return "CAUSE_UNKNOWN_CHROME";
77 : case InputContextAction::CAUSE_KEY:
78 0 : return "CAUSE_KEY";
79 : case InputContextAction::CAUSE_MOUSE:
80 0 : return "CAUSE_MOUSE";
81 : case InputContextAction::CAUSE_TOUCH:
82 0 : return "CAUSE_TOUCH";
83 : default:
84 0 : return "illegal value";
85 : }
86 : }
87 :
88 : static const char*
89 0 : GetActionFocusChangeName(InputContextAction::FocusChange aFocusChange)
90 : {
91 0 : switch (aFocusChange) {
92 : case InputContextAction::FOCUS_NOT_CHANGED:
93 0 : return "FOCUS_NOT_CHANGED";
94 : case InputContextAction::GOT_FOCUS:
95 0 : return "GOT_FOCUS";
96 : case InputContextAction::LOST_FOCUS:
97 0 : return "LOST_FOCUS";
98 : case InputContextAction::MENU_GOT_PSEUDO_FOCUS:
99 0 : return "MENU_GOT_PSEUDO_FOCUS";
100 : case InputContextAction::MENU_LOST_PSEUDO_FOCUS:
101 0 : return "MENU_LOST_PSEUDO_FOCUS";
102 : default:
103 0 : return "illegal value";
104 : }
105 : }
106 :
107 : static const char*
108 0 : GetIMEStateEnabledName(IMEState::Enabled aEnabled)
109 : {
110 0 : switch (aEnabled) {
111 : case IMEState::DISABLED:
112 0 : return "DISABLED";
113 : case IMEState::ENABLED:
114 0 : return "ENABLED";
115 : case IMEState::PASSWORD:
116 0 : return "PASSWORD";
117 : case IMEState::PLUGIN:
118 0 : return "PLUGIN";
119 : default:
120 0 : return "illegal value";
121 : }
122 : }
123 :
124 : static const char*
125 0 : GetIMEStateSetOpenName(IMEState::Open aOpen)
126 : {
127 0 : switch (aOpen) {
128 : case IMEState::DONT_CHANGE_OPEN_STATE:
129 0 : return "DONT_CHANGE_OPEN_STATE";
130 : case IMEState::OPEN:
131 0 : return "OPEN";
132 : case IMEState::CLOSED:
133 0 : return "CLOSED";
134 : default:
135 0 : return "illegal value";
136 : }
137 : }
138 :
139 : static bool
140 0 : IsSameProcess(const TabParent* aTabParent1, const TabParent* aTabParent2)
141 : {
142 0 : if (aTabParent1 == aTabParent2) {
143 0 : return true;
144 : }
145 0 : if (!aTabParent1 != !aTabParent2) {
146 0 : return false;
147 : }
148 0 : return aTabParent1->Manager() == aTabParent2->Manager();
149 : }
150 :
151 3 : StaticRefPtr<nsIContent> IMEStateManager::sContent;
152 3 : StaticRefPtr<nsPresContext> IMEStateManager::sPresContext;
153 : nsIWidget* IMEStateManager::sWidget = nullptr;
154 : nsIWidget* IMEStateManager::sFocusedIMEWidget = nullptr;
155 3 : StaticRefPtr<TabParent> IMEStateManager::sFocusedIMETabParent;
156 : nsIWidget* IMEStateManager::sActiveInputContextWidget = nullptr;
157 3 : StaticRefPtr<TabParent> IMEStateManager::sActiveTabParent;
158 3 : StaticRefPtr<IMEContentObserver> IMEStateManager::sActiveIMEContentObserver;
159 : TextCompositionArray* IMEStateManager::sTextCompositions = nullptr;
160 : InputContext::Origin IMEStateManager::sOrigin = InputContext::ORIGIN_MAIN;
161 : bool IMEStateManager::sInstalledMenuKeyboardListener = false;
162 : bool IMEStateManager::sIsGettingNewIMEState = false;
163 : bool IMEStateManager::sCheckForIMEUnawareWebApps = false;
164 : bool IMEStateManager::sInputModeSupported = false;
165 :
166 : // static
167 : void
168 3 : IMEStateManager::Init()
169 : {
170 : Preferences::AddBoolVarCache(
171 : &sCheckForIMEUnawareWebApps,
172 : "intl.ime.hack.on_ime_unaware_apps.fire_key_events_for_composition",
173 3 : false);
174 :
175 : Preferences::AddBoolVarCache(
176 : &sInputModeSupported,
177 : "dom.forms.inputmode",
178 3 : false);
179 :
180 3 : sOrigin = XRE_IsParentProcess() ? InputContext::ORIGIN_MAIN :
181 : InputContext::ORIGIN_CONTENT;
182 3 : }
183 :
184 : // static
185 : void
186 0 : IMEStateManager::Shutdown()
187 : {
188 0 : MOZ_LOG(sISMLog, LogLevel::Info,
189 : ("Shutdown(), sTextCompositions=0x%p, sTextCompositions->Length()=%" PRIuSIZE,
190 : sTextCompositions, sTextCompositions ? sTextCompositions->Length() : 0));
191 :
192 0 : MOZ_ASSERT(!sTextCompositions || !sTextCompositions->Length());
193 0 : delete sTextCompositions;
194 0 : sTextCompositions = nullptr;
195 0 : }
196 :
197 : // static
198 : void
199 0 : IMEStateManager::OnTabParentDestroying(TabParent* aTabParent)
200 : {
201 0 : if (sActiveTabParent != aTabParent) {
202 0 : return;
203 : }
204 0 : MOZ_LOG(sISMLog, LogLevel::Info,
205 : ("OnTabParentDestroying(aTabParent=0x%p), "
206 : "The active TabParent is being destroyed", aTabParent));
207 :
208 : // The active remote process might have crashed.
209 0 : sActiveTabParent = nullptr;
210 :
211 : // TODO: Need to cancel composition without TextComposition and make
212 : // disable IME.
213 : }
214 :
215 : // static
216 : void
217 0 : IMEStateManager::WidgetDestroyed(nsIWidget* aWidget)
218 : {
219 0 : if (sWidget == aWidget) {
220 0 : sWidget = nullptr;
221 : }
222 0 : if (sFocusedIMEWidget == aWidget) {
223 0 : sFocusedIMEWidget = nullptr;
224 : }
225 0 : if (sActiveInputContextWidget == aWidget) {
226 0 : sActiveInputContextWidget = nullptr;
227 : }
228 0 : }
229 :
230 : // static
231 : void
232 0 : IMEStateManager::StopIMEStateManagement()
233 : {
234 0 : MOZ_LOG(sISMLog, LogLevel::Info,
235 : ("StopIMEStateManagement()"));
236 :
237 : // NOTE: Don't set input context from here since this has already lost
238 : // the rights to change input context.
239 :
240 0 : if (sTextCompositions && sPresContext) {
241 0 : NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, sPresContext, sActiveTabParent);
242 : }
243 0 : sActiveInputContextWidget = nullptr;
244 0 : sPresContext = nullptr;
245 0 : sContent = nullptr;
246 0 : sActiveTabParent = nullptr;
247 0 : DestroyIMEContentObserver();
248 0 : }
249 :
250 : // static
251 : void
252 0 : IMEStateManager::MaybeStartOffsetUpdatedInChild(nsIWidget* aWidget,
253 : uint32_t aStartOffset)
254 : {
255 0 : if (NS_WARN_IF(!sTextCompositions)) {
256 0 : MOZ_LOG(sISMLog, LogLevel::Warning,
257 : ("MaybeStartOffsetUpdatedInChild(aWidget=0x%p, aStartOffset=%u), "
258 : "called when there is no composition", aWidget, aStartOffset));
259 0 : return;
260 : }
261 :
262 0 : RefPtr<TextComposition> composition = GetTextCompositionFor(aWidget);
263 0 : if (NS_WARN_IF(!composition)) {
264 0 : MOZ_LOG(sISMLog, LogLevel::Warning,
265 : ("MaybeStartOffsetUpdatedInChild(aWidget=0x%p, aStartOffset=%u), "
266 : "called when there is no composition", aWidget, aStartOffset));
267 0 : return;
268 : }
269 :
270 0 : if (composition->NativeOffsetOfStartComposition() == aStartOffset) {
271 0 : return;
272 : }
273 :
274 0 : MOZ_LOG(sISMLog, LogLevel::Info,
275 : ("MaybeStartOffsetUpdatedInChild(aWidget=0x%p, aStartOffset=%u), "
276 : "old offset=%u",
277 : aWidget, aStartOffset, composition->NativeOffsetOfStartComposition()));
278 0 : composition->OnStartOffsetUpdatedInChild(aStartOffset);
279 : }
280 :
281 : // static
282 : nsresult
283 4 : IMEStateManager::OnDestroyPresContext(nsPresContext* aPresContext)
284 : {
285 4 : NS_ENSURE_ARG_POINTER(aPresContext);
286 :
287 : // First, if there is a composition in the aPresContext, clean up it.
288 4 : if (sTextCompositions) {
289 : TextCompositionArray::index_type i =
290 0 : sTextCompositions->IndexOf(aPresContext);
291 0 : if (i != TextCompositionArray::NoIndex) {
292 0 : MOZ_LOG(sISMLog, LogLevel::Debug,
293 : (" OnDestroyPresContext(), "
294 : "removing TextComposition instance from the array (index=%" PRIuSIZE ")", i));
295 : // there should be only one composition per presContext object.
296 0 : sTextCompositions->ElementAt(i)->Destroy();
297 0 : sTextCompositions->RemoveElementAt(i);
298 0 : if (sTextCompositions->IndexOf(aPresContext) !=
299 : TextCompositionArray::NoIndex) {
300 0 : MOZ_LOG(sISMLog, LogLevel::Error,
301 : (" OnDestroyPresContext(), FAILED to remove "
302 : "TextComposition instance from the array"));
303 0 : MOZ_CRASH("Failed to remove TextComposition instance from the array");
304 : }
305 : }
306 : }
307 :
308 4 : if (aPresContext != sPresContext) {
309 3 : return NS_OK;
310 : }
311 :
312 1 : MOZ_LOG(sISMLog, LogLevel::Info,
313 : ("OnDestroyPresContext(aPresContext=0x%p), "
314 : "sPresContext=0x%p, sContent=0x%p, sTextCompositions=0x%p",
315 : aPresContext, sPresContext.get(), sContent.get(), sTextCompositions));
316 :
317 1 : DestroyIMEContentObserver();
318 :
319 1 : if (sWidget) {
320 1 : IMEState newState = GetNewIMEState(sPresContext, nullptr);
321 : InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
322 1 : InputContextAction::LOST_FOCUS);
323 : InputContext::Origin origin =
324 1 : sActiveTabParent ? InputContext::ORIGIN_CONTENT : sOrigin;
325 1 : SetIMEState(newState, nullptr, sWidget, action, origin);
326 : }
327 1 : sWidget = nullptr;
328 1 : sContent = nullptr;
329 1 : sPresContext = nullptr;
330 1 : sActiveTabParent = nullptr;
331 1 : return NS_OK;
332 : }
333 :
334 : // static
335 : nsresult
336 11 : IMEStateManager::OnRemoveContent(nsPresContext* aPresContext,
337 : nsIContent* aContent)
338 : {
339 11 : NS_ENSURE_ARG_POINTER(aPresContext);
340 :
341 : // First, if there is a composition in the aContent, clean up it.
342 11 : if (sTextCompositions) {
343 : RefPtr<TextComposition> compositionInContent =
344 0 : sTextCompositions->GetCompositionInContent(aPresContext, aContent);
345 :
346 0 : if (compositionInContent) {
347 0 : MOZ_LOG(sISMLog, LogLevel::Debug,
348 : (" OnRemoveContent(), "
349 : "composition is in the content"));
350 :
351 : // Try resetting the native IME state. Be aware, typically, this method
352 : // is called during the content being removed. Then, the native
353 : // composition events which are caused by following APIs are ignored due
354 : // to unsafe to run script (in PresShell::HandleEvent()).
355 : nsresult rv =
356 0 : compositionInContent->NotifyIME(REQUEST_TO_CANCEL_COMPOSITION);
357 0 : if (NS_FAILED(rv)) {
358 0 : compositionInContent->NotifyIME(REQUEST_TO_COMMIT_COMPOSITION);
359 : }
360 : }
361 : }
362 :
363 13 : if (!sPresContext || !sContent ||
364 2 : !nsContentUtils::ContentIsDescendantOf(sContent, aContent)) {
365 11 : return NS_OK;
366 : }
367 :
368 0 : MOZ_LOG(sISMLog, LogLevel::Info,
369 : ("OnRemoveContent(aPresContext=0x%p, aContent=0x%p), "
370 : "sPresContext=0x%p, sContent=0x%p, sTextCompositions=0x%p",
371 : aPresContext, aContent, sPresContext.get(), sContent.get(), sTextCompositions));
372 :
373 0 : DestroyIMEContentObserver();
374 :
375 : // Current IME transaction should commit
376 0 : if (sWidget) {
377 0 : IMEState newState = GetNewIMEState(sPresContext, nullptr);
378 : InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
379 0 : InputContextAction::LOST_FOCUS);
380 : InputContext::Origin origin =
381 0 : sActiveTabParent ? InputContext::ORIGIN_CONTENT : sOrigin;
382 0 : SetIMEState(newState, nullptr, sWidget, action, origin);
383 : }
384 :
385 0 : sWidget = nullptr;
386 0 : sContent = nullptr;
387 0 : sPresContext = nullptr;
388 0 : sActiveTabParent = nullptr;
389 :
390 0 : return NS_OK;
391 : }
392 :
393 : // static
394 : bool
395 11 : IMEStateManager::CanHandleWith(nsPresContext* aPresContext)
396 : {
397 11 : return aPresContext &&
398 22 : aPresContext->GetPresShell() &&
399 22 : !aPresContext->PresShell()->IsDestroying();
400 : }
401 :
402 : // static
403 : nsresult
404 4 : IMEStateManager::OnChangeFocus(nsPresContext* aPresContext,
405 : nsIContent* aContent,
406 : InputContextAction::Cause aCause)
407 : {
408 4 : MOZ_LOG(sISMLog, LogLevel::Info,
409 : ("OnChangeFocus(aPresContext=0x%p, aContent=0x%p, aCause=%s)",
410 : aPresContext, aContent, GetActionCauseName(aCause)));
411 :
412 4 : InputContextAction action(aCause);
413 4 : return OnChangeFocusInternal(aPresContext, aContent, action);
414 : }
415 :
416 : // static
417 : nsresult
418 4 : IMEStateManager::OnChangeFocusInternal(nsPresContext* aPresContext,
419 : nsIContent* aContent,
420 : InputContextAction aAction)
421 : {
422 8 : RefPtr<TabParent> newTabParent = TabParent::GetFrom(aContent);
423 :
424 4 : MOZ_LOG(sISMLog, LogLevel::Info,
425 : ("OnChangeFocusInternal(aPresContext=0x%p (available: %s), "
426 : "aContent=0x%p (TabParent=0x%p), aAction={ mCause=%s, mFocusChange=%s }), "
427 : "sPresContext=0x%p (available: %s), sContent=0x%p, "
428 : "sWidget=0x%p (available: %s), sActiveTabParent=0x%p, "
429 : "sActiveIMEContentObserver=0x%p, sInstalledMenuKeyboardListener=%s",
430 : aPresContext, GetBoolName(CanHandleWith(aPresContext)), aContent,
431 : newTabParent.get(), GetActionCauseName(aAction.mCause),
432 : GetActionFocusChangeName(aAction.mFocusChange),
433 : sPresContext.get(), GetBoolName(CanHandleWith(sPresContext)),
434 : sContent.get(), sWidget, GetBoolName(sWidget && !sWidget->Destroyed()),
435 : sActiveTabParent.get(), sActiveIMEContentObserver.get(),
436 : GetBoolName(sInstalledMenuKeyboardListener)));
437 :
438 : // If new aPresShell has been destroyed, this should handle the focus change
439 : // as nobody is getting focus.
440 4 : if (NS_WARN_IF(aPresContext && !CanHandleWith(aPresContext))) {
441 0 : MOZ_LOG(sISMLog, LogLevel::Warning,
442 : (" OnChangeFocusInternal(), called with destroyed PresShell, "
443 : "handling this call as nobody getting focus"));
444 0 : aPresContext = nullptr;
445 0 : aContent = nullptr;
446 : }
447 :
448 8 : nsCOMPtr<nsIWidget> oldWidget = sWidget;
449 : nsCOMPtr<nsIWidget> newWidget =
450 8 : aPresContext ? aPresContext->GetRootWidget() : nullptr;
451 : bool focusActuallyChanging =
452 8 : (sContent != aContent || sPresContext != aPresContext ||
453 6 : oldWidget != newWidget || sActiveTabParent != newTabParent);
454 :
455 4 : if (oldWidget && focusActuallyChanging) {
456 : // If we're deactivating, we shouldn't commit composition forcibly because
457 : // the user may want to continue the composition.
458 0 : if (aPresContext) {
459 0 : NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, oldWidget, sFocusedIMETabParent);
460 : }
461 : }
462 :
463 4 : if (sActiveIMEContentObserver) {
464 : // If there is active IMEContentObserver, it means that focused content was
465 : // in this process. So, if a tab parent gets focus, it means that the
466 : // focused editor in this process is being blurred.
467 0 : if (newTabParent) {
468 0 : DestroyIMEContentObserver();
469 : }
470 : // If the process is being inactivated, then, IMEContentObserver should
471 : // stop observing the contents unless native IME requests to keep
472 : // composition even during deactivated.
473 0 : else if (!aPresContext) {
474 0 : if (!sActiveIMEContentObserver->KeepAliveDuringDeactive()) {
475 0 : DestroyIMEContentObserver();
476 : }
477 : }
478 : // Otherwise, i.e., new focused content is in this process, let's check
479 : // whether the new focused content is already being managed by the
480 : // active IME content observer.
481 0 : else if (!sActiveIMEContentObserver->IsManaging(aPresContext, aContent)) {
482 0 : DestroyIMEContentObserver();
483 : }
484 : } else {
485 : // If there is no active IMEContentObserver, it means that focused content
486 : // may be in another process.
487 :
488 : // If focus is moving from current focused remote process to different
489 : // process while the process has IME focus too, we need to notify IME of
490 : // blur here because it may be too late the blur notification to reach
491 : // this process especially when closing active window.
492 4 : if (sFocusedIMETabParent &&
493 4 : !IsSameProcess(sFocusedIMETabParent, newTabParent)) {
494 0 : MOZ_LOG(sISMLog, LogLevel::Info,
495 : (" OnChangeFocusInternal(), notifying IME of blur of previous focused "
496 : "remote process because it may be too late actual notification to "
497 : "reach this process"));
498 0 : NotifyIME(NOTIFY_IME_OF_BLUR, sFocusedIMEWidget, sFocusedIMETabParent);
499 : }
500 : }
501 :
502 4 : if (!aPresContext) {
503 0 : MOZ_LOG(sISMLog, LogLevel::Debug,
504 : (" OnChangeFocusInternal(), "
505 : "no nsPresContext is being activated"));
506 0 : return NS_OK;
507 : }
508 :
509 4 : if (sActiveTabParent && !IsSameProcess(sActiveTabParent, newTabParent)) {
510 0 : MOZ_LOG(sISMLog, LogLevel::Debug,
511 : (" OnChangeFocusInternal(), notifying previous "
512 : "focused child process of parent process or another child process "
513 : "getting focus"));
514 0 : Unused << sActiveTabParent->SendStopIMEStateManagement();
515 : }
516 :
517 4 : if (NS_WARN_IF(!newWidget)) {
518 0 : MOZ_LOG(sISMLog, LogLevel::Error,
519 : (" OnChangeFocusInternal(), FAILED due to "
520 : "no widget to manage its IME state"));
521 0 : return NS_OK;
522 : }
523 :
524 : // Update the cached widget since root view of the presContext may be
525 : // changed to different view.
526 4 : sWidget = newWidget;
527 :
528 : // If a child process has focus, we should disable IME state until the child
529 : // process actually gets focus because if user types keys before that they
530 : // are handled by IME.
531 : IMEState newState =
532 : newTabParent ? IMEState(IMEState::DISABLED) :
533 4 : GetNewIMEState(aPresContext, aContent);
534 4 : bool setIMEState = true;
535 :
536 4 : if (newTabParent) {
537 2 : if (aAction.mFocusChange == InputContextAction::MENU_GOT_PSEUDO_FOCUS ||
538 1 : aAction.mFocusChange == InputContextAction::MENU_LOST_PSEUDO_FOCUS) {
539 : // XXX When menu keyboard listener is being uninstalled, IME state needs
540 : // to be restored by the child process asynchronously. Therefore,
541 : // some key events which are fired immediately after closing menu
542 : // may not be handled by IME.
543 0 : Unused << newTabParent->
544 0 : SendMenuKeyboardListenerInstalled(sInstalledMenuKeyboardListener);
545 0 : setIMEState = sInstalledMenuKeyboardListener;
546 1 : } else if (focusActuallyChanging) {
547 2 : InputContext context = newWidget->GetInputContext();
548 1 : if (context.mIMEState.mEnabled == IMEState::DISABLED &&
549 0 : context.mOrigin == InputContext::ORIGIN_CONTENT) {
550 0 : setIMEState = false;
551 0 : MOZ_LOG(sISMLog, LogLevel::Debug,
552 : (" OnChangeFocusInternal(), doesn't set IME "
553 : "state because focused element (or document) is in a child process "
554 : "and the IME state is already disabled by a remote process"));
555 : } else {
556 1 : MOZ_LOG(sISMLog, LogLevel::Debug,
557 : (" OnChangeFocusInternal(), will disable IME "
558 : "until new focused element (or document) in the child process "
559 : "will get focus actually"));
560 : }
561 0 : } else if (newWidget->GetInputContext().mOrigin !=
562 : InputContext::ORIGIN_CONTENT) {
563 : // When focus is NOT changed actually, we shouldn't set IME state if
564 : // current input context was set by a remote process since that means
565 : // that the window is being activated and the child process may have
566 : // composition. Then, we shouldn't commit the composition with making
567 : // IME state disabled.
568 0 : setIMEState = false;
569 0 : MOZ_LOG(sISMLog, LogLevel::Debug,
570 : (" OnChangeFocusInternal(), doesn't set IME "
571 : "state because focused element (or document) is already in the child "
572 : "process"));
573 : }
574 : }
575 :
576 4 : if (setIMEState) {
577 4 : if (!focusActuallyChanging) {
578 : // actual focus isn't changing, but if IME enabled state is changing,
579 : // we should do it.
580 1 : InputContext context = newWidget->GetInputContext();
581 1 : if (context.mIMEState.mEnabled == newState.mEnabled) {
582 1 : MOZ_LOG(sISMLog, LogLevel::Debug,
583 : (" OnChangeFocusInternal(), "
584 : "neither focus nor IME state is changing"));
585 1 : return NS_OK;
586 : }
587 0 : aAction.mFocusChange = InputContextAction::FOCUS_NOT_CHANGED;
588 :
589 : // Even if focus isn't changing actually, we should commit current
590 : // composition here since the IME state is changing.
591 0 : if (sPresContext && oldWidget && !focusActuallyChanging) {
592 0 : NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, oldWidget,
593 0 : sFocusedIMETabParent);
594 : }
595 3 : } else if (aAction.mFocusChange == InputContextAction::FOCUS_NOT_CHANGED) {
596 : // If aContent isn't null or aContent is null but editable, somebody gets
597 : // focus.
598 3 : bool gotFocus = aContent || (newState.mEnabled == IMEState::ENABLED);
599 3 : aAction.mFocusChange =
600 3 : gotFocus ? InputContextAction::GOT_FOCUS :
601 : InputContextAction::LOST_FOCUS;
602 : }
603 :
604 : // Update IME state for new focus widget
605 3 : SetIMEState(newState, aContent, newWidget, aAction,
606 6 : newTabParent ? InputContext::ORIGIN_CONTENT : sOrigin);
607 : }
608 :
609 3 : sActiveTabParent = newTabParent;
610 3 : sPresContext = aPresContext;
611 3 : sContent = aContent;
612 :
613 : // Don't call CreateIMEContentObserver() here except when a plugin gets
614 : // focus because it will be called from the focus event handler of focused
615 : // editor.
616 3 : if (newState.mEnabled == IMEState::PLUGIN) {
617 0 : CreateIMEContentObserver(nullptr);
618 0 : if (sActiveIMEContentObserver) {
619 0 : MOZ_LOG(sISMLog, LogLevel::Debug,
620 : (" OnChangeFocusInternal(), an "
621 : "IMEContentObserver instance is created for plugin and trying to "
622 : "flush its pending notifications..."));
623 0 : sActiveIMEContentObserver->TryToFlushPendingNotifications();
624 : }
625 : }
626 :
627 3 : return NS_OK;
628 : }
629 :
630 : // static
631 : void
632 0 : IMEStateManager::OnInstalledMenuKeyboardListener(bool aInstalling)
633 : {
634 0 : MOZ_LOG(sISMLog, LogLevel::Info,
635 : ("OnInstalledMenuKeyboardListener(aInstalling=%s), "
636 : "sInstalledMenuKeyboardListener=%s",
637 : GetBoolName(aInstalling), GetBoolName(sInstalledMenuKeyboardListener)));
638 :
639 0 : sInstalledMenuKeyboardListener = aInstalling;
640 :
641 : InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
642 : aInstalling ? InputContextAction::MENU_GOT_PSEUDO_FOCUS :
643 0 : InputContextAction::MENU_LOST_PSEUDO_FOCUS);
644 0 : OnChangeFocusInternal(sPresContext, sContent, action);
645 0 : }
646 :
647 : // static
648 : bool
649 0 : IMEStateManager::OnMouseButtonEventInEditor(nsPresContext* aPresContext,
650 : nsIContent* aContent,
651 : WidgetMouseEvent* aMouseEvent)
652 : {
653 0 : MOZ_LOG(sISMLog, LogLevel::Info,
654 : ("OnMouseButtonEventInEditor(aPresContext=0x%p, "
655 : "aContent=0x%p, aMouseEvent=0x%p), sPresContext=0x%p, sContent=0x%p",
656 : aPresContext, aContent, aMouseEvent, sPresContext.get(), sContent.get()));
657 :
658 0 : if (NS_WARN_IF(!aMouseEvent)) {
659 0 : MOZ_LOG(sISMLog, LogLevel::Debug,
660 : (" OnMouseButtonEventInEditor(), aMouseEvent is nullptr"));
661 0 : return false;
662 : }
663 :
664 0 : if (sPresContext != aPresContext || sContent != aContent) {
665 0 : MOZ_LOG(sISMLog, LogLevel::Debug,
666 : (" OnMouseButtonEventInEditor(), "
667 : "the mouse event isn't fired on the editor managed by ISM"));
668 0 : return false;
669 : }
670 :
671 0 : if (!sActiveIMEContentObserver) {
672 0 : MOZ_LOG(sISMLog, LogLevel::Debug,
673 : (" OnMouseButtonEventInEditor(), "
674 : "there is no active IMEContentObserver"));
675 0 : return false;
676 : }
677 :
678 0 : if (!sActiveIMEContentObserver->IsManaging(aPresContext, aContent)) {
679 0 : MOZ_LOG(sISMLog, LogLevel::Debug,
680 : (" OnMouseButtonEventInEditor(), "
681 : "the active IMEContentObserver isn't managing the editor"));
682 0 : return false;
683 : }
684 :
685 : bool consumed =
686 0 : sActiveIMEContentObserver->OnMouseButtonEvent(aPresContext, aMouseEvent);
687 :
688 0 : if (MOZ_LOG_TEST(sISMLog, LogLevel::Info)) {
689 0 : nsAutoString eventType;
690 0 : MOZ_LOG(sISMLog, LogLevel::Info,
691 : (" OnMouseButtonEventInEditor(), "
692 : "mouse event (mMessage=%s, button=%d) is %s",
693 : ToChar(aMouseEvent->mMessage), aMouseEvent->button,
694 : consumed ? "consumed" : "not consumed"));
695 : }
696 :
697 0 : return consumed;
698 : }
699 :
700 : // static
701 : void
702 0 : IMEStateManager::OnClickInEditor(nsPresContext* aPresContext,
703 : nsIContent* aContent,
704 : const WidgetMouseEvent* aMouseEvent)
705 : {
706 0 : MOZ_LOG(sISMLog, LogLevel::Info,
707 : ("OnClickInEditor(aPresContext=0x%p, aContent=0x%p, aMouseEvent=0x%p), "
708 : "sPresContext=0x%p, sContent=0x%p, sWidget=0x%p (available: %s)",
709 : aPresContext, aContent, aMouseEvent, sPresContext.get(), sContent.get(),
710 : sWidget, GetBoolName(sWidget && !sWidget->Destroyed())));
711 :
712 0 : if (NS_WARN_IF(!aMouseEvent)) {
713 0 : return;
714 : }
715 :
716 0 : if (sPresContext != aPresContext || sContent != aContent ||
717 0 : NS_WARN_IF(!sPresContext) || NS_WARN_IF(!sWidget) ||
718 0 : NS_WARN_IF(sWidget->Destroyed())) {
719 0 : MOZ_LOG(sISMLog, LogLevel::Debug,
720 : (" OnClickInEditor(), "
721 : "the mouse event isn't fired on the editor managed by ISM"));
722 0 : return;
723 : }
724 :
725 0 : nsCOMPtr<nsIWidget> widget(sWidget);
726 :
727 0 : MOZ_ASSERT(!sPresContext->GetRootWidget() ||
728 : sPresContext->GetRootWidget() == widget);
729 :
730 0 : if (!aMouseEvent->IsTrusted()) {
731 0 : MOZ_LOG(sISMLog, LogLevel::Debug,
732 : (" OnClickInEditor(), "
733 : "the mouse event isn't a trusted event"));
734 0 : return; // ignore untrusted event.
735 : }
736 :
737 0 : if (aMouseEvent->button) {
738 0 : MOZ_LOG(sISMLog, LogLevel::Debug,
739 : (" OnClickInEditor(), "
740 : "the mouse event isn't a left mouse button event"));
741 0 : return; // not a left click event.
742 : }
743 :
744 0 : if (aMouseEvent->mClickCount != 1) {
745 0 : MOZ_LOG(sISMLog, LogLevel::Debug,
746 : (" OnClickInEditor(), "
747 : "the mouse event isn't a single click event"));
748 0 : return; // should notify only first click event.
749 : }
750 :
751 : InputContextAction::Cause cause =
752 0 : aMouseEvent->inputSource == nsIDOMMouseEvent::MOZ_SOURCE_TOUCH ?
753 0 : InputContextAction::CAUSE_TOUCH : InputContextAction::CAUSE_MOUSE;
754 :
755 0 : InputContextAction action(cause, InputContextAction::FOCUS_NOT_CHANGED);
756 0 : IMEState newState = GetNewIMEState(aPresContext, aContent);
757 0 : SetIMEState(newState, aContent, widget, action, sOrigin);
758 : }
759 :
760 : // static
761 : void
762 0 : IMEStateManager::OnFocusInEditor(nsPresContext* aPresContext,
763 : nsIContent* aContent,
764 : EditorBase& aEditorBase)
765 : {
766 0 : MOZ_LOG(sISMLog, LogLevel::Info,
767 : ("OnFocusInEditor(aPresContext=0x%p, aContent=0x%p, aEditorBase=0x%p), "
768 : "sPresContext=0x%p, sContent=0x%p, sActiveIMEContentObserver=0x%p",
769 : aPresContext, aContent, &aEditorBase, sPresContext.get(), sContent.get(),
770 : sActiveIMEContentObserver.get()));
771 :
772 0 : if (sPresContext != aPresContext || sContent != aContent) {
773 0 : MOZ_LOG(sISMLog, LogLevel::Debug,
774 : (" OnFocusInEditor(), "
775 : "an editor not managed by ISM gets focus"));
776 0 : return;
777 : }
778 :
779 : // If the IMEContentObserver instance isn't managing the editor actually,
780 : // we need to recreate the instance.
781 0 : if (sActiveIMEContentObserver) {
782 0 : if (sActiveIMEContentObserver->IsManaging(aPresContext, aContent)) {
783 0 : MOZ_LOG(sISMLog, LogLevel::Debug,
784 : (" OnFocusInEditor(), "
785 : "the editor is already being managed by sActiveIMEContentObserver"));
786 0 : return;
787 : }
788 0 : DestroyIMEContentObserver();
789 : }
790 :
791 0 : CreateIMEContentObserver(&aEditorBase);
792 :
793 : // Let's flush the focus notification now.
794 0 : if (sActiveIMEContentObserver) {
795 0 : MOZ_LOG(sISMLog, LogLevel::Debug,
796 : (" OnFocusInEditor(), new IMEContentObserver is "
797 : "created, trying to flush pending notifications..."));
798 0 : sActiveIMEContentObserver->TryToFlushPendingNotifications();
799 : }
800 : }
801 :
802 : // static
803 : void
804 2 : IMEStateManager::OnEditorInitialized(EditorBase& aEditorBase)
805 : {
806 2 : if (!sActiveIMEContentObserver ||
807 0 : !sActiveIMEContentObserver->WasInitializedWith(aEditorBase)) {
808 2 : return;
809 : }
810 :
811 0 : MOZ_LOG(sISMLog, LogLevel::Info,
812 : ("OnEditorInitialized(aEditorBase=0x%p)",
813 : &aEditorBase));
814 :
815 0 : sActiveIMEContentObserver->UnsuppressNotifyingIME();
816 : }
817 :
818 : // static
819 : void
820 1 : IMEStateManager::OnEditorDestroying(EditorBase& aEditorBase)
821 : {
822 1 : if (!sActiveIMEContentObserver ||
823 0 : !sActiveIMEContentObserver->WasInitializedWith(aEditorBase)) {
824 1 : return;
825 : }
826 :
827 0 : MOZ_LOG(sISMLog, LogLevel::Info,
828 : ("OnEditorDestroying(aEditorBase=0x%p)",
829 : &aEditorBase));
830 :
831 : // The IMEContentObserver shouldn't notify IME of anything until reframing
832 : // is finished.
833 0 : sActiveIMEContentObserver->SuppressNotifyingIME();
834 : }
835 :
836 : // static
837 : void
838 0 : IMEStateManager::UpdateIMEState(const IMEState& aNewIMEState,
839 : nsIContent* aContent,
840 : EditorBase& aEditorBase)
841 : {
842 0 : MOZ_LOG(sISMLog, LogLevel::Info,
843 : ("UpdateIMEState(aNewIMEState={ mEnabled=%s, "
844 : "mOpen=%s }, aContent=0x%p, aEditorBase=0x%p), "
845 : "sPresContext=0x%p, sContent=0x%p, sWidget=0x%p (available: %s), "
846 : "sActiveIMEContentObserver=0x%p, sIsGettingNewIMEState=%s",
847 : GetIMEStateEnabledName(aNewIMEState.mEnabled),
848 : GetIMEStateSetOpenName(aNewIMEState.mOpen), aContent, &aEditorBase,
849 : sPresContext.get(), sContent.get(),
850 : sWidget, GetBoolName(sWidget && !sWidget->Destroyed()),
851 : sActiveIMEContentObserver.get(),
852 : GetBoolName(sIsGettingNewIMEState)));
853 :
854 0 : if (sIsGettingNewIMEState) {
855 0 : MOZ_LOG(sISMLog, LogLevel::Debug,
856 : (" UpdateIMEState(), "
857 : "does nothing because of called while getting new IME state"));
858 0 : return;
859 : }
860 :
861 0 : nsCOMPtr<nsIPresShell> presShell = aEditorBase.GetPresShell();
862 0 : if (NS_WARN_IF(!presShell)) {
863 0 : MOZ_LOG(sISMLog, LogLevel::Error,
864 : (" UpdateIMEState(), FAILED due to "
865 : "editor doesn't have PresShell"));
866 0 : return;
867 : }
868 :
869 0 : nsPresContext* presContext = presShell->GetPresContext();
870 0 : if (NS_WARN_IF(!presContext)) {
871 0 : MOZ_LOG(sISMLog, LogLevel::Error,
872 : (" UpdateIMEState(), FAILED due to "
873 : "editor doesn't have PresContext"));
874 0 : return;
875 : }
876 :
877 : // IMEStateManager::UpdateIMEState() should be called after
878 : // IMEStateManager::OnChangeFocus() is called for setting focus to aContent
879 : // and aEditorBase. However, when aEditorBase is an HTMLEditor, this may be
880 : // called by nsIEditor::PostCreate() before IMEStateManager::OnChangeFocus().
881 : // Similarly, when aEditorBase is a TextEditor, this may be called by
882 : // nsIEditor::SetFlags(). In such cases, this method should do nothing
883 : // because input context should be updated when
884 : // IMEStateManager::OnChangeFocus() is called later.
885 0 : if (sPresContext != presContext) {
886 0 : MOZ_LOG(sISMLog, LogLevel::Warning,
887 : (" UpdateIMEState(), does nothing due to "
888 : "the editor hasn't managed by IMEStateManager yet"));
889 0 : return;
890 : }
891 :
892 : // If IMEStateManager doesn't manage any document, this cannot update IME
893 : // state of any widget.
894 0 : if (NS_WARN_IF(!sPresContext)) {
895 0 : MOZ_LOG(sISMLog, LogLevel::Error,
896 : (" UpdateIMEState(), FAILED due to "
897 : "no managing nsPresContext"));
898 0 : return;
899 : }
900 :
901 0 : if (NS_WARN_IF(!sWidget) || NS_WARN_IF(sWidget->Destroyed())) {
902 0 : MOZ_LOG(sISMLog, LogLevel::Error,
903 : (" UpdateIMEState(), FAILED due to "
904 : "the widget for the managing nsPresContext has gone"));
905 0 : return;
906 : }
907 :
908 0 : nsCOMPtr<nsIWidget> widget(sWidget);
909 :
910 0 : MOZ_ASSERT(!sPresContext->GetRootWidget() ||
911 : sPresContext->GetRootWidget() == widget);
912 :
913 : // Even if there is active IMEContentObserver, it may not be observing the
914 : // editor with current editable root content due to reframed. In such case,
915 : // We should try to reinitialize the IMEContentObserver.
916 0 : if (sActiveIMEContentObserver && IsIMEObserverNeeded(aNewIMEState)) {
917 0 : MOZ_LOG(sISMLog, LogLevel::Debug,
918 : (" UpdateIMEState(), try to reinitialize the "
919 : "active IMEContentObserver"));
920 0 : RefPtr<IMEContentObserver> contentObserver = sActiveIMEContentObserver;
921 0 : if (!contentObserver->MaybeReinitialize(widget, sPresContext,
922 : aContent, &aEditorBase)) {
923 0 : MOZ_LOG(sISMLog, LogLevel::Error,
924 : (" UpdateIMEState(), failed to reinitialize the "
925 : "active IMEContentObserver"));
926 : }
927 0 : if (NS_WARN_IF(widget->Destroyed())) {
928 0 : MOZ_LOG(sISMLog, LogLevel::Error,
929 : (" UpdateIMEState(), widget has gone during reinitializing the "
930 : "active IMEContentObserver"));
931 0 : return;
932 : }
933 : }
934 :
935 : // If there is no active IMEContentObserver or it isn't observing the
936 : // editor correctly, we should recreate it.
937 : bool createTextStateManager =
938 0 : (!sActiveIMEContentObserver ||
939 0 : !sActiveIMEContentObserver->IsManaging(sPresContext, aContent));
940 :
941 : bool updateIMEState =
942 0 : (widget->GetInputContext().mIMEState.mEnabled != aNewIMEState.mEnabled);
943 0 : if (NS_WARN_IF(widget->Destroyed())) {
944 0 : MOZ_LOG(sISMLog, LogLevel::Error,
945 : (" UpdateIMEState(), widget has gone during getting input context"));
946 0 : return;
947 : }
948 :
949 0 : if (updateIMEState) {
950 : // commit current composition before modifying IME state.
951 0 : NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, widget, sFocusedIMETabParent);
952 0 : if (NS_WARN_IF(widget->Destroyed())) {
953 0 : MOZ_LOG(sISMLog, LogLevel::Error,
954 : (" UpdateIMEState(), widget has gone during committing composition"));
955 0 : return;
956 : }
957 : }
958 :
959 0 : if (createTextStateManager) {
960 0 : DestroyIMEContentObserver();
961 : }
962 :
963 0 : if (updateIMEState) {
964 : InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
965 0 : InputContextAction::FOCUS_NOT_CHANGED);
966 0 : SetIMEState(aNewIMEState, aContent, widget, action, sOrigin);
967 0 : if (NS_WARN_IF(widget->Destroyed())) {
968 0 : MOZ_LOG(sISMLog, LogLevel::Error,
969 : (" UpdateIMEState(), widget has gone during setting input context"));
970 0 : return;
971 : }
972 : }
973 :
974 0 : if (createTextStateManager) {
975 : // XXX In this case, it might not be enough safe to notify IME of anything.
976 : // So, don't try to flush pending notifications of IMEContentObserver
977 : // here.
978 0 : CreateIMEContentObserver(&aEditorBase);
979 : }
980 : }
981 :
982 : // static
983 : IMEState
984 4 : IMEStateManager::GetNewIMEState(nsPresContext* aPresContext,
985 : nsIContent* aContent)
986 : {
987 4 : MOZ_LOG(sISMLog, LogLevel::Info,
988 : ("GetNewIMEState(aPresContext=0x%p, aContent=0x%p), "
989 : "sInstalledMenuKeyboardListener=%s",
990 : aPresContext, aContent, GetBoolName(sInstalledMenuKeyboardListener)));
991 :
992 4 : if (!CanHandleWith(aPresContext)) {
993 0 : MOZ_LOG(sISMLog, LogLevel::Debug,
994 : (" GetNewIMEState() returns DISABLED because "
995 : "the nsPresContext has been destroyed"));
996 0 : return IMEState(IMEState::DISABLED);
997 : }
998 :
999 : // On Printing or Print Preview, we don't need IME.
1000 8 : if (aPresContext->Type() == nsPresContext::eContext_PrintPreview ||
1001 4 : aPresContext->Type() == nsPresContext::eContext_Print) {
1002 0 : MOZ_LOG(sISMLog, LogLevel::Debug,
1003 : (" GetNewIMEState() returns DISABLED because "
1004 : "the nsPresContext is for print or print preview"));
1005 0 : return IMEState(IMEState::DISABLED);
1006 : }
1007 :
1008 4 : if (sInstalledMenuKeyboardListener) {
1009 0 : MOZ_LOG(sISMLog, LogLevel::Debug,
1010 : (" GetNewIMEState() returns DISABLED because "
1011 : "menu keyboard listener was installed"));
1012 0 : return IMEState(IMEState::DISABLED);
1013 : }
1014 :
1015 4 : if (!aContent) {
1016 : // Even if there are no focused content, the focused document might be
1017 : // editable, such case is design mode.
1018 4 : nsIDocument* doc = aPresContext->Document();
1019 4 : if (doc && doc->HasFlag(NODE_IS_EDITABLE)) {
1020 0 : MOZ_LOG(sISMLog, LogLevel::Debug,
1021 : (" GetNewIMEState() returns ENABLED because "
1022 : "design mode editor has focus"));
1023 0 : return IMEState(IMEState::ENABLED);
1024 : }
1025 4 : MOZ_LOG(sISMLog, LogLevel::Debug,
1026 : (" GetNewIMEState() returns DISABLED because "
1027 : "no content has focus"));
1028 4 : return IMEState(IMEState::DISABLED);
1029 : }
1030 :
1031 : // nsIContent::GetDesiredIMEState() may cause a call of UpdateIMEState()
1032 : // from EditorBase::PostCreate() because GetDesiredIMEState() needs to
1033 : // retrieve an editor instance for the element if it's editable element.
1034 : // For avoiding such nested IME state updates, we should set
1035 : // sIsGettingNewIMEState here and UpdateIMEState() should check it.
1036 0 : GettingNewIMEStateBlocker blocker;
1037 :
1038 0 : IMEState newIMEState = aContent->GetDesiredIMEState();
1039 0 : MOZ_LOG(sISMLog, LogLevel::Debug,
1040 : (" GetNewIMEState() returns { mEnabled=%s, "
1041 : "mOpen=%s }",
1042 : GetIMEStateEnabledName(newIMEState.mEnabled),
1043 : GetIMEStateSetOpenName(newIMEState.mOpen)));
1044 0 : return newIMEState;
1045 : }
1046 :
1047 : static bool
1048 0 : MayBeIMEUnawareWebApp(nsINode* aNode)
1049 : {
1050 0 : bool haveKeyEventsListener = false;
1051 :
1052 0 : while (aNode) {
1053 0 : EventListenerManager* const mgr = aNode->GetExistingListenerManager();
1054 0 : if (mgr) {
1055 0 : if (mgr->MayHaveInputOrCompositionEventListener()) {
1056 0 : return false;
1057 : }
1058 0 : haveKeyEventsListener |= mgr->MayHaveKeyEventListener();
1059 : }
1060 0 : aNode = aNode->GetParentNode();
1061 : }
1062 :
1063 0 : return haveKeyEventsListener;
1064 : }
1065 :
1066 : // static
1067 : void
1068 3 : IMEStateManager::SetInputContextForChildProcess(
1069 : TabParent* aTabParent,
1070 : const InputContext& aInputContext,
1071 : const InputContextAction& aAction)
1072 : {
1073 3 : MOZ_LOG(sISMLog, LogLevel::Info,
1074 : ("SetInputContextForChildProcess(aTabParent=0x%p, "
1075 : "aInputContext={ mIMEState={ mEnabled=%s, mOpen=%s }, "
1076 : "mHTMLInputType=\"%s\", mHTMLInputInputmode=\"%s\", mActionHint=\"%s\" }, "
1077 : "aAction={ mCause=%s, mAction=%s }), "
1078 : "sPresContext=0x%p (available: %s), sWidget=0x%p (available: %s), "
1079 : "sActiveTabParent=0x%p",
1080 : aTabParent, GetIMEStateEnabledName(aInputContext.mIMEState.mEnabled),
1081 : GetIMEStateSetOpenName(aInputContext.mIMEState.mOpen),
1082 : NS_ConvertUTF16toUTF8(aInputContext.mHTMLInputType).get(),
1083 : NS_ConvertUTF16toUTF8(aInputContext.mHTMLInputInputmode).get(),
1084 : NS_ConvertUTF16toUTF8(aInputContext.mActionHint).get(),
1085 : GetActionCauseName(aAction.mCause),
1086 : GetActionFocusChangeName(aAction.mFocusChange),
1087 : sPresContext.get(), GetBoolName(CanHandleWith(sPresContext)),
1088 : sWidget, GetBoolName(sWidget && !sWidget->Destroyed()),
1089 : sActiveTabParent.get()));
1090 :
1091 3 : if (aTabParent != sActiveTabParent) {
1092 0 : MOZ_LOG(sISMLog, LogLevel::Error,
1093 : (" SetInputContextForChildProcess(), FAILED, "
1094 : "because non-focused tab parent tries to set input context"));
1095 0 : return;
1096 : }
1097 :
1098 3 : if (NS_WARN_IF(!CanHandleWith(sPresContext))) {
1099 0 : MOZ_LOG(sISMLog, LogLevel::Error,
1100 : (" SetInputContextForChildProcess(), FAILED, "
1101 : "due to no focused presContext"));
1102 0 : return;
1103 : }
1104 :
1105 3 : if (NS_WARN_IF(!sWidget) || NS_WARN_IF(sWidget->Destroyed())) {
1106 0 : MOZ_LOG(sISMLog, LogLevel::Error,
1107 : (" SetInputContextForChildProcess(), FAILED, "
1108 : "due to the widget for the nsPresContext has gone"));
1109 0 : return;
1110 : }
1111 :
1112 6 : nsCOMPtr<nsIWidget> widget(sWidget);
1113 :
1114 3 : MOZ_ASSERT(!sPresContext->GetRootWidget() ||
1115 : sPresContext->GetRootWidget() == widget);
1116 3 : MOZ_ASSERT(aInputContext.mOrigin == InputContext::ORIGIN_CONTENT);
1117 :
1118 3 : SetInputContext(widget, aInputContext, aAction);
1119 : }
1120 :
1121 : // static
1122 : void
1123 4 : IMEStateManager::SetIMEState(const IMEState& aState,
1124 : nsIContent* aContent,
1125 : nsIWidget* aWidget,
1126 : InputContextAction aAction,
1127 : InputContext::Origin aOrigin)
1128 : {
1129 4 : MOZ_LOG(sISMLog, LogLevel::Info,
1130 : ("SetIMEState(aState={ mEnabled=%s, mOpen=%s }, "
1131 : "aContent=0x%p (TabParent=0x%p), aWidget=0x%p, aAction={ mCause=%s, "
1132 : "mFocusChange=%s }, aOrigin=%s)",
1133 : GetIMEStateEnabledName(aState.mEnabled),
1134 : GetIMEStateSetOpenName(aState.mOpen), aContent,
1135 : TabParent::GetFrom(aContent), aWidget,
1136 : GetActionCauseName(aAction.mCause),
1137 : GetActionFocusChangeName(aAction.mFocusChange),
1138 : ToChar(aOrigin)));
1139 :
1140 4 : NS_ENSURE_TRUE_VOID(aWidget);
1141 :
1142 8 : InputContext context;
1143 4 : context.mIMEState = aState;
1144 4 : context.mOrigin = aOrigin;
1145 8 : context.mMayBeIMEUnaware = context.mIMEState.IsEditable() &&
1146 4 : sCheckForIMEUnawareWebApps && MayBeIMEUnawareWebApp(aContent);
1147 :
1148 5 : if (aContent &&
1149 1 : aContent->IsAnyOfHTMLElements(nsGkAtoms::input, nsGkAtoms::textarea)) {
1150 0 : if (!aContent->IsHTMLElement(nsGkAtoms::textarea)) {
1151 : // <input type=number> has an anonymous <input type=text> descendant
1152 : // that gets focus whenever anyone tries to focus the number control. We
1153 : // need to check if aContent is one of those anonymous text controls and,
1154 : // if so, use the number control instead:
1155 0 : nsIContent* content = aContent;
1156 : HTMLInputElement* inputElement =
1157 0 : HTMLInputElement::FromContentOrNull(aContent);
1158 0 : if (inputElement) {
1159 : HTMLInputElement* ownerNumberControl =
1160 0 : inputElement->GetOwnerNumberControl();
1161 0 : if (ownerNumberControl) {
1162 0 : content = ownerNumberControl; // an <input type=number>
1163 : }
1164 : }
1165 : content->GetAttr(kNameSpaceID_None, nsGkAtoms::type,
1166 0 : context.mHTMLInputType);
1167 : } else {
1168 0 : context.mHTMLInputType.Assign(nsGkAtoms::textarea->GetUTF16String());
1169 : }
1170 :
1171 0 : if (sInputModeSupported ||
1172 0 : nsContentUtils::IsChromeDoc(aContent->OwnerDoc())) {
1173 : aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::inputmode,
1174 0 : context.mHTMLInputInputmode);
1175 : }
1176 :
1177 : aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::moz_action_hint,
1178 0 : context.mActionHint);
1179 :
1180 : // Get the input content corresponding to the focused node,
1181 : // which may be an anonymous child of the input content.
1182 0 : nsIContent* inputContent = aContent->FindFirstNonChromeOnlyAccessContent();
1183 :
1184 : // If we don't have an action hint and
1185 : // return won't submit the form, use "next".
1186 0 : if (context.mActionHint.IsEmpty() &&
1187 0 : inputContent->IsHTMLElement(nsGkAtoms::input)) {
1188 0 : bool willSubmit = false;
1189 0 : nsCOMPtr<nsIFormControl> control(do_QueryInterface(inputContent));
1190 0 : mozilla::dom::Element* formElement = nullptr;
1191 0 : nsCOMPtr<nsIForm> form;
1192 0 : if (control) {
1193 0 : formElement = control->GetFormElement();
1194 : // is this a form and does it have a default submit element?
1195 0 : if ((form = do_QueryInterface(formElement)) &&
1196 0 : form->GetDefaultSubmitElement()) {
1197 0 : willSubmit = true;
1198 : // is this an html form and does it only have a single text input element?
1199 0 : } else if (formElement && formElement->IsHTMLElement(nsGkAtoms::form) &&
1200 0 : !static_cast<dom::HTMLFormElement*>(formElement)->
1201 0 : ImplicitSubmissionIsDisabled()) {
1202 0 : willSubmit = true;
1203 : }
1204 : }
1205 0 : context.mActionHint.Assign(
1206 0 : willSubmit ? (control->ControlType() == NS_FORM_INPUT_SEARCH ?
1207 0 : NS_LITERAL_STRING("search") : NS_LITERAL_STRING("go")) :
1208 0 : (formElement ?
1209 0 : NS_LITERAL_STRING("next") : EmptyString()));
1210 : }
1211 : }
1212 :
1213 : // XXX I think that we should use nsContentUtils::IsCallerChrome() instead
1214 : // of the process type.
1215 8 : if (aAction.mCause == InputContextAction::CAUSE_UNKNOWN &&
1216 4 : !XRE_IsContentProcess()) {
1217 1 : aAction.mCause = InputContextAction::CAUSE_UNKNOWN_CHROME;
1218 : }
1219 :
1220 4 : SetInputContext(aWidget, context, aAction);
1221 : }
1222 :
1223 : // static
1224 : void
1225 7 : IMEStateManager::SetInputContext(nsIWidget* aWidget,
1226 : const InputContext& aInputContext,
1227 : const InputContextAction& aAction)
1228 : {
1229 7 : MOZ_LOG(sISMLog, LogLevel::Info,
1230 : ("SetInputContext(aWidget=0x%p, aInputContext={ "
1231 : "mIMEState={ mEnabled=%s, mOpen=%s }, mHTMLInputType=\"%s\", "
1232 : "mHTMLInputInputmode=\"%s\", mActionHint=\"%s\" }, "
1233 : "aAction={ mCause=%s, mAction=%s }), sActiveTabParent=0x%p",
1234 : aWidget,
1235 : GetIMEStateEnabledName(aInputContext.mIMEState.mEnabled),
1236 : GetIMEStateSetOpenName(aInputContext.mIMEState.mOpen),
1237 : NS_ConvertUTF16toUTF8(aInputContext.mHTMLInputType).get(),
1238 : NS_ConvertUTF16toUTF8(aInputContext.mHTMLInputInputmode).get(),
1239 : NS_ConvertUTF16toUTF8(aInputContext.mActionHint).get(),
1240 : GetActionCauseName(aAction.mCause),
1241 : GetActionFocusChangeName(aAction.mFocusChange),
1242 : sActiveTabParent.get()));
1243 :
1244 7 : MOZ_RELEASE_ASSERT(aWidget);
1245 :
1246 14 : nsCOMPtr<nsIWidget> widget(aWidget);
1247 7 : widget->SetInputContext(aInputContext, aAction);
1248 7 : sActiveInputContextWidget = widget;
1249 7 : }
1250 :
1251 : // static
1252 : void
1253 0 : IMEStateManager::EnsureTextCompositionArray()
1254 : {
1255 0 : if (sTextCompositions) {
1256 0 : return;
1257 : }
1258 0 : sTextCompositions = new TextCompositionArray();
1259 : }
1260 :
1261 : // static
1262 : void
1263 0 : IMEStateManager::DispatchCompositionEvent(
1264 : nsINode* aEventTargetNode,
1265 : nsPresContext* aPresContext,
1266 : WidgetCompositionEvent* aCompositionEvent,
1267 : nsEventStatus* aStatus,
1268 : EventDispatchingCallback* aCallBack,
1269 : bool aIsSynthesized)
1270 : {
1271 : RefPtr<TabParent> tabParent =
1272 0 : aEventTargetNode->IsContent() ?
1273 0 : TabParent::GetFrom(aEventTargetNode->AsContent()) : nullptr;
1274 :
1275 0 : MOZ_LOG(sISMLog, LogLevel::Info,
1276 : ("DispatchCompositionEvent(aNode=0x%p, "
1277 : "aPresContext=0x%p, aCompositionEvent={ mMessage=%s, "
1278 : "mNativeIMEContext={ mRawNativeIMEContext=0x%" PRIXPTR ", "
1279 : "mOriginProcessID=0x%" PRIX64 " }, mWidget(0x%p)={ "
1280 : "GetNativeIMEContext()={ mRawNativeIMEContext=0x%" PRIXPTR ", "
1281 : "mOriginProcessID=0x%" PRIX64 " }, Destroyed()=%s }, "
1282 : "mFlags={ mIsTrusted=%s, mPropagationStopped=%s } }, "
1283 : "aIsSynthesized=%s), tabParent=%p",
1284 : aEventTargetNode, aPresContext,
1285 : ToChar(aCompositionEvent->mMessage),
1286 : aCompositionEvent->mNativeIMEContext.mRawNativeIMEContext,
1287 : aCompositionEvent->mNativeIMEContext.mOriginProcessID,
1288 : aCompositionEvent->mWidget.get(),
1289 : aCompositionEvent->mWidget->GetNativeIMEContext().mRawNativeIMEContext,
1290 : aCompositionEvent->mWidget->GetNativeIMEContext().mOriginProcessID,
1291 : GetBoolName(aCompositionEvent->mWidget->Destroyed()),
1292 : GetBoolName(aCompositionEvent->mFlags.mIsTrusted),
1293 : GetBoolName(aCompositionEvent->mFlags.mPropagationStopped),
1294 : GetBoolName(aIsSynthesized), tabParent.get()));
1295 :
1296 0 : if (!aCompositionEvent->IsTrusted() ||
1297 0 : aCompositionEvent->PropagationStopped()) {
1298 0 : return;
1299 : }
1300 :
1301 0 : MOZ_ASSERT(aCompositionEvent->mMessage != eCompositionUpdate,
1302 : "compositionupdate event shouldn't be dispatched manually");
1303 :
1304 0 : EnsureTextCompositionArray();
1305 :
1306 : RefPtr<TextComposition> composition =
1307 0 : sTextCompositions->GetCompositionFor(aCompositionEvent);
1308 0 : if (!composition) {
1309 : // If synthesized event comes after delayed native composition events
1310 : // for request of commit or cancel, we should ignore it.
1311 0 : if (NS_WARN_IF(aIsSynthesized)) {
1312 0 : return;
1313 : }
1314 0 : MOZ_LOG(sISMLog, LogLevel::Debug,
1315 : (" DispatchCompositionEvent(), "
1316 : "adding new TextComposition to the array"));
1317 0 : MOZ_ASSERT(aCompositionEvent->mMessage == eCompositionStart);
1318 : composition =
1319 : new TextComposition(aPresContext, aEventTargetNode, tabParent,
1320 0 : aCompositionEvent);
1321 0 : sTextCompositions->AppendElement(composition);
1322 : }
1323 : #ifdef DEBUG
1324 : else {
1325 0 : MOZ_ASSERT(aCompositionEvent->mMessage != eCompositionStart);
1326 : }
1327 : #endif // #ifdef DEBUG
1328 :
1329 : // Dispatch the event on composing target.
1330 0 : composition->DispatchCompositionEvent(aCompositionEvent, aStatus, aCallBack,
1331 0 : aIsSynthesized);
1332 :
1333 : // WARNING: the |composition| might have been destroyed already.
1334 :
1335 : // Remove the ended composition from the array.
1336 : // NOTE: When TextComposition is synthesizing compositionend event for
1337 : // emulating a commit, the instance shouldn't be removed from the array
1338 : // because IME may perform it later. Then, we need to ignore the
1339 : // following commit events in TextComposition::DispatchEvent().
1340 : // However, if commit or cancel for a request is performed synchronously
1341 : // during not safe to dispatch events, PresShell must have discarded
1342 : // compositionend event. Then, the synthesized compositionend event is
1343 : // the last event for the composition. In this case, we need to
1344 : // destroy the TextComposition with synthesized compositionend event.
1345 0 : if ((!aIsSynthesized ||
1346 0 : composition->WasNativeCompositionEndEventDiscarded()) &&
1347 0 : aCompositionEvent->CausesDOMCompositionEndEvent()) {
1348 : TextCompositionArray::index_type i =
1349 0 : sTextCompositions->IndexOf(aCompositionEvent->mWidget);
1350 0 : if (i != TextCompositionArray::NoIndex) {
1351 0 : MOZ_LOG(sISMLog, LogLevel::Debug,
1352 : (" DispatchCompositionEvent(), "
1353 : "removing TextComposition from the array since NS_COMPOSTION_END "
1354 : "was dispatched"));
1355 0 : sTextCompositions->ElementAt(i)->Destroy();
1356 0 : sTextCompositions->RemoveElementAt(i);
1357 : }
1358 : }
1359 : }
1360 :
1361 : // static
1362 : IMEContentObserver*
1363 0 : IMEStateManager::GetActiveContentObserver()
1364 : {
1365 0 : return sActiveIMEContentObserver;
1366 : }
1367 :
1368 : // static
1369 : nsIContent*
1370 0 : IMEStateManager::GetRootContent(nsPresContext* aPresContext)
1371 : {
1372 0 : nsIDocument* doc = aPresContext->Document();
1373 0 : if (NS_WARN_IF(!doc)) {
1374 0 : return nullptr;
1375 : }
1376 0 : return doc->GetRootElement();
1377 : }
1378 :
1379 : // static
1380 : void
1381 0 : IMEStateManager::HandleSelectionEvent(nsPresContext* aPresContext,
1382 : nsIContent* aEventTargetContent,
1383 : WidgetSelectionEvent* aSelectionEvent)
1384 : {
1385 : nsIContent* eventTargetContent =
1386 0 : aEventTargetContent ? aEventTargetContent :
1387 0 : GetRootContent(aPresContext);
1388 : RefPtr<TabParent> tabParent =
1389 0 : eventTargetContent ? TabParent::GetFrom(eventTargetContent) : nullptr;
1390 :
1391 0 : MOZ_LOG(sISMLog, LogLevel::Info,
1392 : ("HandleSelectionEvent(aPresContext=0x%p, "
1393 : "aEventTargetContent=0x%p, aSelectionEvent={ mMessage=%s, "
1394 : "mFlags={ mIsTrusted=%s } }), tabParent=%p",
1395 : aPresContext, aEventTargetContent,
1396 : ToChar(aSelectionEvent->mMessage),
1397 : GetBoolName(aSelectionEvent->mFlags.mIsTrusted),
1398 : tabParent.get()));
1399 :
1400 0 : if (!aSelectionEvent->IsTrusted()) {
1401 0 : return;
1402 : }
1403 :
1404 : RefPtr<TextComposition> composition = sTextCompositions ?
1405 0 : sTextCompositions->GetCompositionFor(aSelectionEvent->mWidget) : nullptr;
1406 0 : if (composition) {
1407 : // When there is a composition, TextComposition should guarantee that the
1408 : // selection event will be handled in same target as composition events.
1409 0 : composition->HandleSelectionEvent(aSelectionEvent);
1410 : } else {
1411 : // When there is no composition, the selection event should be handled
1412 : // in the aPresContext or tabParent.
1413 0 : TextComposition::HandleSelectionEvent(aPresContext, tabParent,
1414 0 : aSelectionEvent);
1415 : }
1416 : }
1417 :
1418 : // static
1419 : void
1420 0 : IMEStateManager::OnCompositionEventDiscarded(
1421 : WidgetCompositionEvent* aCompositionEvent)
1422 : {
1423 : // Note that this method is never called for synthesized events for emulating
1424 : // commit or cancel composition.
1425 :
1426 0 : MOZ_LOG(sISMLog, LogLevel::Info,
1427 : ("OnCompositionEventDiscarded(aCompositionEvent={ "
1428 : "mMessage=%s, mNativeIMEContext={ mRawNativeIMEContext=0x%" PRIXPTR ", "
1429 : "mOriginProcessID=0x%" PRIX64 " }, mWidget(0x%p)={ "
1430 : "GetNativeIMEContext()={ mRawNativeIMEContext=0x%" PRIXPTR ", "
1431 : "mOriginProcessID=0x%" PRIX64 " }, Destroyed()=%s }, "
1432 : "mFlags={ mIsTrusted=%s } })",
1433 : ToChar(aCompositionEvent->mMessage),
1434 : aCompositionEvent->mNativeIMEContext.mRawNativeIMEContext,
1435 : aCompositionEvent->mNativeIMEContext.mOriginProcessID,
1436 : aCompositionEvent->mWidget.get(),
1437 : aCompositionEvent->mWidget->GetNativeIMEContext().mRawNativeIMEContext,
1438 : aCompositionEvent->mWidget->GetNativeIMEContext().mOriginProcessID,
1439 : GetBoolName(aCompositionEvent->mWidget->Destroyed()),
1440 : GetBoolName(aCompositionEvent->mFlags.mIsTrusted)));
1441 :
1442 0 : if (!aCompositionEvent->IsTrusted()) {
1443 0 : return;
1444 : }
1445 :
1446 : // Ignore compositionstart for now because sTextCompositions may not have
1447 : // been created yet.
1448 0 : if (aCompositionEvent->mMessage == eCompositionStart) {
1449 0 : return;
1450 : }
1451 :
1452 : RefPtr<TextComposition> composition =
1453 0 : sTextCompositions->GetCompositionFor(aCompositionEvent->mWidget);
1454 0 : if (!composition) {
1455 : // If the PresShell has been being destroyed during composition,
1456 : // a TextComposition instance for the composition was already removed from
1457 : // the array and destroyed in OnDestroyPresContext(). Therefore, we may
1458 : // fail to retrieve a TextComposition instance here.
1459 0 : MOZ_LOG(sISMLog, LogLevel::Info,
1460 : (" OnCompositionEventDiscarded(), "
1461 : "TextComposition instance for the widget has already gone"));
1462 0 : return;
1463 : }
1464 0 : composition->OnCompositionEventDiscarded(aCompositionEvent);
1465 : }
1466 :
1467 : // static
1468 : nsresult
1469 0 : IMEStateManager::NotifyIME(IMEMessage aMessage,
1470 : nsIWidget* aWidget,
1471 : TabParent* aTabParent)
1472 : {
1473 0 : return IMEStateManager::NotifyIME(IMENotification(aMessage), aWidget,
1474 0 : aTabParent);
1475 : }
1476 :
1477 : // static
1478 : nsresult
1479 0 : IMEStateManager::NotifyIME(const IMENotification& aNotification,
1480 : nsIWidget* aWidget,
1481 : TabParent* aTabParent)
1482 : {
1483 0 : MOZ_LOG(sISMLog, LogLevel::Info,
1484 : ("NotifyIME(aNotification={ mMessage=%s }, "
1485 : "aWidget=0x%p, aTabParent=0x%p), sFocusedIMEWidget=0x%p, "
1486 : "sActiveTabParent=0x%p, sFocusedIMETabParent=0x%p, "
1487 : "IsSameProcess(aTabParent, sActiveTabParent)=%s, "
1488 : "IsSameProcess(aTabParent, sFocusedIMETabParent)=%s",
1489 : ToChar(aNotification.mMessage), aWidget,
1490 : aTabParent, sFocusedIMEWidget, sActiveTabParent.get(),
1491 : sFocusedIMETabParent.get(),
1492 : GetBoolName(IsSameProcess(aTabParent, sActiveTabParent)),
1493 : GetBoolName(IsSameProcess(aTabParent, sFocusedIMETabParent))));
1494 :
1495 0 : if (NS_WARN_IF(!aWidget)) {
1496 0 : MOZ_LOG(sISMLog, LogLevel::Error,
1497 : (" NotifyIME(), FAILED due to no widget"));
1498 0 : return NS_ERROR_INVALID_ARG;
1499 : }
1500 :
1501 0 : switch (aNotification.mMessage) {
1502 : case NOTIFY_IME_OF_FOCUS: {
1503 : // If focus notification comes from a remote process which already lost
1504 : // focus, we shouldn't accept the focus notification. Then, following
1505 : // notifications from the process will be ignored.
1506 0 : if (NS_WARN_IF(!IsSameProcess(aTabParent, sActiveTabParent))) {
1507 0 : MOZ_ASSERT(aTabParent,
1508 : "Why was the input context initialized for a remote process but "
1509 : "does this process get IME focus?");
1510 0 : MOZ_LOG(sISMLog, LogLevel::Warning,
1511 : (" NotifyIME(), WARNING, the received focus notification is ignored "
1512 : "because input context was initialized for %s, perhaps, it came "
1513 : "from a busy remote process",
1514 : sActiveTabParent ? "another remote process" : "current process"));
1515 0 : return NS_OK;
1516 : }
1517 : // If there is pending blur notification for current focused IME,
1518 : // we should notify IME of blur by ourselves. Then, we should ignore
1519 : // following notifications coming from the process.
1520 0 : if (sFocusedIMEWidget) {
1521 0 : MOZ_ASSERT(sFocusedIMETabParent || aTabParent,
1522 : "This case shouldn't be caused by focus move in this process");
1523 0 : nsCOMPtr<nsIWidget> focusedIMEWidget(sFocusedIMEWidget);
1524 0 : sFocusedIMEWidget = nullptr;
1525 0 : sFocusedIMETabParent = nullptr;
1526 0 : focusedIMEWidget->NotifyIME(IMENotification(NOTIFY_IME_OF_BLUR));
1527 : }
1528 0 : sFocusedIMETabParent = aTabParent;
1529 0 : sFocusedIMEWidget = aWidget;
1530 0 : nsCOMPtr<nsIWidget> widget(aWidget);
1531 0 : return widget->NotifyIME(aNotification);
1532 : }
1533 : case NOTIFY_IME_OF_BLUR: {
1534 0 : if (!IsSameProcess(aTabParent, sFocusedIMETabParent)) {
1535 0 : MOZ_LOG(sISMLog, LogLevel::Warning,
1536 : (" NotifyIME(), WARNING, the received blur notification is ignored "
1537 : "because it's not from current focused IME process"));
1538 0 : return NS_OK;
1539 : }
1540 0 : if (!sFocusedIMEWidget) {
1541 0 : MOZ_LOG(sISMLog, LogLevel::Error,
1542 : (" NotifyIME(), WARNING, received blur notification but there is "
1543 : "no focused IME widget"));
1544 0 : return NS_OK;
1545 : }
1546 0 : if (NS_WARN_IF(sFocusedIMEWidget != aWidget)) {
1547 0 : MOZ_LOG(sISMLog, LogLevel::Warning,
1548 : (" NotifyIME(), WARNING, the received blur notification is ignored "
1549 : "because it's not for current focused IME widget"));
1550 0 : return NS_OK;
1551 : }
1552 0 : nsCOMPtr<nsIWidget> focusedIMEWidget(sFocusedIMEWidget);
1553 0 : sFocusedIMEWidget = nullptr;
1554 0 : sFocusedIMETabParent = nullptr;
1555 0 : return focusedIMEWidget->NotifyIME(IMENotification(NOTIFY_IME_OF_BLUR));
1556 : }
1557 : case NOTIFY_IME_OF_SELECTION_CHANGE:
1558 : case NOTIFY_IME_OF_TEXT_CHANGE:
1559 : case NOTIFY_IME_OF_POSITION_CHANGE:
1560 : case NOTIFY_IME_OF_MOUSE_BUTTON_EVENT:
1561 : case NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED: {
1562 0 : if (!IsSameProcess(aTabParent, sFocusedIMETabParent)) {
1563 0 : MOZ_LOG(sISMLog, LogLevel::Warning,
1564 : (" NotifyIME(), WARNING, the received content change notification "
1565 : "is ignored because it's not from current focused IME process"));
1566 0 : return NS_OK;
1567 : }
1568 0 : if (!sFocusedIMEWidget) {
1569 0 : MOZ_LOG(sISMLog, LogLevel::Warning,
1570 : (" NotifyIME(), WARNING, the received content change notification "
1571 : "is ignored because there is no focused IME widget"));
1572 0 : return NS_OK;
1573 : }
1574 0 : if (NS_WARN_IF(sFocusedIMEWidget != aWidget)) {
1575 0 : MOZ_LOG(sISMLog, LogLevel::Warning,
1576 : (" NotifyIME(), WARNING, the received content change notification "
1577 : "is ignored because it's not for current focused IME widget"));
1578 0 : return NS_OK;
1579 : }
1580 0 : nsCOMPtr<nsIWidget> widget(aWidget);
1581 0 : return widget->NotifyIME(aNotification);
1582 : }
1583 : default:
1584 : // Other notifications should be sent only when there is composition.
1585 : // So, we need to handle the others below.
1586 0 : break;
1587 : }
1588 :
1589 0 : if (!sTextCompositions) {
1590 0 : MOZ_LOG(sISMLog, LogLevel::Info,
1591 : (" NotifyIME(), the request to IME is ignored because "
1592 : "there have been no compositions yet"));
1593 0 : return NS_OK;
1594 : }
1595 :
1596 : RefPtr<TextComposition> composition =
1597 0 : sTextCompositions->GetCompositionFor(aWidget);
1598 0 : if (!composition) {
1599 0 : MOZ_LOG(sISMLog, LogLevel::Info,
1600 : (" NotifyIME(), the request to IME is ignored because "
1601 : "there is no active composition"));
1602 0 : return NS_OK;
1603 : }
1604 :
1605 0 : if (!IsSameProcess(aTabParent, composition->GetTabParent())) {
1606 0 : MOZ_LOG(sISMLog, LogLevel::Warning,
1607 : (" NotifyIME(), WARNING, the request to IME is ignored because "
1608 : "it does not come from the remote process which has the composition "
1609 : "on aWidget"));
1610 0 : return NS_OK;
1611 : }
1612 :
1613 0 : switch (aNotification.mMessage) {
1614 : case REQUEST_TO_COMMIT_COMPOSITION:
1615 0 : return composition->RequestToCommit(aWidget, false);
1616 : case REQUEST_TO_CANCEL_COMPOSITION:
1617 0 : return composition->RequestToCommit(aWidget, true);
1618 : default:
1619 0 : MOZ_CRASH("Unsupported notification");
1620 : }
1621 : MOZ_CRASH(
1622 : "Failed to handle the notification for non-synthesized composition");
1623 : return NS_ERROR_FAILURE;
1624 : }
1625 :
1626 : // static
1627 : nsresult
1628 0 : IMEStateManager::NotifyIME(IMEMessage aMessage,
1629 : nsPresContext* aPresContext,
1630 : TabParent* aTabParent)
1631 : {
1632 0 : MOZ_LOG(sISMLog, LogLevel::Info,
1633 : ("NotifyIME(aMessage=%s, aPresContext=0x%p, aTabParent=0x%p)",
1634 : ToChar(aMessage), aPresContext, aTabParent));
1635 :
1636 0 : if (NS_WARN_IF(!CanHandleWith(aPresContext))) {
1637 0 : return NS_ERROR_INVALID_ARG;
1638 : }
1639 :
1640 0 : nsIWidget* widget = aPresContext->GetRootWidget();
1641 0 : if (NS_WARN_IF(!widget)) {
1642 0 : MOZ_LOG(sISMLog, LogLevel::Error,
1643 : (" NotifyIME(), FAILED due to no widget for the "
1644 : "nsPresContext"));
1645 0 : return NS_ERROR_NOT_AVAILABLE;
1646 : }
1647 0 : return NotifyIME(aMessage, widget, aTabParent);
1648 : }
1649 :
1650 : // static
1651 : bool
1652 0 : IMEStateManager::IsEditable(nsINode* node)
1653 : {
1654 0 : if (node->IsEditable()) {
1655 0 : return true;
1656 : }
1657 : // |node| might be readwrite (for example, a text control)
1658 0 : if (node->IsElement() &&
1659 0 : node->AsElement()->State().HasState(NS_EVENT_STATE_MOZ_READWRITE)) {
1660 0 : return true;
1661 : }
1662 0 : return false;
1663 : }
1664 :
1665 : // static
1666 : nsINode*
1667 0 : IMEStateManager::GetRootEditableNode(nsPresContext* aPresContext,
1668 : nsIContent* aContent)
1669 : {
1670 0 : if (aContent) {
1671 0 : nsINode* root = nullptr;
1672 0 : nsINode* node = aContent;
1673 0 : while (node && IsEditable(node)) {
1674 : // If the node has independent selection like <input type="text"> or
1675 : // <textarea>, the node should be the root editable node for aContent.
1676 : // FYI: <select> element also has independent selection but IsEditable()
1677 : // returns false.
1678 : // XXX: If somebody adds new editable element which has independent
1679 : // selection but doesn't own editor, we'll need more checks here.
1680 0 : if (node->IsContent() &&
1681 0 : node->AsContent()->HasIndependentSelection()) {
1682 0 : return node;
1683 : }
1684 0 : root = node;
1685 0 : node = node->GetParentNode();
1686 : }
1687 0 : return root;
1688 : }
1689 0 : if (aPresContext) {
1690 0 : nsIDocument* document = aPresContext->Document();
1691 0 : if (document && document->IsEditable()) {
1692 0 : return document;
1693 : }
1694 : }
1695 0 : return nullptr;
1696 : }
1697 :
1698 : // static
1699 : bool
1700 0 : IMEStateManager::IsIMEObserverNeeded(const IMEState& aState)
1701 : {
1702 0 : return aState.MaybeEditable();
1703 : }
1704 :
1705 : // static
1706 : void
1707 1 : IMEStateManager::DestroyIMEContentObserver()
1708 : {
1709 1 : MOZ_LOG(sISMLog, LogLevel::Info,
1710 : ("DestroyIMEContentObserver(), sActiveIMEContentObserver=0x%p",
1711 : sActiveIMEContentObserver.get()));
1712 :
1713 1 : if (!sActiveIMEContentObserver) {
1714 1 : MOZ_LOG(sISMLog, LogLevel::Debug,
1715 : (" DestroyIMEContentObserver() does nothing"));
1716 1 : return;
1717 : }
1718 :
1719 0 : MOZ_LOG(sISMLog, LogLevel::Debug,
1720 : (" DestroyIMEContentObserver(), destroying "
1721 : "the active IMEContentObserver..."));
1722 0 : RefPtr<IMEContentObserver> tsm = sActiveIMEContentObserver.get();
1723 0 : sActiveIMEContentObserver = nullptr;
1724 0 : tsm->Destroy();
1725 : }
1726 :
1727 : // static
1728 : void
1729 0 : IMEStateManager::CreateIMEContentObserver(EditorBase* aEditorBase)
1730 : {
1731 0 : MOZ_LOG(sISMLog, LogLevel::Info,
1732 : ("CreateIMEContentObserver(aEditorBase=0x%p), "
1733 : "sPresContext=0x%p, sContent=0x%p, sWidget=0x%p (available: %s), "
1734 : "sActiveIMEContentObserver=0x%p, "
1735 : "sActiveIMEContentObserver->IsManaging(sPresContext, sContent)=%s",
1736 : aEditorBase, sPresContext.get(), sContent.get(),
1737 : sWidget, GetBoolName(sWidget && !sWidget->Destroyed()),
1738 : sActiveIMEContentObserver.get(),
1739 : GetBoolName(sActiveIMEContentObserver ?
1740 : sActiveIMEContentObserver->IsManaging(sPresContext, sContent) : false)));
1741 :
1742 0 : if (NS_WARN_IF(sActiveIMEContentObserver)) {
1743 0 : MOZ_LOG(sISMLog, LogLevel::Error,
1744 : (" CreateIMEContentObserver(), FAILED due to "
1745 : "there is already an active IMEContentObserver"));
1746 0 : MOZ_ASSERT(sActiveIMEContentObserver->IsManaging(sPresContext, sContent));
1747 0 : return;
1748 : }
1749 :
1750 0 : if (!sWidget || NS_WARN_IF(sWidget->Destroyed())) {
1751 0 : MOZ_LOG(sISMLog, LogLevel::Error,
1752 : (" CreateIMEContentObserver(), FAILED due to "
1753 : "the widget for the nsPresContext has gone"));
1754 0 : return; // Sometimes, there are no widgets.
1755 : }
1756 :
1757 0 : nsCOMPtr<nsIWidget> widget(sWidget);
1758 :
1759 : // If it's not text editable, we don't need to create IMEContentObserver.
1760 0 : if (!IsIMEObserverNeeded(widget->GetInputContext().mIMEState)) {
1761 0 : MOZ_LOG(sISMLog, LogLevel::Debug,
1762 : (" CreateIMEContentObserver() doesn't create "
1763 : "IMEContentObserver because of non-editable IME state"));
1764 0 : return;
1765 : }
1766 :
1767 0 : if (NS_WARN_IF(widget->Destroyed())) {
1768 0 : MOZ_LOG(sISMLog, LogLevel::Error,
1769 : (" CreateIMEContentObserver(), FAILED due to "
1770 : "the widget for the nsPresContext has gone"));
1771 0 : return;
1772 : }
1773 :
1774 0 : MOZ_ASSERT(sPresContext->GetRootWidget() == widget);
1775 :
1776 0 : MOZ_LOG(sISMLog, LogLevel::Debug,
1777 : (" CreateIMEContentObserver() is creating an "
1778 : "IMEContentObserver instance..."));
1779 0 : sActiveIMEContentObserver = new IMEContentObserver();
1780 :
1781 : // IMEContentObserver::Init() might create another IMEContentObserver
1782 : // instance. So, sActiveIMEContentObserver would be replaced with new one.
1783 : // We should hold the current instance here.
1784 0 : RefPtr<IMEContentObserver> activeIMEContentObserver(sActiveIMEContentObserver);
1785 0 : activeIMEContentObserver->Init(widget, sPresContext, sContent, aEditorBase);
1786 : }
1787 :
1788 : // static
1789 : nsresult
1790 0 : IMEStateManager::GetFocusSelectionAndRoot(nsISelection** aSelection,
1791 : nsIContent** aRootContent)
1792 : {
1793 0 : if (!sActiveIMEContentObserver) {
1794 0 : return NS_ERROR_NOT_AVAILABLE;
1795 : }
1796 0 : return sActiveIMEContentObserver->GetSelectionAndRoot(aSelection,
1797 0 : aRootContent);
1798 : }
1799 :
1800 : // static
1801 : already_AddRefed<TextComposition>
1802 0 : IMEStateManager::GetTextCompositionFor(nsIWidget* aWidget)
1803 : {
1804 0 : if (!sTextCompositions) {
1805 0 : return nullptr;
1806 : }
1807 : RefPtr<TextComposition> textComposition =
1808 0 : sTextCompositions->GetCompositionFor(aWidget);
1809 0 : return textComposition.forget();
1810 : }
1811 :
1812 : // static
1813 : already_AddRefed<TextComposition>
1814 0 : IMEStateManager::GetTextCompositionFor(
1815 : const WidgetCompositionEvent* aCompositionEvent)
1816 : {
1817 0 : if (!sTextCompositions) {
1818 0 : return nullptr;
1819 : }
1820 : RefPtr<TextComposition> textComposition =
1821 0 : sTextCompositions->GetCompositionFor(aCompositionEvent);
1822 0 : return textComposition.forget();
1823 : }
1824 :
1825 : // static
1826 : already_AddRefed<TextComposition>
1827 0 : IMEStateManager::GetTextCompositionFor(nsPresContext* aPresContext)
1828 : {
1829 0 : if (!sTextCompositions) {
1830 0 : return nullptr;
1831 : }
1832 : RefPtr<TextComposition> textComposition =
1833 0 : sTextCompositions->GetCompositionFor(aPresContext);
1834 0 : return textComposition.forget();
1835 : }
1836 :
1837 : } // namespace mozilla
|