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"
38 #include "nsContentUtils.h"
39 #include "nsFocusManager.h"
40 #include "nsIContent.h"
41 #include "nsIContentInlines.h"
42 #include "nsIFormControl.h"
44 #include "nsISupports.h"
46 #include "nsIURIMutator.h"
47 #include "nsPresContext.h"
52 using namespace widget
;
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
;
90 void IMEStateManager::Init() {
91 sOrigin
= XRE_IsParentProcess() ? InputContext::ORIGIN_MAIN
92 : InputContext::ORIGIN_CONTENT
;
93 ResetActiveChildInputContext();
97 void IMEStateManager::Shutdown() {
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
120 sActiveChildInputContext
.ShutDown();
124 void IMEStateManager::OnFocusMovedBetweenBrowsers(BrowserParent
* aBlur
,
125 BrowserParent
* aFocus
) {
126 MOZ_ASSERT(aBlur
!= aFocus
);
127 MOZ_ASSERT(XRE_IsParentProcess());
129 if (sPendingFocusedBrowserSwitchingData
.isSome()) {
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
==
136 sPendingFocusedBrowserSwitchingData
.reset();
137 MOZ_LOG(sISMLog
, LogLevel::Info
,
138 (" OnFocusMovedBetweenBrowsers(), canceled all pending focus "
139 "moves between browsers"));
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
&&
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"));
165 sPendingFocusedBrowserSwitchingData
.reset();
167 const nsCOMPtr
<nsIWidget
> oldWidget
= sTextInputHandlingWidget
;
168 // In the chrome-process case, we'll get sTextInputHandlingWidget from a
170 sTextInputHandlingWidget
=
171 aFocus
? nsCOMPtr
<nsIWidget
>(aFocus
->GetTextInputHandlingWidget()).get()
173 if (oldWidget
&& sTextCompositions
) {
174 RefPtr
<TextComposition
> composition
=
175 sTextCompositions
->GetCompositionFor(oldWidget
);
178 sISMLog
, LogLevel::Debug
,
179 (" OnFocusMovedBetweenBrowsers(), requesting to commit "
181 "the (previous) focused widget (would request=%s)",
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 "
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
);
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.
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;
252 void IMEStateManager::WidgetOnQuit(nsIWidget
* aWidget
) {
253 if (sFocusedIMEWidget
== aWidget
) {
255 sISMLog
, LogLevel::Debug
,
256 ("WidgetOnQuit(aWidget=0x%p (available %s)), sFocusedIMEWidget=0x%p",
257 aWidget
, GetBoolName(aWidget
&& !aWidget
->Destroyed()),
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
);
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;
295 DestroyIMEContentObserver();
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
));
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
));
318 if (composition
->NativeOffsetOfStartComposition() == aStartOffset
) {
323 sISMLog
, LogLevel::Info
,
324 ("MaybeStartOffsetUpdatedInChild(aWidget=0x%p, aStartOffset=%u), "
326 aWidget
, aStartOffset
, composition
->NativeOffsetOfStartComposition()));
327 composition
->OnStartOffsetUpdatedInChild(aStartOffset
);
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)",
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
) {
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(),
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
,
378 sTextInputHandlingWidget
= nullptr;
379 sFocusedElement
= nullptr;
380 sFocusedPresContext
= nullptr;
385 nsresult
IMEStateManager::OnRemoveContent(nsPresContext
& aPresContext
,
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()).
402 compositionInContent
->NotifyIME(REQUEST_TO_CANCEL_COMPOSITION
);
404 compositionInContent
->NotifyIME(REQUEST_TO_COMMIT_COMPOSITION
);
409 if (!sFocusedPresContext
|| !sFocusedElement
||
410 !sFocusedElement
->IsInclusiveDescendantOf(&aElement
)) {
413 MOZ_ASSERT(sFocusedPresContext
== &aPresContext
);
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
) {
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
,
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);
456 bool IMEStateManager::CanHandleWith(const nsPresContext
* aPresContext
) {
457 return aPresContext
&& aPresContext
->GetPresShell() &&
458 !aPresContext
->PresShell()->IsDestroying();
462 nsresult
IMEStateManager::OnChangeFocus(nsPresContext
* aPresContext
,
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
);
474 nsresult
IMEStateManager::OnChangeFocusInternal(nsPresContext
* aPresContext
,
476 InputContextAction aAction
) {
477 NS_ASSERTION(!aElement
|| aElement
->GetPresContext(
478 Element::PresContextFor::eForComposedDoc
) ==
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
;
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
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()));
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;
548 } else if (!aPresContext
) {
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
);
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.
575 !oldWidget
->IMENotificationRequestsRef().WantDuringDeactive()) {
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.");
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();
604 MOZ_LOG(sISMLog
, LogLevel::Debug
,
605 (" OnChangeFocusInternal(), no nsPresContext is being activated"));
609 if (NS_WARN_IF(!newWidget
)) {
610 MOZ_LOG(sISMLog
, LogLevel::Error
,
611 (" OnChangeFocusInternal(), FAILED due to no widget to manage its "
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.
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()) {
639 newState
= sActiveChildInputContext
.mIMEState
;
643 } else if (focusActuallyChanging
) {
644 InputContext context
= newWidget
->GetInputContext();
645 if (context
.mIMEState
.mEnabled
== IMEEnabled::Disabled
&&
646 context
.mOrigin
== InputContext::ORIGIN_CONTENT
) {
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"));
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 "
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.
670 sISMLog
, LogLevel::Debug
,
671 (" OnChangeFocusInternal(), doesn't set IME state because focused "
672 "element (or document) is already in the child process"));
675 // When this process gets focus, we should forget input context coming
676 // from remote process.
677 ResetActiveChildInputContext();
681 if (!focusActuallyChanging
) {
682 // actual focus isn't changing, but if IME enabled state is changing,
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 "
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
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
712 SetInputContext(*newWidget
, sActiveChildInputContext
, aAction
);
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()));
735 void IMEStateManager::OnInstalledMenuKeyboardListener(bool aInstalling
) {
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
,
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
);
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"));
774 if (!sActiveIMEContentObserver
) {
775 MOZ_LOG(sISMLog
, LogLevel::Debug
,
776 (" OnMouseButtonEventInEditor(), "
777 "there is no active IMEContentObserver"));
781 if (!sActiveIMEContentObserver
->IsManaging(aPresContext
, aElement
)) {
782 MOZ_LOG(sISMLog
, LogLevel::Debug
,
783 (" OnMouseButtonEventInEditor(), "
784 "the active IMEContentObserver isn't managing the editor"));
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"));
799 void IMEStateManager::OnClickInEditor(nsPresContext
& aPresContext
,
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"));
822 const OwningNonNull
<nsIWidget
> textInputHandlingWidget
=
823 *sTextInputHandlingWidget
;
827 nsCOMPtr
<nsIWidget
> currentTextInputHandlingWidget
=
828 sFocusedPresContext
->GetTextInputHandlingWidget();
829 MOZ_ASSERT(!currentTextInputHandlingWidget
||
830 currentTextInputHandlingWidget
== textInputHandlingWidget
);
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
,
874 bool IMEStateManager::IsFocusedElement(const nsPresContext
& aPresContext
,
875 const Element
* aFocusedElement
) {
876 if (!sFocusedPresContext
|| &aPresContext
!= sFocusedPresContext
) {
880 if (sFocusedElement
== aFocusedElement
) {
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
) {
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
) {
896 // If the aFocusedElement is in design mode, sFocusedElement may be nullptr.
897 if (aFocusedElement
->IsInDesignMode()) {
898 MOZ_ASSERT(&aPresContext
== sFocusedPresContext
&& !sFocusedElement
);
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
;
911 void IMEStateManager::OnFocusInEditor(nsPresContext
& aPresContext
,
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"));
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
)) {
935 sISMLog
, LogLevel::Debug
,
936 (" OnFocusInEditor(), "
937 "the editor is already being managed by sActiveIMEContentObserver"));
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
,
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"));
959 if (!sActiveIMEContentObserver
&& sTextInputHandlingWidget
&&
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()));
980 void IMEStateManager::OnEditorInitialized(EditorBase
& aEditorBase
) {
981 if (!sActiveIMEContentObserver
||
982 !sActiveIMEContentObserver
->WasInitializedWith(aEditorBase
)) {
986 MOZ_LOG(sISMLog
, LogLevel::Info
,
987 ("OnEditorInitialized(aEditorBase=0x%p)", &aEditorBase
));
989 sActiveIMEContentObserver
->UnsuppressNotifyingIME();
993 void IMEStateManager::OnEditorDestroying(EditorBase
& aEditorBase
) {
994 if (!sActiveIMEContentObserver
||
995 !sActiveIMEContentObserver
->WasInitializedWith(aEditorBase
)) {
999 MOZ_LOG(sISMLog
, LogLevel::Info
,
1000 ("OnEditorDestroying(aEditorBase=0x%p)", &aEditorBase
));
1002 // The IMEContentObserver shouldn't notify IME of anything until reframing
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())) {
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"));
1028 MOZ_ASSERT(&aElement
== sFocusedElement
.get());
1030 if (!UserActivation::IsHandlingUserInput() ||
1031 UserActivation::IsHandlingKeyboardInput()) {
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()) {
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
,
1057 void IMEStateManager::MaybeOnEditableStateDisabled(nsPresContext
& aPresContext
,
1058 dom::Element
* aElement
) {
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"));
1079 if (&aPresContext
!= sFocusedPresContext
|| aElement
!= sFocusedElement
) {
1080 MOZ_LOG(sISMLog
, LogLevel::Debug
,
1081 (" MaybeOnEditableStateDisabled(), "
1082 "does nothing because of another element already has focus"));
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"));
1094 const OwningNonNull
<nsIWidget
> textInputHandlingWidget
=
1095 *sTextInputHandlingWidget
;
1099 nsCOMPtr
<nsIWidget
> currentTextInputHandlingWidget
=
1100 aPresContext
.GetTextInputHandlingWidget();
1101 MOZ_ASSERT(!currentTextInputHandlingWidget
||
1102 currentTextInputHandlingWidget
== textInputHandlingWidget
);
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"));
1117 // Otherwise, disable IME on the widget and destroy active IMEContentObserver
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"));
1127 if (sActiveIMEContentObserver
) {
1128 DestroyIMEContentObserver();
1131 InputContextAction
action(InputContextAction::CAUSE_UNKNOWN
,
1132 InputContextAction::FOCUS_NOT_CHANGED
);
1133 SetIMEState(newIMEState
, &aPresContext
, aElement
, textInputHandlingWidget
,
1138 void IMEStateManager::UpdateIMEState(const IMEState
& aNewIMEState
,
1139 Element
* aElement
, EditorBase
& aEditorBase
,
1140 const UpdateIMEStateOptions
& aOptions
) {
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"));
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"));
1170 const RefPtr
<nsPresContext
> presContext
=
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"));
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"));
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"));
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"));
1213 const OwningNonNull
<nsIWidget
> textInputHandlingWidget
=
1214 *sTextInputHandlingWidget
;
1218 nsCOMPtr
<nsIWidget
> currentTextInputHandlingWidget
=
1219 sFocusedPresContext
->GetTextInputHandlingWidget();
1220 MOZ_ASSERT(!currentTextInputHandlingWidget
||
1221 currentTextInputHandlingWidget
== textInputHandlingWidget
);
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"));
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())) {
1271 sISMLog
, LogLevel::Error
,
1272 (" UpdateIMEState(), widget has gone during getting input context"));
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 "
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 "
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 "
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 "
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"));
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
);
1343 MOZ_LOG(sISMLog
, LogLevel::Error
,
1344 (" UpdateIMEState(), wanted to create IMEContentObserver, but "
1351 IMEState
IMEStateManager::GetNewIMEState(const nsPresContext
& aPresContext
,
1352 Element
* aElement
) {
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
);
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
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()));
1419 static bool MayBeIMEUnawareWebApp(nsINode
* aNode
) {
1420 bool haveKeyEventsListener
= false;
1423 EventListenerManager
* const mgr
= aNode
->GetExistingListenerManager();
1425 if (mgr
->MayHaveInputOrCompositionEventListener()) {
1428 haveKeyEventsListener
|= mgr
->MayHaveKeyEventListener();
1430 aNode
= aNode
->GetParentNode();
1433 return haveKeyEventsListener
;
1437 void IMEStateManager::ResetActiveChildInputContext() {
1438 sActiveChildInputContext
.mIMEState
.mEnabled
= IMEEnabled::Unknown
;
1442 bool IMEStateManager::HasActiveChildSetInputContext() {
1443 return sActiveChildInputContext
.mIMEState
.mEnabled
!= IMEEnabled::Unknown
;
1447 void IMEStateManager::SetInputContextForChildProcess(
1448 BrowserParent
* aBrowserParent
, const InputContext
& aInputContext
,
1449 const InputContextAction
& aAction
) {
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"));
1474 if (NS_WARN_IF(!CanHandleWith(sFocusedPresContext
))) {
1475 MOZ_LOG(sISMLog
, LogLevel::Error
,
1476 (" SetInputContextForChildProcess(), FAILED, "
1477 "due to no focused presContext"));
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"));
1489 const OwningNonNull
<nsIWidget
> textInputHandlingWidget
=
1490 *sTextInputHandlingWidget
;
1493 nsCOMPtr
<nsIWidget
> currentTextInputHandlingWidget
=
1494 sFocusedPresContext
->GetTextInputHandlingWidget();
1495 MOZ_ASSERT(!currentTextInputHandlingWidget
||
1496 currentTextInputHandlingWidget
== textInputHandlingWidget
);
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"));
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
)) {
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
) {
1534 nextContent
= nextContent
->FindFirstNonChromeOnlyAccessContent();
1535 nsCOMPtr
<nsIFormControl
> nextControl
= do_QueryInterface(nextContent
);
1536 if (!nextControl
|| !nextControl
->IsTextControl(false)) {
1540 // XXX We don't consider next element is date/time control yet.
1541 nsGenericHTMLElement
* nextElement
=
1542 nsGenericHTMLElement::FromNodeOrNull(nextContent
);
1546 bool focusable
= false;
1547 nextElement
->IsHTMLFocusable(false, &focusable
, nullptr);
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");
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()) {
1598 if (!aContent
.IsAnyOfHTMLElements(nsGkAtoms::input
, nsGkAtoms::textarea
)) {
1602 // XXX This is old compatibility, but we might be able to remove this.
1603 aContent
.AsElement()->GetAttr(kNameSpaceID_None
, nsGkAtoms::moz_action_hint
,
1606 if (!aActionHint
.IsEmpty()) {
1607 ToLowerCase(aActionHint
);
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
) {
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?
1626 if (formElement
->IsLastActiveElement(inputElement
)) {
1627 isLastElement
= true;
1630 if (formElement
->GetDefaultSubmitElement()) {
1632 // is this an html form...
1634 // ... and does it only have a single text input element ?
1635 if (!formElement
->ImplicitSubmissionIsDisabled() ||
1636 // ... or is this the last non-disabled element?
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");
1659 if (inputElement
->ControlType() == FormControlType::InputSearch
) {
1660 aActionHint
.AssignLiteral("search");
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
,
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();
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(
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
;
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
)
1739 .Finalize(exposableURL
))) {
1740 context
.mURI
= std::move(exposableURL
);
1745 context
.mOrigin
= aOrigin
;
1746 context
.mMayBeIMEUnaware
=
1747 context
.mIMEState
.IsEditable() &&
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
=
1757 nsContentUtils::IsInPrivateBrowsing(aPresContext
->Document());
1759 const RefPtr
<Element
> focusedElement
=
1760 aElement
? Element::FromNodeOrNull(
1761 aElement
->FindFirstNonChromeOnlyAccessContent())
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()) {
1781 UserActivation::IsHandlingKeyboardInput()
1782 ? InputContextAction::CAUSE_UNKNOWN_DURING_KEYBOARD_INPUT
1783 : InputContextAction::CAUSE_UNKNOWN_DURING_NON_KEYBOARD_INPUT
;
1786 SetInputContext(aWidget
, context
, aAction
);
1790 void IMEStateManager::SetInputContext(nsIWidget
& aWidget
,
1791 const InputContext
& aInputContext
,
1792 const InputContextAction
& aAction
) {
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
;
1807 void IMEStateManager::EnsureTextCompositionArray() {
1808 if (sTextCompositions
) {
1811 sTextCompositions
= new TextCompositionArray();
1815 void IMEStateManager::DispatchCompositionEvent(
1816 nsINode
* aEventTargetNode
, nsPresContext
* aPresContext
,
1817 BrowserParent
* aBrowserParent
, WidgetCompositionEvent
* aCompositionEvent
,
1818 nsEventStatus
* aStatus
, EventDispatchingCallback
* aCallBack
,
1819 bool aIsSynthesized
) {
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())) {
1846 MOZ_ASSERT(aCompositionEvent
->mMessage
!= eCompositionUpdate
,
1847 "compositionupdate event shouldn't be dispatched manually");
1849 EnsureTextCompositionArray();
1851 RefPtr
<TextComposition
> composition
=
1852 sTextCompositions
->GetCompositionFor(aCompositionEvent
);
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
)) {
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
);
1869 MOZ_ASSERT(aCompositionEvent
->mMessage
!= eCompositionStart
);
1871 #endif // #ifdef DEBUG
1873 // Dispatch the event on composing target.
1874 composition
->DispatchCompositionEvent(aCompositionEvent
, aStatus
, aCallBack
,
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
) {
1896 sISMLog
, LogLevel::Debug
,
1897 (" DispatchCompositionEvent(), "
1898 "removing TextComposition from the array since NS_COMPOSTION_END "
1900 sTextCompositions
->ElementAt(i
)->Destroy();
1901 sTextCompositions
->RemoveElementAt(i
);
1907 IMEContentObserver
* IMEStateManager::GetActiveContentObserver() {
1908 return sActiveIMEContentObserver
;
1912 nsIContent
* IMEStateManager::GetRootContent(nsPresContext
* aPresContext
) {
1913 Document
* doc
= aPresContext
->Document();
1914 if (NS_WARN_IF(!doc
)) {
1917 return doc
->GetRootElement();
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
));
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()) {
1943 RefPtr
<TextComposition
> composition
=
1945 ? sTextCompositions
->GetCompositionFor(aSelectionEvent
->mWidget
)
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
);
1952 // When there is no composition, the selection event should be handled
1953 // in the aPresContext or browserParent.
1954 TextComposition::HandleSelectionEvent(aPresContext
, browserParent
,
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.
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()) {
1986 // Ignore compositionstart for now because sTextCompositions may not have
1987 // been created yet.
1988 if (aCompositionEvent
->mMessage
== eCompositionStart
) {
1992 RefPtr
<TextComposition
> composition
=
1993 sTextCompositions
->GetCompositionFor(aCompositionEvent
->mWidget
);
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"));
2004 composition
->OnCompositionEventDiscarded(aCompositionEvent
);
2008 nsresult
IMEStateManager::NotifyIME(IMEMessage aMessage
, nsIWidget
* aWidget
,
2009 BrowserParent
* aBrowserParent
) {
2010 return IMEStateManager::NotifyIME(IMENotification(aMessage
), aWidget
,
2015 nsresult
IMEStateManager::NotifyIME(const IMENotification
& aNotification
,
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."));
2052 // If IME focus is already set, first blur the currently-focused
2054 if (sFocusedIMEWidget
) {
2055 // XXX Why don't we first request the previously-focused IME
2056 // widget to commit the composition?
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
));
2070 if (aBrowserParent
) {
2071 nsCOMPtr
<nsIWidget
> browserParentWidget
=
2072 aBrowserParent
->GetTextInputHandlingWidget();
2073 MOZ_ASSERT(browserParentWidget
== aWidget
);
2076 sFocusedIMEBrowserParent
= aBrowserParent
;
2077 sFocusedIMEWidget
= aWidget
;
2078 nsCOMPtr
<nsIWidget
> widget(aWidget
);
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 "
2089 "because it's not from current focused IME browser"));
2092 if (!sFocusedIMEWidget
) {
2094 sISMLog
, LogLevel::Error
,
2095 (" NotifyIME(), WARNING, received blur notification but there is "
2096 "no focused IME widget"));
2099 if (NS_WARN_IF(sFocusedIMEWidget
!= aWidget
)) {
2100 MOZ_LOG(sISMLog
, LogLevel::Warning
,
2101 (" NotifyIME(), WARNING, the received blur notification is "
2103 "because it's not for current focused IME widget"));
2106 nsCOMPtr
<nsIWidget
> focusedIMEWidget(sFocusedIMEWidget
);
2107 sFocusedIMEWidget
= nullptr;
2108 sFocusedIMEBrowserParent
= nullptr;
2109 return CanSendNotificationToWidget()
2110 ? focusedIMEWidget
->NotifyIME(
2111 IMENotification(NOTIFY_IME_OF_BLUR
))
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
) {
2121 sISMLog
, LogLevel::Warning
,
2122 (" NotifyIME(), WARNING, the received content change notification "
2123 "is ignored because it's not from current focused IME browser"));
2126 if (!sFocusedIMEWidget
) {
2128 sISMLog
, LogLevel::Warning
,
2129 (" NotifyIME(), WARNING, the received content change notification "
2130 "is ignored because there is no focused IME widget"));
2133 if (NS_WARN_IF(sFocusedIMEWidget
!= aWidget
)) {
2135 sISMLog
, LogLevel::Warning
,
2136 (" NotifyIME(), WARNING, the received content change notification "
2137 "is ignored because it's not for current focused IME widget"));
2140 if (!CanSendNotificationToWidget()) {
2143 nsCOMPtr
<nsIWidget
> widget(aWidget
);
2144 return widget
->NotifyIME(aNotification
);
2147 // Other notifications should be sent only when there is composition.
2148 // So, we need to handle the others below.
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"));
2159 RefPtr
<TextComposition
> composition
=
2160 sTextCompositions
->GetCompositionFor(aWidget
);
2162 MOZ_LOG(sISMLog
, LogLevel::Info
,
2163 (" NotifyIME(), the request to IME is ignored because "
2164 "there is no active composition"));
2168 if (aBrowserParent
!= composition
->GetBrowserParent()) {
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 "
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);
2183 MOZ_CRASH("Unsupported notification");
2186 "Failed to handle the notification for non-synthesized composition");
2187 return NS_ERROR_FAILURE
;
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 "
2207 return NS_ERROR_NOT_AVAILABLE
;
2209 return NotifyIME(aMessage
, widget
, aBrowserParent
);
2213 bool IMEStateManager::IsEditable(nsINode
* node
) {
2214 if (node
->IsEditable()) {
2217 // |node| might be readwrite (for example, a text control)
2218 if (node
->IsElement() &&
2219 node
->AsElement()->State().HasState(ElementState::READWRITE
)) {
2226 nsINode
* IMEStateManager::GetRootEditableNode(const nsPresContext
& aPresContext
,
2227 const Element
* 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()
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()) {
2249 candidateRootNode
= node
;
2251 return candidateRootNode
;
2254 return aPresContext
.Document() && aPresContext
.Document()->IsInDesignMode()
2255 ? aPresContext
.Document()
2260 bool IMEStateManager::IsIMEObserverNeeded(const IMEState
& aState
) {
2261 return aState
.IsEditable();
2265 void IMEStateManager::DestroyIMEContentObserver() {
2266 if (!sActiveIMEContentObserver
) {
2267 MOZ_LOG(sISMLog
, LogLevel::Verbose
,
2268 ("DestroyIMEContentObserver() does nothing"));
2272 MOZ_LOG(sISMLog
, LogLevel::Info
,
2273 ("DestroyIMEContentObserver(), destroying "
2274 "the active IMEContentObserver..."));
2275 RefPtr
<IMEContentObserver
> tsm
= sActiveIMEContentObserver
.get();
2276 sActiveIMEContentObserver
= nullptr;
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"));
2312 const OwningNonNull
<nsIWidget
> textInputHandlingWidget
=
2313 *sTextInputHandlingWidget
;
2317 nsCOMPtr
<nsIWidget
> currentTextInputHandlingWidget
=
2318 sFocusedPresContext
->GetTextInputHandlingWidget();
2319 MOZ_ASSERT(currentTextInputHandlingWidget
== textInputHandlingWidget
);
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
);
2340 nsresult
IMEStateManager::GetFocusSelectionAndRootElement(
2341 Selection
** aSelection
, Element
** aRootElement
) {
2342 if (!sActiveIMEContentObserver
) {
2343 return NS_ERROR_NOT_AVAILABLE
;
2345 return sActiveIMEContentObserver
->GetSelectionAndRoot(aSelection
,
2350 already_AddRefed
<TextComposition
> IMEStateManager::GetTextCompositionFor(
2351 nsIWidget
* aWidget
) {
2352 if (!sTextCompositions
) {
2355 RefPtr
<TextComposition
> textComposition
=
2356 sTextCompositions
->GetCompositionFor(aWidget
);
2357 return textComposition
.forget();
2361 already_AddRefed
<TextComposition
> IMEStateManager::GetTextCompositionFor(
2362 const WidgetCompositionEvent
* aCompositionEvent
) {
2363 if (!sTextCompositions
) {
2366 RefPtr
<TextComposition
> textComposition
=
2367 sTextCompositions
->GetCompositionFor(aCompositionEvent
);
2368 return textComposition
.forget();
2372 already_AddRefed
<TextComposition
> IMEStateManager::GetTextCompositionFor(
2373 nsPresContext
* aPresContext
) {
2374 if (!sTextCompositions
) {
2377 RefPtr
<TextComposition
> textComposition
=
2378 sTextCompositions
->GetCompositionFor(aPresContext
);
2379 return textComposition
.forget();
2382 } // namespace mozilla