Bug 1826136 [wpt PR 39338] - Update wpt metadata, a=testonly
[gecko.git] / dom / events / IMEStateManager.cpp
blob51a521ccc1ee3d8b508d97b1a40a5827725fe7d1
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 "IMEStateManager.h"
9 #include "mozilla/Logging.h"
11 #include "mozilla/Attributes.h"
12 #include "mozilla/AutoRestore.h"
13 #include "mozilla/EditorBase.h"
14 #include "mozilla/EventListenerManager.h"
15 #include "mozilla/EventStateManager.h"
16 #include "mozilla/HTMLEditor.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/Element.h"
29 #include "mozilla/dom/HTMLFormElement.h"
30 #include "mozilla/dom/HTMLTextAreaElement.h"
31 #include "mozilla/dom/MouseEventBinding.h"
32 #include "mozilla/dom/UserActivation.h"
34 #include "HTMLInputElement.h"
35 #include "IMEContentObserver.h"
37 #include "nsCOMPtr.h"
38 #include "nsContentUtils.h"
39 #include "nsFocusManager.h"
40 #include "nsIContent.h"
41 #include "nsIContentInlines.h"
42 #include "nsIFormControl.h"
43 #include "nsINode.h"
44 #include "nsISupports.h"
45 #include "nsIURI.h"
46 #include "nsIURIMutator.h"
47 #include "nsPresContext.h"
49 namespace mozilla {
51 using namespace dom;
52 using namespace widget;
54 /**
55 * When a method is called, log its arguments and/or related static variables
56 * with LogLevel::Info. However, if it puts too many logs like
57 * OnDestroyPresContext(), should long only when the method actually does
58 * something. In this case, the log should start with "<method name>".
60 * When a method quits due to unexpected situation, log the reason with
61 * LogLevel::Error. In this case, the log should start with
62 * "<method name>(), FAILED". The indent makes the log look easier.
64 * When a method does something only in some situations and it may be important
65 * for debug, log the information with LogLevel::Debug. In this case, the log
66 * should start with " <method name>(),".
68 LazyLogModule sISMLog("IMEStateManager");
70 static const char* GetBoolName(bool aBool) { return aBool ? "true" : "false"; }
72 StaticRefPtr<Element> IMEStateManager::sFocusedElement;
73 StaticRefPtr<nsPresContext> IMEStateManager::sFocusedPresContext;
74 nsIWidget* IMEStateManager::sTextInputHandlingWidget = nullptr;
75 nsIWidget* IMEStateManager::sFocusedIMEWidget = nullptr;
76 StaticRefPtr<BrowserParent> IMEStateManager::sFocusedIMEBrowserParent;
77 nsIWidget* IMEStateManager::sActiveInputContextWidget = nullptr;
78 StaticRefPtr<IMEContentObserver> IMEStateManager::sActiveIMEContentObserver;
79 TextCompositionArray* IMEStateManager::sTextCompositions = nullptr;
80 InputContext::Origin IMEStateManager::sOrigin = InputContext::ORIGIN_MAIN;
81 InputContext IMEStateManager::sActiveChildInputContext;
82 bool IMEStateManager::sInstalledMenuKeyboardListener = false;
83 bool IMEStateManager::sIsGettingNewIMEState = false;
84 bool IMEStateManager::sCleaningUpForStoppingIMEStateManagement = false;
85 bool IMEStateManager::sIsActive = false;
86 Maybe<IMEStateManager::PendingFocusedBrowserSwitchingData>
87 IMEStateManager::sPendingFocusedBrowserSwitchingData;
89 // static
90 void IMEStateManager::Init() {
91 sOrigin = XRE_IsParentProcess() ? InputContext::ORIGIN_MAIN
92 : InputContext::ORIGIN_CONTENT;
93 ResetActiveChildInputContext();
96 // static
97 void IMEStateManager::Shutdown() {
98 MOZ_LOG(
99 sISMLog, LogLevel::Info,
100 ("Shutdown(), sTextCompositions=0x%p, sTextCompositions->Length()=%zu, "
101 "sPendingFocusedBrowserSwitchingData.isSome()=%s",
102 sTextCompositions, sTextCompositions ? sTextCompositions->Length() : 0,
103 GetBoolName(sPendingFocusedBrowserSwitchingData.isSome())));
104 MOZ_LOG(sISMLog, LogLevel::Debug,
105 (" Shutdown(), sFocusedElement=0x%p, sFocusedPresContext=0x%p, "
106 "sTextInputHandlingWidget=0x%p, sFocusedIMEWidget=0x%p, "
107 "sFocusedIMEBrowserParent=0x%p, sActiveInputContextWidget=0x%p, "
108 "sActiveIMEContentObserver=0x%p",
109 sFocusedElement.get(), sFocusedPresContext.get(),
110 sTextInputHandlingWidget, sFocusedIMEWidget,
111 sFocusedIMEBrowserParent.get(), sActiveInputContextWidget,
112 sActiveIMEContentObserver.get()));
114 sPendingFocusedBrowserSwitchingData.reset();
115 MOZ_ASSERT(!sTextCompositions || !sTextCompositions->Length());
116 delete sTextCompositions;
117 sTextCompositions = nullptr;
118 // All string instances in the global space need to be empty after XPCOM
119 // shutdown.
120 sActiveChildInputContext.ShutDown();
123 // static
124 void IMEStateManager::OnFocusMovedBetweenBrowsers(BrowserParent* aBlur,
125 BrowserParent* aFocus) {
126 MOZ_ASSERT(aBlur != aFocus);
127 MOZ_ASSERT(XRE_IsParentProcess());
129 if (sPendingFocusedBrowserSwitchingData.isSome()) {
130 MOZ_ASSERT(aBlur ==
131 sPendingFocusedBrowserSwitchingData.ref().mBrowserParentFocused);
132 // If focus is not changed between browsers actually, we need to do
133 // nothing here. Let's cancel handling what this method does.
134 if (sPendingFocusedBrowserSwitchingData.ref().mBrowserParentBlurred ==
135 aFocus) {
136 sPendingFocusedBrowserSwitchingData.reset();
137 MOZ_LOG(sISMLog, LogLevel::Info,
138 (" OnFocusMovedBetweenBrowsers(), canceled all pending focus "
139 "moves between browsers"));
140 return;
142 aBlur = sPendingFocusedBrowserSwitchingData.ref().mBrowserParentBlurred;
143 sPendingFocusedBrowserSwitchingData.ref().mBrowserParentFocused = aFocus;
144 MOZ_ASSERT(aBlur != aFocus);
147 // If application was inactive, but is now activated, and the last focused
148 // this is called by BrowserParent::UnsetTopLevelWebFocusAll() from
149 // nsFocusManager::WindowRaised(). If a content has focus in a remote
150 // process and it has composition, it may get focus back later and the
151 // composition shouldn't be commited now. Therefore, we should put off to
152 // handle this until getting another call of this method or a call of
153 //`OnFocusChangeInternal()`.
154 if (aBlur && !aFocus && !sIsActive && sTextInputHandlingWidget &&
155 sTextCompositions &&
156 sTextCompositions->GetCompositionFor(sTextInputHandlingWidget)) {
157 if (sPendingFocusedBrowserSwitchingData.isNothing()) {
158 sPendingFocusedBrowserSwitchingData.emplace(aBlur, aFocus);
160 MOZ_LOG(sISMLog, LogLevel::Debug,
161 (" OnFocusMovedBetweenBrowsers(), put off to handle it until "
162 "next OnFocusChangeInternal() call"));
163 return;
165 sPendingFocusedBrowserSwitchingData.reset();
167 const nsCOMPtr<nsIWidget> oldWidget = sTextInputHandlingWidget;
168 // In the chrome-process case, we'll get sTextInputHandlingWidget from a
169 // PresShell later.
170 sTextInputHandlingWidget =
171 aFocus ? nsCOMPtr<nsIWidget>(aFocus->GetTextInputHandlingWidget()).get()
172 : nullptr;
173 if (oldWidget && sTextCompositions) {
174 RefPtr<TextComposition> composition =
175 sTextCompositions->GetCompositionFor(oldWidget);
176 if (composition) {
177 MOZ_LOG(
178 sISMLog, LogLevel::Debug,
179 (" OnFocusMovedBetweenBrowsers(), requesting to commit "
180 "composition to "
181 "the (previous) focused widget (would request=%s)",
182 GetBoolName(
183 !oldWidget->IMENotificationRequestsRef().WantDuringDeactive())));
184 NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, oldWidget,
185 composition->GetBrowserParent());
189 // The manager check is to avoid telling the content process to stop
190 // IME state management after focus has already moved there between
191 // two same-process-hosted out-of-process iframes.
192 if (aBlur && (!aFocus || (aBlur->Manager() != aFocus->Manager()))) {
193 MOZ_LOG(sISMLog, LogLevel::Debug,
194 (" OnFocusMovedBetweenBrowsers(), notifying previous "
195 "focused child process of parent process or another child process "
196 "getting focus"));
197 aBlur->StopIMEStateManagement();
200 if (sActiveIMEContentObserver) {
201 DestroyIMEContentObserver();
204 if (sFocusedIMEWidget) {
205 // sFocusedIMEBrowserParent can be null, if IME focus hasn't been
206 // taken before BrowserParent blur.
207 // aBlur can be null when keyboard focus moves not actually
208 // between tabs but an open menu is involved.
209 MOZ_ASSERT(!sFocusedIMEBrowserParent || !aBlur ||
210 (sFocusedIMEBrowserParent == aBlur));
211 MOZ_LOG(sISMLog, LogLevel::Debug,
212 (" OnFocusMovedBetweenBrowsers(), notifying IME of blur"));
213 NotifyIME(NOTIFY_IME_OF_BLUR, sFocusedIMEWidget, sFocusedIMEBrowserParent);
215 MOZ_ASSERT(!sFocusedIMEBrowserParent);
216 MOZ_ASSERT(!sFocusedIMEWidget);
218 } else {
219 MOZ_ASSERT(!sFocusedIMEBrowserParent);
222 // We deliberately don't null out sFocusedElement or sFocusedPresContext here.
223 // When focus is in remote content, as far as layout in the chrome process is
224 // concerned, the corresponding content is the top-level XUL browser. Changes
225 // among out-of-process iframes don't change that, so dropping the pointer to
226 // the XUL browser upon such a change would break IME handling.
229 // static
230 void IMEStateManager::WidgetDestroyed(nsIWidget* aWidget) {
231 MOZ_LOG(sISMLog, LogLevel::Debug,
232 ("WidgetDestroyed(aWidget=0x%p), sFocusedIMEWidget=0x%p, "
233 "sActiveInputContextWidget=0x%p, sFocusedIMEBrowserParent=0x%p",
234 aWidget, sFocusedIMEWidget, sActiveInputContextWidget,
235 sFocusedIMEBrowserParent.get()));
236 if (sTextInputHandlingWidget == aWidget) {
237 sTextInputHandlingWidget = nullptr;
239 if (sFocusedIMEWidget == aWidget) {
240 if (sFocusedIMEBrowserParent) {
241 OnFocusMovedBetweenBrowsers(sFocusedIMEBrowserParent, nullptr);
242 MOZ_ASSERT(!sFocusedIMEBrowserParent);
244 sFocusedIMEWidget = nullptr;
246 if (sActiveInputContextWidget == aWidget) {
247 sActiveInputContextWidget = nullptr;
251 // static
252 void IMEStateManager::WidgetOnQuit(nsIWidget* aWidget) {
253 if (sFocusedIMEWidget == aWidget) {
254 MOZ_LOG(
255 sISMLog, LogLevel::Debug,
256 ("WidgetOnQuit(aWidget=0x%p (available %s)), sFocusedIMEWidget=0x%p",
257 aWidget, GetBoolName(aWidget && !aWidget->Destroyed()),
258 sFocusedIMEWidget));
259 // Notify IME of blur (which is done by IMEContentObserver::Destroy
260 // automatically) when the widget still has IME focus before forgetting the
261 // focused widget because the focused widget is required to clean up native
262 // IME handler with sending blur notification. Fortunately, the widget
263 // has not been destroyed yet here since some methods to sending blur
264 // notification won't work with destroyed widget.
265 IMEStateManager::DestroyIMEContentObserver();
266 // Finally, clean up the widget and related objects for avoiding to leak.
267 IMEStateManager::WidgetDestroyed(aWidget);
271 // static
272 void IMEStateManager::StopIMEStateManagement() {
273 MOZ_ASSERT(XRE_IsContentProcess());
274 MOZ_LOG(sISMLog, LogLevel::Info, ("StopIMEStateManagement()"));
276 // NOTE: Don't set input context from here since this has already lost
277 // the rights to change input context.
279 // The requestee of this method in the main process must destroy its
280 // active IMEContentObserver for making existing composition end and
281 // make it be possible to start new composition in new focused process.
282 // Therefore, we shouldn't notify the main process of any changes which
283 // occurred after here.
284 AutoRestore<bool> restoreStoppingIMEStateManagementState(
285 sCleaningUpForStoppingIMEStateManagement);
286 sCleaningUpForStoppingIMEStateManagement = true;
288 if (sTextCompositions && sFocusedPresContext) {
289 NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, sFocusedPresContext, nullptr);
291 sActiveInputContextWidget = nullptr;
292 sFocusedPresContext = nullptr;
293 sFocusedElement = nullptr;
294 sIsActive = false;
295 DestroyIMEContentObserver();
298 // static
299 void IMEStateManager::MaybeStartOffsetUpdatedInChild(nsIWidget* aWidget,
300 uint32_t aStartOffset) {
301 if (NS_WARN_IF(!sTextCompositions)) {
302 MOZ_LOG(sISMLog, LogLevel::Warning,
303 ("MaybeStartOffsetUpdatedInChild(aWidget=0x%p, aStartOffset=%u), "
304 "called when there is no composition",
305 aWidget, aStartOffset));
306 return;
309 RefPtr<TextComposition> composition = GetTextCompositionFor(aWidget);
310 if (NS_WARN_IF(!composition)) {
311 MOZ_LOG(sISMLog, LogLevel::Warning,
312 ("MaybeStartOffsetUpdatedInChild(aWidget=0x%p, aStartOffset=%u), "
313 "called when there is no composition",
314 aWidget, aStartOffset));
315 return;
318 if (composition->NativeOffsetOfStartComposition() == aStartOffset) {
319 return;
322 MOZ_LOG(
323 sISMLog, LogLevel::Info,
324 ("MaybeStartOffsetUpdatedInChild(aWidget=0x%p, aStartOffset=%u), "
325 "old offset=%u",
326 aWidget, aStartOffset, composition->NativeOffsetOfStartComposition()));
327 composition->OnStartOffsetUpdatedInChild(aStartOffset);
330 // static
331 nsresult IMEStateManager::OnDestroyPresContext(nsPresContext& aPresContext) {
332 // First, if there is a composition in the aPresContext, clean up it.
333 if (sTextCompositions) {
334 TextCompositionArray::index_type i =
335 sTextCompositions->IndexOf(&aPresContext);
336 if (i != TextCompositionArray::NoIndex) {
337 MOZ_LOG(sISMLog, LogLevel::Debug,
338 (" OnDestroyPresContext(), "
339 "removing TextComposition instance from the array (index=%zu)",
340 i));
341 // there should be only one composition per presContext object.
342 sTextCompositions->ElementAt(i)->Destroy();
343 sTextCompositions->RemoveElementAt(i);
344 if (sTextCompositions->IndexOf(&aPresContext) !=
345 TextCompositionArray::NoIndex) {
346 MOZ_LOG(sISMLog, LogLevel::Error,
347 (" OnDestroyPresContext(), FAILED to remove "
348 "TextComposition instance from the array"));
349 MOZ_CRASH("Failed to remove TextComposition instance from the array");
354 if (&aPresContext != sFocusedPresContext) {
355 return NS_OK;
358 MOZ_LOG(
359 sISMLog, LogLevel::Info,
360 ("OnDestroyPresContext(aPresContext=0x%p), "
361 "sFocusedPresContext=0x%p, sFocusedElement=0x%p, sTextCompositions=0x%p",
362 &aPresContext, sFocusedPresContext.get(), sFocusedElement.get(),
363 sTextCompositions));
365 DestroyIMEContentObserver();
367 if (sTextInputHandlingWidget) {
368 IMEState newState = GetNewIMEState(*sFocusedPresContext, nullptr);
369 InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
370 InputContextAction::LOST_FOCUS);
371 InputContext::Origin origin =
372 BrowserParent::GetFocused() ? InputContext::ORIGIN_CONTENT : sOrigin;
373 OwningNonNull<nsIWidget> textInputHandlingWidget =
374 *sTextInputHandlingWidget;
375 SetIMEState(newState, nullptr, nullptr, textInputHandlingWidget, action,
376 origin);
378 sTextInputHandlingWidget = nullptr;
379 sFocusedElement = nullptr;
380 sFocusedPresContext = nullptr;
381 return NS_OK;
384 // static
385 nsresult IMEStateManager::OnRemoveContent(nsPresContext& aPresContext,
386 Element& aElement) {
387 // First, if there is a composition in the aElement, clean up it.
388 if (sTextCompositions) {
389 const RefPtr<TextComposition> compositionInContent =
390 sTextCompositions->GetCompositionInContent(&aPresContext, &aElement);
392 if (compositionInContent) {
393 MOZ_LOG(sISMLog, LogLevel::Debug,
394 (" OnRemoveContent(), "
395 "composition is in the content"));
397 // Try resetting the native IME state. Be aware, typically, this method
398 // is called during the content being removed. Then, the native
399 // composition events which are caused by following APIs are ignored due
400 // to unsafe to run script (in PresShell::HandleEvent()).
401 nsresult rv =
402 compositionInContent->NotifyIME(REQUEST_TO_CANCEL_COMPOSITION);
403 if (NS_FAILED(rv)) {
404 compositionInContent->NotifyIME(REQUEST_TO_COMMIT_COMPOSITION);
409 if (!sFocusedPresContext || !sFocusedElement ||
410 !sFocusedElement->IsInclusiveDescendantOf(&aElement)) {
411 return NS_OK;
413 MOZ_ASSERT(sFocusedPresContext == &aPresContext);
415 MOZ_LOG(
416 sISMLog, LogLevel::Info,
417 ("OnRemoveContent(aPresContext=0x%p, aElement=0x%p), "
418 "sFocusedPresContext=0x%p, sFocusedElement=0x%p, sTextCompositions=0x%p",
419 &aPresContext, &aElement, sFocusedPresContext.get(),
420 sFocusedElement.get(), sTextCompositions));
422 DestroyIMEContentObserver();
424 // FYI: Don't clear sTextInputHandlingWidget and sFocusedPresContext because
425 // the window/document keeps having focus.
426 sFocusedElement = nullptr;
428 // Current IME transaction should commit
429 if (!sTextInputHandlingWidget) {
430 return NS_OK;
433 IMEState newState = GetNewIMEState(*sFocusedPresContext, nullptr);
434 InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
435 InputContextAction::LOST_FOCUS);
436 InputContext::Origin origin =
437 BrowserParent::GetFocused() ? InputContext::ORIGIN_CONTENT : sOrigin;
438 OwningNonNull<nsIWidget> textInputHandlingWidget = *sTextInputHandlingWidget;
439 SetIMEState(newState, &aPresContext, nullptr, textInputHandlingWidget, action,
440 origin);
441 if (sFocusedPresContext != &aPresContext || sFocusedElement) {
442 return NS_OK; // Some body must have set focus
445 if (IsIMEObserverNeeded(newState)) {
446 if (RefPtr<HTMLEditor> htmlEditor =
447 nsContentUtils::GetHTMLEditor(&aPresContext)) {
448 CreateIMEContentObserver(*htmlEditor, nullptr);
452 return NS_OK;
455 // static
456 bool IMEStateManager::CanHandleWith(const nsPresContext* aPresContext) {
457 return aPresContext && aPresContext->GetPresShell() &&
458 !aPresContext->PresShell()->IsDestroying();
461 // static
462 nsresult IMEStateManager::OnChangeFocus(nsPresContext* aPresContext,
463 Element* aElement,
464 InputContextAction::Cause aCause) {
465 MOZ_LOG(sISMLog, LogLevel::Info,
466 ("OnChangeFocus(aPresContext=0x%p, aElement=0x%p, aCause=%s)",
467 aPresContext, aElement, ToString(aCause).c_str()));
469 InputContextAction action(aCause);
470 return OnChangeFocusInternal(aPresContext, aElement, action);
473 // static
474 nsresult IMEStateManager::OnChangeFocusInternal(nsPresContext* aPresContext,
475 Element* aElement,
476 InputContextAction aAction) {
477 NS_ASSERTION(!aElement || aElement->GetPresContext(
478 Element::PresContextFor::eForComposedDoc) ==
479 aPresContext,
480 "aPresContext does not match with one of aElement");
482 bool remoteHasFocus = EventStateManager::IsRemoteTarget(aElement);
483 // If we've handled focused content, we were inactive but now active,
484 // a remote process has focus, and setting focus to same content in the main
485 // process, it means that we're restoring focus without changing DOM focus
486 // both in the main process and the remote process.
487 const bool restoringContextForRemoteContent =
488 XRE_IsParentProcess() && remoteHasFocus && !sIsActive && aPresContext &&
489 sFocusedPresContext && sFocusedElement &&
490 sFocusedPresContext.get() == aPresContext &&
491 sFocusedElement.get() == aElement &&
492 aAction.mFocusChange != InputContextAction::MENU_GOT_PSEUDO_FOCUS;
494 MOZ_LOG(
495 sISMLog, LogLevel::Info,
496 ("OnChangeFocusInternal(aPresContext=0x%p (available: %s), "
497 "aElement=0x%p (remote: %s), aAction={ mCause=%s, "
498 "mFocusChange=%s }), sFocusedPresContext=0x%p (available: %s), "
499 "sFocusedElement=0x%p, sTextInputHandlingWidget=0x%p (available: %s), "
500 "BrowserParent::GetFocused()=0x%p, sActiveIMEContentObserver=0x%p, "
501 "sInstalledMenuKeyboardListener=%s, sIsActive=%s, "
502 "restoringContextForRemoteContent=%s",
503 aPresContext, GetBoolName(CanHandleWith(aPresContext)), aElement,
504 GetBoolName(remoteHasFocus), ToString(aAction.mCause).c_str(),
505 ToString(aAction.mFocusChange).c_str(), sFocusedPresContext.get(),
506 GetBoolName(CanHandleWith(sFocusedPresContext)), sFocusedElement.get(),
507 sTextInputHandlingWidget,
508 GetBoolName(sTextInputHandlingWidget &&
509 !sTextInputHandlingWidget->Destroyed()),
510 BrowserParent::GetFocused(), sActiveIMEContentObserver.get(),
511 GetBoolName(sInstalledMenuKeyboardListener), GetBoolName(sIsActive),
512 GetBoolName(restoringContextForRemoteContent)));
514 sIsActive = !!aPresContext;
515 if (sPendingFocusedBrowserSwitchingData.isSome()) {
516 MOZ_ASSERT(XRE_IsParentProcess());
517 RefPtr<Element> focusedElement = sFocusedElement;
518 RefPtr<nsPresContext> focusedPresContext = sFocusedPresContext;
519 RefPtr<BrowserParent> browserParentBlurred =
520 sPendingFocusedBrowserSwitchingData.ref().mBrowserParentBlurred;
521 RefPtr<BrowserParent> browserParentFocused =
522 sPendingFocusedBrowserSwitchingData.ref().mBrowserParentFocused;
523 OnFocusMovedBetweenBrowsers(browserParentBlurred, browserParentFocused);
524 // If another call of this method happens during the
525 // OnFocusMovedBetweenBrowsers call, we shouldn't take back focus to
526 // the old one.
527 if (focusedElement != sFocusedElement.get() ||
528 focusedPresContext != sFocusedPresContext.get()) {
529 MOZ_LOG(sISMLog, LogLevel::Debug,
530 (" OnChangeFocusInternal(aPresContext=0x%p, aElement=0x%p) "
531 "stoped handling it because the focused content was changed to "
532 "sFocusedPresContext=0x%p, sFocusedElement=0x%p by another call",
533 aPresContext, aElement, sFocusedPresContext.get(),
534 sFocusedElement.get()));
535 return NS_OK;
539 // If new aPresShell has been destroyed, this should handle the focus change
540 // as nobody is getting focus.
541 MOZ_ASSERT_IF(!aPresContext, !aElement);
542 if (NS_WARN_IF(aPresContext && !CanHandleWith(aPresContext))) {
543 MOZ_LOG(sISMLog, LogLevel::Warning,
544 (" OnChangeFocusInternal(), called with destroyed PresShell, "
545 "handling this call as nobody getting focus"));
546 aPresContext = nullptr;
547 aElement = nullptr;
548 } else if (!aPresContext) {
549 aElement = nullptr;
552 const nsCOMPtr<nsIWidget> oldWidget = sTextInputHandlingWidget;
553 const nsCOMPtr<nsIWidget> newWidget =
554 aPresContext ? aPresContext->GetTextInputHandlingWidget() : nullptr;
555 const bool focusActuallyChanging =
556 (sFocusedElement != aElement || sFocusedPresContext != aPresContext ||
557 oldWidget != newWidget ||
558 (remoteHasFocus && !restoringContextForRemoteContent &&
559 (aAction.mFocusChange != InputContextAction::MENU_GOT_PSEUDO_FOCUS)));
561 // If old widget has composition, we may need to commit composition since
562 // a native IME context is shared on all editors on some widgets or all
563 // widgets (it depends on platforms).
564 if (oldWidget && focusActuallyChanging && sTextCompositions) {
565 RefPtr<TextComposition> composition =
566 sTextCompositions->GetCompositionFor(oldWidget);
567 if (composition) {
568 // However, don't commit the composition if we're being inactivated
569 // but the composition should be kept even during deactive.
570 // Note that oldWidget and sFocusedIMEWidget may be different here (in
571 // such case, sFocusedIMEWidget is perhaps nullptr). For example, IME
572 // may receive only blur notification but still has composition.
573 // We need to clean up only the oldWidget's composition state here.
574 if (aPresContext ||
575 !oldWidget->IMENotificationRequestsRef().WantDuringDeactive()) {
576 MOZ_LOG(
577 sISMLog, LogLevel::Info,
578 (" OnChangeFocusInternal(), requesting to commit composition to "
579 "the (previous) focused widget"));
580 NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, oldWidget,
581 composition->GetBrowserParent());
586 if (sActiveIMEContentObserver) {
587 MOZ_ASSERT(!remoteHasFocus || XRE_IsContentProcess(),
588 "IMEContentObserver should have been destroyed by "
589 "OnFocusMovedBetweenBrowsers.");
590 if (!aPresContext) {
591 if (!sActiveIMEContentObserver->KeepAliveDuringDeactive()) {
592 DestroyIMEContentObserver();
595 // Otherwise, i.e., new focused content is in this process, let's check
596 // whether the new focused content is already being managed by the
597 // active IME content observer.
598 else if (!sActiveIMEContentObserver->IsManaging(*aPresContext, aElement)) {
599 DestroyIMEContentObserver();
603 if (!aPresContext) {
604 MOZ_LOG(sISMLog, LogLevel::Debug,
605 (" OnChangeFocusInternal(), no nsPresContext is being activated"));
606 return NS_OK;
609 if (NS_WARN_IF(!newWidget)) {
610 MOZ_LOG(sISMLog, LogLevel::Error,
611 (" OnChangeFocusInternal(), FAILED due to no widget to manage its "
612 "IME state"));
613 return NS_OK;
616 // Update the cached widget since root view of the presContext may be
617 // changed to different view.
618 sTextInputHandlingWidget = newWidget;
620 // If a child process has focus, we should disable IME state until the child
621 // process actually gets focus because if user types keys before that they
622 // are handled by IME.
623 IMEState newState = remoteHasFocus ? IMEState(IMEEnabled::Disabled)
624 : GetNewIMEState(*aPresContext, aElement);
625 bool setIMEState = true;
627 if (remoteHasFocus && XRE_IsParentProcess()) {
628 if (aAction.mFocusChange == InputContextAction::MENU_GOT_PSEUDO_FOCUS) {
629 // If menu keyboard listener is installed, we need to disable IME now.
630 setIMEState = true;
631 } else if (aAction.mFocusChange ==
632 InputContextAction::MENU_LOST_PSEUDO_FOCUS) {
633 // If menu keyboard listener is uninstalled, we need to restore
634 // input context which was set by the remote process. However, if
635 // the remote process hasn't been set input context yet, we need to
636 // wait next SetInputContextForChildProcess() call.
637 if (HasActiveChildSetInputContext()) {
638 setIMEState = true;
639 newState = sActiveChildInputContext.mIMEState;
640 } else {
641 setIMEState = false;
643 } else if (focusActuallyChanging) {
644 InputContext context = newWidget->GetInputContext();
645 if (context.mIMEState.mEnabled == IMEEnabled::Disabled &&
646 context.mOrigin == InputContext::ORIGIN_CONTENT) {
647 setIMEState = false;
648 MOZ_LOG(sISMLog, LogLevel::Debug,
649 (" OnChangeFocusInternal(), doesn't set IME state because "
650 "focused element (or document) is in a child process and the "
651 "IME state is already disabled by a remote process"));
652 } else {
653 // When new remote process gets focus, we should forget input context
654 // coming from old focused remote process.
655 ResetActiveChildInputContext();
656 MOZ_LOG(sISMLog, LogLevel::Debug,
657 (" OnChangeFocusInternal(), will disable IME until new "
658 "focused element (or document) in the child process will get "
659 "focus actually"));
661 } else if (newWidget->GetInputContext().mOrigin !=
662 InputContext::ORIGIN_MAIN) {
663 // When focus is NOT changed actually, we shouldn't set IME state if
664 // current input context was set by a remote process since that means
665 // that the window is being activated and the child process may have
666 // composition. Then, we shouldn't commit the composition with making
667 // IME state disabled.
668 setIMEState = false;
669 MOZ_LOG(
670 sISMLog, LogLevel::Debug,
671 (" OnChangeFocusInternal(), doesn't set IME state because focused "
672 "element (or document) is already in the child process"));
674 } else {
675 // When this process gets focus, we should forget input context coming
676 // from remote process.
677 ResetActiveChildInputContext();
680 if (setIMEState) {
681 if (!focusActuallyChanging) {
682 // actual focus isn't changing, but if IME enabled state is changing,
683 // we should do it.
684 InputContext context = newWidget->GetInputContext();
685 if (context.mIMEState.mEnabled == newState.mEnabled) {
686 MOZ_LOG(sISMLog, LogLevel::Debug,
687 (" OnChangeFocusInternal(), neither focus nor IME state is "
688 "changing"));
689 return NS_OK;
691 aAction.mFocusChange = InputContextAction::FOCUS_NOT_CHANGED;
693 // Even if focus isn't changing actually, we should commit current
694 // composition here since the IME state is changing.
695 if (sFocusedPresContext && oldWidget && !focusActuallyChanging) {
696 NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, oldWidget,
697 sFocusedIMEBrowserParent);
699 } else if (aAction.mFocusChange == InputContextAction::FOCUS_NOT_CHANGED) {
700 // If aElement isn't null or aElement is null but editable, somebody gets
701 // focus.
702 bool gotFocus = aElement || (newState.mEnabled == IMEEnabled::Enabled);
703 aAction.mFocusChange = gotFocus ? InputContextAction::GOT_FOCUS
704 : InputContextAction::LOST_FOCUS;
707 if (remoteHasFocus && HasActiveChildSetInputContext() &&
708 aAction.mFocusChange == InputContextAction::MENU_LOST_PSEUDO_FOCUS) {
709 // Restore the input context in the active remote process when
710 // menu keyboard listener is uninstalled and active remote tab has
711 // focus.
712 SetInputContext(*newWidget, sActiveChildInputContext, aAction);
713 } else {
714 // Update IME state for new focus widget
715 SetIMEState(newState, aPresContext, aElement, *newWidget, aAction,
716 remoteHasFocus ? InputContext::ORIGIN_CONTENT : sOrigin);
720 sFocusedPresContext = aPresContext;
721 sFocusedElement = aElement;
723 // Don't call CreateIMEContentObserver() here because it will be called from
724 // the focus event handler of focused editor.
726 MOZ_LOG(sISMLog, LogLevel::Debug,
727 (" OnChangeFocusInternal(), modified IME state for "
728 "sFocusedPresContext=0x%p, sFocusedElement=0x%p",
729 sFocusedPresContext.get(), sFocusedElement.get()));
731 return NS_OK;
734 // static
735 void IMEStateManager::OnInstalledMenuKeyboardListener(bool aInstalling) {
736 MOZ_LOG(
737 sISMLog, LogLevel::Info,
738 ("OnInstalledMenuKeyboardListener(aInstalling=%s), "
739 "sInstalledMenuKeyboardListener=%s, BrowserParent::GetFocused()=0x%p, "
740 "sActiveChildInputContext=%s",
741 GetBoolName(aInstalling), GetBoolName(sInstalledMenuKeyboardListener),
742 BrowserParent::GetFocused(),
743 ToString(sActiveChildInputContext).c_str()));
745 sInstalledMenuKeyboardListener = aInstalling;
747 InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
748 aInstalling
749 ? InputContextAction::MENU_GOT_PSEUDO_FOCUS
750 : InputContextAction::MENU_LOST_PSEUDO_FOCUS);
751 RefPtr<nsPresContext> focusedPresContext = sFocusedPresContext;
752 RefPtr<Element> focusedElement = sFocusedElement;
753 OnChangeFocusInternal(focusedPresContext, focusedElement, action);
756 // static
757 bool IMEStateManager::OnMouseButtonEventInEditor(
758 nsPresContext& aPresContext, Element* aElement,
759 WidgetMouseEvent& aMouseEvent) {
760 MOZ_LOG(sISMLog, LogLevel::Info,
761 ("OnMouseButtonEventInEditor(aPresContext=0x%p (available: %s), "
762 "aElement=0x%p, aMouseEvent=0x%p), sFocusedPresContext=0x%p, "
763 "sFocusedElement=0x%p",
764 &aPresContext, GetBoolName(CanHandleWith(&aPresContext)), aElement,
765 &aMouseEvent, sFocusedPresContext.get(), sFocusedElement.get()));
767 if (sFocusedPresContext != &aPresContext || sFocusedElement != aElement) {
768 MOZ_LOG(sISMLog, LogLevel::Debug,
769 (" OnMouseButtonEventInEditor(), "
770 "the mouse event isn't fired on the editor managed by ISM"));
771 return false;
774 if (!sActiveIMEContentObserver) {
775 MOZ_LOG(sISMLog, LogLevel::Debug,
776 (" OnMouseButtonEventInEditor(), "
777 "there is no active IMEContentObserver"));
778 return false;
781 if (!sActiveIMEContentObserver->IsManaging(aPresContext, aElement)) {
782 MOZ_LOG(sISMLog, LogLevel::Debug,
783 (" OnMouseButtonEventInEditor(), "
784 "the active IMEContentObserver isn't managing the editor"));
785 return false;
788 OwningNonNull<IMEContentObserver> observer = *sActiveIMEContentObserver;
789 bool consumed = observer->OnMouseButtonEvent(aPresContext, aMouseEvent);
790 MOZ_LOG(sISMLog, LogLevel::Info,
791 (" OnMouseButtonEventInEditor(), "
792 "mouse event (mMessage=%s, mButton=%d) is %s",
793 ToChar(aMouseEvent.mMessage), aMouseEvent.mButton,
794 consumed ? "consumed" : "not consumed"));
795 return consumed;
798 // static
799 void IMEStateManager::OnClickInEditor(nsPresContext& aPresContext,
800 Element* aElement,
801 const WidgetMouseEvent& aMouseEvent) {
802 MOZ_LOG(sISMLog, LogLevel::Info,
803 ("OnClickInEditor(aPresContext=0x%p (available: %s), aElement=0x%p, "
804 "aMouseEvent=0x%p), sFocusedPresContext=0x%p, sFocusedElement=0x%p, "
805 "sTextInputHandlingWidget=0x%p (available: %s)",
806 &aPresContext, GetBoolName(CanHandleWith(&aPresContext)), aElement,
807 &aMouseEvent, sFocusedPresContext.get(), sFocusedElement.get(),
808 sTextInputHandlingWidget,
809 GetBoolName(sTextInputHandlingWidget &&
810 !sTextInputHandlingWidget->Destroyed())));
812 if (sFocusedPresContext != &aPresContext || sFocusedElement != aElement ||
813 NS_WARN_IF(!sFocusedPresContext) ||
814 NS_WARN_IF(!sTextInputHandlingWidget) ||
815 NS_WARN_IF(sTextInputHandlingWidget->Destroyed())) {
816 MOZ_LOG(sISMLog, LogLevel::Debug,
817 (" OnClickInEditor(), "
818 "the mouse event isn't fired on the editor managed by ISM"));
819 return;
822 const OwningNonNull<nsIWidget> textInputHandlingWidget =
823 *sTextInputHandlingWidget;
825 #ifdef DEBUG
827 nsCOMPtr<nsIWidget> currentTextInputHandlingWidget =
828 sFocusedPresContext->GetTextInputHandlingWidget();
829 MOZ_ASSERT(!currentTextInputHandlingWidget ||
830 currentTextInputHandlingWidget == textInputHandlingWidget);
832 #endif // DEBUG
834 if (!aMouseEvent.IsTrusted()) {
835 MOZ_LOG(sISMLog, LogLevel::Debug,
836 (" OnClickInEditor(), "
837 "the mouse event isn't a trusted event"));
838 return; // ignore untrusted event.
841 if (aMouseEvent.mButton) {
842 MOZ_LOG(sISMLog, LogLevel::Debug,
843 (" OnClickInEditor(), "
844 "the mouse event isn't a left mouse button event"));
845 return; // not a left click event.
848 if (aMouseEvent.mClickCount != 1) {
849 MOZ_LOG(sISMLog, LogLevel::Debug,
850 (" OnClickInEditor(), "
851 "the mouse event isn't a single click event"));
852 return; // should notify only first click event.
855 MOZ_ASSERT_IF(aElement, !EventStateManager::IsRemoteTarget(aElement));
856 InputContextAction::Cause cause =
857 aMouseEvent.mInputSource == MouseEvent_Binding::MOZ_SOURCE_TOUCH
858 ? InputContextAction::CAUSE_TOUCH
859 : InputContextAction::CAUSE_MOUSE;
861 InputContextAction action(cause, InputContextAction::FOCUS_NOT_CHANGED);
862 IMEState newState = GetNewIMEState(aPresContext, aElement);
863 // If the state is not editable, there should be no active IMEContentObserver.
864 // However, if this click sets focus to the editor, IMEContentObserver may
865 // have not been created yet. Instead, if there is active IMEContentObserver,
866 // it should be editable.
867 MOZ_ASSERT_IF(!newState.IsEditable(), !sActiveIMEContentObserver);
868 MOZ_ASSERT_IF(sActiveIMEContentObserver, newState.IsEditable());
869 SetIMEState(newState, &aPresContext, aElement, textInputHandlingWidget,
870 action, sOrigin);
873 // static
874 bool IMEStateManager::IsFocusedElement(const nsPresContext& aPresContext,
875 const Element* aFocusedElement) {
876 if (!sFocusedPresContext || &aPresContext != sFocusedPresContext) {
877 return false;
880 if (sFocusedElement == aFocusedElement) {
881 return true;
884 // If sFocusedElement is not nullptr, but aFocusedElement is nullptr, it does
885 // not have focus from point of view of IMEStateManager.
886 if (sFocusedElement) {
887 return false;
890 // If the caller does not think that nobody has focus, but we know there is
891 // a focused content, the caller must be called with wrong content.
892 if (!aFocusedElement) {
893 return false;
896 // If the aFocusedElement is in design mode, sFocusedElement may be nullptr.
897 if (aFocusedElement->IsInDesignMode()) {
898 MOZ_ASSERT(&aPresContext == sFocusedPresContext && !sFocusedElement);
899 return true;
902 // Otherwise, only when aFocusedElement is the root element, it can have
903 // focus, but IMEStateManager::OnChangeFocus is called with nullptr for
904 // aFocusedElement if it was not editable.
905 // XXX In this case, should the caller update sFocusedElement?
906 return aFocusedElement->IsEditable() && sFocusedPresContext->Document() &&
907 sFocusedPresContext->Document()->GetRootElement() == aFocusedElement;
910 // static
911 void IMEStateManager::OnFocusInEditor(nsPresContext& aPresContext,
912 Element* aElement,
913 EditorBase& aEditorBase) {
914 MOZ_LOG(sISMLog, LogLevel::Info,
915 ("OnFocusInEditor(aPresContext=0x%p (available: %s), aElement=0x%p, "
916 "aEditorBase=0x%p), sFocusedPresContext=0x%p, sFocusedElement=0x%p, "
917 "sActiveIMEContentObserver=0x%p",
918 &aPresContext, GetBoolName(CanHandleWith(&aPresContext)), aElement,
919 &aEditorBase, sFocusedPresContext.get(), sFocusedElement.get(),
920 sActiveIMEContentObserver.get()));
922 if (!IsFocusedElement(aPresContext, aElement)) {
923 MOZ_LOG(sISMLog, LogLevel::Debug,
924 (" OnFocusInEditor(), "
925 "an editor not managed by ISM gets focus"));
926 return;
928 MOZ_ASSERT(sTextInputHandlingWidget);
930 // If the IMEContentObserver instance isn't managing the editor actually,
931 // we need to recreate the instance.
932 if (sActiveIMEContentObserver) {
933 if (sActiveIMEContentObserver->IsManaging(aPresContext, aElement)) {
934 MOZ_LOG(
935 sISMLog, LogLevel::Debug,
936 (" OnFocusInEditor(), "
937 "the editor is already being managed by sActiveIMEContentObserver"));
938 return;
940 // If the IMEContentObserver has not finished initializing itself yet,
941 // we don't need to recreate it because the following
942 // TryToFlushPendingNotifications call must make it initialized.
943 const nsCOMPtr<nsIWidget> textInputHandlingWidget =
944 sTextInputHandlingWidget;
945 if (!sActiveIMEContentObserver->IsBeingInitializedFor(aPresContext,
946 aElement)) {
947 DestroyIMEContentObserver();
949 if (NS_WARN_IF(!IsFocusedElement(aPresContext, aElement)) ||
950 NS_WARN_IF(!sTextInputHandlingWidget) ||
951 NS_WARN_IF(sTextInputHandlingWidget != textInputHandlingWidget)) {
952 MOZ_LOG(sISMLog, LogLevel::Error,
953 (" OnFocusInEditor(), detected unexpected focus change with "
954 "re-initializing active IMEContentObserver"));
955 return;
959 if (!sActiveIMEContentObserver && sTextInputHandlingWidget &&
960 IsIMEObserverNeeded(
961 sTextInputHandlingWidget->GetInputContext().mIMEState)) {
962 CreateIMEContentObserver(aEditorBase, aElement);
963 if (sActiveIMEContentObserver) {
964 MOZ_LOG(sISMLog, LogLevel::Debug,
965 (" OnFocusInEditor(), new IMEContentObserver is created (0x%p)",
966 sActiveIMEContentObserver.get()));
970 if (sActiveIMEContentObserver) {
971 sActiveIMEContentObserver->TryToFlushPendingNotifications(false);
972 MOZ_LOG(sISMLog, LogLevel::Debug,
973 (" OnFocusInEditor(), trying to send pending notifications in "
974 "the active IMEContentObserver (0x%p)...",
975 sActiveIMEContentObserver.get()));
979 // static
980 void IMEStateManager::OnEditorInitialized(EditorBase& aEditorBase) {
981 if (!sActiveIMEContentObserver ||
982 !sActiveIMEContentObserver->WasInitializedWith(aEditorBase)) {
983 return;
986 MOZ_LOG(sISMLog, LogLevel::Info,
987 ("OnEditorInitialized(aEditorBase=0x%p)", &aEditorBase));
989 sActiveIMEContentObserver->UnsuppressNotifyingIME();
992 // static
993 void IMEStateManager::OnEditorDestroying(EditorBase& aEditorBase) {
994 if (!sActiveIMEContentObserver ||
995 !sActiveIMEContentObserver->WasInitializedWith(aEditorBase)) {
996 return;
999 MOZ_LOG(sISMLog, LogLevel::Info,
1000 ("OnEditorDestroying(aEditorBase=0x%p)", &aEditorBase));
1002 // The IMEContentObserver shouldn't notify IME of anything until reframing
1003 // is finished.
1004 sActiveIMEContentObserver->SuppressNotifyingIME();
1007 void IMEStateManager::OnReFocus(nsPresContext& aPresContext,
1008 Element& aElement) {
1009 MOZ_LOG(sISMLog, LogLevel::Info,
1010 ("OnReFocus(aPresContext=0x%p (available: %s), aElement=0x%p), "
1011 "sActiveIMEContentObserver=0x%p, aElement=0x%p",
1012 &aPresContext, GetBoolName(CanHandleWith(&aPresContext)), &aElement,
1013 sActiveIMEContentObserver.get(), sFocusedElement.get()));
1015 if (NS_WARN_IF(!sTextInputHandlingWidget) ||
1016 NS_WARN_IF(sTextInputHandlingWidget->Destroyed())) {
1017 return;
1020 if (!sActiveIMEContentObserver ||
1021 !sActiveIMEContentObserver->IsManaging(aPresContext, &aElement)) {
1022 MOZ_LOG(sISMLog, LogLevel::Debug,
1023 (" OnReFocus(), there is no valid IMEContentObserver, so we don't "
1024 "manage this. Ignore this"));
1025 return;
1028 MOZ_ASSERT(&aElement == sFocusedElement.get());
1030 if (!UserActivation::IsHandlingUserInput() ||
1031 UserActivation::IsHandlingKeyboardInput()) {
1032 return;
1035 const OwningNonNull<nsIWidget> textInputHandlingWidget =
1036 *sTextInputHandlingWidget;
1038 // Don't update IME state during composition.
1039 if (sTextCompositions) {
1040 if (const TextComposition* composition =
1041 sTextCompositions->GetCompositionFor(textInputHandlingWidget)) {
1042 if (composition->IsComposing()) {
1043 return;
1048 InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
1049 InputContextAction::FOCUS_NOT_CHANGED);
1050 IMEState newState = GetNewIMEState(aPresContext, &aElement);
1051 MOZ_ASSERT(newState.IsEditable());
1052 SetIMEState(newState, &aPresContext, &aElement, textInputHandlingWidget,
1053 action, sOrigin);
1056 // static
1057 void IMEStateManager::MaybeOnEditableStateDisabled(nsPresContext& aPresContext,
1058 dom::Element* aElement) {
1059 MOZ_LOG(
1060 sISMLog, LogLevel::Info,
1061 ("MaybeOnEditableStateDisabled(aPresContext=0x%p, aElement=0x%p), "
1062 "sFocusedPresContext=0x%p (available: %s), "
1063 "sFocusedElement=0x%p, sTextInputHandlingWidget=0x%p (available: %s), "
1064 "sActiveIMEContentObserver=0x%p, sIsGettingNewIMEState=%s",
1065 &aPresContext, aElement, sFocusedPresContext.get(),
1066 GetBoolName(CanHandleWith(sFocusedPresContext)), sFocusedElement.get(),
1067 sTextInputHandlingWidget,
1068 GetBoolName(sTextInputHandlingWidget &&
1069 !sTextInputHandlingWidget->Destroyed()),
1070 sActiveIMEContentObserver.get(), GetBoolName(sIsGettingNewIMEState)));
1072 if (sIsGettingNewIMEState) {
1073 MOZ_LOG(sISMLog, LogLevel::Debug,
1074 (" MaybeOnEditableStateDisabled(), "
1075 "does nothing because of called while getting new IME state"));
1076 return;
1079 if (&aPresContext != sFocusedPresContext || aElement != sFocusedElement) {
1080 MOZ_LOG(sISMLog, LogLevel::Debug,
1081 (" MaybeOnEditableStateDisabled(), "
1082 "does nothing because of another element already has focus"));
1083 return;
1086 if (NS_WARN_IF(!sTextInputHandlingWidget) ||
1087 NS_WARN_IF(sTextInputHandlingWidget->Destroyed())) {
1088 MOZ_LOG(sISMLog, LogLevel::Error,
1089 (" MaybeOnEditableStateDisabled(), FAILED due to "
1090 "the widget for the managing the nsPresContext has gone"));
1091 return;
1094 const OwningNonNull<nsIWidget> textInputHandlingWidget =
1095 *sTextInputHandlingWidget;
1097 #ifdef DEBUG
1099 nsCOMPtr<nsIWidget> currentTextInputHandlingWidget =
1100 aPresContext.GetTextInputHandlingWidget();
1101 MOZ_ASSERT(!currentTextInputHandlingWidget ||
1102 currentTextInputHandlingWidget == textInputHandlingWidget);
1104 #endif // DEBUG
1106 const IMEState newIMEState = GetNewIMEState(aPresContext, aElement);
1107 // If IME state becomes editable, HTMLEditor should also be initialized with
1108 // same path as it gets focus. Therefore, IMEStateManager::UpdateIMEState
1109 // should be called by HTMLEditor instead.
1110 if (newIMEState.IsEditable()) {
1111 MOZ_LOG(sISMLog, LogLevel::Debug,
1112 (" MaybeOnEditableStateDisabled(), "
1113 "does nothing because IME state does not become disabled"));
1114 return;
1117 // Otherwise, disable IME on the widget and destroy active IMEContentObserver
1118 // if there is.
1119 const InputContext inputContext = textInputHandlingWidget->GetInputContext();
1120 if (inputContext.mIMEState.mEnabled == newIMEState.mEnabled) {
1121 MOZ_LOG(sISMLog, LogLevel::Debug,
1122 (" MaybeOnEditableStateDisabled(), "
1123 "does nothing because IME state is not changed"));
1124 return;
1127 if (sActiveIMEContentObserver) {
1128 DestroyIMEContentObserver();
1131 InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
1132 InputContextAction::FOCUS_NOT_CHANGED);
1133 SetIMEState(newIMEState, &aPresContext, aElement, textInputHandlingWidget,
1134 action, sOrigin);
1137 // static
1138 void IMEStateManager::UpdateIMEState(const IMEState& aNewIMEState,
1139 Element* aElement, EditorBase& aEditorBase,
1140 const UpdateIMEStateOptions& aOptions) {
1141 MOZ_LOG(
1142 sISMLog, LogLevel::Info,
1143 ("UpdateIMEState(aNewIMEState=%s, aElement=0x%p, aEditorBase=0x%p, "
1144 "aOptions=0x%0x), sFocusedPresContext=0x%p (available: %s), "
1145 "sFocusedElement=0x%p, sTextInputHandlingWidget=0x%p (available: %s), "
1146 "sActiveIMEContentObserver=0x%p, sIsGettingNewIMEState=%s",
1147 ToString(aNewIMEState).c_str(), aElement, &aEditorBase,
1148 aOptions.serialize(), sFocusedPresContext.get(),
1149 GetBoolName(CanHandleWith(sFocusedPresContext)), sFocusedElement.get(),
1150 sTextInputHandlingWidget,
1151 GetBoolName(sTextInputHandlingWidget &&
1152 !sTextInputHandlingWidget->Destroyed()),
1153 sActiveIMEContentObserver.get(), GetBoolName(sIsGettingNewIMEState)));
1155 if (sIsGettingNewIMEState) {
1156 MOZ_LOG(sISMLog, LogLevel::Debug,
1157 (" UpdateIMEState(), "
1158 "does nothing because of called while getting new IME state"));
1159 return;
1162 RefPtr<PresShell> presShell(aEditorBase.GetPresShell());
1163 if (NS_WARN_IF(!presShell)) {
1164 MOZ_LOG(sISMLog, LogLevel::Error,
1165 (" UpdateIMEState(), FAILED due to "
1166 "editor doesn't have PresShell"));
1167 return;
1170 const RefPtr<nsPresContext> presContext =
1171 aElement
1172 ? aElement->GetPresContext(Element::PresContextFor::eForComposedDoc)
1173 : aEditorBase.GetPresContext();
1174 if (NS_WARN_IF(!presContext)) {
1175 MOZ_LOG(sISMLog, LogLevel::Error,
1176 (" UpdateIMEState(), FAILED due to "
1177 "editor doesn't have PresContext"));
1178 return;
1181 // IMEStateManager::UpdateIMEState() should be called after
1182 // IMEStateManager::OnChangeFocus() is called for setting focus to aElement
1183 // and aEditorBase. However, when aEditorBase is an HTMLEditor, this may be
1184 // called by nsIEditor::PostCreate() before IMEStateManager::OnChangeFocus().
1185 // Similarly, when aEditorBase is a TextEditor, this may be called by
1186 // nsIEditor::SetFlags(). In such cases, this method should do nothing
1187 // because input context should be updated when
1188 // IMEStateManager::OnChangeFocus() is called later.
1189 if (sFocusedPresContext != presContext) {
1190 MOZ_LOG(sISMLog, LogLevel::Warning,
1191 (" UpdateIMEState(), does nothing due to "
1192 "the editor hasn't managed by IMEStateManager yet"));
1193 return;
1196 // If IMEStateManager doesn't manage any document, this cannot update IME
1197 // state of any widget.
1198 if (NS_WARN_IF(!sFocusedPresContext)) {
1199 MOZ_LOG(sISMLog, LogLevel::Error,
1200 (" UpdateIMEState(), FAILED due to "
1201 "no managing nsPresContext"));
1202 return;
1205 if (NS_WARN_IF(!sTextInputHandlingWidget) ||
1206 NS_WARN_IF(sTextInputHandlingWidget->Destroyed())) {
1207 MOZ_LOG(sISMLog, LogLevel::Error,
1208 (" UpdateIMEState(), FAILED due to "
1209 "the widget for the managing nsPresContext has gone"));
1210 return;
1213 const OwningNonNull<nsIWidget> textInputHandlingWidget =
1214 *sTextInputHandlingWidget;
1216 #ifdef DEBUG
1218 nsCOMPtr<nsIWidget> currentTextInputHandlingWidget =
1219 sFocusedPresContext->GetTextInputHandlingWidget();
1220 MOZ_ASSERT(!currentTextInputHandlingWidget ||
1221 currentTextInputHandlingWidget == textInputHandlingWidget);
1223 #endif // DEBUG
1225 // TODO: Investigate if we could put off to initialize IMEContentObserver
1226 // later because a lot of callers need to be marked as
1227 // MOZ_CAN_RUN_SCRIPT otherwise.
1229 // Even if there is active IMEContentObserver, it may not be observing the
1230 // editor with current editable root content due to reframed. In such case,
1231 // We should try to reinitialize the IMEContentObserver.
1232 if (sActiveIMEContentObserver && IsIMEObserverNeeded(aNewIMEState)) {
1233 MOZ_LOG(sISMLog, LogLevel::Debug,
1234 (" UpdateIMEState(), try to reinitialize the "
1235 "active IMEContentObserver"));
1236 OwningNonNull<IMEContentObserver> contentObserver =
1237 *sActiveIMEContentObserver;
1238 OwningNonNull<nsPresContext> presContext = *sFocusedPresContext;
1239 if (!contentObserver->MaybeReinitialize(
1240 textInputHandlingWidget, presContext, aElement, aEditorBase)) {
1241 MOZ_LOG(sISMLog, LogLevel::Error,
1242 (" UpdateIMEState(), failed to reinitialize the "
1243 "active IMEContentObserver"));
1245 if (NS_WARN_IF(textInputHandlingWidget->Destroyed())) {
1246 MOZ_LOG(sISMLog, LogLevel::Error,
1247 (" UpdateIMEState(), widget has gone during re-initializing the "
1248 "active IMEContentObserver"));
1249 return;
1253 // If there is no active IMEContentObserver or it isn't observing the
1254 // editor correctly, we should recreate it.
1255 const bool createTextStateManager =
1256 IsIMEObserverNeeded(aNewIMEState) &&
1257 (!sActiveIMEContentObserver ||
1258 !sActiveIMEContentObserver->IsManaging(*sFocusedPresContext, aElement));
1259 // If we're recreating new IMEContentObserver or new state does not need
1260 // IMEContentObserver, destroy the active IMEContentObserver.
1261 const bool destroyTextStateManager =
1262 sActiveIMEContentObserver &&
1263 (createTextStateManager || !IsIMEObserverNeeded(aNewIMEState));
1265 const bool updateIMEState =
1266 aOptions.contains(UpdateIMEStateOption::ForceUpdate) ||
1267 (textInputHandlingWidget->GetInputContext().mIMEState.mEnabled !=
1268 aNewIMEState.mEnabled);
1269 if (NS_WARN_IF(textInputHandlingWidget->Destroyed())) {
1270 MOZ_LOG(
1271 sISMLog, LogLevel::Error,
1272 (" UpdateIMEState(), widget has gone during getting input context"));
1273 return;
1276 if (updateIMEState &&
1277 !aOptions.contains(UpdateIMEStateOption::DontCommitComposition)) {
1278 // commit current composition before modifying IME state.
1279 NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, textInputHandlingWidget,
1280 sFocusedIMEBrowserParent);
1281 if (NS_WARN_IF(textInputHandlingWidget->Destroyed())) {
1282 MOZ_LOG(sISMLog, LogLevel::Error,
1283 (" UpdateIMEState(), widget has gone during committing "
1284 "composition"));
1285 return;
1287 // FIXME: If committing composition changes IME state recursively, we should
1288 // not keep updating IME state here. However, how can we manage it?
1289 // Is a generation of the state is required?
1292 if (destroyTextStateManager) {
1293 DestroyIMEContentObserver();
1294 if (NS_WARN_IF(textInputHandlingWidget->Destroyed()) ||
1295 NS_WARN_IF(sTextInputHandlingWidget != textInputHandlingWidget)) {
1296 MOZ_LOG(sISMLog, LogLevel::Error,
1297 (" UpdateIMEState(), has set input context, but the widget is "
1298 "not focused"));
1299 return;
1303 if (updateIMEState) {
1304 InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
1305 InputContextAction::FOCUS_NOT_CHANGED);
1306 RefPtr<nsPresContext> editorPresContext = aEditorBase.GetPresContext();
1307 if (NS_WARN_IF(!editorPresContext)) {
1308 MOZ_LOG(sISMLog, LogLevel::Error,
1309 (" UpdateIMEState(), nsPresContext for editor has already been "
1310 "lost"));
1311 return;
1313 SetIMEState(aNewIMEState, editorPresContext, aElement,
1314 textInputHandlingWidget, action, sOrigin);
1315 if (NS_WARN_IF(textInputHandlingWidget->Destroyed()) ||
1316 NS_WARN_IF(sTextInputHandlingWidget != textInputHandlingWidget)) {
1317 MOZ_LOG(sISMLog, LogLevel::Error,
1318 (" UpdateIMEState(), has set input context, but the widget is "
1319 "not focused"));
1320 return;
1322 if (NS_WARN_IF(
1323 sTextInputHandlingWidget->GetInputContext().mIMEState.mEnabled !=
1324 aNewIMEState.mEnabled)) {
1325 MOZ_LOG(sISMLog, LogLevel::Error,
1326 (" UpdateIMEState(), has set input context, but IME enabled "
1327 "state was overridden by somebody else"));
1328 return;
1332 NS_ASSERTION(IsFocusedElement(*presContext, aElement),
1333 "aElement does not match with sFocusedElement");
1335 if (createTextStateManager) {
1336 if (!sActiveIMEContentObserver && sFocusedPresContext &&
1337 sTextInputHandlingWidget) {
1338 // XXX In this case, it might not be enough safe to notify IME of
1339 // anything. So, don't try to flush pending notifications of
1340 // IMEContentObserver here.
1341 CreateIMEContentObserver(aEditorBase, aElement);
1342 } else {
1343 MOZ_LOG(sISMLog, LogLevel::Error,
1344 (" UpdateIMEState(), wanted to create IMEContentObserver, but "
1345 "lost focus"));
1350 // static
1351 IMEState IMEStateManager::GetNewIMEState(const nsPresContext& aPresContext,
1352 Element* aElement) {
1353 MOZ_LOG(
1354 sISMLog, LogLevel::Info,
1355 ("GetNewIMEState(aPresContext=0x%p, aElement=0x%p), "
1356 "sInstalledMenuKeyboardListener=%s",
1357 &aPresContext, aElement, GetBoolName(sInstalledMenuKeyboardListener)));
1359 if (!CanHandleWith(&aPresContext)) {
1360 MOZ_LOG(sISMLog, LogLevel::Debug,
1361 (" GetNewIMEState() returns IMEEnabled::Disabled because "
1362 "the nsPresContext has been destroyed"));
1363 return IMEState(IMEEnabled::Disabled);
1366 // On Printing or Print Preview, we don't need IME.
1367 if (aPresContext.Type() == nsPresContext::eContext_PrintPreview ||
1368 aPresContext.Type() == nsPresContext::eContext_Print) {
1369 MOZ_LOG(sISMLog, LogLevel::Debug,
1370 (" GetNewIMEState() returns IMEEnabled::Disabled because "
1371 "the nsPresContext is for print or print preview"));
1372 return IMEState(IMEEnabled::Disabled);
1375 if (sInstalledMenuKeyboardListener) {
1376 MOZ_LOG(sISMLog, LogLevel::Debug,
1377 (" GetNewIMEState() returns IMEEnabled::Disabled because "
1378 "menu keyboard listener was installed"));
1379 return IMEState(IMEEnabled::Disabled);
1382 if (!aElement) {
1383 // Even if there are no focused content, the focused document might be
1384 // editable, such case is design mode.
1385 if (aPresContext.Document() && aPresContext.Document()->IsInDesignMode()) {
1386 MOZ_LOG(sISMLog, LogLevel::Debug,
1387 (" GetNewIMEState() returns IMEEnabled::Enabled because "
1388 "design mode editor has focus"));
1389 return IMEState(IMEEnabled::Enabled);
1391 MOZ_LOG(sISMLog, LogLevel::Debug,
1392 (" GetNewIMEState() returns IMEEnabled::Disabled because "
1393 "no content has focus"));
1394 return IMEState(IMEEnabled::Disabled);
1397 // If aElement is in designMode, aElement should be the root node of the
1398 // document.
1399 if (aElement && aElement->IsInDesignMode()) {
1400 MOZ_LOG(sISMLog, LogLevel::Debug,
1401 (" GetNewIMEState() returns IMEEnabled::Enabled because "
1402 "a content node in design mode editor has focus"));
1403 return IMEState(IMEEnabled::Enabled);
1406 // nsIContent::GetDesiredIMEState() may cause a call of UpdateIMEState()
1407 // from EditorBase::PostCreate() because GetDesiredIMEState() needs to
1408 // retrieve an editor instance for the element if it's editable element.
1409 // For avoiding such nested IME state updates, we should set
1410 // sIsGettingNewIMEState here and UpdateIMEState() should check it.
1411 GettingNewIMEStateBlocker blocker;
1413 IMEState newIMEState = aElement->GetDesiredIMEState();
1414 MOZ_LOG(sISMLog, LogLevel::Debug,
1415 (" GetNewIMEState() returns %s", ToString(newIMEState).c_str()));
1416 return newIMEState;
1419 static bool MayBeIMEUnawareWebApp(nsINode* aNode) {
1420 bool haveKeyEventsListener = false;
1422 while (aNode) {
1423 EventListenerManager* const mgr = aNode->GetExistingListenerManager();
1424 if (mgr) {
1425 if (mgr->MayHaveInputOrCompositionEventListener()) {
1426 return false;
1428 haveKeyEventsListener |= mgr->MayHaveKeyEventListener();
1430 aNode = aNode->GetParentNode();
1433 return haveKeyEventsListener;
1436 // static
1437 void IMEStateManager::ResetActiveChildInputContext() {
1438 sActiveChildInputContext.mIMEState.mEnabled = IMEEnabled::Unknown;
1441 // static
1442 bool IMEStateManager::HasActiveChildSetInputContext() {
1443 return sActiveChildInputContext.mIMEState.mEnabled != IMEEnabled::Unknown;
1446 // static
1447 void IMEStateManager::SetInputContextForChildProcess(
1448 BrowserParent* aBrowserParent, const InputContext& aInputContext,
1449 const InputContextAction& aAction) {
1450 MOZ_LOG(
1451 sISMLog, LogLevel::Info,
1452 ("SetInputContextForChildProcess(aBrowserParent=0x%p, "
1453 "aInputContext=%s , aAction={ mCause=%s, mAction=%s }), "
1454 "sFocusedPresContext=0x%p (available: %s), "
1455 "sTextInputHandlingWidget=0x%p (available: %s), "
1456 "BrowserParent::GetFocused()=0x%p, sInstalledMenuKeyboardListener=%s",
1457 aBrowserParent, ToString(aInputContext).c_str(),
1458 ToString(aAction.mCause).c_str(), ToString(aAction.mFocusChange).c_str(),
1459 sFocusedPresContext.get(),
1460 GetBoolName(CanHandleWith(sFocusedPresContext)),
1461 sTextInputHandlingWidget,
1462 GetBoolName(sTextInputHandlingWidget &&
1463 !sTextInputHandlingWidget->Destroyed()),
1464 BrowserParent::GetFocused(),
1465 GetBoolName(sInstalledMenuKeyboardListener)));
1467 if (aBrowserParent != BrowserParent::GetFocused()) {
1468 MOZ_LOG(sISMLog, LogLevel::Error,
1469 (" SetInputContextForChildProcess(), FAILED, "
1470 "because non-focused tab parent tries to set input context"));
1471 return;
1474 if (NS_WARN_IF(!CanHandleWith(sFocusedPresContext))) {
1475 MOZ_LOG(sISMLog, LogLevel::Error,
1476 (" SetInputContextForChildProcess(), FAILED, "
1477 "due to no focused presContext"));
1478 return;
1481 if (NS_WARN_IF(!sTextInputHandlingWidget) ||
1482 NS_WARN_IF(sTextInputHandlingWidget->Destroyed())) {
1483 MOZ_LOG(sISMLog, LogLevel::Error,
1484 (" SetInputContextForChildProcess(), FAILED, "
1485 "due to the widget for the nsPresContext has gone"));
1486 return;
1489 const OwningNonNull<nsIWidget> textInputHandlingWidget =
1490 *sTextInputHandlingWidget;
1491 #ifdef DEBUG
1493 nsCOMPtr<nsIWidget> currentTextInputHandlingWidget =
1494 sFocusedPresContext->GetTextInputHandlingWidget();
1495 MOZ_ASSERT(!currentTextInputHandlingWidget ||
1496 currentTextInputHandlingWidget == textInputHandlingWidget);
1498 #endif // DEBUG
1499 MOZ_ASSERT(aInputContext.mOrigin == InputContext::ORIGIN_CONTENT);
1501 sActiveChildInputContext = aInputContext;
1502 MOZ_ASSERT(HasActiveChildSetInputContext());
1504 // If input context is changed in remote process while menu keyboard listener
1505 // is installed, this process shouldn't set input context now. When it's
1506 // uninstalled, input context should be restored from
1507 // sActiveChildInputContext.
1508 if (sInstalledMenuKeyboardListener) {
1509 MOZ_LOG(sISMLog, LogLevel::Info,
1510 (" SetInputContextForChildProcess(), waiting to set input context "
1511 "until menu keyboard listener is uninstalled"));
1512 return;
1515 SetInputContext(textInputHandlingWidget, aInputContext, aAction);
1518 MOZ_CAN_RUN_SCRIPT static bool IsNextFocusableElementTextControl(
1519 const Element* aInputContent) {
1520 RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager();
1521 if (MOZ_UNLIKELY(!fm)) {
1522 return false;
1524 nsCOMPtr<nsIContent> nextContent;
1525 const RefPtr<Element> inputContent = const_cast<Element*>(aInputContent);
1526 const nsCOMPtr<nsPIDOMWindowOuter> outerWindow =
1527 aInputContent->OwnerDoc()->GetWindow();
1528 nsresult rv = fm->DetermineElementToMoveFocus(
1529 outerWindow, inputContent, nsIFocusManager::MOVEFOCUS_FORWARD, true,
1530 false, getter_AddRefs(nextContent));
1531 if (NS_WARN_IF(NS_FAILED(rv)) || !nextContent) {
1532 return false;
1534 nextContent = nextContent->FindFirstNonChromeOnlyAccessContent();
1535 nsCOMPtr<nsIFormControl> nextControl = do_QueryInterface(nextContent);
1536 if (!nextControl || !nextControl->IsTextControl(false)) {
1537 return false;
1540 // XXX We don't consider next element is date/time control yet.
1541 nsGenericHTMLElement* nextElement =
1542 nsGenericHTMLElement::FromNodeOrNull(nextContent);
1543 if (!nextElement) {
1544 return false;
1546 bool focusable = false;
1547 nextElement->IsHTMLFocusable(false, &focusable, nullptr);
1548 if (!focusable) {
1549 return false;
1552 // Check readonly attribute.
1553 if (nextElement->IsHTMLElement(nsGkAtoms::textarea)) {
1554 HTMLTextAreaElement* textAreaElement =
1555 HTMLTextAreaElement::FromNodeOrNull(nextElement);
1556 return !textAreaElement->ReadOnly();
1559 // If neither textarea nor input, what element type?
1560 MOZ_DIAGNOSTIC_ASSERT(nextElement->IsHTMLElement(nsGkAtoms::input));
1562 HTMLInputElement* inputElement =
1563 HTMLInputElement::FromNodeOrNull(nextElement);
1564 return !inputElement->ReadOnly();
1567 static void GetInputType(const IMEState& aState, const nsIContent& aContent,
1568 nsAString& aInputType) {
1569 // NOTE: If you change here, you may need to update
1570 // widget::InputContext::GatNativeKeyBindings too.
1571 if (aContent.IsHTMLElement(nsGkAtoms::input)) {
1572 const HTMLInputElement* inputElement =
1573 HTMLInputElement::FromNode(&aContent);
1574 if (inputElement->HasBeenTypePassword() && aState.IsEditable()) {
1575 aInputType.AssignLiteral("password");
1576 } else {
1577 inputElement->GetType(aInputType);
1579 } else if (aContent.IsHTMLElement(nsGkAtoms::textarea)) {
1580 aInputType.Assign(nsGkAtoms::textarea->GetUTF16String());
1584 MOZ_CAN_RUN_SCRIPT static void GetActionHint(const IMEState& aState,
1585 const nsIContent& aContent,
1586 nsAString& aActionHint) {
1587 MOZ_ASSERT(aContent.IsHTMLElement());
1589 if (aState.IsEditable() && StaticPrefs::dom_forms_enterkeyhint()) {
1590 nsGenericHTMLElement::FromNode(&aContent)->GetEnterKeyHint(aActionHint);
1592 // If enterkeyhint is set, we don't infer action hint.
1593 if (!aActionHint.IsEmpty()) {
1594 return;
1598 if (!aContent.IsAnyOfHTMLElements(nsGkAtoms::input, nsGkAtoms::textarea)) {
1599 return;
1602 // XXX This is old compatibility, but we might be able to remove this.
1603 aContent.AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::moz_action_hint,
1604 aActionHint);
1606 if (!aActionHint.IsEmpty()) {
1607 ToLowerCase(aActionHint);
1608 return;
1611 // Get the input content corresponding to the focused node,
1612 // which may be an anonymous child of the input content.
1613 MOZ_ASSERT(&aContent == aContent.FindFirstNonChromeOnlyAccessContent());
1614 const HTMLInputElement* inputElement = HTMLInputElement::FromNode(aContent);
1615 if (!inputElement) {
1616 return;
1619 // If we don't have an action hint and
1620 // return won't submit the form, use "maybenext".
1621 bool willSubmit = false;
1622 bool isLastElement = false;
1623 HTMLFormElement* formElement = inputElement->GetForm();
1624 // is this a form and does it have a default submit element?
1625 if (formElement) {
1626 if (formElement->IsLastActiveElement(inputElement)) {
1627 isLastElement = true;
1630 if (formElement->GetDefaultSubmitElement()) {
1631 willSubmit = true;
1632 // is this an html form...
1633 } else {
1634 // ... and does it only have a single text input element ?
1635 if (!formElement->ImplicitSubmissionIsDisabled() ||
1636 // ... or is this the last non-disabled element?
1637 isLastElement) {
1638 willSubmit = true;
1643 if (!isLastElement && formElement) {
1644 // If next tabbable content in form is text control, hint should be "next"
1645 // even there is submit in form.
1646 // MOZ_KnownLive(inputElement) because it's an alias of aContent.
1647 if (IsNextFocusableElementTextControl(MOZ_KnownLive(inputElement))) {
1648 // This is focusable text control
1649 // XXX What good hint for read only field?
1650 aActionHint.AssignLiteral("maybenext");
1651 return;
1655 if (!willSubmit) {
1656 return;
1659 if (inputElement->ControlType() == FormControlType::InputSearch) {
1660 aActionHint.AssignLiteral("search");
1661 return;
1664 aActionHint.AssignLiteral("go");
1667 static void GetInputMode(const IMEState& aState, const nsIContent& aContent,
1668 nsAString& aInputMode) {
1669 if (aState.IsEditable() &&
1670 (StaticPrefs::dom_forms_inputmode() ||
1671 nsContentUtils::IsChromeDoc(aContent.OwnerDoc()))) {
1672 aContent.AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::inputmode,
1673 aInputMode);
1674 if (aContent.IsHTMLElement(nsGkAtoms::input) &&
1675 aInputMode.EqualsLiteral("mozAwesomebar")) {
1676 if (!nsContentUtils::IsChromeDoc(aContent.OwnerDoc())) {
1677 // mozAwesomebar should be allowed only in chrome
1678 aInputMode.Truncate();
1680 } else {
1681 ToLowerCase(aInputMode);
1686 static void GetAutocapitalize(const IMEState& aState, const Element& aElement,
1687 const InputContext& aInputContext,
1688 nsAString& aAutocapitalize) {
1689 if (aElement.IsHTMLElement() && aState.IsEditable() &&
1690 StaticPrefs::dom_forms_autocapitalize() &&
1691 aInputContext.IsAutocapitalizeSupported()) {
1692 nsGenericHTMLElement::FromNode(&aElement)->GetAutocapitalize(
1693 aAutocapitalize);
1697 // static
1698 void IMEStateManager::SetIMEState(const IMEState& aState,
1699 const nsPresContext* aPresContext,
1700 Element* aElement, nsIWidget& aWidget,
1701 InputContextAction aAction,
1702 InputContext::Origin aOrigin) {
1703 MOZ_LOG(sISMLog, LogLevel::Info,
1704 ("SetIMEState(aState=%s, nsPresContext=0x%p, aElement=0x%p "
1705 "(BrowserParent=0x%p), aWidget=0x%p, aAction={ mCause=%s, "
1706 "mFocusChange=%s }, aOrigin=%s)",
1707 ToString(aState).c_str(), aPresContext, aElement,
1708 BrowserParent::GetFrom(aElement), &aWidget,
1709 ToString(aAction.mCause).c_str(),
1710 ToString(aAction.mFocusChange).c_str(), ToChar(aOrigin)));
1712 InputContext context;
1713 context.mIMEState = aState;
1714 if (aPresContext) {
1715 if (nsIURI* uri = aPresContext->Document()->GetDocumentURI()) {
1716 // We don't need to and should not expose special URLs such as:
1717 // about: Any apps like IME should work normally and constantly in any
1718 // default pages such as about:blank, about:home, etc in either
1719 // the main process or a content process.
1720 // blob: This may contain big data. If we copy it to the main process,
1721 // it may make the heap dirty which makes the process slower.
1722 // chrome: Same as about, any apps should work normally and constantly in
1723 // any chrome documents.
1724 // data: Any native apps in the environment shouldn't change the behavior
1725 // with the data URL's content and it may contain too big data.
1726 // file: The file path may contain private things and we shouldn't let
1727 // other apps like IME know which one is touched by the user because
1728 // malicious text services may like files which are explicitly used
1729 // by the user better.
1730 if (uri->SchemeIs("http") || uri->SchemeIs("https")) {
1731 // Note that we don't need to expose UserPass, Query and Reference to
1732 // IME since they may contain sensitive data, but non-malicious text
1733 // services must not require these data.
1734 nsCOMPtr<nsIURI> exposableURL;
1735 if (NS_SUCCEEDED(NS_MutateURI(uri)
1736 .SetQuery(""_ns)
1737 .SetRef(""_ns)
1738 .SetUserPass(""_ns)
1739 .Finalize(exposableURL))) {
1740 context.mURI = std::move(exposableURL);
1745 context.mOrigin = aOrigin;
1746 context.mMayBeIMEUnaware =
1747 context.mIMEState.IsEditable() &&
1748 StaticPrefs::
1749 intl_ime_hack_on_ime_unaware_apps_fire_key_events_for_composition() &&
1750 MayBeIMEUnawareWebApp(aElement);
1752 context.mHasHandledUserInput =
1753 aPresContext && aPresContext->PresShell()->HasHandledUserInput();
1755 context.mInPrivateBrowsing =
1756 aPresContext &&
1757 nsContentUtils::IsInPrivateBrowsing(aPresContext->Document());
1759 const RefPtr<Element> focusedElement =
1760 aElement ? Element::FromNodeOrNull(
1761 aElement->FindFirstNonChromeOnlyAccessContent())
1762 : nullptr;
1764 if (focusedElement && focusedElement->IsHTMLElement()) {
1765 GetInputType(aState, *focusedElement, context.mHTMLInputType);
1766 GetActionHint(aState, *focusedElement, context.mActionHint);
1767 GetInputMode(aState, *focusedElement, context.mHTMLInputMode);
1768 GetAutocapitalize(aState, *focusedElement, context,
1769 context.mAutocapitalize);
1772 if (aAction.mCause == InputContextAction::CAUSE_UNKNOWN &&
1773 nsContentUtils::LegacyIsCallerChromeOrNativeCode()) {
1774 aAction.mCause = InputContextAction::CAUSE_UNKNOWN_CHROME;
1777 if ((aAction.mCause == InputContextAction::CAUSE_UNKNOWN ||
1778 aAction.mCause == InputContextAction::CAUSE_UNKNOWN_CHROME) &&
1779 UserActivation::IsHandlingUserInput()) {
1780 aAction.mCause =
1781 UserActivation::IsHandlingKeyboardInput()
1782 ? InputContextAction::CAUSE_UNKNOWN_DURING_KEYBOARD_INPUT
1783 : InputContextAction::CAUSE_UNKNOWN_DURING_NON_KEYBOARD_INPUT;
1786 SetInputContext(aWidget, context, aAction);
1789 // static
1790 void IMEStateManager::SetInputContext(nsIWidget& aWidget,
1791 const InputContext& aInputContext,
1792 const InputContextAction& aAction) {
1793 MOZ_LOG(
1794 sISMLog, LogLevel::Info,
1795 ("SetInputContext(aWidget=0x%p, aInputContext=%s, "
1796 "aAction={ mCause=%s, mAction=%s }), BrowserParent::GetFocused()=0x%p",
1797 &aWidget, ToString(aInputContext).c_str(),
1798 ToString(aAction.mCause).c_str(), ToString(aAction.mFocusChange).c_str(),
1799 BrowserParent::GetFocused()));
1801 OwningNonNull<nsIWidget> widget = aWidget;
1802 widget->SetInputContext(aInputContext, aAction);
1803 sActiveInputContextWidget = widget;
1806 // static
1807 void IMEStateManager::EnsureTextCompositionArray() {
1808 if (sTextCompositions) {
1809 return;
1811 sTextCompositions = new TextCompositionArray();
1814 // static
1815 void IMEStateManager::DispatchCompositionEvent(
1816 nsINode* aEventTargetNode, nsPresContext* aPresContext,
1817 BrowserParent* aBrowserParent, WidgetCompositionEvent* aCompositionEvent,
1818 nsEventStatus* aStatus, EventDispatchingCallback* aCallBack,
1819 bool aIsSynthesized) {
1820 MOZ_LOG(
1821 sISMLog, LogLevel::Info,
1822 ("DispatchCompositionEvent(aNode=0x%p, "
1823 "aPresContext=0x%p, aCompositionEvent={ mMessage=%s, "
1824 "mNativeIMEContext={ mRawNativeIMEContext=0x%" PRIXPTR ", "
1825 "mOriginProcessID=0x%" PRIX64 " }, mWidget(0x%p)={ "
1826 "GetNativeIMEContext()={ mRawNativeIMEContext=0x%" PRIXPTR ", "
1827 "mOriginProcessID=0x%" PRIX64 " }, Destroyed()=%s }, "
1828 "mFlags={ mIsTrusted=%s, mPropagationStopped=%s } }, "
1829 "aIsSynthesized=%s), browserParent=%p",
1830 aEventTargetNode, aPresContext, ToChar(aCompositionEvent->mMessage),
1831 aCompositionEvent->mNativeIMEContext.mRawNativeIMEContext,
1832 aCompositionEvent->mNativeIMEContext.mOriginProcessID,
1833 aCompositionEvent->mWidget.get(),
1834 aCompositionEvent->mWidget->GetNativeIMEContext().mRawNativeIMEContext,
1835 aCompositionEvent->mWidget->GetNativeIMEContext().mOriginProcessID,
1836 GetBoolName(aCompositionEvent->mWidget->Destroyed()),
1837 GetBoolName(aCompositionEvent->mFlags.mIsTrusted),
1838 GetBoolName(aCompositionEvent->mFlags.mPropagationStopped),
1839 GetBoolName(aIsSynthesized), aBrowserParent));
1841 if (NS_WARN_IF(!aCompositionEvent->IsTrusted()) ||
1842 NS_WARN_IF(aCompositionEvent->PropagationStopped())) {
1843 return;
1846 MOZ_ASSERT(aCompositionEvent->mMessage != eCompositionUpdate,
1847 "compositionupdate event shouldn't be dispatched manually");
1849 EnsureTextCompositionArray();
1851 RefPtr<TextComposition> composition =
1852 sTextCompositions->GetCompositionFor(aCompositionEvent);
1853 if (!composition) {
1854 // If synthesized event comes after delayed native composition events
1855 // for request of commit or cancel, we should ignore it.
1856 if (NS_WARN_IF(aIsSynthesized)) {
1857 return;
1859 MOZ_LOG(sISMLog, LogLevel::Debug,
1860 (" DispatchCompositionEvent(), "
1861 "adding new TextComposition to the array"));
1862 MOZ_ASSERT(aCompositionEvent->mMessage == eCompositionStart);
1863 composition = new TextComposition(aPresContext, aEventTargetNode,
1864 aBrowserParent, aCompositionEvent);
1865 sTextCompositions->AppendElement(composition);
1867 #ifdef DEBUG
1868 else {
1869 MOZ_ASSERT(aCompositionEvent->mMessage != eCompositionStart);
1871 #endif // #ifdef DEBUG
1873 // Dispatch the event on composing target.
1874 composition->DispatchCompositionEvent(aCompositionEvent, aStatus, aCallBack,
1875 aIsSynthesized);
1877 // WARNING: the |composition| might have been destroyed already.
1879 // Remove the ended composition from the array.
1880 // NOTE: When TextComposition is synthesizing compositionend event for
1881 // emulating a commit, the instance shouldn't be removed from the array
1882 // because IME may perform it later. Then, we need to ignore the
1883 // following commit events in TextComposition::DispatchEvent().
1884 // However, if commit or cancel for a request is performed synchronously
1885 // during not safe to dispatch events, PresShell must have discarded
1886 // compositionend event. Then, the synthesized compositionend event is
1887 // the last event for the composition. In this case, we need to
1888 // destroy the TextComposition with synthesized compositionend event.
1889 if ((!aIsSynthesized ||
1890 composition->WasNativeCompositionEndEventDiscarded()) &&
1891 aCompositionEvent->CausesDOMCompositionEndEvent()) {
1892 TextCompositionArray::index_type i =
1893 sTextCompositions->IndexOf(aCompositionEvent->mWidget);
1894 if (i != TextCompositionArray::NoIndex) {
1895 MOZ_LOG(
1896 sISMLog, LogLevel::Debug,
1897 (" DispatchCompositionEvent(), "
1898 "removing TextComposition from the array since NS_COMPOSTION_END "
1899 "was dispatched"));
1900 sTextCompositions->ElementAt(i)->Destroy();
1901 sTextCompositions->RemoveElementAt(i);
1906 // static
1907 IMEContentObserver* IMEStateManager::GetActiveContentObserver() {
1908 return sActiveIMEContentObserver;
1911 // static
1912 nsIContent* IMEStateManager::GetRootContent(nsPresContext* aPresContext) {
1913 Document* doc = aPresContext->Document();
1914 if (NS_WARN_IF(!doc)) {
1915 return nullptr;
1917 return doc->GetRootElement();
1920 // static
1921 void IMEStateManager::HandleSelectionEvent(
1922 nsPresContext* aPresContext, nsIContent* aEventTargetContent,
1923 WidgetSelectionEvent* aSelectionEvent) {
1924 RefPtr<BrowserParent> browserParent = GetActiveBrowserParent();
1925 if (!browserParent) {
1926 browserParent = BrowserParent::GetFrom(aEventTargetContent
1927 ? aEventTargetContent
1928 : GetRootContent(aPresContext));
1931 MOZ_LOG(
1932 sISMLog, LogLevel::Info,
1933 ("HandleSelectionEvent(aPresContext=0x%p, "
1934 "aEventTargetContent=0x%p, aSelectionEvent={ mMessage=%s, "
1935 "mFlags={ mIsTrusted=%s } }), browserParent=%p",
1936 aPresContext, aEventTargetContent, ToChar(aSelectionEvent->mMessage),
1937 GetBoolName(aSelectionEvent->mFlags.mIsTrusted), browserParent.get()));
1939 if (!aSelectionEvent->IsTrusted()) {
1940 return;
1943 RefPtr<TextComposition> composition =
1944 sTextCompositions
1945 ? sTextCompositions->GetCompositionFor(aSelectionEvent->mWidget)
1946 : nullptr;
1947 if (composition) {
1948 // When there is a composition, TextComposition should guarantee that the
1949 // selection event will be handled in same target as composition events.
1950 composition->HandleSelectionEvent(aSelectionEvent);
1951 } else {
1952 // When there is no composition, the selection event should be handled
1953 // in the aPresContext or browserParent.
1954 TextComposition::HandleSelectionEvent(aPresContext, browserParent,
1955 aSelectionEvent);
1959 // static
1960 void IMEStateManager::OnCompositionEventDiscarded(
1961 WidgetCompositionEvent* aCompositionEvent) {
1962 // Note that this method is never called for synthesized events for emulating
1963 // commit or cancel composition.
1965 MOZ_LOG(
1966 sISMLog, LogLevel::Info,
1967 ("OnCompositionEventDiscarded(aCompositionEvent={ "
1968 "mMessage=%s, mNativeIMEContext={ mRawNativeIMEContext=0x%" PRIXPTR ", "
1969 "mOriginProcessID=0x%" PRIX64 " }, mWidget(0x%p)={ "
1970 "GetNativeIMEContext()={ mRawNativeIMEContext=0x%" PRIXPTR ", "
1971 "mOriginProcessID=0x%" PRIX64 " }, Destroyed()=%s }, "
1972 "mFlags={ mIsTrusted=%s } })",
1973 ToChar(aCompositionEvent->mMessage),
1974 aCompositionEvent->mNativeIMEContext.mRawNativeIMEContext,
1975 aCompositionEvent->mNativeIMEContext.mOriginProcessID,
1976 aCompositionEvent->mWidget.get(),
1977 aCompositionEvent->mWidget->GetNativeIMEContext().mRawNativeIMEContext,
1978 aCompositionEvent->mWidget->GetNativeIMEContext().mOriginProcessID,
1979 GetBoolName(aCompositionEvent->mWidget->Destroyed()),
1980 GetBoolName(aCompositionEvent->mFlags.mIsTrusted)));
1982 if (!aCompositionEvent->IsTrusted()) {
1983 return;
1986 // Ignore compositionstart for now because sTextCompositions may not have
1987 // been created yet.
1988 if (aCompositionEvent->mMessage == eCompositionStart) {
1989 return;
1992 RefPtr<TextComposition> composition =
1993 sTextCompositions->GetCompositionFor(aCompositionEvent->mWidget);
1994 if (!composition) {
1995 // If the PresShell has been being destroyed during composition,
1996 // a TextComposition instance for the composition was already removed from
1997 // the array and destroyed in OnDestroyPresContext(). Therefore, we may
1998 // fail to retrieve a TextComposition instance here.
1999 MOZ_LOG(sISMLog, LogLevel::Info,
2000 (" OnCompositionEventDiscarded(), "
2001 "TextComposition instance for the widget has already gone"));
2002 return;
2004 composition->OnCompositionEventDiscarded(aCompositionEvent);
2007 // static
2008 nsresult IMEStateManager::NotifyIME(IMEMessage aMessage, nsIWidget* aWidget,
2009 BrowserParent* aBrowserParent) {
2010 return IMEStateManager::NotifyIME(IMENotification(aMessage), aWidget,
2011 aBrowserParent);
2014 // static
2015 nsresult IMEStateManager::NotifyIME(const IMENotification& aNotification,
2016 nsIWidget* aWidget,
2017 BrowserParent* aBrowserParent) {
2018 MOZ_LOG(sISMLog, LogLevel::Info,
2019 ("NotifyIME(aNotification={ mMessage=%s }, "
2020 "aWidget=0x%p, aBrowserParent=0x%p), sFocusedIMEWidget=0x%p, "
2021 "BrowserParent::GetFocused()=0x%p, sFocusedIMEBrowserParent=0x%p, "
2022 "aBrowserParent == BrowserParent::GetFocused()=%s, "
2023 "aBrowserParent == sFocusedIMEBrowserParent=%s, "
2024 "CanSendNotificationToWidget()=%s",
2025 ToChar(aNotification.mMessage), aWidget, aBrowserParent,
2026 sFocusedIMEWidget, BrowserParent::GetFocused(),
2027 sFocusedIMEBrowserParent.get(),
2028 GetBoolName(aBrowserParent == BrowserParent::GetFocused()),
2029 GetBoolName(aBrowserParent == sFocusedIMEBrowserParent),
2030 GetBoolName(CanSendNotificationToWidget())));
2032 if (NS_WARN_IF(!aWidget)) {
2033 MOZ_LOG(sISMLog, LogLevel::Error,
2034 (" NotifyIME(), FAILED due to no widget"));
2035 return NS_ERROR_INVALID_ARG;
2038 switch (aNotification.mMessage) {
2039 case NOTIFY_IME_OF_FOCUS: {
2040 MOZ_ASSERT(CanSendNotificationToWidget());
2042 // If focus notification comes from a remote browser which already lost
2043 // focus, we shouldn't accept the focus notification. Then, following
2044 // notifications from the process will be ignored.
2045 if (aBrowserParent != BrowserParent::GetFocused()) {
2046 MOZ_LOG(sISMLog, LogLevel::Warning,
2047 (" NotifyIME(), WARNING, the received focus notification is "
2048 "ignored, because its associated BrowserParent did not match"
2049 "the focused BrowserParent."));
2050 return NS_OK;
2052 // If IME focus is already set, first blur the currently-focused
2053 // IME widget
2054 if (sFocusedIMEWidget) {
2055 // XXX Why don't we first request the previously-focused IME
2056 // widget to commit the composition?
2057 MOZ_ASSERT(
2058 sFocusedIMEBrowserParent || aBrowserParent,
2059 "This case shouldn't be caused by focus move in this process");
2060 MOZ_LOG(sISMLog, LogLevel::Warning,
2061 (" NotifyIME(), WARNING, received focus notification with ")
2062 "non-null sFocusedIMEWidget. How come "
2063 "OnFocusMovedBetweenBrowsers did not blur it already?");
2064 nsCOMPtr<nsIWidget> focusedIMEWidget(sFocusedIMEWidget);
2065 sFocusedIMEWidget = nullptr;
2066 sFocusedIMEBrowserParent = nullptr;
2067 focusedIMEWidget->NotifyIME(IMENotification(NOTIFY_IME_OF_BLUR));
2069 #ifdef DEBUG
2070 if (aBrowserParent) {
2071 nsCOMPtr<nsIWidget> browserParentWidget =
2072 aBrowserParent->GetTextInputHandlingWidget();
2073 MOZ_ASSERT(browserParentWidget == aWidget);
2075 #endif
2076 sFocusedIMEBrowserParent = aBrowserParent;
2077 sFocusedIMEWidget = aWidget;
2078 nsCOMPtr<nsIWidget> widget(aWidget);
2079 MOZ_LOG(
2080 sISMLog, LogLevel::Info,
2081 (" NotifyIME(), about to call widget->NotifyIME() for IME focus"));
2082 return widget->NotifyIME(aNotification);
2084 case NOTIFY_IME_OF_BLUR: {
2085 if (aBrowserParent != sFocusedIMEBrowserParent) {
2086 MOZ_LOG(sISMLog, LogLevel::Warning,
2087 (" NotifyIME(), WARNING, the received blur notification is "
2088 "ignored "
2089 "because it's not from current focused IME browser"));
2090 return NS_OK;
2092 if (!sFocusedIMEWidget) {
2093 MOZ_LOG(
2094 sISMLog, LogLevel::Error,
2095 (" NotifyIME(), WARNING, received blur notification but there is "
2096 "no focused IME widget"));
2097 return NS_OK;
2099 if (NS_WARN_IF(sFocusedIMEWidget != aWidget)) {
2100 MOZ_LOG(sISMLog, LogLevel::Warning,
2101 (" NotifyIME(), WARNING, the received blur notification is "
2102 "ignored "
2103 "because it's not for current focused IME widget"));
2104 return NS_OK;
2106 nsCOMPtr<nsIWidget> focusedIMEWidget(sFocusedIMEWidget);
2107 sFocusedIMEWidget = nullptr;
2108 sFocusedIMEBrowserParent = nullptr;
2109 return CanSendNotificationToWidget()
2110 ? focusedIMEWidget->NotifyIME(
2111 IMENotification(NOTIFY_IME_OF_BLUR))
2112 : NS_OK;
2114 case NOTIFY_IME_OF_SELECTION_CHANGE:
2115 case NOTIFY_IME_OF_TEXT_CHANGE:
2116 case NOTIFY_IME_OF_POSITION_CHANGE:
2117 case NOTIFY_IME_OF_MOUSE_BUTTON_EVENT:
2118 case NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED: {
2119 if (aBrowserParent != sFocusedIMEBrowserParent) {
2120 MOZ_LOG(
2121 sISMLog, LogLevel::Warning,
2122 (" NotifyIME(), WARNING, the received content change notification "
2123 "is ignored because it's not from current focused IME browser"));
2124 return NS_OK;
2126 if (!sFocusedIMEWidget) {
2127 MOZ_LOG(
2128 sISMLog, LogLevel::Warning,
2129 (" NotifyIME(), WARNING, the received content change notification "
2130 "is ignored because there is no focused IME widget"));
2131 return NS_OK;
2133 if (NS_WARN_IF(sFocusedIMEWidget != aWidget)) {
2134 MOZ_LOG(
2135 sISMLog, LogLevel::Warning,
2136 (" NotifyIME(), WARNING, the received content change notification "
2137 "is ignored because it's not for current focused IME widget"));
2138 return NS_OK;
2140 if (!CanSendNotificationToWidget()) {
2141 return NS_OK;
2143 nsCOMPtr<nsIWidget> widget(aWidget);
2144 return widget->NotifyIME(aNotification);
2146 default:
2147 // Other notifications should be sent only when there is composition.
2148 // So, we need to handle the others below.
2149 break;
2152 if (!sTextCompositions) {
2153 MOZ_LOG(sISMLog, LogLevel::Info,
2154 (" NotifyIME(), the request to IME is ignored because "
2155 "there have been no compositions yet"));
2156 return NS_OK;
2159 RefPtr<TextComposition> composition =
2160 sTextCompositions->GetCompositionFor(aWidget);
2161 if (!composition) {
2162 MOZ_LOG(sISMLog, LogLevel::Info,
2163 (" NotifyIME(), the request to IME is ignored because "
2164 "there is no active composition"));
2165 return NS_OK;
2168 if (aBrowserParent != composition->GetBrowserParent()) {
2169 MOZ_LOG(
2170 sISMLog, LogLevel::Warning,
2171 (" NotifyIME(), WARNING, the request to IME is ignored because "
2172 "it does not come from the remote browser which has the composition "
2173 "on aWidget"));
2174 return NS_OK;
2177 switch (aNotification.mMessage) {
2178 case REQUEST_TO_COMMIT_COMPOSITION:
2179 return composition->RequestToCommit(aWidget, false);
2180 case REQUEST_TO_CANCEL_COMPOSITION:
2181 return composition->RequestToCommit(aWidget, true);
2182 default:
2183 MOZ_CRASH("Unsupported notification");
2185 MOZ_CRASH(
2186 "Failed to handle the notification for non-synthesized composition");
2187 return NS_ERROR_FAILURE;
2190 // static
2191 nsresult IMEStateManager::NotifyIME(IMEMessage aMessage,
2192 nsPresContext* aPresContext,
2193 BrowserParent* aBrowserParent) {
2194 MOZ_LOG(sISMLog, LogLevel::Info,
2195 ("NotifyIME(aMessage=%s, aPresContext=0x%p, aBrowserParent=0x%p)",
2196 ToChar(aMessage), aPresContext, aBrowserParent));
2198 if (NS_WARN_IF(!CanHandleWith(aPresContext))) {
2199 return NS_ERROR_INVALID_ARG;
2202 nsCOMPtr<nsIWidget> widget = aPresContext->GetTextInputHandlingWidget();
2203 if (NS_WARN_IF(!widget)) {
2204 MOZ_LOG(sISMLog, LogLevel::Error,
2205 (" NotifyIME(), FAILED due to no widget for the "
2206 "nsPresContext"));
2207 return NS_ERROR_NOT_AVAILABLE;
2209 return NotifyIME(aMessage, widget, aBrowserParent);
2212 // static
2213 bool IMEStateManager::IsEditable(nsINode* node) {
2214 if (node->IsEditable()) {
2215 return true;
2217 // |node| might be readwrite (for example, a text control)
2218 if (node->IsElement() &&
2219 node->AsElement()->State().HasState(ElementState::READWRITE)) {
2220 return true;
2222 return false;
2225 // static
2226 nsINode* IMEStateManager::GetRootEditableNode(const nsPresContext& aPresContext,
2227 const Element* aElement) {
2228 if (aElement) {
2229 // If the focused content is in design mode, return is composed document
2230 // because aElement may be in UA widget shadow tree.
2231 if (aElement->IsInDesignMode()) {
2232 return aElement->GetComposedDoc();
2235 nsINode* candidateRootNode = const_cast<Element*>(aElement);
2236 for (nsINode* node = candidateRootNode; node && IsEditable(node);
2237 node = node->GetParentNode()) {
2238 // If the node has independent selection like <input type="text"> or
2239 // <textarea>, the node should be the root editable node for aElement.
2240 // FYI: <select> element also has independent selection but IsEditable()
2241 // returns false.
2242 // XXX: If somebody adds new editable element which has independent
2243 // selection but doesn't own editor, we'll need more checks here.
2244 // XXX: If aElement is not in native anonymous subtree, checking
2245 // independent selection must be wrong, see bug 1731005.
2246 if (node->IsContent() && node->AsContent()->HasIndependentSelection()) {
2247 return node;
2249 candidateRootNode = node;
2251 return candidateRootNode;
2254 return aPresContext.Document() && aPresContext.Document()->IsInDesignMode()
2255 ? aPresContext.Document()
2256 : nullptr;
2259 // static
2260 bool IMEStateManager::IsIMEObserverNeeded(const IMEState& aState) {
2261 return aState.IsEditable();
2264 // static
2265 void IMEStateManager::DestroyIMEContentObserver() {
2266 if (!sActiveIMEContentObserver) {
2267 MOZ_LOG(sISMLog, LogLevel::Verbose,
2268 ("DestroyIMEContentObserver() does nothing"));
2269 return;
2272 MOZ_LOG(sISMLog, LogLevel::Info,
2273 ("DestroyIMEContentObserver(), destroying "
2274 "the active IMEContentObserver..."));
2275 RefPtr<IMEContentObserver> tsm = sActiveIMEContentObserver.get();
2276 sActiveIMEContentObserver = nullptr;
2277 tsm->Destroy();
2280 // static
2281 void IMEStateManager::CreateIMEContentObserver(EditorBase& aEditorBase,
2282 Element* aFocusedElement) {
2283 MOZ_ASSERT(!sActiveIMEContentObserver);
2284 MOZ_ASSERT(sTextInputHandlingWidget);
2285 MOZ_ASSERT(sFocusedPresContext);
2286 MOZ_ASSERT(IsIMEObserverNeeded(
2287 sTextInputHandlingWidget->GetInputContext().mIMEState));
2289 MOZ_LOG(sISMLog, LogLevel::Info,
2290 ("CreateIMEContentObserver(aEditorBase=0x%p, aFocusedElement=0x%p), "
2291 "sFocusedPresContext=0x%p, sFocusedElement=0x%p, "
2292 "sTextInputHandlingWidget=0x%p (available: %s), "
2293 "sActiveIMEContentObserver=0x%p, "
2294 "sActiveIMEContentObserver->IsManaging(sFocusedPresContext, "
2295 "sFocusedElement)=%s",
2296 &aEditorBase, aFocusedElement, sFocusedPresContext.get(),
2297 sFocusedElement.get(), sTextInputHandlingWidget,
2298 GetBoolName(sTextInputHandlingWidget &&
2299 !sTextInputHandlingWidget->Destroyed()),
2300 sActiveIMEContentObserver.get(),
2301 GetBoolName(sActiveIMEContentObserver && sFocusedPresContext &&
2302 sActiveIMEContentObserver->IsManaging(
2303 *sFocusedPresContext, sFocusedElement))));
2305 if (NS_WARN_IF(sTextInputHandlingWidget->Destroyed())) {
2306 MOZ_LOG(sISMLog, LogLevel::Error,
2307 (" CreateIMEContentObserver(), FAILED due to "
2308 "the widget for the nsPresContext has gone"));
2309 return;
2312 const OwningNonNull<nsIWidget> textInputHandlingWidget =
2313 *sTextInputHandlingWidget;
2315 #ifdef DEBUG
2317 nsCOMPtr<nsIWidget> currentTextInputHandlingWidget =
2318 sFocusedPresContext->GetTextInputHandlingWidget();
2319 MOZ_ASSERT(currentTextInputHandlingWidget == textInputHandlingWidget);
2321 #endif // DEBUG
2323 MOZ_LOG(sISMLog, LogLevel::Debug,
2324 (" CreateIMEContentObserver() is creating an "
2325 "IMEContentObserver instance..."));
2326 sActiveIMEContentObserver = new IMEContentObserver();
2328 // IMEContentObserver::Init() might create another IMEContentObserver
2329 // instance. So, sActiveIMEContentObserver would be replaced with new one.
2330 // We should hold the current instance here.
2331 OwningNonNull<IMEContentObserver> activeIMEContentObserver =
2332 *sActiveIMEContentObserver;
2333 OwningNonNull<nsPresContext> focusedPresContext = *sFocusedPresContext;
2334 RefPtr<Element> focusedElement = aFocusedElement;
2335 activeIMEContentObserver->Init(textInputHandlingWidget, focusedPresContext,
2336 focusedElement, aEditorBase);
2339 // static
2340 nsresult IMEStateManager::GetFocusSelectionAndRootElement(
2341 Selection** aSelection, Element** aRootElement) {
2342 if (!sActiveIMEContentObserver) {
2343 return NS_ERROR_NOT_AVAILABLE;
2345 return sActiveIMEContentObserver->GetSelectionAndRoot(aSelection,
2346 aRootElement);
2349 // static
2350 already_AddRefed<TextComposition> IMEStateManager::GetTextCompositionFor(
2351 nsIWidget* aWidget) {
2352 if (!sTextCompositions) {
2353 return nullptr;
2355 RefPtr<TextComposition> textComposition =
2356 sTextCompositions->GetCompositionFor(aWidget);
2357 return textComposition.forget();
2360 // static
2361 already_AddRefed<TextComposition> IMEStateManager::GetTextCompositionFor(
2362 const WidgetCompositionEvent* aCompositionEvent) {
2363 if (!sTextCompositions) {
2364 return nullptr;
2366 RefPtr<TextComposition> textComposition =
2367 sTextCompositions->GetCompositionFor(aCompositionEvent);
2368 return textComposition.forget();
2371 // static
2372 already_AddRefed<TextComposition> IMEStateManager::GetTextCompositionFor(
2373 nsPresContext* aPresContext) {
2374 if (!sTextCompositions) {
2375 return nullptr;
2377 RefPtr<TextComposition> textComposition =
2378 sTextCompositions->GetCompositionFor(aPresContext);
2379 return textComposition.forget();
2382 } // namespace mozilla