Bug 1732219 - Add API for fetching the preview image. r=geckoview-reviewers,agi,mconley
[gecko.git] / dom / events / IMEStateManager.cpp
blob8aaf529ace8394e10940c1b9e743df6f0cbebccd
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 #include "mozilla/Logging.h"
9 #include "mozilla/IMEStateManager.h"
11 #include "mozilla/Attributes.h"
12 #include "mozilla/AutoRestore.h"
13 #include "mozilla/EditorBase.h"
14 #include "mozilla/EventListenerManager.h"
15 #include "mozilla/EventStates.h"
16 #include "mozilla/EventStateManager.h"
17 #include "mozilla/MouseEvents.h"
18 #include "mozilla/PresShell.h"
19 #include "mozilla/StaticPrefs_dom.h"
20 #include "mozilla/StaticPrefs_intl.h"
21 #include "mozilla/TextComposition.h"
22 #include "mozilla/TextEvents.h"
23 #include "mozilla/ToString.h"
24 #include "mozilla/Unused.h"
25 #include "mozilla/dom/BrowserBridgeChild.h"
26 #include "mozilla/dom/BrowserParent.h"
27 #include "mozilla/dom/Document.h"
28 #include "mozilla/dom/HTMLFormElement.h"
29 #include "mozilla/dom/HTMLTextAreaElement.h"
30 #include "mozilla/dom/MouseEventBinding.h"
31 #include "mozilla/dom/UserActivation.h"
33 #include "HTMLInputElement.h"
34 #include "IMEContentObserver.h"
36 #include "nsCOMPtr.h"
37 #include "nsContentUtils.h"
38 #include "nsFocusManager.h"
39 #include "nsIContent.h"
40 #include "nsIContentInlines.h"
41 #include "nsIFormControl.h"
42 #include "nsINode.h"
43 #include "nsISupports.h"
44 #include "nsPresContext.h"
46 namespace mozilla {
48 using namespace dom;
49 using namespace widget;
51 /**
52 * When a method is called, log its arguments and/or related static variables
53 * with LogLevel::Info. However, if it puts too many logs like
54 * OnDestroyPresContext(), should long only when the method actually does
55 * something. In this case, the log should start with "<method name>".
57 * When a method quits due to unexpected situation, log the reason with
58 * LogLevel::Error. In this case, the log should start with
59 * "<method name>(), FAILED". The indent makes the log look easier.
61 * When a method does something only in some situations and it may be important
62 * for debug, log the information with LogLevel::Debug. In this case, the log
63 * should start with " <method name>(),".
65 LazyLogModule sISMLog("IMEStateManager");
67 static const char* GetBoolName(bool aBool) { return aBool ? "true" : "false"; }
69 StaticRefPtr<nsIContent> IMEStateManager::sContent;
70 StaticRefPtr<nsPresContext> IMEStateManager::sPresContext;
71 nsIWidget* IMEStateManager::sWidget = nullptr;
72 nsIWidget* IMEStateManager::sFocusedIMEWidget = nullptr;
73 StaticRefPtr<BrowserParent> IMEStateManager::sFocusedIMEBrowserParent;
74 nsIWidget* IMEStateManager::sActiveInputContextWidget = nullptr;
75 StaticRefPtr<IMEContentObserver> IMEStateManager::sActiveIMEContentObserver;
76 TextCompositionArray* IMEStateManager::sTextCompositions = nullptr;
77 InputContext::Origin IMEStateManager::sOrigin = InputContext::ORIGIN_MAIN;
78 InputContext IMEStateManager::sActiveChildInputContext;
79 bool IMEStateManager::sInstalledMenuKeyboardListener = false;
80 bool IMEStateManager::sIsGettingNewIMEState = false;
81 bool IMEStateManager::sCleaningUpForStoppingIMEStateManagement = false;
82 bool IMEStateManager::sIsActive = false;
83 Maybe<IMEStateManager::PendingFocusedBrowserSwitchingData>
84 IMEStateManager::sPendingFocusedBrowserSwitchingData;
86 // static
87 void IMEStateManager::Init() {
88 sOrigin = XRE_IsParentProcess() ? InputContext::ORIGIN_MAIN
89 : InputContext::ORIGIN_CONTENT;
90 ResetActiveChildInputContext();
93 // static
94 void IMEStateManager::Shutdown() {
95 MOZ_LOG(
96 sISMLog, LogLevel::Info,
97 ("Shutdown(), sTextCompositions=0x%p, sTextCompositions->Length()=%zu, "
98 "sPendingFocusedBrowserSwitchingData.isSome()=%s",
99 sTextCompositions, sTextCompositions ? sTextCompositions->Length() : 0,
100 GetBoolName(sPendingFocusedBrowserSwitchingData.isSome())));
102 sPendingFocusedBrowserSwitchingData.reset();
103 MOZ_ASSERT(!sTextCompositions || !sTextCompositions->Length());
104 delete sTextCompositions;
105 sTextCompositions = nullptr;
106 // All string instances in the global space need to be empty after XPCOM
107 // shutdown.
108 sActiveChildInputContext.ShutDown();
111 // static
112 void IMEStateManager::OnFocusMovedBetweenBrowsers(BrowserParent* aBlur,
113 BrowserParent* aFocus) {
114 MOZ_ASSERT(aBlur != aFocus);
115 MOZ_ASSERT(XRE_IsParentProcess());
117 if (sPendingFocusedBrowserSwitchingData.isSome()) {
118 MOZ_ASSERT(aBlur ==
119 sPendingFocusedBrowserSwitchingData.ref().mBrowserParentFocused);
120 // If focus is not changed between browsers actually, we need to do
121 // nothing here. Let's cancel handling what this method does.
122 if (sPendingFocusedBrowserSwitchingData.ref().mBrowserParentBlurred ==
123 aFocus) {
124 sPendingFocusedBrowserSwitchingData.reset();
125 MOZ_LOG(sISMLog, LogLevel::Info,
126 (" OnFocusMovedBetweenBrowsers(), canceled all pending focus "
127 "moves between browsers"));
128 return;
130 aBlur = sPendingFocusedBrowserSwitchingData.ref().mBrowserParentBlurred;
131 sPendingFocusedBrowserSwitchingData.ref().mBrowserParentFocused = aFocus;
132 MOZ_ASSERT(aBlur != aFocus);
135 // If application was inactive, but is now activated, and the last focused
136 // this is called by BrowserParent::UnsetTopLevelWebFocusAll() from
137 // nsFocusManager::WindowRaised(). If a content has focus in a remote
138 // process and it has composition, it may get focus back later and the
139 // composition shouldn't be commited now. Therefore, we should put off to
140 // handle this until getting another call of this method or a call of
141 //`OnFocusChangeInternal()`.
142 if (aBlur && !aFocus && !sIsActive && sWidget && sTextCompositions &&
143 sTextCompositions->GetCompositionFor(sWidget)) {
144 if (sPendingFocusedBrowserSwitchingData.isNothing()) {
145 sPendingFocusedBrowserSwitchingData.emplace(aBlur, aFocus);
147 MOZ_LOG(sISMLog, LogLevel::Debug,
148 (" OnFocusMovedBetweenBrowsers(), put off to handle it until "
149 "next OnFocusChangeInternal() call"));
150 return;
152 sPendingFocusedBrowserSwitchingData.reset();
154 nsCOMPtr<nsIWidget> oldWidget = sWidget;
155 nsCOMPtr<nsIWidget> newWidget =
156 aFocus ? aFocus->GetTextInputHandlingWidget() : nullptr;
157 // In the chrome-process case, we'll get sWidget from a PresShell later.
158 sWidget = newWidget;
159 if (oldWidget && sTextCompositions) {
160 RefPtr<TextComposition> composition =
161 sTextCompositions->GetCompositionFor(oldWidget);
162 if (composition) {
163 MOZ_LOG(
164 sISMLog, LogLevel::Debug,
165 (" OnFocusMovedBetweenBrowsers(), requesting to commit "
166 "composition to "
167 "the (previous) focused widget (would request=%s)",
168 GetBoolName(
169 !oldWidget->IMENotificationRequestsRef().WantDuringDeactive())));
170 NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, oldWidget,
171 composition->GetBrowserParent());
175 // The manager check is to avoid telling the content process to stop
176 // IME state management after focus has already moved there between
177 // two same-process-hosted out-of-process iframes.
178 if (aBlur && (!aFocus || (aBlur->Manager() != aFocus->Manager()))) {
179 MOZ_LOG(sISMLog, LogLevel::Debug,
180 (" OnFocusMovedBetweenBrowsers(), notifying previous "
181 "focused child process of parent process or another child process "
182 "getting focus"));
183 aBlur->StopIMEStateManagement();
186 if (sActiveIMEContentObserver) {
187 DestroyIMEContentObserver();
190 if (sFocusedIMEWidget) {
191 // sFocusedIMEBrowserParent can be null, if IME focus hasn't been
192 // taken before BrowserParent blur.
193 // aBlur can be null when keyboard focus moves not actually
194 // between tabs but an open menu is involved.
195 MOZ_ASSERT(!sFocusedIMEBrowserParent || !aBlur ||
196 (sFocusedIMEBrowserParent == aBlur));
197 MOZ_LOG(sISMLog, LogLevel::Debug,
198 (" OnFocusMovedBetweenBrowsers(), notifying IME of blur"));
199 NotifyIME(NOTIFY_IME_OF_BLUR, sFocusedIMEWidget, sFocusedIMEBrowserParent);
201 MOZ_ASSERT(!sFocusedIMEBrowserParent);
202 MOZ_ASSERT(!sFocusedIMEWidget);
204 } else {
205 MOZ_ASSERT(!sFocusedIMEBrowserParent);
208 // We deliberely don't null out sContent or sPresContext here. When
209 // focus is in remote content, as far as layout in the chrome process
210 // is concerned, the corresponding content is the top-level XUL
211 // browser. Changes among out-of-process iframes don't change that,
212 // so dropping the pointer to the XUL browser upon such a change
213 // would break IME handling.
216 // static
217 void IMEStateManager::WidgetDestroyed(nsIWidget* aWidget) {
218 if (sWidget == aWidget) {
219 sWidget = nullptr;
221 if (sFocusedIMEWidget == aWidget) {
222 if (sFocusedIMEBrowserParent) {
223 OnFocusMovedBetweenBrowsers(sFocusedIMEBrowserParent, nullptr);
224 MOZ_ASSERT(!sFocusedIMEBrowserParent);
226 sFocusedIMEWidget = nullptr;
228 if (sActiveInputContextWidget == aWidget) {
229 sActiveInputContextWidget = nullptr;
233 // static
234 void IMEStateManager::WidgetOnQuit(nsIWidget* aWidget) {
235 if (sFocusedIMEWidget == aWidget) {
236 // Try to do it the normal way first.
237 IMEStateManager::WidgetDestroyed(aWidget);
239 // And then in case the normal way didn't work:
240 nsCOMPtr<nsIWidget> quittingWidget(aWidget);
241 quittingWidget->NotifyIME(IMENotification(NOTIFY_IME_OF_BLUR));
244 // static
245 void IMEStateManager::StopIMEStateManagement() {
246 MOZ_ASSERT(XRE_IsContentProcess());
247 MOZ_LOG(sISMLog, LogLevel::Info, ("StopIMEStateManagement()"));
249 // NOTE: Don't set input context from here since this has already lost
250 // the rights to change input context.
252 // The requestee of this method in the main process must destroy its
253 // active IMEContentObserver for making existing composition end and
254 // make it be possible to start new composition in new focused process.
255 // Therefore, we shouldn't notify the main process of any changes which
256 // occurred after here.
257 AutoRestore<bool> restoreStoppingIMEStateManagementState(
258 sCleaningUpForStoppingIMEStateManagement);
259 sCleaningUpForStoppingIMEStateManagement = true;
261 if (sTextCompositions && sPresContext) {
262 NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, sPresContext, nullptr);
264 sActiveInputContextWidget = nullptr;
265 sPresContext = nullptr;
266 sContent = nullptr;
267 sIsActive = false;
268 DestroyIMEContentObserver();
271 // static
272 void IMEStateManager::MaybeStartOffsetUpdatedInChild(nsIWidget* aWidget,
273 uint32_t aStartOffset) {
274 if (NS_WARN_IF(!sTextCompositions)) {
275 MOZ_LOG(sISMLog, LogLevel::Warning,
276 ("MaybeStartOffsetUpdatedInChild(aWidget=0x%p, aStartOffset=%u), "
277 "called when there is no composition",
278 aWidget, aStartOffset));
279 return;
282 RefPtr<TextComposition> composition = GetTextCompositionFor(aWidget);
283 if (NS_WARN_IF(!composition)) {
284 MOZ_LOG(sISMLog, LogLevel::Warning,
285 ("MaybeStartOffsetUpdatedInChild(aWidget=0x%p, aStartOffset=%u), "
286 "called when there is no composition",
287 aWidget, aStartOffset));
288 return;
291 if (composition->NativeOffsetOfStartComposition() == aStartOffset) {
292 return;
295 MOZ_LOG(
296 sISMLog, LogLevel::Info,
297 ("MaybeStartOffsetUpdatedInChild(aWidget=0x%p, aStartOffset=%u), "
298 "old offset=%u",
299 aWidget, aStartOffset, composition->NativeOffsetOfStartComposition()));
300 composition->OnStartOffsetUpdatedInChild(aStartOffset);
303 // static
304 nsresult IMEStateManager::OnDestroyPresContext(nsPresContext* aPresContext) {
305 NS_ENSURE_ARG_POINTER(aPresContext);
307 // First, if there is a composition in the aPresContext, clean up it.
308 if (sTextCompositions) {
309 TextCompositionArray::index_type i =
310 sTextCompositions->IndexOf(aPresContext);
311 if (i != TextCompositionArray::NoIndex) {
312 MOZ_LOG(sISMLog, LogLevel::Debug,
313 (" OnDestroyPresContext(), "
314 "removing TextComposition instance from the array (index=%zu)",
315 i));
316 // there should be only one composition per presContext object.
317 sTextCompositions->ElementAt(i)->Destroy();
318 sTextCompositions->RemoveElementAt(i);
319 if (sTextCompositions->IndexOf(aPresContext) !=
320 TextCompositionArray::NoIndex) {
321 MOZ_LOG(sISMLog, LogLevel::Error,
322 (" OnDestroyPresContext(), FAILED to remove "
323 "TextComposition instance from the array"));
324 MOZ_CRASH("Failed to remove TextComposition instance from the array");
329 if (aPresContext != sPresContext) {
330 return NS_OK;
333 MOZ_LOG(
334 sISMLog, LogLevel::Info,
335 ("OnDestroyPresContext(aPresContext=0x%p), "
336 "sPresContext=0x%p, sContent=0x%p, sTextCompositions=0x%p",
337 aPresContext, sPresContext.get(), sContent.get(), sTextCompositions));
339 DestroyIMEContentObserver();
341 if (sWidget) {
342 IMEState newState = GetNewIMEState(sPresContext, nullptr);
343 InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
344 InputContextAction::LOST_FOCUS);
345 InputContext::Origin origin =
346 BrowserParent::GetFocused() ? InputContext::ORIGIN_CONTENT : sOrigin;
347 SetIMEState(newState, nullptr, nullptr, sWidget, action, origin);
349 sWidget = nullptr;
350 sContent = nullptr;
351 sPresContext = nullptr;
352 return NS_OK;
355 // static
356 nsresult IMEStateManager::OnRemoveContent(nsPresContext* aPresContext,
357 nsIContent* aContent) {
358 NS_ENSURE_ARG_POINTER(aPresContext);
360 // First, if there is a composition in the aContent, clean up it.
361 if (sTextCompositions) {
362 RefPtr<TextComposition> compositionInContent =
363 sTextCompositions->GetCompositionInContent(aPresContext, aContent);
365 if (compositionInContent) {
366 MOZ_LOG(sISMLog, LogLevel::Debug,
367 (" OnRemoveContent(), "
368 "composition is in the content"));
370 // Try resetting the native IME state. Be aware, typically, this method
371 // is called during the content being removed. Then, the native
372 // composition events which are caused by following APIs are ignored due
373 // to unsafe to run script (in PresShell::HandleEvent()).
374 nsresult rv =
375 compositionInContent->NotifyIME(REQUEST_TO_CANCEL_COMPOSITION);
376 if (NS_FAILED(rv)) {
377 compositionInContent->NotifyIME(REQUEST_TO_COMMIT_COMPOSITION);
382 if (!sPresContext || !sContent ||
383 !sContent->IsInclusiveDescendantOf(aContent)) {
384 return NS_OK;
387 MOZ_LOG(sISMLog, LogLevel::Info,
388 ("OnRemoveContent(aPresContext=0x%p, aContent=0x%p), "
389 "sPresContext=0x%p, sContent=0x%p, sTextCompositions=0x%p",
390 aPresContext, aContent, sPresContext.get(), sContent.get(),
391 sTextCompositions));
393 DestroyIMEContentObserver();
395 // Current IME transaction should commit
396 if (sWidget) {
397 IMEState newState = GetNewIMEState(sPresContext, nullptr);
398 InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
399 InputContextAction::LOST_FOCUS);
400 InputContext::Origin origin =
401 BrowserParent::GetFocused() ? InputContext::ORIGIN_CONTENT : sOrigin;
402 SetIMEState(newState, aPresContext, nullptr, sWidget, action, origin);
405 sWidget = nullptr;
406 sContent = nullptr;
407 sPresContext = nullptr;
409 return NS_OK;
412 // static
413 bool IMEStateManager::CanHandleWith(nsPresContext* aPresContext) {
414 return aPresContext && aPresContext->GetPresShell() &&
415 !aPresContext->PresShell()->IsDestroying();
418 // static
419 nsresult IMEStateManager::OnChangeFocus(nsPresContext* aPresContext,
420 nsIContent* aContent,
421 InputContextAction::Cause aCause) {
422 MOZ_LOG(sISMLog, LogLevel::Info,
423 ("OnChangeFocus(aPresContext=0x%p, aContent=0x%p, aCause=%s)",
424 aPresContext, aContent, ToString(aCause).c_str()));
426 InputContextAction action(aCause);
427 return OnChangeFocusInternal(aPresContext, aContent, action);
430 // static
431 nsresult IMEStateManager::OnChangeFocusInternal(nsPresContext* aPresContext,
432 nsIContent* aContent,
433 InputContextAction aAction) {
434 bool remoteHasFocus = EventStateManager::IsRemoteTarget(aContent);
435 // If we've handled focused content, we were inactive but now active,
436 // a remote process has focus, and setting focus to same content in the main
437 // process, it means that we're restoring focus without changing DOM focus
438 // both in the main process and the remote process.
439 const bool restoringContextForRemoteContent =
440 XRE_IsParentProcess() && remoteHasFocus && !sIsActive && aPresContext &&
441 sPresContext && sContent && sPresContext.get() == aPresContext &&
442 sContent.get() == aContent &&
443 aAction.mFocusChange != InputContextAction::MENU_GOT_PSEUDO_FOCUS;
445 MOZ_LOG(sISMLog, LogLevel::Info,
446 ("OnChangeFocusInternal(aPresContext=0x%p (available: %s), "
447 "aContent=0x%p (remote: %s), aAction={ mCause=%s, "
448 "mFocusChange=%s }), "
449 "sPresContext=0x%p (available: %s), sContent=0x%p, "
450 "sWidget=0x%p (available: %s), BrowserParent::GetFocused()=0x%p, "
451 "sActiveIMEContentObserver=0x%p, sInstalledMenuKeyboardListener=%s, "
452 "sIsActive=%s, restoringContextForRemoteContent=%s",
453 aPresContext, GetBoolName(CanHandleWith(aPresContext)), aContent,
454 GetBoolName(remoteHasFocus), ToString(aAction.mCause).c_str(),
455 ToString(aAction.mFocusChange).c_str(), sPresContext.get(),
456 GetBoolName(CanHandleWith(sPresContext)), sContent.get(), sWidget,
457 GetBoolName(sWidget && !sWidget->Destroyed()),
458 BrowserParent::GetFocused(), sActiveIMEContentObserver.get(),
459 GetBoolName(sInstalledMenuKeyboardListener), GetBoolName(sIsActive),
460 GetBoolName(restoringContextForRemoteContent)));
462 sIsActive = !!aPresContext;
463 if (sPendingFocusedBrowserSwitchingData.isSome()) {
464 MOZ_ASSERT(XRE_IsParentProcess());
465 nsCOMPtr<nsIContent> currentContent = sContent.get();
466 RefPtr<nsPresContext> currentPresContext = sPresContext.get();
467 RefPtr<BrowserParent> browserParentBlurred =
468 sPendingFocusedBrowserSwitchingData.ref().mBrowserParentBlurred;
469 RefPtr<BrowserParent> browserParentFocused =
470 sPendingFocusedBrowserSwitchingData.ref().mBrowserParentFocused;
471 OnFocusMovedBetweenBrowsers(browserParentBlurred, browserParentFocused);
472 // If another call of this method happens during the
473 // OnFocusMovedBetweenBrowsers call, we shouldn't take back focus to
474 // the old one.
475 if (currentContent != sContent.get() ||
476 currentPresContext != sPresContext.get()) {
477 MOZ_LOG(sISMLog, LogLevel::Debug,
478 (" OnChangeFocusInternal(aPresContext=0x%p, aContent=0x%p) "
479 "stoped handling it because the focused content was changed to "
480 "sPresContext=0x%p, sContent=0x%p by another call",
481 aPresContext, aContent, sPresContext.get(), sContent.get()));
482 return NS_OK;
486 // If new aPresShell has been destroyed, this should handle the focus change
487 // as nobody is getting focus.
488 if (NS_WARN_IF(aPresContext && !CanHandleWith(aPresContext))) {
489 MOZ_LOG(sISMLog, LogLevel::Warning,
490 (" OnChangeFocusInternal(), called with destroyed PresShell, "
491 "handling this call as nobody getting focus"));
492 aPresContext = nullptr;
493 aContent = nullptr;
496 nsCOMPtr<nsIWidget> oldWidget = sWidget;
497 nsCOMPtr<nsIWidget> newWidget =
498 aPresContext ? aPresContext->GetTextInputHandlingWidget() : nullptr;
499 bool focusActuallyChanging =
500 (sContent != aContent || sPresContext != aPresContext ||
501 oldWidget != newWidget ||
502 (remoteHasFocus && !restoringContextForRemoteContent &&
503 (aAction.mFocusChange != InputContextAction::MENU_GOT_PSEUDO_FOCUS)));
505 // If old widget has composition, we may need to commit composition since
506 // a native IME context is shared on all editors on some widgets or all
507 // widgets (it depends on platforms).
508 if (oldWidget && focusActuallyChanging && sTextCompositions) {
509 RefPtr<TextComposition> composition =
510 sTextCompositions->GetCompositionFor(oldWidget);
511 if (composition) {
512 // However, don't commit the composition if we're being inactivated
513 // but the composition should be kept even during deactive.
514 // Note that oldWidget and sFocusedIMEWidget may be different here (in
515 // such case, sFocusedIMEWidget is perhaps nullptr). For example, IME
516 // may receive only blur notification but still has composition.
517 // We need to clean up only the oldWidget's composition state here.
518 if (aPresContext ||
519 !oldWidget->IMENotificationRequestsRef().WantDuringDeactive()) {
520 MOZ_LOG(
521 sISMLog, LogLevel::Info,
522 (" OnChangeFocusInternal(), requesting to commit composition to "
523 "the (previous) focused widget"));
524 NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, oldWidget,
525 composition->GetBrowserParent());
530 if (sActiveIMEContentObserver) {
531 MOZ_ASSERT(!remoteHasFocus || XRE_IsContentProcess(),
532 "IMEContentObserver should have been destroyed by "
533 "OnFocusMovedBetweenBrowsers.");
534 if (!aPresContext) {
535 if (!sActiveIMEContentObserver->KeepAliveDuringDeactive()) {
536 DestroyIMEContentObserver();
539 // Otherwise, i.e., new focused content is in this process, let's check
540 // whether the new focused content is already being managed by the
541 // active IME content observer.
542 else if (!sActiveIMEContentObserver->IsManaging(aPresContext, aContent)) {
543 DestroyIMEContentObserver();
547 if (!aPresContext) {
548 MOZ_LOG(sISMLog, LogLevel::Debug,
549 (" OnChangeFocusInternal(), no nsPresContext is being activated"));
550 return NS_OK;
553 if (NS_WARN_IF(!newWidget)) {
554 MOZ_LOG(sISMLog, LogLevel::Error,
555 (" OnChangeFocusInternal(), FAILED due to no widget to manage its "
556 "IME state"));
557 return NS_OK;
560 // Update the cached widget since root view of the presContext may be
561 // changed to different view.
562 sWidget = newWidget;
564 // If a child process has focus, we should disable IME state until the child
565 // process actually gets focus because if user types keys before that they
566 // are handled by IME.
567 IMEState newState = remoteHasFocus ? IMEState(IMEEnabled::Disabled)
568 : GetNewIMEState(aPresContext, aContent);
569 bool setIMEState = true;
571 if (remoteHasFocus && XRE_IsParentProcess()) {
572 if (aAction.mFocusChange == InputContextAction::MENU_GOT_PSEUDO_FOCUS) {
573 // If menu keyboard listener is installed, we need to disable IME now.
574 setIMEState = true;
575 } else if (aAction.mFocusChange ==
576 InputContextAction::MENU_LOST_PSEUDO_FOCUS) {
577 // If menu keyboard listener is uninstalled, we need to restore
578 // input context which was set by the remote process. However, if
579 // the remote process hasn't been set input context yet, we need to
580 // wait next SetInputContextForChildProcess() call.
581 if (HasActiveChildSetInputContext()) {
582 setIMEState = true;
583 newState = sActiveChildInputContext.mIMEState;
584 } else {
585 setIMEState = false;
587 } else if (focusActuallyChanging) {
588 InputContext context = newWidget->GetInputContext();
589 if (context.mIMEState.mEnabled == IMEEnabled::Disabled &&
590 context.mOrigin == InputContext::ORIGIN_CONTENT) {
591 setIMEState = false;
592 MOZ_LOG(sISMLog, LogLevel::Debug,
593 (" OnChangeFocusInternal(), doesn't set IME state because "
594 "focused element (or document) is in a child process and the "
595 "IME state is already disabled by a remote process"));
596 } else {
597 // When new remote process gets focus, we should forget input context
598 // coming from old focused remote process.
599 ResetActiveChildInputContext();
600 MOZ_LOG(sISMLog, LogLevel::Debug,
601 (" OnChangeFocusInternal(), will disable IME until new "
602 "focused element (or document) in the child process will get "
603 "focus actually"));
605 } else if (newWidget->GetInputContext().mOrigin !=
606 InputContext::ORIGIN_MAIN) {
607 // When focus is NOT changed actually, we shouldn't set IME state if
608 // current input context was set by a remote process since that means
609 // that the window is being activated and the child process may have
610 // composition. Then, we shouldn't commit the composition with making
611 // IME state disabled.
612 setIMEState = false;
613 MOZ_LOG(
614 sISMLog, LogLevel::Debug,
615 (" OnChangeFocusInternal(), doesn't set IME state because focused "
616 "element (or document) is already in the child process"));
618 } else {
619 // When this process gets focus, we should forget input context coming
620 // from remote process.
621 ResetActiveChildInputContext();
624 if (setIMEState) {
625 if (!focusActuallyChanging) {
626 // actual focus isn't changing, but if IME enabled state is changing,
627 // we should do it.
628 InputContext context = newWidget->GetInputContext();
629 if (context.mIMEState.mEnabled == newState.mEnabled) {
630 MOZ_LOG(sISMLog, LogLevel::Debug,
631 (" OnChangeFocusInternal(), neither focus nor IME state is "
632 "changing"));
633 return NS_OK;
635 aAction.mFocusChange = InputContextAction::FOCUS_NOT_CHANGED;
637 // Even if focus isn't changing actually, we should commit current
638 // composition here since the IME state is changing.
639 if (sPresContext && oldWidget && !focusActuallyChanging) {
640 NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, oldWidget,
641 sFocusedIMEBrowserParent);
643 } else if (aAction.mFocusChange == InputContextAction::FOCUS_NOT_CHANGED) {
644 // If aContent isn't null or aContent is null but editable, somebody gets
645 // focus.
646 bool gotFocus = aContent || (newState.mEnabled == IMEEnabled::Enabled);
647 aAction.mFocusChange = gotFocus ? InputContextAction::GOT_FOCUS
648 : InputContextAction::LOST_FOCUS;
651 if (remoteHasFocus && HasActiveChildSetInputContext() &&
652 aAction.mFocusChange == InputContextAction::MENU_LOST_PSEUDO_FOCUS) {
653 // Restore the input context in the active remote process when
654 // menu keyboard listener is uninstalled and active remote tab has
655 // focus.
656 SetInputContext(newWidget, sActiveChildInputContext, aAction);
657 } else {
658 // Update IME state for new focus widget
659 SetIMEState(newState, aPresContext, aContent, newWidget, aAction,
660 remoteHasFocus ? InputContext::ORIGIN_CONTENT : sOrigin);
664 sPresContext = aPresContext;
665 sContent = aContent;
667 // Don't call CreateIMEContentObserver() here because it will be called from
668 // the focus event handler of focused editor.
670 MOZ_LOG(sISMLog, LogLevel::Debug,
671 (" OnChangeFocusInternal(), modified IME state for "
672 "sPresContext=0x%p, sContent=0x%p",
673 sPresContext.get(), sContent.get()));
675 return NS_OK;
678 // static
679 void IMEStateManager::OnInstalledMenuKeyboardListener(bool aInstalling) {
680 MOZ_LOG(
681 sISMLog, LogLevel::Info,
682 ("OnInstalledMenuKeyboardListener(aInstalling=%s), "
683 "sInstalledMenuKeyboardListener=%s, BrowserParent::GetFocused()=0x%p, "
684 "sActiveChildInputContext=%s",
685 GetBoolName(aInstalling), GetBoolName(sInstalledMenuKeyboardListener),
686 BrowserParent::GetFocused(),
687 ToString(sActiveChildInputContext).c_str()));
689 sInstalledMenuKeyboardListener = aInstalling;
691 InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
692 aInstalling
693 ? InputContextAction::MENU_GOT_PSEUDO_FOCUS
694 : InputContextAction::MENU_LOST_PSEUDO_FOCUS);
695 OnChangeFocusInternal(sPresContext, sContent, action);
698 // static
699 bool IMEStateManager::OnMouseButtonEventInEditor(
700 nsPresContext* aPresContext, nsIContent* aContent,
701 WidgetMouseEvent* aMouseEvent) {
702 MOZ_LOG(sISMLog, LogLevel::Info,
703 ("OnMouseButtonEventInEditor(aPresContext=0x%p (available: %s), "
704 "aContent=0x%p, aMouseEvent=0x%p), sPresContext=0x%p, sContent=0x%p",
705 aPresContext, GetBoolName(CanHandleWith(aPresContext)), aContent,
706 aMouseEvent, sPresContext.get(), sContent.get()));
708 if (NS_WARN_IF(!aMouseEvent)) {
709 MOZ_LOG(sISMLog, LogLevel::Debug,
710 (" OnMouseButtonEventInEditor(), aMouseEvent is nullptr"));
711 return false;
714 if (sPresContext != aPresContext || sContent != aContent) {
715 MOZ_LOG(sISMLog, LogLevel::Debug,
716 (" OnMouseButtonEventInEditor(), "
717 "the mouse event isn't fired on the editor managed by ISM"));
718 return false;
721 if (!sActiveIMEContentObserver) {
722 MOZ_LOG(sISMLog, LogLevel::Debug,
723 (" OnMouseButtonEventInEditor(), "
724 "there is no active IMEContentObserver"));
725 return false;
728 if (!sActiveIMEContentObserver->IsManaging(aPresContext, aContent)) {
729 MOZ_LOG(sISMLog, LogLevel::Debug,
730 (" OnMouseButtonEventInEditor(), "
731 "the active IMEContentObserver isn't managing the editor"));
732 return false;
735 RefPtr<IMEContentObserver> observer = sActiveIMEContentObserver;
736 bool consumed = observer->OnMouseButtonEvent(aPresContext, aMouseEvent);
738 if (MOZ_LOG_TEST(sISMLog, LogLevel::Info)) {
739 nsAutoString eventType;
740 MOZ_LOG(sISMLog, LogLevel::Info,
741 (" OnMouseButtonEventInEditor(), "
742 "mouse event (mMessage=%s, mButton=%d) is %s",
743 ToChar(aMouseEvent->mMessage), aMouseEvent->mButton,
744 consumed ? "consumed" : "not consumed"));
747 return consumed;
750 // static
751 void IMEStateManager::OnClickInEditor(nsPresContext* aPresContext,
752 nsIContent* aContent,
753 const WidgetMouseEvent* aMouseEvent) {
754 MOZ_LOG(sISMLog, LogLevel::Info,
755 ("OnClickInEditor(aPresContext=0x%p (available: %s), aContent=0x%p, "
756 "aMouseEvent=0x%p), sPresContext=0x%p, sContent=0x%p, sWidget=0x%p "
757 "(available: %s)",
758 aPresContext, GetBoolName(CanHandleWith(aPresContext)), aContent,
759 aMouseEvent, sPresContext.get(), sContent.get(), sWidget,
760 GetBoolName(sWidget && !sWidget->Destroyed())));
762 if (NS_WARN_IF(!aMouseEvent)) {
763 return;
766 if (sPresContext != aPresContext || sContent != aContent ||
767 NS_WARN_IF(!sPresContext) || NS_WARN_IF(!sWidget) ||
768 NS_WARN_IF(sWidget->Destroyed())) {
769 MOZ_LOG(sISMLog, LogLevel::Debug,
770 (" OnClickInEditor(), "
771 "the mouse event isn't fired on the editor managed by ISM"));
772 return;
775 nsCOMPtr<nsIWidget> widget(sWidget);
777 #ifdef DEBUG
779 nsCOMPtr<nsIWidget> tihWidget = sPresContext->GetTextInputHandlingWidget();
780 MOZ_ASSERT(!tihWidget || tihWidget == widget);
782 #endif // DEBUG
784 if (!aMouseEvent->IsTrusted()) {
785 MOZ_LOG(sISMLog, LogLevel::Debug,
786 (" OnClickInEditor(), "
787 "the mouse event isn't a trusted event"));
788 return; // ignore untrusted event.
791 if (aMouseEvent->mButton) {
792 MOZ_LOG(sISMLog, LogLevel::Debug,
793 (" OnClickInEditor(), "
794 "the mouse event isn't a left mouse button event"));
795 return; // not a left click event.
798 if (aMouseEvent->mClickCount != 1) {
799 MOZ_LOG(sISMLog, LogLevel::Debug,
800 (" OnClickInEditor(), "
801 "the mouse event isn't a single click event"));
802 return; // should notify only first click event.
805 InputContextAction::Cause cause =
806 aMouseEvent->mInputSource == MouseEvent_Binding::MOZ_SOURCE_TOUCH
807 ? InputContextAction::CAUSE_TOUCH
808 : InputContextAction::CAUSE_MOUSE;
810 InputContextAction action(cause, InputContextAction::FOCUS_NOT_CHANGED);
811 IMEState newState = GetNewIMEState(aPresContext, aContent);
812 SetIMEState(newState, aPresContext, aContent, widget, action, sOrigin);
815 // static
816 bool IMEStateManager::IsFocusedContent(const nsPresContext* aPresContext,
817 const nsIContent* aFocusedContent) {
818 if (!aPresContext || !sPresContext || aPresContext != sPresContext) {
819 return false;
822 if (sContent == aFocusedContent) {
823 return true;
826 // If sContent is not nullptr, but aFocusedContent is nullptr, it does not
827 // have focus from point of view of IMEStateManager.
828 if (sContent) {
829 return false;
832 // If the caller does not think that nobody has focus, but we know there is
833 // a focused content, the caller must be called with wrong content.
834 if (!aFocusedContent) {
835 return false;
838 // If the aFocusedContent is in design mode, sContent may be nullptr.
839 if (aFocusedContent->IsInDesignMode()) {
840 MOZ_ASSERT(aPresContext == sPresContext && !sContent);
841 return true;
844 // Otherwise, only when aFocusedContent is the root element, it can have
845 // focus, but IMEStateManager::OnChangeFocus is called with nullptr for
846 // aContent if it was not editable.
847 // XXX In this case, should the caller update sContent?
848 return aFocusedContent->IsEditable() &&
849 sPresContext->Document()->GetRootElement() == aFocusedContent;
852 // static
853 void IMEStateManager::OnFocusInEditor(nsPresContext* aPresContext,
854 nsIContent* aContent,
855 EditorBase& aEditorBase) {
856 MOZ_LOG(sISMLog, LogLevel::Info,
857 ("OnFocusInEditor(aPresContext=0x%p (available: %s), aContent=0x%p, "
858 "aEditorBase=0x%p), sPresContext=0x%p, sContent=0x%p, "
859 "sActiveIMEContentObserver=0x%p",
860 aPresContext, GetBoolName(CanHandleWith(aPresContext)), aContent,
861 &aEditorBase, sPresContext.get(), sContent.get(),
862 sActiveIMEContentObserver.get()));
864 if (!IsFocusedContent(aPresContext, aContent)) {
865 MOZ_LOG(sISMLog, LogLevel::Debug,
866 (" OnFocusInEditor(), "
867 "an editor not managed by ISM gets focus"));
868 return;
871 // If the IMEContentObserver instance isn't managing the editor actually,
872 // we need to recreate the instance.
873 if (sActiveIMEContentObserver) {
874 if (sActiveIMEContentObserver->IsManaging(aPresContext, aContent)) {
875 MOZ_LOG(
876 sISMLog, LogLevel::Debug,
877 (" OnFocusInEditor(), "
878 "the editor is already being managed by sActiveIMEContentObserver"));
879 return;
881 // If the IMEContentObserver has not finished initializing itself yet,
882 // we don't need to recreate it because the following
883 // TryToFlushPendingNotifications call must make it initialized.
884 if (!sActiveIMEContentObserver->IsBeingInitializedFor(aPresContext,
885 aContent)) {
886 DestroyIMEContentObserver();
890 if (!sActiveIMEContentObserver) {
891 CreateIMEContentObserver(aEditorBase, aContent);
892 if (sActiveIMEContentObserver) {
893 MOZ_LOG(sISMLog, LogLevel::Debug,
894 (" OnFocusInEditor(), new IMEContentObserver is created (0x%p)",
895 sActiveIMEContentObserver.get()));
899 if (sActiveIMEContentObserver) {
900 sActiveIMEContentObserver->TryToFlushPendingNotifications(false);
901 MOZ_LOG(sISMLog, LogLevel::Debug,
902 (" OnFocusInEditor(), trying to send pending notifications in "
903 "the active IMEContentObserver (0x%p)...",
904 sActiveIMEContentObserver.get()));
908 // static
909 void IMEStateManager::OnEditorInitialized(EditorBase& aEditorBase) {
910 if (!sActiveIMEContentObserver ||
911 !sActiveIMEContentObserver->WasInitializedWith(aEditorBase)) {
912 return;
915 MOZ_LOG(sISMLog, LogLevel::Info,
916 ("OnEditorInitialized(aEditorBase=0x%p)", &aEditorBase));
918 sActiveIMEContentObserver->UnsuppressNotifyingIME();
921 // static
922 void IMEStateManager::OnEditorDestroying(EditorBase& aEditorBase) {
923 if (!sActiveIMEContentObserver ||
924 !sActiveIMEContentObserver->WasInitializedWith(aEditorBase)) {
925 return;
928 MOZ_LOG(sISMLog, LogLevel::Info,
929 ("OnEditorDestroying(aEditorBase=0x%p)", &aEditorBase));
931 // The IMEContentObserver shouldn't notify IME of anything until reframing
932 // is finished.
933 sActiveIMEContentObserver->SuppressNotifyingIME();
936 void IMEStateManager::OnReFocus(nsPresContext* aPresContext,
937 nsIContent& aContent) {
938 MOZ_LOG(sISMLog, LogLevel::Info,
939 ("OnReFocus(aPresContext=0x%p (available: %s), aContent=0x%p), "
940 "sActiveIMEContentObserver=0x%p, sContent=0x%p",
941 aPresContext, GetBoolName(CanHandleWith(aPresContext)), &aContent,
942 sActiveIMEContentObserver.get(), sContent.get()));
944 if (NS_WARN_IF(!sWidget) || NS_WARN_IF(sWidget->Destroyed())) {
945 return;
948 if (!sActiveIMEContentObserver ||
949 !sActiveIMEContentObserver->IsManaging(aPresContext, &aContent)) {
950 MOZ_LOG(sISMLog, LogLevel::Debug,
951 (" OnReFocus(), there is no valid IMEContentObserver, so we don't "
952 "manage this. Ignore this"));
953 return;
956 MOZ_ASSERT(&aContent == sContent.get());
958 if (!UserActivation::IsHandlingUserInput() ||
959 UserActivation::IsHandlingKeyboardInput()) {
960 return;
963 nsCOMPtr<nsIWidget> widget(sWidget);
965 // Don't update IME state during composition.
966 if (sTextCompositions) {
967 if (TextComposition* composition =
968 sTextCompositions->GetCompositionFor(widget)) {
969 if (composition->IsComposing()) {
970 return;
975 InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
976 InputContextAction::FOCUS_NOT_CHANGED);
977 IMEState newState = GetNewIMEState(aPresContext, &aContent);
978 SetIMEState(newState, aPresContext, &aContent, widget, action, sOrigin);
981 // static
982 void IMEStateManager::UpdateIMEState(const IMEState& aNewIMEState,
983 nsIContent* aContent,
984 EditorBase& aEditorBase,
985 const UpdateIMEStateOptions& aOptions) {
986 MOZ_LOG(
987 sISMLog, LogLevel::Info,
988 ("UpdateIMEState(aNewIMEState=%s, aContent=0x%p, aEditorBase=0x%p, "
989 "aOptions=0x%0x), sPresContext=0x%p (available: %s), sContent=0x%p, "
990 "sWidget=0x%p (available: %s), sActiveIMEContentObserver=0x%p, "
991 "sIsGettingNewIMEState=%s",
992 ToString(aNewIMEState).c_str(), aContent, &aEditorBase,
993 aOptions.serialize(), sPresContext.get(),
994 GetBoolName(CanHandleWith(sPresContext)), sContent.get(), sWidget,
995 GetBoolName(sWidget && !sWidget->Destroyed()),
996 sActiveIMEContentObserver.get(), GetBoolName(sIsGettingNewIMEState)));
998 if (sIsGettingNewIMEState) {
999 MOZ_LOG(sISMLog, LogLevel::Debug,
1000 (" UpdateIMEState(), "
1001 "does nothing because of called while getting new IME state"));
1002 return;
1005 RefPtr<PresShell> presShell(aEditorBase.GetPresShell());
1006 if (NS_WARN_IF(!presShell)) {
1007 MOZ_LOG(sISMLog, LogLevel::Error,
1008 (" UpdateIMEState(), FAILED due to "
1009 "editor doesn't have PresShell"));
1010 return;
1013 nsPresContext* presContext = presShell->GetPresContext();
1014 if (NS_WARN_IF(!presContext)) {
1015 MOZ_LOG(sISMLog, LogLevel::Error,
1016 (" UpdateIMEState(), FAILED due to "
1017 "editor doesn't have PresContext"));
1018 return;
1021 // IMEStateManager::UpdateIMEState() should be called after
1022 // IMEStateManager::OnChangeFocus() is called for setting focus to aContent
1023 // and aEditorBase. However, when aEditorBase is an HTMLEditor, this may be
1024 // called by nsIEditor::PostCreate() before IMEStateManager::OnChangeFocus().
1025 // Similarly, when aEditorBase is a TextEditor, this may be called by
1026 // nsIEditor::SetFlags(). In such cases, this method should do nothing
1027 // because input context should be updated when
1028 // IMEStateManager::OnChangeFocus() is called later.
1029 if (sPresContext != presContext) {
1030 MOZ_LOG(sISMLog, LogLevel::Warning,
1031 (" UpdateIMEState(), does nothing due to "
1032 "the editor hasn't managed by IMEStateManager yet"));
1033 return;
1036 // If IMEStateManager doesn't manage any document, this cannot update IME
1037 // state of any widget.
1038 if (NS_WARN_IF(!sPresContext)) {
1039 MOZ_LOG(sISMLog, LogLevel::Error,
1040 (" UpdateIMEState(), FAILED due to "
1041 "no managing nsPresContext"));
1042 return;
1045 if (NS_WARN_IF(!sWidget) || NS_WARN_IF(sWidget->Destroyed())) {
1046 MOZ_LOG(sISMLog, LogLevel::Error,
1047 (" UpdateIMEState(), FAILED due to "
1048 "the widget for the managing nsPresContext has gone"));
1049 return;
1052 OwningNonNull<nsIWidget> widget(*sWidget);
1054 #ifdef DEBUG
1056 nsCOMPtr<nsIWidget> tihWidget = sPresContext->GetTextInputHandlingWidget();
1057 MOZ_ASSERT(!tihWidget || tihWidget == widget);
1059 #endif // DEBUG
1061 // TODO: Investigate if we could put off to initialize IMEContentObserver
1062 // later because a lot of callers need to be marked as
1063 // MOZ_CAN_RUN_SCRIPT otherwise.
1065 // Even if there is active IMEContentObserver, it may not be observing the
1066 // editor with current editable root content due to reframed. In such case,
1067 // We should try to reinitialize the IMEContentObserver.
1068 if (sActiveIMEContentObserver && IsIMEObserverNeeded(aNewIMEState)) {
1069 MOZ_LOG(sISMLog, LogLevel::Debug,
1070 (" UpdateIMEState(), try to reinitialize the "
1071 "active IMEContentObserver"));
1072 RefPtr<IMEContentObserver> contentObserver = sActiveIMEContentObserver;
1073 OwningNonNull<nsPresContext> presContext(*sPresContext);
1074 if (!contentObserver->MaybeReinitialize(widget, presContext, aContent,
1075 aEditorBase)) {
1076 MOZ_LOG(sISMLog, LogLevel::Error,
1077 (" UpdateIMEState(), failed to reinitialize the "
1078 "active IMEContentObserver"));
1080 if (NS_WARN_IF(widget->Destroyed())) {
1081 MOZ_LOG(sISMLog, LogLevel::Error,
1082 (" UpdateIMEState(), widget has gone during reinitializing the "
1083 "active IMEContentObserver"));
1084 return;
1088 // If there is no active IMEContentObserver or it isn't observing the
1089 // editor correctly, we should recreate it.
1090 bool createTextStateManager =
1091 (!sActiveIMEContentObserver ||
1092 !sActiveIMEContentObserver->IsManaging(sPresContext, aContent));
1094 bool updateIMEState =
1095 aOptions.contains(UpdateIMEStateOption::ForceUpdate) ||
1096 (widget->GetInputContext().mIMEState.mEnabled != aNewIMEState.mEnabled);
1097 if (NS_WARN_IF(widget->Destroyed())) {
1098 MOZ_LOG(
1099 sISMLog, LogLevel::Error,
1100 (" UpdateIMEState(), widget has gone during getting input context"));
1101 return;
1104 if (updateIMEState &&
1105 !aOptions.contains(UpdateIMEStateOption::DontCommitComposition)) {
1106 // commit current composition before modifying IME state.
1107 NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, widget, sFocusedIMEBrowserParent);
1108 if (NS_WARN_IF(widget->Destroyed())) {
1109 MOZ_LOG(sISMLog, LogLevel::Error,
1110 (" UpdateIMEState(), widget has gone during committing "
1111 "composition"));
1112 return;
1116 if (createTextStateManager) {
1117 DestroyIMEContentObserver();
1120 if (updateIMEState) {
1121 InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
1122 InputContextAction::FOCUS_NOT_CHANGED);
1123 SetIMEState(aNewIMEState, presContext, aContent, widget, action, sOrigin);
1124 if (NS_WARN_IF(widget->Destroyed())) {
1125 MOZ_LOG(
1126 sISMLog, LogLevel::Error,
1127 (" UpdateIMEState(), widget has gone during setting input context"));
1128 return;
1132 NS_ASSERTION(IsFocusedContent(presContext, aContent),
1133 "aContent does not match with sContent");
1135 if (createTextStateManager) {
1136 // XXX In this case, it might not be enough safe to notify IME of anything.
1137 // So, don't try to flush pending notifications of IMEContentObserver
1138 // here.
1139 CreateIMEContentObserver(aEditorBase, aContent);
1143 // static
1144 IMEState IMEStateManager::GetNewIMEState(nsPresContext* aPresContext,
1145 nsIContent* aContent) {
1146 MOZ_LOG(
1147 sISMLog, LogLevel::Info,
1148 ("GetNewIMEState(aPresContext=0x%p, aContent=0x%p), "
1149 "sInstalledMenuKeyboardListener=%s",
1150 aPresContext, aContent, GetBoolName(sInstalledMenuKeyboardListener)));
1152 if (!CanHandleWith(aPresContext)) {
1153 MOZ_LOG(sISMLog, LogLevel::Debug,
1154 (" GetNewIMEState() returns IMEEnabled::Disabled because "
1155 "the nsPresContext has been destroyed"));
1156 return IMEState(IMEEnabled::Disabled);
1159 // On Printing or Print Preview, we don't need IME.
1160 if (aPresContext->Type() == nsPresContext::eContext_PrintPreview ||
1161 aPresContext->Type() == nsPresContext::eContext_Print) {
1162 MOZ_LOG(sISMLog, LogLevel::Debug,
1163 (" GetNewIMEState() returns IMEEnabled::Disabled because "
1164 "the nsPresContext is for print or print preview"));
1165 return IMEState(IMEEnabled::Disabled);
1168 if (sInstalledMenuKeyboardListener) {
1169 MOZ_LOG(sISMLog, LogLevel::Debug,
1170 (" GetNewIMEState() returns IMEEnabled::Disabled because "
1171 "menu keyboard listener was installed"));
1172 return IMEState(IMEEnabled::Disabled);
1175 if (!aContent) {
1176 // Even if there are no focused content, the focused document might be
1177 // editable, such case is design mode.
1178 if (aPresContext->Document() &&
1179 aPresContext->Document()->IsInDesignMode()) {
1180 MOZ_LOG(sISMLog, LogLevel::Debug,
1181 (" GetNewIMEState() returns IMEEnabled::Enabled because "
1182 "design mode editor has focus"));
1183 return IMEState(IMEEnabled::Enabled);
1185 MOZ_LOG(sISMLog, LogLevel::Debug,
1186 (" GetNewIMEState() returns IMEEnabled::Disabled because "
1187 "no content has focus"));
1188 return IMEState(IMEEnabled::Disabled);
1191 // If aContent is in designMode, aContent should be the root node of the
1192 // document.
1193 if (aContent && aContent->IsInDesignMode()) {
1194 MOZ_LOG(sISMLog, LogLevel::Debug,
1195 (" GetNewIMEState() returns IMEEnabled::Enabled because "
1196 "a content node in design mode editor has focus"));
1197 return IMEState(IMEEnabled::Enabled);
1200 // nsIContent::GetDesiredIMEState() may cause a call of UpdateIMEState()
1201 // from EditorBase::PostCreate() because GetDesiredIMEState() needs to
1202 // retrieve an editor instance for the element if it's editable element.
1203 // For avoiding such nested IME state updates, we should set
1204 // sIsGettingNewIMEState here and UpdateIMEState() should check it.
1205 GettingNewIMEStateBlocker blocker;
1207 IMEState newIMEState = aContent->GetDesiredIMEState();
1208 MOZ_LOG(sISMLog, LogLevel::Debug,
1209 (" GetNewIMEState() returns %s", ToString(newIMEState).c_str()));
1210 return newIMEState;
1213 static bool MayBeIMEUnawareWebApp(nsINode* aNode) {
1214 bool haveKeyEventsListener = false;
1216 while (aNode) {
1217 EventListenerManager* const mgr = aNode->GetExistingListenerManager();
1218 if (mgr) {
1219 if (mgr->MayHaveInputOrCompositionEventListener()) {
1220 return false;
1222 haveKeyEventsListener |= mgr->MayHaveKeyEventListener();
1224 aNode = aNode->GetParentNode();
1227 return haveKeyEventsListener;
1230 // static
1231 void IMEStateManager::ResetActiveChildInputContext() {
1232 sActiveChildInputContext.mIMEState.mEnabled = IMEEnabled::Unknown;
1235 // static
1236 bool IMEStateManager::HasActiveChildSetInputContext() {
1237 return sActiveChildInputContext.mIMEState.mEnabled != IMEEnabled::Unknown;
1240 // static
1241 void IMEStateManager::SetInputContextForChildProcess(
1242 BrowserParent* aBrowserParent, const InputContext& aInputContext,
1243 const InputContextAction& aAction) {
1244 MOZ_LOG(
1245 sISMLog, LogLevel::Info,
1246 ("SetInputContextForChildProcess(aBrowserParent=0x%p, "
1247 "aInputContext=%s , aAction={ mCause=%s, mAction=%s }), "
1248 "sPresContext=0x%p (available: %s), sWidget=0x%p (available: %s), "
1249 "BrowserParent::GetFocused()=0x%p, sInstalledMenuKeyboardListener=%s",
1250 aBrowserParent, ToString(aInputContext).c_str(),
1251 ToString(aAction.mCause).c_str(), ToString(aAction.mFocusChange).c_str(),
1252 sPresContext.get(), GetBoolName(CanHandleWith(sPresContext)), sWidget,
1253 GetBoolName(sWidget && !sWidget->Destroyed()),
1254 BrowserParent::GetFocused(),
1255 GetBoolName(sInstalledMenuKeyboardListener)));
1257 if (aBrowserParent != BrowserParent::GetFocused()) {
1258 MOZ_LOG(sISMLog, LogLevel::Error,
1259 (" SetInputContextForChildProcess(), FAILED, "
1260 "because non-focused tab parent tries to set input context"));
1261 return;
1264 if (NS_WARN_IF(!CanHandleWith(sPresContext))) {
1265 MOZ_LOG(sISMLog, LogLevel::Error,
1266 (" SetInputContextForChildProcess(), FAILED, "
1267 "due to no focused presContext"));
1268 return;
1271 if (NS_WARN_IF(!sWidget) || NS_WARN_IF(sWidget->Destroyed())) {
1272 MOZ_LOG(sISMLog, LogLevel::Error,
1273 (" SetInputContextForChildProcess(), FAILED, "
1274 "due to the widget for the nsPresContext has gone"));
1275 return;
1278 nsCOMPtr<nsIWidget> widget(sWidget);
1280 #ifdef DEBUG
1282 nsCOMPtr<nsIWidget> tihWidget = sPresContext->GetTextInputHandlingWidget();
1283 MOZ_ASSERT(!tihWidget || tihWidget == widget);
1285 #endif // DEBUG
1286 MOZ_ASSERT(aInputContext.mOrigin == InputContext::ORIGIN_CONTENT);
1288 sActiveChildInputContext = aInputContext;
1289 MOZ_ASSERT(HasActiveChildSetInputContext());
1291 // If input context is changed in remote process while menu keyboard listener
1292 // is installed, this process shouldn't set input context now. When it's
1293 // uninstalled, input context should be restored from
1294 // sActiveChildInputContext.
1295 if (sInstalledMenuKeyboardListener) {
1296 MOZ_LOG(sISMLog, LogLevel::Info,
1297 (" SetInputContextForChildProcess(), waiting to set input context "
1298 "until menu keyboard listener is uninstalled"));
1299 return;
1302 SetInputContext(widget, aInputContext, aAction);
1305 static bool IsNextFocusableElementTextControl(const Element* aInputContent) {
1306 nsFocusManager* fm = nsFocusManager::GetFocusManager();
1307 if (!fm) {
1308 return false;
1310 nsCOMPtr<nsIContent> nextContent;
1311 nsresult rv = fm->DetermineElementToMoveFocus(
1312 aInputContent->OwnerDoc()->GetWindow(),
1313 const_cast<Element*>(aInputContent), nsIFocusManager::MOVEFOCUS_FORWARD,
1314 true, false, getter_AddRefs(nextContent));
1315 if (NS_WARN_IF(NS_FAILED(rv)) || !nextContent) {
1316 return false;
1318 nextContent = nextContent->FindFirstNonChromeOnlyAccessContent();
1319 nsCOMPtr<nsIFormControl> nextControl = do_QueryInterface(nextContent);
1320 if (!nextControl || !nextControl->IsTextControl(false)) {
1321 return false;
1324 // XXX We don't consider next element is date/time control yet.
1325 nsGenericHTMLElement* nextElement =
1326 nsGenericHTMLElement::FromNodeOrNull(nextContent);
1327 if (!nextElement) {
1328 return false;
1330 bool focusable = false;
1331 nextElement->IsHTMLFocusable(false, &focusable, nullptr);
1332 if (!focusable) {
1333 return false;
1336 // Check readonly attribute.
1337 if (nextElement->IsHTMLElement(nsGkAtoms::textarea)) {
1338 HTMLTextAreaElement* textAreaElement =
1339 HTMLTextAreaElement::FromNodeOrNull(nextElement);
1340 return !textAreaElement->ReadOnly();
1343 // If neither textarea nor input, what element type?
1344 MOZ_DIAGNOSTIC_ASSERT(nextElement->IsHTMLElement(nsGkAtoms::input));
1346 HTMLInputElement* inputElement =
1347 HTMLInputElement::FromNodeOrNull(nextElement);
1348 return !inputElement->ReadOnly();
1351 static void GetInputType(const IMEState& aState, const nsIContent& aContent,
1352 nsAString& aInputType) {
1353 if (aContent.IsHTMLElement(nsGkAtoms::input)) {
1354 const HTMLInputElement* inputElement =
1355 HTMLInputElement::FromNode(&aContent);
1356 if (inputElement->HasBeenTypePassword() && aState.IsEditable()) {
1357 aInputType.AssignLiteral("password");
1358 } else {
1359 inputElement->GetType(aInputType);
1361 } else if (aContent.IsHTMLElement(nsGkAtoms::textarea)) {
1362 aInputType.Assign(nsGkAtoms::textarea->GetUTF16String());
1366 static void GetActionHint(const IMEState& aState, const nsIContent& aContent,
1367 nsAString& aActionHint) {
1368 MOZ_ASSERT(aContent.IsHTMLElement());
1370 if (aState.IsEditable() && StaticPrefs::dom_forms_enterkeyhint()) {
1371 nsGenericHTMLElement::FromNode(&aContent)->GetEnterKeyHint(aActionHint);
1373 // If enterkeyhint is set, we don't infer action hint.
1374 if (!aActionHint.IsEmpty()) {
1375 return;
1379 if (!aContent.IsAnyOfHTMLElements(nsGkAtoms::input, nsGkAtoms::textarea)) {
1380 return;
1383 // XXX This is old compatibility, but we might be able to remove this.
1384 aContent.AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::moz_action_hint,
1385 aActionHint);
1387 if (!aActionHint.IsEmpty()) {
1388 ToLowerCase(aActionHint);
1389 return;
1392 // Get the input content corresponding to the focused node,
1393 // which may be an anonymous child of the input content.
1394 MOZ_ASSERT(&aContent == aContent.FindFirstNonChromeOnlyAccessContent());
1395 const HTMLInputElement* inputElement = HTMLInputElement::FromNode(aContent);
1396 if (!inputElement) {
1397 return;
1400 // If we don't have an action hint and
1401 // return won't submit the form, use "maybenext".
1402 bool willSubmit = false;
1403 bool isLastElement = false;
1404 HTMLFormElement* formElement = inputElement->GetForm();
1405 // is this a form and does it have a default submit element?
1406 if (formElement) {
1407 if (formElement->IsLastActiveElement(inputElement)) {
1408 isLastElement = true;
1411 if (formElement->GetDefaultSubmitElement()) {
1412 willSubmit = true;
1413 // is this an html form...
1414 } else {
1415 // ... and does it only have a single text input element ?
1416 if (!formElement->ImplicitSubmissionIsDisabled() ||
1417 // ... or is this the last non-disabled element?
1418 isLastElement) {
1419 willSubmit = true;
1424 if (!isLastElement && formElement) {
1425 // If next tabbable content in form is text control, hint should be "next"
1426 // even there is submit in form.
1427 if (IsNextFocusableElementTextControl(inputElement)) {
1428 // This is focusable text control
1429 // XXX What good hint for read only field?
1430 aActionHint.AssignLiteral("maybenext");
1431 return;
1435 if (!willSubmit) {
1436 return;
1439 if (inputElement->ControlType() == FormControlType::InputSearch) {
1440 aActionHint.AssignLiteral("search");
1441 return;
1444 aActionHint.AssignLiteral("go");
1447 static void GetInputmode(const IMEState& aState, const nsIContent& aContent,
1448 nsAString& aInputmode) {
1449 if (aState.IsEditable() &&
1450 (StaticPrefs::dom_forms_inputmode() ||
1451 nsContentUtils::IsChromeDoc(aContent.OwnerDoc()))) {
1452 aContent.AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::inputmode,
1453 aInputmode);
1454 if (aContent.IsHTMLElement(nsGkAtoms::input) &&
1455 aInputmode.EqualsLiteral("mozAwesomebar")) {
1456 if (!nsContentUtils::IsChromeDoc(aContent.OwnerDoc())) {
1457 // mozAwesomebar should be allowed only in chrome
1458 aInputmode.Truncate();
1460 } else {
1461 ToLowerCase(aInputmode);
1466 static void GetAutocapitalize(const IMEState& aState,
1467 const nsIContent& aContent,
1468 const InputContext& aInputContext,
1469 nsAString& aAutocapitalize) {
1470 if (aContent.IsHTMLElement() && aState.IsEditable() &&
1471 StaticPrefs::dom_forms_autocapitalize() &&
1472 aInputContext.IsAutocapitalizeSupported()) {
1473 nsGenericHTMLElement::FromNode(&aContent)->GetAutocapitalize(
1474 aAutocapitalize);
1478 // static
1479 void IMEStateManager::SetIMEState(const IMEState& aState,
1480 nsPresContext* aPresContext,
1481 nsIContent* aContent, nsIWidget* aWidget,
1482 InputContextAction aAction,
1483 InputContext::Origin aOrigin) {
1484 MOZ_LOG(sISMLog, LogLevel::Info,
1485 ("SetIMEState(aState=%s, aContent=0x%p (BrowserParent=0x%p), "
1486 "aWidget=0x%p, aAction={ mCause=%s, mFocusChange=%s }, aOrigin=%s)",
1487 ToString(aState).c_str(), aContent, BrowserParent::GetFrom(aContent),
1488 aWidget, ToString(aAction.mCause).c_str(),
1489 ToString(aAction.mFocusChange).c_str(), ToChar(aOrigin)));
1491 NS_ENSURE_TRUE_VOID(aWidget);
1493 InputContext context;
1494 context.mIMEState = aState;
1495 context.mOrigin = aOrigin;
1496 context.mMayBeIMEUnaware =
1497 context.mIMEState.IsEditable() &&
1498 StaticPrefs::
1499 intl_ime_hack_on_ime_unaware_apps_fire_key_events_for_composition() &&
1500 MayBeIMEUnawareWebApp(aContent);
1502 context.mHasHandledUserInput =
1503 aPresContext && aPresContext->PresShell()->HasHandledUserInput();
1505 context.mInPrivateBrowsing =
1506 aPresContext &&
1507 nsContentUtils::IsInPrivateBrowsing(aPresContext->Document());
1509 nsIContent* focusedContent =
1510 aContent ? aContent->FindFirstNonChromeOnlyAccessContent() : nullptr;
1512 if (focusedContent && focusedContent->IsHTMLElement()) {
1513 GetInputType(aState, *focusedContent, context.mHTMLInputType);
1514 GetActionHint(aState, *focusedContent, context.mActionHint);
1515 GetInputmode(aState, *focusedContent, context.mHTMLInputInputmode);
1516 GetAutocapitalize(aState, *focusedContent, context,
1517 context.mAutocapitalize);
1520 if (aAction.mCause == InputContextAction::CAUSE_UNKNOWN &&
1521 nsContentUtils::LegacyIsCallerChromeOrNativeCode()) {
1522 aAction.mCause = InputContextAction::CAUSE_UNKNOWN_CHROME;
1525 if ((aAction.mCause == InputContextAction::CAUSE_UNKNOWN ||
1526 aAction.mCause == InputContextAction::CAUSE_UNKNOWN_CHROME) &&
1527 UserActivation::IsHandlingUserInput()) {
1528 aAction.mCause =
1529 UserActivation::IsHandlingKeyboardInput()
1530 ? InputContextAction::CAUSE_UNKNOWN_DURING_KEYBOARD_INPUT
1531 : InputContextAction::CAUSE_UNKNOWN_DURING_NON_KEYBOARD_INPUT;
1534 SetInputContext(aWidget, context, aAction);
1537 // static
1538 void IMEStateManager::SetInputContext(nsIWidget* aWidget,
1539 const InputContext& aInputContext,
1540 const InputContextAction& aAction) {
1541 MOZ_LOG(
1542 sISMLog, LogLevel::Info,
1543 ("SetInputContext(aWidget=0x%p, aInputContext=%s, "
1544 "aAction={ mCause=%s, mAction=%s }), BrowserParent::GetFocused()=0x%p",
1545 aWidget, ToString(aInputContext).c_str(),
1546 ToString(aAction.mCause).c_str(), ToString(aAction.mFocusChange).c_str(),
1547 BrowserParent::GetFocused()));
1549 MOZ_RELEASE_ASSERT(aWidget);
1551 nsCOMPtr<nsIWidget> widget(aWidget);
1552 widget->SetInputContext(aInputContext, aAction);
1553 sActiveInputContextWidget = widget;
1556 // static
1557 void IMEStateManager::EnsureTextCompositionArray() {
1558 if (sTextCompositions) {
1559 return;
1561 sTextCompositions = new TextCompositionArray();
1564 // static
1565 void IMEStateManager::DispatchCompositionEvent(
1566 nsINode* aEventTargetNode, nsPresContext* aPresContext,
1567 BrowserParent* aBrowserParent, WidgetCompositionEvent* aCompositionEvent,
1568 nsEventStatus* aStatus, EventDispatchingCallback* aCallBack,
1569 bool aIsSynthesized) {
1570 MOZ_LOG(
1571 sISMLog, LogLevel::Info,
1572 ("DispatchCompositionEvent(aNode=0x%p, "
1573 "aPresContext=0x%p, aCompositionEvent={ mMessage=%s, "
1574 "mNativeIMEContext={ mRawNativeIMEContext=0x%" PRIXPTR ", "
1575 "mOriginProcessID=0x%" PRIX64 " }, mWidget(0x%p)={ "
1576 "GetNativeIMEContext()={ mRawNativeIMEContext=0x%" PRIXPTR ", "
1577 "mOriginProcessID=0x%" PRIX64 " }, Destroyed()=%s }, "
1578 "mFlags={ mIsTrusted=%s, mPropagationStopped=%s } }, "
1579 "aIsSynthesized=%s), browserParent=%p",
1580 aEventTargetNode, aPresContext, ToChar(aCompositionEvent->mMessage),
1581 aCompositionEvent->mNativeIMEContext.mRawNativeIMEContext,
1582 aCompositionEvent->mNativeIMEContext.mOriginProcessID,
1583 aCompositionEvent->mWidget.get(),
1584 aCompositionEvent->mWidget->GetNativeIMEContext().mRawNativeIMEContext,
1585 aCompositionEvent->mWidget->GetNativeIMEContext().mOriginProcessID,
1586 GetBoolName(aCompositionEvent->mWidget->Destroyed()),
1587 GetBoolName(aCompositionEvent->mFlags.mIsTrusted),
1588 GetBoolName(aCompositionEvent->mFlags.mPropagationStopped),
1589 GetBoolName(aIsSynthesized), aBrowserParent));
1591 if (NS_WARN_IF(!aCompositionEvent->IsTrusted()) ||
1592 NS_WARN_IF(aCompositionEvent->PropagationStopped())) {
1593 return;
1596 MOZ_ASSERT(aCompositionEvent->mMessage != eCompositionUpdate,
1597 "compositionupdate event shouldn't be dispatched manually");
1599 EnsureTextCompositionArray();
1601 RefPtr<TextComposition> composition =
1602 sTextCompositions->GetCompositionFor(aCompositionEvent);
1603 if (!composition) {
1604 // If synthesized event comes after delayed native composition events
1605 // for request of commit or cancel, we should ignore it.
1606 if (NS_WARN_IF(aIsSynthesized)) {
1607 return;
1609 MOZ_LOG(sISMLog, LogLevel::Debug,
1610 (" DispatchCompositionEvent(), "
1611 "adding new TextComposition to the array"));
1612 MOZ_ASSERT(aCompositionEvent->mMessage == eCompositionStart);
1613 composition = new TextComposition(aPresContext, aEventTargetNode,
1614 aBrowserParent, aCompositionEvent);
1615 sTextCompositions->AppendElement(composition);
1617 #ifdef DEBUG
1618 else {
1619 MOZ_ASSERT(aCompositionEvent->mMessage != eCompositionStart);
1621 #endif // #ifdef DEBUG
1623 // Dispatch the event on composing target.
1624 composition->DispatchCompositionEvent(aCompositionEvent, aStatus, aCallBack,
1625 aIsSynthesized);
1627 // WARNING: the |composition| might have been destroyed already.
1629 // Remove the ended composition from the array.
1630 // NOTE: When TextComposition is synthesizing compositionend event for
1631 // emulating a commit, the instance shouldn't be removed from the array
1632 // because IME may perform it later. Then, we need to ignore the
1633 // following commit events in TextComposition::DispatchEvent().
1634 // However, if commit or cancel for a request is performed synchronously
1635 // during not safe to dispatch events, PresShell must have discarded
1636 // compositionend event. Then, the synthesized compositionend event is
1637 // the last event for the composition. In this case, we need to
1638 // destroy the TextComposition with synthesized compositionend event.
1639 if ((!aIsSynthesized ||
1640 composition->WasNativeCompositionEndEventDiscarded()) &&
1641 aCompositionEvent->CausesDOMCompositionEndEvent()) {
1642 TextCompositionArray::index_type i =
1643 sTextCompositions->IndexOf(aCompositionEvent->mWidget);
1644 if (i != TextCompositionArray::NoIndex) {
1645 MOZ_LOG(
1646 sISMLog, LogLevel::Debug,
1647 (" DispatchCompositionEvent(), "
1648 "removing TextComposition from the array since NS_COMPOSTION_END "
1649 "was dispatched"));
1650 sTextCompositions->ElementAt(i)->Destroy();
1651 sTextCompositions->RemoveElementAt(i);
1656 // static
1657 IMEContentObserver* IMEStateManager::GetActiveContentObserver() {
1658 return sActiveIMEContentObserver;
1661 // static
1662 nsIContent* IMEStateManager::GetRootContent(nsPresContext* aPresContext) {
1663 Document* doc = aPresContext->Document();
1664 if (NS_WARN_IF(!doc)) {
1665 return nullptr;
1667 return doc->GetRootElement();
1670 // static
1671 void IMEStateManager::HandleSelectionEvent(
1672 nsPresContext* aPresContext, nsIContent* aEventTargetContent,
1673 WidgetSelectionEvent* aSelectionEvent) {
1674 RefPtr<BrowserParent> browserParent = GetActiveBrowserParent();
1675 if (!browserParent) {
1676 browserParent = BrowserParent::GetFrom(aEventTargetContent
1677 ? aEventTargetContent
1678 : GetRootContent(aPresContext));
1681 MOZ_LOG(
1682 sISMLog, LogLevel::Info,
1683 ("HandleSelectionEvent(aPresContext=0x%p, "
1684 "aEventTargetContent=0x%p, aSelectionEvent={ mMessage=%s, "
1685 "mFlags={ mIsTrusted=%s } }), browserParent=%p",
1686 aPresContext, aEventTargetContent, ToChar(aSelectionEvent->mMessage),
1687 GetBoolName(aSelectionEvent->mFlags.mIsTrusted), browserParent.get()));
1689 if (!aSelectionEvent->IsTrusted()) {
1690 return;
1693 RefPtr<TextComposition> composition =
1694 sTextCompositions
1695 ? sTextCompositions->GetCompositionFor(aSelectionEvent->mWidget)
1696 : nullptr;
1697 if (composition) {
1698 // When there is a composition, TextComposition should guarantee that the
1699 // selection event will be handled in same target as composition events.
1700 composition->HandleSelectionEvent(aSelectionEvent);
1701 } else {
1702 // When there is no composition, the selection event should be handled
1703 // in the aPresContext or browserParent.
1704 TextComposition::HandleSelectionEvent(aPresContext, browserParent,
1705 aSelectionEvent);
1709 // static
1710 void IMEStateManager::OnCompositionEventDiscarded(
1711 WidgetCompositionEvent* aCompositionEvent) {
1712 // Note that this method is never called for synthesized events for emulating
1713 // commit or cancel composition.
1715 MOZ_LOG(
1716 sISMLog, LogLevel::Info,
1717 ("OnCompositionEventDiscarded(aCompositionEvent={ "
1718 "mMessage=%s, mNativeIMEContext={ mRawNativeIMEContext=0x%" PRIXPTR ", "
1719 "mOriginProcessID=0x%" PRIX64 " }, mWidget(0x%p)={ "
1720 "GetNativeIMEContext()={ mRawNativeIMEContext=0x%" PRIXPTR ", "
1721 "mOriginProcessID=0x%" PRIX64 " }, Destroyed()=%s }, "
1722 "mFlags={ mIsTrusted=%s } })",
1723 ToChar(aCompositionEvent->mMessage),
1724 aCompositionEvent->mNativeIMEContext.mRawNativeIMEContext,
1725 aCompositionEvent->mNativeIMEContext.mOriginProcessID,
1726 aCompositionEvent->mWidget.get(),
1727 aCompositionEvent->mWidget->GetNativeIMEContext().mRawNativeIMEContext,
1728 aCompositionEvent->mWidget->GetNativeIMEContext().mOriginProcessID,
1729 GetBoolName(aCompositionEvent->mWidget->Destroyed()),
1730 GetBoolName(aCompositionEvent->mFlags.mIsTrusted)));
1732 if (!aCompositionEvent->IsTrusted()) {
1733 return;
1736 // Ignore compositionstart for now because sTextCompositions may not have
1737 // been created yet.
1738 if (aCompositionEvent->mMessage == eCompositionStart) {
1739 return;
1742 RefPtr<TextComposition> composition =
1743 sTextCompositions->GetCompositionFor(aCompositionEvent->mWidget);
1744 if (!composition) {
1745 // If the PresShell has been being destroyed during composition,
1746 // a TextComposition instance for the composition was already removed from
1747 // the array and destroyed in OnDestroyPresContext(). Therefore, we may
1748 // fail to retrieve a TextComposition instance here.
1749 MOZ_LOG(sISMLog, LogLevel::Info,
1750 (" OnCompositionEventDiscarded(), "
1751 "TextComposition instance for the widget has already gone"));
1752 return;
1754 composition->OnCompositionEventDiscarded(aCompositionEvent);
1757 // static
1758 nsresult IMEStateManager::NotifyIME(IMEMessage aMessage, nsIWidget* aWidget,
1759 BrowserParent* aBrowserParent) {
1760 return IMEStateManager::NotifyIME(IMENotification(aMessage), aWidget,
1761 aBrowserParent);
1764 // static
1765 nsresult IMEStateManager::NotifyIME(const IMENotification& aNotification,
1766 nsIWidget* aWidget,
1767 BrowserParent* aBrowserParent) {
1768 MOZ_LOG(sISMLog, LogLevel::Info,
1769 ("NotifyIME(aNotification={ mMessage=%s }, "
1770 "aWidget=0x%p, aBrowserParent=0x%p), sFocusedIMEWidget=0x%p, "
1771 "BrowserParent::GetFocused()=0x%p, sFocusedIMEBrowserParent=0x%p, "
1772 "aBrowserParent == BrowserParent::GetFocused()=%s, "
1773 "aBrowserParent == sFocusedIMEBrowserParent=%s, "
1774 "CanSendNotificationToWidget()=%s",
1775 ToChar(aNotification.mMessage), aWidget, aBrowserParent,
1776 sFocusedIMEWidget, BrowserParent::GetFocused(),
1777 sFocusedIMEBrowserParent.get(),
1778 GetBoolName(aBrowserParent == BrowserParent::GetFocused()),
1779 GetBoolName(aBrowserParent == sFocusedIMEBrowserParent),
1780 GetBoolName(CanSendNotificationToWidget())));
1782 if (NS_WARN_IF(!aWidget)) {
1783 MOZ_LOG(sISMLog, LogLevel::Error,
1784 (" NotifyIME(), FAILED due to no widget"));
1785 return NS_ERROR_INVALID_ARG;
1788 switch (aNotification.mMessage) {
1789 case NOTIFY_IME_OF_FOCUS: {
1790 MOZ_ASSERT(CanSendNotificationToWidget());
1792 // If focus notification comes from a remote browser which already lost
1793 // focus, we shouldn't accept the focus notification. Then, following
1794 // notifications from the process will be ignored.
1795 if (aBrowserParent != BrowserParent::GetFocused()) {
1796 MOZ_LOG(sISMLog, LogLevel::Warning,
1797 (" NotifyIME(), WARNING, the received focus notification is "
1798 "ignored, because its associated BrowserParent did not match"
1799 "the focused BrowserParent."));
1800 return NS_OK;
1802 // If IME focus is already set, first blur the currently-focused
1803 // IME widget
1804 if (sFocusedIMEWidget) {
1805 // XXX Why don't we first request the previously-focused IME
1806 // widget to commit the composition?
1807 MOZ_ASSERT(
1808 sFocusedIMEBrowserParent || aBrowserParent,
1809 "This case shouldn't be caused by focus move in this process");
1810 MOZ_LOG(sISMLog, LogLevel::Warning,
1811 (" NotifyIME(), WARNING, received focus notification with ")
1812 "non-null sFocusedIMEWidget. How come "
1813 "OnFocusMovedBetweenBrowsers did not blur it already?");
1814 nsCOMPtr<nsIWidget> focusedIMEWidget(sFocusedIMEWidget);
1815 sFocusedIMEWidget = nullptr;
1816 sFocusedIMEBrowserParent = nullptr;
1817 focusedIMEWidget->NotifyIME(IMENotification(NOTIFY_IME_OF_BLUR));
1819 #ifdef DEBUG
1820 if (aBrowserParent) {
1821 nsCOMPtr<nsIWidget> browserParentWidget =
1822 aBrowserParent->GetTextInputHandlingWidget();
1823 MOZ_ASSERT(browserParentWidget == aWidget);
1825 #endif
1826 sFocusedIMEBrowserParent = aBrowserParent;
1827 sFocusedIMEWidget = aWidget;
1828 nsCOMPtr<nsIWidget> widget(aWidget);
1829 MOZ_LOG(
1830 sISMLog, LogLevel::Info,
1831 (" NotifyIME(), about to call widget->NotifyIME() for IME focus"));
1832 return widget->NotifyIME(aNotification);
1834 case NOTIFY_IME_OF_BLUR: {
1835 if (aBrowserParent != sFocusedIMEBrowserParent) {
1836 MOZ_LOG(sISMLog, LogLevel::Warning,
1837 (" NotifyIME(), WARNING, the received blur notification is "
1838 "ignored "
1839 "because it's not from current focused IME browser"));
1840 return NS_OK;
1842 if (!sFocusedIMEWidget) {
1843 MOZ_LOG(
1844 sISMLog, LogLevel::Error,
1845 (" NotifyIME(), WARNING, received blur notification but there is "
1846 "no focused IME widget"));
1847 return NS_OK;
1849 if (NS_WARN_IF(sFocusedIMEWidget != aWidget)) {
1850 MOZ_LOG(sISMLog, LogLevel::Warning,
1851 (" NotifyIME(), WARNING, the received blur notification is "
1852 "ignored "
1853 "because it's not for current focused IME widget"));
1854 return NS_OK;
1856 nsCOMPtr<nsIWidget> focusedIMEWidget(sFocusedIMEWidget);
1857 sFocusedIMEWidget = nullptr;
1858 sFocusedIMEBrowserParent = nullptr;
1859 return CanSendNotificationToWidget()
1860 ? focusedIMEWidget->NotifyIME(
1861 IMENotification(NOTIFY_IME_OF_BLUR))
1862 : NS_OK;
1864 case NOTIFY_IME_OF_SELECTION_CHANGE:
1865 case NOTIFY_IME_OF_TEXT_CHANGE:
1866 case NOTIFY_IME_OF_POSITION_CHANGE:
1867 case NOTIFY_IME_OF_MOUSE_BUTTON_EVENT:
1868 case NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED: {
1869 if (aBrowserParent != sFocusedIMEBrowserParent) {
1870 MOZ_LOG(
1871 sISMLog, LogLevel::Warning,
1872 (" NotifyIME(), WARNING, the received content change notification "
1873 "is ignored because it's not from current focused IME browser"));
1874 return NS_OK;
1876 if (!sFocusedIMEWidget) {
1877 MOZ_LOG(
1878 sISMLog, LogLevel::Warning,
1879 (" NotifyIME(), WARNING, the received content change notification "
1880 "is ignored because there is no focused IME widget"));
1881 return NS_OK;
1883 if (NS_WARN_IF(sFocusedIMEWidget != aWidget)) {
1884 MOZ_LOG(
1885 sISMLog, LogLevel::Warning,
1886 (" NotifyIME(), WARNING, the received content change notification "
1887 "is ignored because it's not for current focused IME widget"));
1888 return NS_OK;
1890 if (!CanSendNotificationToWidget()) {
1891 return NS_OK;
1893 nsCOMPtr<nsIWidget> widget(aWidget);
1894 return widget->NotifyIME(aNotification);
1896 default:
1897 // Other notifications should be sent only when there is composition.
1898 // So, we need to handle the others below.
1899 break;
1902 if (!sTextCompositions) {
1903 MOZ_LOG(sISMLog, LogLevel::Info,
1904 (" NotifyIME(), the request to IME is ignored because "
1905 "there have been no compositions yet"));
1906 return NS_OK;
1909 RefPtr<TextComposition> composition =
1910 sTextCompositions->GetCompositionFor(aWidget);
1911 if (!composition) {
1912 MOZ_LOG(sISMLog, LogLevel::Info,
1913 (" NotifyIME(), the request to IME is ignored because "
1914 "there is no active composition"));
1915 return NS_OK;
1918 if (aBrowserParent != composition->GetBrowserParent()) {
1919 MOZ_LOG(
1920 sISMLog, LogLevel::Warning,
1921 (" NotifyIME(), WARNING, the request to IME is ignored because "
1922 "it does not come from the remote browser which has the composition "
1923 "on aWidget"));
1924 return NS_OK;
1927 switch (aNotification.mMessage) {
1928 case REQUEST_TO_COMMIT_COMPOSITION:
1929 return composition->RequestToCommit(aWidget, false);
1930 case REQUEST_TO_CANCEL_COMPOSITION:
1931 return composition->RequestToCommit(aWidget, true);
1932 default:
1933 MOZ_CRASH("Unsupported notification");
1935 MOZ_CRASH(
1936 "Failed to handle the notification for non-synthesized composition");
1937 return NS_ERROR_FAILURE;
1940 // static
1941 nsresult IMEStateManager::NotifyIME(IMEMessage aMessage,
1942 nsPresContext* aPresContext,
1943 BrowserParent* aBrowserParent) {
1944 MOZ_LOG(sISMLog, LogLevel::Info,
1945 ("NotifyIME(aMessage=%s, aPresContext=0x%p, aBrowserParent=0x%p)",
1946 ToChar(aMessage), aPresContext, aBrowserParent));
1948 if (NS_WARN_IF(!CanHandleWith(aPresContext))) {
1949 return NS_ERROR_INVALID_ARG;
1952 nsCOMPtr<nsIWidget> widget = aPresContext->GetTextInputHandlingWidget();
1953 if (NS_WARN_IF(!widget)) {
1954 MOZ_LOG(sISMLog, LogLevel::Error,
1955 (" NotifyIME(), FAILED due to no widget for the "
1956 "nsPresContext"));
1957 return NS_ERROR_NOT_AVAILABLE;
1959 return NotifyIME(aMessage, widget, aBrowserParent);
1962 // static
1963 bool IMEStateManager::IsEditable(nsINode* node) {
1964 if (node->IsEditable()) {
1965 return true;
1967 // |node| might be readwrite (for example, a text control)
1968 if (node->IsElement() &&
1969 node->AsElement()->State().HasState(NS_EVENT_STATE_READWRITE)) {
1970 return true;
1972 return false;
1975 // static
1976 nsINode* IMEStateManager::GetRootEditableNode(const nsPresContext* aPresContext,
1977 const nsIContent* aContent) {
1978 if (aContent) {
1979 // If the focused content is in design mode, return is composed document
1980 // because aContent may be in UA widget shadow tree.
1981 if (aContent->IsInDesignMode()) {
1982 return aContent->GetComposedDoc();
1985 nsINode* root = nullptr;
1986 nsINode* node = const_cast<nsIContent*>(aContent);
1987 while (node && IsEditable(node)) {
1988 // If the node has independent selection like <input type="text"> or
1989 // <textarea>, the node should be the root editable node for aContent.
1990 // FYI: <select> element also has independent selection but IsEditable()
1991 // returns false.
1992 // XXX: If somebody adds new editable element which has independent
1993 // selection but doesn't own editor, we'll need more checks here.
1994 // XXX: If aContent is not in native anonymous subtree, checking
1995 // independent selection must be wrong, see bug 1731005.
1996 if (node->IsContent() && node->AsContent()->HasIndependentSelection()) {
1997 return node;
1999 root = node;
2000 node = node->GetParentNode();
2002 return root;
2004 if (aPresContext && aPresContext->Document() &&
2005 aPresContext->Document()->IsInDesignMode()) {
2006 return aPresContext->Document();
2008 return nullptr;
2011 // static
2012 bool IMEStateManager::IsIMEObserverNeeded(const IMEState& aState) {
2013 return aState.IsEditable();
2016 // static
2017 void IMEStateManager::DestroyIMEContentObserver() {
2018 MOZ_LOG(sISMLog, LogLevel::Info,
2019 ("DestroyIMEContentObserver(), sActiveIMEContentObserver=0x%p",
2020 sActiveIMEContentObserver.get()));
2022 if (!sActiveIMEContentObserver) {
2023 MOZ_LOG(sISMLog, LogLevel::Debug,
2024 (" DestroyIMEContentObserver() does nothing"));
2025 return;
2028 MOZ_LOG(sISMLog, LogLevel::Debug,
2029 (" DestroyIMEContentObserver(), destroying "
2030 "the active IMEContentObserver..."));
2031 RefPtr<IMEContentObserver> tsm = sActiveIMEContentObserver.get();
2032 sActiveIMEContentObserver = nullptr;
2033 tsm->Destroy();
2036 // static
2037 void IMEStateManager::CreateIMEContentObserver(EditorBase& aEditorBase,
2038 nsIContent* aFocusedContent) {
2039 MOZ_LOG(sISMLog, LogLevel::Info,
2040 ("CreateIMEContentObserver(aEditorBase=0x%p, aFocusedContent=0x%p), "
2041 "sPresContext=0x%p, sContent=0x%p, sWidget=0x%p (available: %s), "
2042 "sActiveIMEContentObserver=0x%p, "
2043 "sActiveIMEContentObserver->IsManaging(sPresContext, sContent)=%s",
2044 &aEditorBase, aFocusedContent, sPresContext.get(), sContent.get(),
2045 sWidget, GetBoolName(sWidget && !sWidget->Destroyed()),
2046 sActiveIMEContentObserver.get(),
2047 GetBoolName(sActiveIMEContentObserver
2048 ? sActiveIMEContentObserver->IsManaging(sPresContext,
2049 sContent)
2050 : false)));
2052 if (NS_WARN_IF(sActiveIMEContentObserver)) {
2053 MOZ_LOG(sISMLog, LogLevel::Error,
2054 (" CreateIMEContentObserver(), FAILED due to "
2055 "there is already an active IMEContentObserver"));
2056 MOZ_ASSERT(sActiveIMEContentObserver->IsManaging(sPresContext, sContent));
2057 return;
2060 if (!sWidget || NS_WARN_IF(sWidget->Destroyed())) {
2061 MOZ_LOG(sISMLog, LogLevel::Error,
2062 (" CreateIMEContentObserver(), FAILED due to "
2063 "the widget for the nsPresContext has gone"));
2064 return; // Sometimes, there are no widgets.
2067 OwningNonNull<nsIWidget> widget(*sWidget);
2069 // If it's not text editable, we don't need to create IMEContentObserver.
2070 if (!IsIMEObserverNeeded(widget->GetInputContext().mIMEState)) {
2071 MOZ_LOG(sISMLog, LogLevel::Debug,
2072 (" CreateIMEContentObserver() doesn't create "
2073 "IMEContentObserver because of non-editable IME state"));
2074 return;
2077 if (NS_WARN_IF(widget->Destroyed())) {
2078 MOZ_LOG(sISMLog, LogLevel::Error,
2079 (" CreateIMEContentObserver(), FAILED due to "
2080 "the widget for the nsPresContext has gone"));
2081 return;
2084 if (NS_WARN_IF(!sPresContext)) {
2085 MOZ_LOG(sISMLog, LogLevel::Error,
2086 (" CreateIMEContentObserver(), FAILED due to "
2087 "the nsPresContext is nullptr"));
2088 return;
2091 #ifdef DEBUG
2093 nsCOMPtr<nsIWidget> tihWidget = sPresContext->GetTextInputHandlingWidget();
2094 MOZ_ASSERT(tihWidget == widget);
2096 #endif // DEBUG
2098 MOZ_LOG(sISMLog, LogLevel::Debug,
2099 (" CreateIMEContentObserver() is creating an "
2100 "IMEContentObserver instance..."));
2101 sActiveIMEContentObserver = new IMEContentObserver();
2103 // IMEContentObserver::Init() might create another IMEContentObserver
2104 // instance. So, sActiveIMEContentObserver would be replaced with new one.
2105 // We should hold the current instance here.
2106 RefPtr<IMEContentObserver> activeIMEContentObserver(
2107 sActiveIMEContentObserver);
2108 OwningNonNull<nsPresContext> presContext(*sPresContext);
2109 nsCOMPtr<nsIContent> content = aFocusedContent;
2110 activeIMEContentObserver->Init(widget, presContext, content, aEditorBase);
2113 // static
2114 nsresult IMEStateManager::GetFocusSelectionAndRoot(Selection** aSelection,
2115 nsIContent** aRootContent) {
2116 if (!sActiveIMEContentObserver) {
2117 return NS_ERROR_NOT_AVAILABLE;
2119 return sActiveIMEContentObserver->GetSelectionAndRoot(aSelection,
2120 aRootContent);
2123 // static
2124 already_AddRefed<TextComposition> IMEStateManager::GetTextCompositionFor(
2125 nsIWidget* aWidget) {
2126 if (!sTextCompositions) {
2127 return nullptr;
2129 RefPtr<TextComposition> textComposition =
2130 sTextCompositions->GetCompositionFor(aWidget);
2131 return textComposition.forget();
2134 // static
2135 already_AddRefed<TextComposition> IMEStateManager::GetTextCompositionFor(
2136 const WidgetCompositionEvent* aCompositionEvent) {
2137 if (!sTextCompositions) {
2138 return nullptr;
2140 RefPtr<TextComposition> textComposition =
2141 sTextCompositions->GetCompositionFor(aCompositionEvent);
2142 return textComposition.forget();
2145 // static
2146 already_AddRefed<TextComposition> IMEStateManager::GetTextCompositionFor(
2147 nsPresContext* aPresContext) {
2148 if (!sTextCompositions) {
2149 return nullptr;
2151 RefPtr<TextComposition> textComposition =
2152 sTextCompositions->GetCompositionFor(aPresContext);
2153 return textComposition.forget();
2156 } // namespace mozilla