Backed out changeset 2450366cf7ca (bug 1891629) for causing win msix mochitest failures
[gecko.git] / dom / events / IMEStateManager.cpp
blob966b2f62f7a3f2fe081d0578b59279357c49d727
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 "IMEContentObserver.h"
10 #include "mozilla/Attributes.h"
11 #include "mozilla/AutoRestore.h"
12 #include "mozilla/EditorBase.h"
13 #include "mozilla/EventListenerManager.h"
14 #include "mozilla/EventStateManager.h"
15 #include "mozilla/HTMLEditor.h"
16 #include "mozilla/Logging.h"
17 #include "mozilla/MouseEvents.h"
18 #include "mozilla/PresShell.h"
19 #include "mozilla/RefPtr.h"
20 #include "mozilla/StaticPrefs_dom.h"
21 #include "mozilla/StaticPrefs_intl.h"
22 #include "mozilla/TextComposition.h"
23 #include "mozilla/TextEvents.h"
24 #include "mozilla/ToString.h"
25 #include "mozilla/Unused.h"
26 #include "mozilla/dom/BrowserBridgeChild.h"
27 #include "mozilla/dom/BrowserParent.h"
28 #include "mozilla/dom/Document.h"
29 #include "mozilla/dom/Element.h"
30 #include "mozilla/dom/HTMLFormElement.h"
31 #include "mozilla/dom/HTMLInputElement.h"
32 #include "mozilla/dom/HTMLTextAreaElement.h"
33 #include "mozilla/dom/MouseEventBinding.h"
34 #include "mozilla/dom/UserActivation.h"
35 #include "mozilla/widget/IMEData.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"
48 #include "nsThreadUtils.h"
50 namespace mozilla {
52 using namespace dom;
53 using namespace widget;
55 /**
56 * When a method is called, log its arguments and/or related static variables
57 * with LogLevel::Info. However, if it puts too many logs like
58 * OnDestroyPresContext(), should long only when the method actually does
59 * something. In this case, the log should start with "<method name>".
61 * When a method quits due to unexpected situation, log the reason with
62 * LogLevel::Error. In this case, the log should start with
63 * "<method name>(), FAILED". The indent makes the log look easier.
65 * When a method does something only in some situations and it may be important
66 * for debug, log the information with LogLevel::Debug. In this case, the log
67 * should start with " <method name>(),".
69 LazyLogModule sISMLog("IMEStateManager");
71 static const char* GetBoolName(bool aBool) { return aBool ? "true" : "false"; }
73 StaticRefPtr<Element> IMEStateManager::sFocusedElement;
74 StaticRefPtr<nsPresContext> IMEStateManager::sFocusedPresContext;
75 nsIWidget* IMEStateManager::sTextInputHandlingWidget = nullptr;
76 nsIWidget* IMEStateManager::sFocusedIMEWidget = nullptr;
77 StaticRefPtr<BrowserParent> IMEStateManager::sFocusedIMEBrowserParent;
78 nsIWidget* IMEStateManager::sActiveInputContextWidget = nullptr;
79 StaticRefPtr<IMEContentObserver> IMEStateManager::sActiveIMEContentObserver;
80 TextCompositionArray* IMEStateManager::sTextCompositions = nullptr;
81 InputContext::Origin IMEStateManager::sOrigin = InputContext::ORIGIN_MAIN;
82 InputContext IMEStateManager::sActiveChildInputContext;
83 bool IMEStateManager::sInstalledMenuKeyboardListener = false;
84 bool IMEStateManager::sIsGettingNewIMEState = false;
85 bool IMEStateManager::sCleaningUpForStoppingIMEStateManagement = false;
86 bool IMEStateManager::sIsActive = false;
87 Maybe<IMEStateManager::PendingFocusedBrowserSwitchingData>
88 IMEStateManager::sPendingFocusedBrowserSwitchingData;
90 class PseudoFocusChangeRunnable : public Runnable {
91 public:
92 explicit PseudoFocusChangeRunnable(bool aInstallingMenuKeyboardListener)
93 : Runnable("PseudoFocusChangeRunnable"),
94 mFocusedPresContext(IMEStateManager::sFocusedPresContext),
95 mFocusedElement(IMEStateManager::sFocusedElement),
96 mInstallMenuKeyboardListener(aInstallingMenuKeyboardListener) {}
98 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD Run() override {
99 IMEStateManager::SetMenubarPseudoFocus(this, mInstallMenuKeyboardListener,
100 mFocusedPresContext);
101 return NS_OK;
104 private:
105 const RefPtr<nsPresContext> mFocusedPresContext;
106 const RefPtr<Element> mFocusedElement;
107 const bool mInstallMenuKeyboardListener;
110 StaticRefPtr<PseudoFocusChangeRunnable>
111 IMEStateManager::sPseudoFocusChangeRunnable;
113 // static
114 void IMEStateManager::Init() {
115 sOrigin = XRE_IsParentProcess() ? InputContext::ORIGIN_MAIN
116 : InputContext::ORIGIN_CONTENT;
117 ResetActiveChildInputContext();
120 // static
121 void IMEStateManager::Shutdown() {
122 MOZ_LOG(
123 sISMLog, LogLevel::Info,
124 ("Shutdown(), sTextCompositions=0x%p, sTextCompositions->Length()=%zu, "
125 "sPendingFocusedBrowserSwitchingData.isSome()=%s",
126 sTextCompositions, sTextCompositions ? sTextCompositions->Length() : 0,
127 GetBoolName(sPendingFocusedBrowserSwitchingData.isSome())));
128 MOZ_LOG(sISMLog, LogLevel::Debug,
129 (" Shutdown(), sFocusedElement=0x%p, sFocusedPresContext=0x%p, "
130 "sTextInputHandlingWidget=0x%p, sFocusedIMEWidget=0x%p, "
131 "sFocusedIMEBrowserParent=0x%p, sActiveInputContextWidget=0x%p, "
132 "sActiveIMEContentObserver=0x%p",
133 sFocusedElement.get(), sFocusedPresContext.get(),
134 sTextInputHandlingWidget, sFocusedIMEWidget,
135 sFocusedIMEBrowserParent.get(), sActiveInputContextWidget,
136 sActiveIMEContentObserver.get()));
138 sPendingFocusedBrowserSwitchingData.reset();
139 MOZ_ASSERT(!sTextCompositions || !sTextCompositions->Length());
140 delete sTextCompositions;
141 sTextCompositions = nullptr;
142 // All string instances in the global space need to be empty after XPCOM
143 // shutdown.
144 sActiveChildInputContext.ShutDown();
147 // static
148 void IMEStateManager::OnFocusMovedBetweenBrowsers(BrowserParent* aBlur,
149 BrowserParent* aFocus) {
150 MOZ_ASSERT(aBlur != aFocus);
151 MOZ_ASSERT(XRE_IsParentProcess());
153 if (sPendingFocusedBrowserSwitchingData.isSome()) {
154 MOZ_ASSERT(aBlur ==
155 sPendingFocusedBrowserSwitchingData.ref().mBrowserParentFocused);
156 // If focus is not changed between browsers actually, we need to do
157 // nothing here. Let's cancel handling what this method does.
158 if (sPendingFocusedBrowserSwitchingData.ref().mBrowserParentBlurred ==
159 aFocus) {
160 sPendingFocusedBrowserSwitchingData.reset();
161 MOZ_LOG(sISMLog, LogLevel::Info,
162 (" OnFocusMovedBetweenBrowsers(), canceled all pending focus "
163 "moves between browsers"));
164 return;
166 aBlur = sPendingFocusedBrowserSwitchingData.ref().mBrowserParentBlurred;
167 sPendingFocusedBrowserSwitchingData.ref().mBrowserParentFocused = aFocus;
168 MOZ_ASSERT(aBlur != aFocus);
171 // If application was inactive, but is now activated, and the last focused
172 // this is called by BrowserParent::UnsetTopLevelWebFocusAll() from
173 // nsFocusManager::WindowRaised(). If a content has focus in a remote
174 // process and it has composition, it may get focus back later and the
175 // composition shouldn't be commited now. Therefore, we should put off to
176 // handle this until getting another call of this method or a call of
177 //`OnFocusChangeInternal()`.
178 if (aBlur && !aFocus && !sIsActive && sTextInputHandlingWidget &&
179 sTextCompositions &&
180 sTextCompositions->GetCompositionFor(sTextInputHandlingWidget)) {
181 if (sPendingFocusedBrowserSwitchingData.isNothing()) {
182 sPendingFocusedBrowserSwitchingData.emplace(aBlur, aFocus);
184 MOZ_LOG(sISMLog, LogLevel::Debug,
185 (" OnFocusMovedBetweenBrowsers(), put off to handle it until "
186 "next OnFocusChangeInternal() call"));
187 return;
189 sPendingFocusedBrowserSwitchingData.reset();
191 const nsCOMPtr<nsIWidget> oldWidget = sTextInputHandlingWidget;
192 // In the chrome-process case, we'll get sTextInputHandlingWidget from a
193 // PresShell later.
194 sTextInputHandlingWidget =
195 aFocus ? nsCOMPtr<nsIWidget>(aFocus->GetTextInputHandlingWidget()).get()
196 : nullptr;
197 if (oldWidget && sTextCompositions) {
198 RefPtr<TextComposition> composition =
199 sTextCompositions->GetCompositionFor(oldWidget);
200 if (composition) {
201 MOZ_LOG(
202 sISMLog, LogLevel::Debug,
203 (" OnFocusMovedBetweenBrowsers(), requesting to commit "
204 "composition to "
205 "the (previous) focused widget (would request=%s)",
206 GetBoolName(
207 !oldWidget->IMENotificationRequestsRef().WantDuringDeactive())));
208 NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, oldWidget,
209 composition->GetBrowserParent());
213 // The manager check is to avoid telling the content process to stop
214 // IME state management after focus has already moved there between
215 // two same-process-hosted out-of-process iframes.
216 if (aBlur && (!aFocus || (aBlur->Manager() != aFocus->Manager()))) {
217 MOZ_LOG(sISMLog, LogLevel::Debug,
218 (" OnFocusMovedBetweenBrowsers(), notifying previous "
219 "focused child process of parent process or another child process "
220 "getting focus"));
221 aBlur->StopIMEStateManagement();
224 if (sActiveIMEContentObserver) {
225 DestroyIMEContentObserver();
228 if (sFocusedIMEWidget) {
229 // sFocusedIMEBrowserParent can be null, if IME focus hasn't been
230 // taken before BrowserParent blur.
231 // aBlur can be null when keyboard focus moves not actually
232 // between tabs but an open menu is involved.
233 MOZ_ASSERT(!sFocusedIMEBrowserParent || !aBlur ||
234 (sFocusedIMEBrowserParent == aBlur));
235 MOZ_LOG(sISMLog, LogLevel::Debug,
236 (" OnFocusMovedBetweenBrowsers(), notifying IME of blur"));
237 NotifyIME(NOTIFY_IME_OF_BLUR, sFocusedIMEWidget, sFocusedIMEBrowserParent);
239 MOZ_ASSERT(!sFocusedIMEBrowserParent);
240 MOZ_ASSERT(!sFocusedIMEWidget);
242 } else {
243 MOZ_ASSERT(!sFocusedIMEBrowserParent);
246 // We deliberately don't null out sFocusedElement or sFocusedPresContext here.
247 // When focus is in remote content, as far as layout in the chrome process is
248 // concerned, the corresponding content is the top-level XUL browser. Changes
249 // among out-of-process iframes don't change that, so dropping the pointer to
250 // the XUL browser upon such a change would break IME handling.
253 // static
254 void IMEStateManager::WidgetDestroyed(nsIWidget* aWidget) {
255 MOZ_LOG(sISMLog, LogLevel::Debug,
256 ("WidgetDestroyed(aWidget=0x%p), sFocusedIMEWidget=0x%p, "
257 "sActiveInputContextWidget=0x%p, sFocusedIMEBrowserParent=0x%p",
258 aWidget, sFocusedIMEWidget, sActiveInputContextWidget,
259 sFocusedIMEBrowserParent.get()));
260 if (sTextInputHandlingWidget == aWidget) {
261 sTextInputHandlingWidget = nullptr;
263 if (sFocusedIMEWidget == aWidget) {
264 if (sFocusedIMEBrowserParent) {
265 OnFocusMovedBetweenBrowsers(sFocusedIMEBrowserParent, nullptr);
266 MOZ_ASSERT(!sFocusedIMEBrowserParent);
268 sFocusedIMEWidget = nullptr;
270 if (sActiveInputContextWidget == aWidget) {
271 sActiveInputContextWidget = nullptr;
275 // static
276 void IMEStateManager::WidgetOnQuit(nsIWidget* aWidget) {
277 if (sFocusedIMEWidget == aWidget) {
278 MOZ_LOG(
279 sISMLog, LogLevel::Debug,
280 ("WidgetOnQuit(aWidget=0x%p (available %s)), sFocusedIMEWidget=0x%p",
281 aWidget, GetBoolName(aWidget && !aWidget->Destroyed()),
282 sFocusedIMEWidget));
283 // Notify IME of blur (which is done by IMEContentObserver::Destroy
284 // automatically) when the widget still has IME focus before forgetting the
285 // focused widget because the focused widget is required to clean up native
286 // IME handler with sending blur notification. Fortunately, the widget
287 // has not been destroyed yet here since some methods to sending blur
288 // notification won't work with destroyed widget.
289 IMEStateManager::DestroyIMEContentObserver();
290 // Finally, clean up the widget and related objects for avoiding to leak.
291 IMEStateManager::WidgetDestroyed(aWidget);
295 // static
296 void IMEStateManager::StopIMEStateManagement() {
297 MOZ_ASSERT(XRE_IsContentProcess());
298 MOZ_LOG(sISMLog, LogLevel::Info, ("StopIMEStateManagement()"));
300 // NOTE: Don't set input context from here since this has already lost
301 // the rights to change input context.
303 // The requestee of this method in the main process must destroy its
304 // active IMEContentObserver for making existing composition end and
305 // make it be possible to start new composition in new focused process.
306 // Therefore, we shouldn't notify the main process of any changes which
307 // occurred after here.
308 AutoRestore<bool> restoreStoppingIMEStateManagementState(
309 sCleaningUpForStoppingIMEStateManagement);
310 sCleaningUpForStoppingIMEStateManagement = true;
312 if (sTextCompositions && sFocusedPresContext) {
313 NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, sFocusedPresContext, nullptr);
315 sActiveInputContextWidget = nullptr;
316 sFocusedPresContext = nullptr;
317 sFocusedElement = nullptr;
318 sIsActive = false;
319 DestroyIMEContentObserver();
322 // static
323 void IMEStateManager::MaybeStartOffsetUpdatedInChild(nsIWidget* aWidget,
324 uint32_t aStartOffset) {
325 if (NS_WARN_IF(!sTextCompositions)) {
326 MOZ_LOG(sISMLog, LogLevel::Warning,
327 ("MaybeStartOffsetUpdatedInChild(aWidget=0x%p, aStartOffset=%u), "
328 "called when there is no composition",
329 aWidget, aStartOffset));
330 return;
333 TextComposition* const composition = GetTextCompositionFor(aWidget);
334 if (NS_WARN_IF(!composition)) {
335 MOZ_LOG(sISMLog, LogLevel::Warning,
336 ("MaybeStartOffsetUpdatedInChild(aWidget=0x%p, aStartOffset=%u), "
337 "called when there is no composition",
338 aWidget, aStartOffset));
339 return;
342 if (composition->NativeOffsetOfStartComposition() == aStartOffset) {
343 return;
346 MOZ_LOG(
347 sISMLog, LogLevel::Info,
348 ("MaybeStartOffsetUpdatedInChild(aWidget=0x%p, aStartOffset=%u), "
349 "old offset=%u",
350 aWidget, aStartOffset, composition->NativeOffsetOfStartComposition()));
351 composition->OnStartOffsetUpdatedInChild(aStartOffset);
354 // static
355 nsresult IMEStateManager::OnDestroyPresContext(nsPresContext& aPresContext) {
356 // First, if there is a composition in the aPresContext, clean up it.
357 if (sTextCompositions) {
358 TextCompositionArray::index_type i =
359 sTextCompositions->IndexOf(&aPresContext);
360 if (i != TextCompositionArray::NoIndex) {
361 MOZ_LOG(sISMLog, LogLevel::Debug,
362 (" OnDestroyPresContext(), "
363 "removing TextComposition instance from the array (index=%zu)",
364 i));
365 // there should be only one composition per presContext object.
366 sTextCompositions->ElementAt(i)->Destroy();
367 sTextCompositions->RemoveElementAt(i);
368 if (sTextCompositions->IndexOf(&aPresContext) !=
369 TextCompositionArray::NoIndex) {
370 MOZ_LOG(sISMLog, LogLevel::Error,
371 (" OnDestroyPresContext(), FAILED to remove "
372 "TextComposition instance from the array"));
373 MOZ_CRASH("Failed to remove TextComposition instance from the array");
378 if (&aPresContext != sFocusedPresContext) {
379 return NS_OK;
382 MOZ_LOG(
383 sISMLog, LogLevel::Info,
384 ("OnDestroyPresContext(aPresContext=0x%p), "
385 "sFocusedPresContext=0x%p, sFocusedElement=0x%p, sTextCompositions=0x%p",
386 &aPresContext, sFocusedPresContext.get(), sFocusedElement.get(),
387 sTextCompositions));
389 DestroyIMEContentObserver();
391 if (sTextInputHandlingWidget) {
392 IMEState newState = GetNewIMEState(*sFocusedPresContext, nullptr);
393 InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
394 InputContextAction::LOST_FOCUS);
395 InputContext::Origin origin =
396 BrowserParent::GetFocused() ? InputContext::ORIGIN_CONTENT : sOrigin;
397 OwningNonNull<nsIWidget> textInputHandlingWidget =
398 *sTextInputHandlingWidget;
399 SetIMEState(newState, nullptr, nullptr, textInputHandlingWidget, action,
400 origin);
402 sTextInputHandlingWidget = nullptr;
403 sFocusedElement = nullptr;
404 sFocusedPresContext = nullptr;
405 return NS_OK;
408 // static
409 nsresult IMEStateManager::OnRemoveContent(nsPresContext& aPresContext,
410 Element& aElement) {
411 // First, if there is a composition in the aElement, clean up it.
412 if (sTextCompositions) {
413 const RefPtr<TextComposition> compositionInContent =
414 sTextCompositions->GetCompositionInContent(&aPresContext, &aElement);
416 if (compositionInContent) {
417 MOZ_LOG(sISMLog, LogLevel::Debug,
418 (" OnRemoveContent(), "
419 "composition is in the content"));
421 // Try resetting the native IME state. Be aware, typically, this method
422 // is called during the content being removed. Then, the native
423 // composition events which are caused by following APIs are ignored due
424 // to unsafe to run script (in PresShell::HandleEvent()).
425 nsresult rv =
426 compositionInContent->NotifyIME(REQUEST_TO_CANCEL_COMPOSITION);
427 if (NS_FAILED(rv)) {
428 compositionInContent->NotifyIME(REQUEST_TO_COMMIT_COMPOSITION);
433 if (!sFocusedPresContext || !sFocusedElement ||
434 !sFocusedElement->IsInclusiveDescendantOf(&aElement)) {
435 return NS_OK;
437 MOZ_ASSERT(sFocusedPresContext == &aPresContext);
439 MOZ_LOG(
440 sISMLog, LogLevel::Info,
441 ("OnRemoveContent(aPresContext=0x%p, aElement=0x%p), "
442 "sFocusedPresContext=0x%p, sFocusedElement=0x%p, sTextCompositions=0x%p",
443 &aPresContext, &aElement, sFocusedPresContext.get(),
444 sFocusedElement.get(), sTextCompositions));
446 DestroyIMEContentObserver();
448 // FYI: Don't clear sTextInputHandlingWidget and sFocusedPresContext because
449 // the window/document keeps having focus.
450 sFocusedElement = nullptr;
452 // Current IME transaction should commit
453 if (!sTextInputHandlingWidget) {
454 return NS_OK;
457 IMEState newState = GetNewIMEState(*sFocusedPresContext, nullptr);
458 InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
459 InputContextAction::LOST_FOCUS);
460 InputContext::Origin origin =
461 BrowserParent::GetFocused() ? InputContext::ORIGIN_CONTENT : sOrigin;
462 OwningNonNull<nsIWidget> textInputHandlingWidget = *sTextInputHandlingWidget;
463 SetIMEState(newState, &aPresContext, nullptr, textInputHandlingWidget, action,
464 origin);
465 if (sFocusedPresContext != &aPresContext || sFocusedElement) {
466 return NS_OK; // Some body must have set focus
469 if (IsIMEObserverNeeded(newState)) {
470 if (RefPtr<HTMLEditor> htmlEditor =
471 nsContentUtils::GetHTMLEditor(&aPresContext)) {
472 CreateIMEContentObserver(*htmlEditor, nullptr);
476 return NS_OK;
479 // static
480 bool IMEStateManager::CanHandleWith(const nsPresContext* aPresContext) {
481 return aPresContext && aPresContext->GetPresShell() &&
482 !aPresContext->PresShell()->IsDestroying();
485 // static
486 nsresult IMEStateManager::OnChangeFocus(nsPresContext* aPresContext,
487 Element* aElement,
488 InputContextAction::Cause aCause) {
489 MOZ_LOG(sISMLog, LogLevel::Info,
490 ("OnChangeFocus(aPresContext=0x%p, aElement=0x%p, aCause=%s)",
491 aPresContext, aElement, ToString(aCause).c_str()));
493 InputContextAction action(aCause);
494 return OnChangeFocusInternal(aPresContext, aElement, action);
497 // static
498 nsresult IMEStateManager::OnChangeFocusInternal(nsPresContext* aPresContext,
499 Element* aElement,
500 InputContextAction aAction) {
501 NS_ASSERTION(!aElement || aElement->GetPresContext(
502 Element::PresContextFor::eForComposedDoc) ==
503 aPresContext,
504 "aPresContext does not match with one of aElement");
506 bool remoteHasFocus = EventStateManager::IsRemoteTarget(aElement);
507 // If we've handled focused content, we were inactive but now active,
508 // a remote process has focus, and setting focus to same content in the main
509 // process, it means that we're restoring focus without changing DOM focus
510 // both in the main process and the remote process.
511 const bool restoringContextForRemoteContent =
512 XRE_IsParentProcess() && remoteHasFocus && !sIsActive && aPresContext &&
513 sFocusedPresContext && sFocusedElement &&
514 sFocusedPresContext.get() == aPresContext &&
515 sFocusedElement.get() == aElement &&
516 aAction.mFocusChange != InputContextAction::MENU_GOT_PSEUDO_FOCUS;
518 MOZ_LOG(
519 sISMLog, LogLevel::Info,
520 ("OnChangeFocusInternal(aPresContext=0x%p (available: %s), "
521 "aElement=0x%p (remote: %s), aAction={ mCause=%s, "
522 "mFocusChange=%s }), sFocusedPresContext=0x%p (available: %s), "
523 "sFocusedElement=0x%p, sTextInputHandlingWidget=0x%p (available: %s), "
524 "BrowserParent::GetFocused()=0x%p, sActiveIMEContentObserver=0x%p, "
525 "sInstalledMenuKeyboardListener=%s, sIsActive=%s, "
526 "restoringContextForRemoteContent=%s",
527 aPresContext, GetBoolName(CanHandleWith(aPresContext)), aElement,
528 GetBoolName(remoteHasFocus), ToString(aAction.mCause).c_str(),
529 ToString(aAction.mFocusChange).c_str(), sFocusedPresContext.get(),
530 GetBoolName(CanHandleWith(sFocusedPresContext)), sFocusedElement.get(),
531 sTextInputHandlingWidget,
532 GetBoolName(sTextInputHandlingWidget &&
533 !sTextInputHandlingWidget->Destroyed()),
534 BrowserParent::GetFocused(), sActiveIMEContentObserver.get(),
535 GetBoolName(sInstalledMenuKeyboardListener), GetBoolName(sIsActive),
536 GetBoolName(restoringContextForRemoteContent)));
538 sIsActive = !!aPresContext;
539 if (sPendingFocusedBrowserSwitchingData.isSome()) {
540 MOZ_ASSERT(XRE_IsParentProcess());
541 RefPtr<Element> focusedElement = sFocusedElement;
542 RefPtr<nsPresContext> focusedPresContext = sFocusedPresContext;
543 RefPtr<BrowserParent> browserParentBlurred =
544 sPendingFocusedBrowserSwitchingData.ref().mBrowserParentBlurred;
545 RefPtr<BrowserParent> browserParentFocused =
546 sPendingFocusedBrowserSwitchingData.ref().mBrowserParentFocused;
547 OnFocusMovedBetweenBrowsers(browserParentBlurred, browserParentFocused);
548 // If another call of this method happens during the
549 // OnFocusMovedBetweenBrowsers call, we shouldn't take back focus to
550 // the old one.
551 if (focusedElement != sFocusedElement.get() ||
552 focusedPresContext != sFocusedPresContext.get()) {
553 MOZ_LOG(sISMLog, LogLevel::Debug,
554 (" OnChangeFocusInternal(aPresContext=0x%p, aElement=0x%p) "
555 "stoped handling it because the focused content was changed to "
556 "sFocusedPresContext=0x%p, sFocusedElement=0x%p by another call",
557 aPresContext, aElement, sFocusedPresContext.get(),
558 sFocusedElement.get()));
559 return NS_OK;
563 // If new aPresShell has been destroyed, this should handle the focus change
564 // as nobody is getting focus.
565 MOZ_ASSERT_IF(!aPresContext, !aElement);
566 if (NS_WARN_IF(aPresContext && !CanHandleWith(aPresContext))) {
567 MOZ_LOG(sISMLog, LogLevel::Warning,
568 (" OnChangeFocusInternal(), called with destroyed PresShell, "
569 "handling this call as nobody getting focus"));
570 aPresContext = nullptr;
571 aElement = nullptr;
572 } else if (!aPresContext) {
573 aElement = nullptr;
576 const nsCOMPtr<nsIWidget> oldWidget = sTextInputHandlingWidget;
577 const nsCOMPtr<nsIWidget> newWidget =
578 aPresContext ? aPresContext->GetTextInputHandlingWidget() : nullptr;
579 const bool focusActuallyChanging =
580 (sFocusedElement != aElement || sFocusedPresContext != aPresContext ||
581 oldWidget != newWidget ||
582 (remoteHasFocus && !restoringContextForRemoteContent &&
583 (aAction.mFocusChange != InputContextAction::MENU_GOT_PSEUDO_FOCUS)));
585 // If old widget has composition, we may need to commit composition since
586 // a native IME context is shared on all editors on some widgets or all
587 // widgets (it depends on platforms).
588 if (oldWidget && focusActuallyChanging && sTextCompositions) {
589 RefPtr<TextComposition> composition =
590 sTextCompositions->GetCompositionFor(oldWidget);
591 if (composition) {
592 // However, don't commit the composition if we're being inactivated
593 // but the composition should be kept even during deactive.
594 // Note that oldWidget and sFocusedIMEWidget may be different here (in
595 // such case, sFocusedIMEWidget is perhaps nullptr). For example, IME
596 // may receive only blur notification but still has composition.
597 // We need to clean up only the oldWidget's composition state here.
598 if (aPresContext ||
599 !oldWidget->IMENotificationRequestsRef().WantDuringDeactive()) {
600 MOZ_LOG(
601 sISMLog, LogLevel::Info,
602 (" OnChangeFocusInternal(), requesting to commit composition to "
603 "the (previous) focused widget"));
604 NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, oldWidget,
605 composition->GetBrowserParent());
610 if (sActiveIMEContentObserver) {
611 MOZ_ASSERT(!remoteHasFocus || XRE_IsContentProcess(),
612 "IMEContentObserver should have been destroyed by "
613 "OnFocusMovedBetweenBrowsers.");
614 if (!aPresContext) {
615 if (!sActiveIMEContentObserver->KeepAliveDuringDeactive()) {
616 DestroyIMEContentObserver();
619 // Otherwise, i.e., new focused content is in this process, let's check
620 // whether the editable content for aElement has already been being observed
621 // by the active IMEContentObserver. If not, let's stop observing the
622 // editable content which is being blurred.
623 else if (!sActiveIMEContentObserver->IsObserving(*aPresContext, aElement)) {
624 DestroyIMEContentObserver();
628 if (!aPresContext) {
629 MOZ_LOG(sISMLog, LogLevel::Debug,
630 (" OnChangeFocusInternal(), no nsPresContext is being activated"));
631 return NS_OK;
634 if (NS_WARN_IF(!newWidget)) {
635 MOZ_LOG(sISMLog, LogLevel::Error,
636 (" OnChangeFocusInternal(), FAILED due to no widget to manage its "
637 "IME state"));
638 return NS_OK;
641 // Update the cached widget since root view of the presContext may be
642 // changed to different view.
643 sTextInputHandlingWidget = newWidget;
645 // If a child process has focus, we should disable IME state until the child
646 // process actually gets focus because if user types keys before that they
647 // are handled by IME.
648 IMEState newState = remoteHasFocus ? IMEState(IMEEnabled::Disabled)
649 : GetNewIMEState(*aPresContext, aElement);
650 bool setIMEState = true;
652 if (remoteHasFocus && XRE_IsParentProcess()) {
653 if (aAction.mFocusChange == InputContextAction::MENU_GOT_PSEUDO_FOCUS) {
654 // If menu keyboard listener is installed, we need to disable IME now.
655 setIMEState = true;
656 } else if (aAction.mFocusChange ==
657 InputContextAction::MENU_LOST_PSEUDO_FOCUS) {
658 // If menu keyboard listener is uninstalled, we need to restore
659 // input context which was set by the remote process. However, if
660 // the remote process hasn't been set input context yet, we need to
661 // wait next SetInputContextForChildProcess() call.
662 if (HasActiveChildSetInputContext()) {
663 setIMEState = true;
664 newState = sActiveChildInputContext.mIMEState;
665 } else {
666 setIMEState = false;
668 } else if (focusActuallyChanging) {
669 InputContext context = newWidget->GetInputContext();
670 if (context.mIMEState.mEnabled == IMEEnabled::Disabled &&
671 context.mOrigin == InputContext::ORIGIN_CONTENT) {
672 setIMEState = false;
673 MOZ_LOG(sISMLog, LogLevel::Debug,
674 (" OnChangeFocusInternal(), doesn't set IME state because "
675 "focused element (or document) is in a child process and the "
676 "IME state is already disabled by a remote process"));
677 } else {
678 // When new remote process gets focus, we should forget input context
679 // coming from old focused remote process.
680 ResetActiveChildInputContext();
681 MOZ_LOG(sISMLog, LogLevel::Debug,
682 (" OnChangeFocusInternal(), will disable IME until new "
683 "focused element (or document) in the child process will get "
684 "focus actually"));
686 } else if (newWidget->GetInputContext().mOrigin !=
687 InputContext::ORIGIN_MAIN) {
688 // When focus is NOT changed actually, we shouldn't set IME state if
689 // current input context was set by a remote process since that means
690 // that the window is being activated and the child process may have
691 // composition. Then, we shouldn't commit the composition with making
692 // IME state disabled.
693 setIMEState = false;
694 MOZ_LOG(
695 sISMLog, LogLevel::Debug,
696 (" OnChangeFocusInternal(), doesn't set IME state because focused "
697 "element (or document) is already in the child process"));
699 } else {
700 // When this process gets focus, we should forget input context coming
701 // from remote process.
702 ResetActiveChildInputContext();
705 if (setIMEState) {
706 if (!focusActuallyChanging) {
707 // actual focus isn't changing, but if IME enabled state is changing,
708 // we should do it.
709 InputContext context = newWidget->GetInputContext();
710 if (context.mIMEState.mEnabled == newState.mEnabled) {
711 MOZ_LOG(sISMLog, LogLevel::Debug,
712 (" OnChangeFocusInternal(), neither focus nor IME state is "
713 "changing"));
714 return NS_OK;
716 aAction.mFocusChange = InputContextAction::FOCUS_NOT_CHANGED;
718 // Even if focus isn't changing actually, we should commit current
719 // composition here since the IME state is changing.
720 if (sFocusedPresContext && oldWidget && !focusActuallyChanging) {
721 NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, oldWidget,
722 sFocusedIMEBrowserParent);
724 } else if (aAction.mFocusChange == InputContextAction::FOCUS_NOT_CHANGED) {
725 // If aElement isn't null or aElement is null but editable, somebody gets
726 // focus.
727 bool gotFocus = aElement || (newState.mEnabled == IMEEnabled::Enabled);
728 aAction.mFocusChange = gotFocus ? InputContextAction::GOT_FOCUS
729 : InputContextAction::LOST_FOCUS;
732 if (remoteHasFocus && HasActiveChildSetInputContext() &&
733 aAction.mFocusChange == InputContextAction::MENU_LOST_PSEUDO_FOCUS) {
734 // Restore the input context in the active remote process when
735 // menu keyboard listener is uninstalled and active remote tab has
736 // focus.
737 SetInputContext(*newWidget, sActiveChildInputContext, aAction);
738 } else {
739 // Update IME state for new focus widget
740 SetIMEState(newState, aPresContext, aElement, *newWidget, aAction,
741 remoteHasFocus ? InputContext::ORIGIN_CONTENT : sOrigin);
745 sFocusedPresContext = aPresContext;
746 sFocusedElement = aElement;
748 // Don't call CreateIMEContentObserver() here because it will be called from
749 // the focus event handler of focused editor.
751 MOZ_LOG(sISMLog, LogLevel::Debug,
752 (" OnChangeFocusInternal(), modified IME state for "
753 "sFocusedPresContext=0x%p, sFocusedElement=0x%p",
754 sFocusedPresContext.get(), sFocusedElement.get()));
756 return NS_OK;
759 // static
760 void IMEStateManager::OnInstalledMenuKeyboardListener(bool aInstalling) {
761 MOZ_LOG(
762 sISMLog, LogLevel::Info,
763 ("OnInstalledMenuKeyboardListener(aInstalling=%s), "
764 "nsContentUtils::IsSafeToRunScript()=%s, "
765 "sInstalledMenuKeyboardListener=%s, BrowserParent::GetFocused()=0x%p, "
766 "sActiveChildInputContext=%s, sFocusedPresContext=0x%p, "
767 "sFocusedElement=0x%p, sPseudoFocusChangeRunnable=0x%p",
768 GetBoolName(aInstalling),
769 GetBoolName(nsContentUtils::IsSafeToRunScript()),
770 GetBoolName(sInstalledMenuKeyboardListener), BrowserParent::GetFocused(),
771 ToString(sActiveChildInputContext).c_str(), sFocusedPresContext.get(),
772 sFocusedElement.get(), sPseudoFocusChangeRunnable.get()));
774 // Update the state whether the menubar has pseudo focus or not immediately.
775 // This will be referred by the runner which is created below.
776 sInstalledMenuKeyboardListener = aInstalling;
777 // However, this may be called when it's not safe to run script. Therefore,
778 // we need to create a runnable to notify IME of the pseudo focus change.
779 if (sPseudoFocusChangeRunnable) {
780 return;
782 sPseudoFocusChangeRunnable = new PseudoFocusChangeRunnable(aInstalling);
783 nsContentUtils::AddScriptRunner(sPseudoFocusChangeRunnable);
786 // static
787 void IMEStateManager::SetMenubarPseudoFocus(
788 PseudoFocusChangeRunnable* aCaller, bool aSetPseudoFocus,
789 nsPresContext* aFocusedPresContextAtRequested) {
790 MOZ_LOG(
791 sISMLog, LogLevel::Info,
792 ("SetMenubarPseudoFocus(aCaller=0x%p, aSetPseudoFocus=%s, "
793 "aFocusedPresContextAtRequested=0x%p), "
794 "sInstalledMenuKeyboardListener=%s, sFocusedPresContext=0x%p, "
795 "sFocusedElement=0x%p, sPseudoFocusChangeRunnable=0x%p",
796 aCaller, GetBoolName(aSetPseudoFocus), aFocusedPresContextAtRequested,
797 GetBoolName(sInstalledMenuKeyboardListener), sFocusedPresContext.get(),
798 sFocusedElement.get(), sPseudoFocusChangeRunnable.get()));
800 MOZ_ASSERT(sPseudoFocusChangeRunnable.get() == aCaller);
802 // Clear the runnable first for nested call of
803 // OnInstalledMenuKeyboardListener().
804 RefPtr<PseudoFocusChangeRunnable> runningOne =
805 sPseudoFocusChangeRunnable.forget();
806 MOZ_ASSERT(!sPseudoFocusChangeRunnable);
808 // If the requested state is still same, let's make the menubar get
809 // pseudo focus with the latest focused PresContext and Element.
810 // Note that this is no problem even after the focused element is changed
811 // after aCaller is created because only sInstalledMenuKeyboardListener
812 // manages the state and current focused PresContext and element should be
813 // used only for restoring the focus from the menubar. So, restoring
814 // focus with the lasted one does make sense.
815 if (sInstalledMenuKeyboardListener == aSetPseudoFocus) {
816 InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
817 aSetPseudoFocus
818 ? InputContextAction::MENU_GOT_PSEUDO_FOCUS
819 : InputContextAction::MENU_LOST_PSEUDO_FOCUS);
820 RefPtr<nsPresContext> focusedPresContext = sFocusedPresContext;
821 RefPtr<Element> focusedElement = sFocusedElement;
822 OnChangeFocusInternal(focusedPresContext, focusedElement, action);
823 return;
826 // If the requested state is different from current state, we don't need to
827 // make the menubar get and lose pseudo focus because of redundant. However,
828 // if there is a composition on the original focused element, we need to
829 // commit it because pseudo focus move should've caused it.
830 if (!aFocusedPresContextAtRequested) {
831 return;
833 RefPtr<TextComposition> composition =
834 GetTextCompositionFor(aFocusedPresContextAtRequested);
835 if (!composition) {
836 return;
838 if (nsCOMPtr<nsIWidget> widget =
839 aFocusedPresContextAtRequested->GetTextInputHandlingWidget()) {
840 composition->RequestToCommit(widget, false);
844 // static
845 bool IMEStateManager::OnMouseButtonEventInEditor(
846 nsPresContext& aPresContext, Element* aElement,
847 WidgetMouseEvent& aMouseEvent) {
848 MOZ_LOG(sISMLog, LogLevel::Info,
849 ("OnMouseButtonEventInEditor(aPresContext=0x%p (available: %s), "
850 "aElement=0x%p, aMouseEvent=0x%p), sFocusedPresContext=0x%p, "
851 "sFocusedElement=0x%p",
852 &aPresContext, GetBoolName(CanHandleWith(&aPresContext)), aElement,
853 &aMouseEvent, sFocusedPresContext.get(), sFocusedElement.get()));
855 if (sFocusedPresContext != &aPresContext || sFocusedElement != aElement) {
856 MOZ_LOG(sISMLog, LogLevel::Debug,
857 (" OnMouseButtonEventInEditor(), "
858 "the mouse event isn't fired on the editor managed by ISM"));
859 return false;
862 if (!sActiveIMEContentObserver) {
863 MOZ_LOG(sISMLog, LogLevel::Debug,
864 (" OnMouseButtonEventInEditor(), "
865 "there is no active IMEContentObserver"));
866 return false;
869 if (!sActiveIMEContentObserver->IsObserving(aPresContext, aElement)) {
870 MOZ_LOG(sISMLog, LogLevel::Debug,
871 (" OnMouseButtonEventInEditor(), "
872 "the active IMEContentObserver isn't managing the editor"));
873 return false;
876 OwningNonNull<IMEContentObserver> observer = *sActiveIMEContentObserver;
877 bool consumed = observer->OnMouseButtonEvent(aPresContext, aMouseEvent);
878 MOZ_LOG(sISMLog, LogLevel::Info,
879 (" OnMouseButtonEventInEditor(), "
880 "mouse event (mMessage=%s, mButton=%d) is %s",
881 ToChar(aMouseEvent.mMessage), aMouseEvent.mButton,
882 consumed ? "consumed" : "not consumed"));
883 return consumed;
886 // static
887 void IMEStateManager::OnClickInEditor(nsPresContext& aPresContext,
888 Element* aElement,
889 const WidgetMouseEvent& aMouseEvent) {
890 MOZ_LOG(sISMLog, LogLevel::Info,
891 ("OnClickInEditor(aPresContext=0x%p (available: %s), aElement=0x%p, "
892 "aMouseEvent=0x%p), sFocusedPresContext=0x%p, sFocusedElement=0x%p, "
893 "sTextInputHandlingWidget=0x%p (available: %s)",
894 &aPresContext, GetBoolName(CanHandleWith(&aPresContext)), aElement,
895 &aMouseEvent, sFocusedPresContext.get(), sFocusedElement.get(),
896 sTextInputHandlingWidget,
897 GetBoolName(sTextInputHandlingWidget &&
898 !sTextInputHandlingWidget->Destroyed())));
900 if (sFocusedPresContext != &aPresContext || sFocusedElement != aElement ||
901 NS_WARN_IF(!sFocusedPresContext) ||
902 NS_WARN_IF(!sTextInputHandlingWidget) ||
903 NS_WARN_IF(sTextInputHandlingWidget->Destroyed())) {
904 MOZ_LOG(sISMLog, LogLevel::Debug,
905 (" OnClickInEditor(), "
906 "the mouse event isn't fired on the editor managed by ISM"));
907 return;
910 const OwningNonNull<nsIWidget> textInputHandlingWidget =
911 *sTextInputHandlingWidget;
912 MOZ_ASSERT_IF(sFocusedPresContext->GetTextInputHandlingWidget(),
913 sFocusedPresContext->GetTextInputHandlingWidget() ==
914 textInputHandlingWidget.get());
916 if (!aMouseEvent.IsTrusted()) {
917 MOZ_LOG(sISMLog, LogLevel::Debug,
918 (" OnClickInEditor(), "
919 "the mouse event isn't a trusted event"));
920 return; // ignore untrusted event.
923 if (aMouseEvent.mButton) {
924 MOZ_LOG(sISMLog, LogLevel::Debug,
925 (" OnClickInEditor(), "
926 "the mouse event isn't a left mouse button event"));
927 return; // not a left click event.
930 if (aMouseEvent.mClickCount != 1) {
931 MOZ_LOG(sISMLog, LogLevel::Debug,
932 (" OnClickInEditor(), "
933 "the mouse event isn't a single click event"));
934 return; // should notify only first click event.
937 MOZ_ASSERT_IF(aElement, !EventStateManager::IsRemoteTarget(aElement));
938 InputContextAction::Cause cause =
939 aMouseEvent.mInputSource == MouseEvent_Binding::MOZ_SOURCE_TOUCH
940 ? InputContextAction::CAUSE_TOUCH
941 : InputContextAction::CAUSE_MOUSE;
943 InputContextAction action(cause, InputContextAction::FOCUS_NOT_CHANGED);
944 IMEState newState = GetNewIMEState(aPresContext, aElement);
945 // If the state is not editable, there should be no active IMEContentObserver.
946 // However, if this click sets focus to the editor, IMEContentObserver may
947 // have not been created yet. Instead, if there is active IMEContentObserver,
948 // it should be editable.
949 MOZ_ASSERT_IF(!newState.IsEditable(), !sActiveIMEContentObserver);
950 MOZ_ASSERT_IF(sActiveIMEContentObserver, newState.IsEditable());
951 SetIMEState(newState, &aPresContext, aElement, textInputHandlingWidget,
952 action, sOrigin);
955 // static
956 Element* IMEStateManager::GetFocusedElement() { return sFocusedElement; }
958 // static
959 bool IMEStateManager::IsFocusedElement(const nsPresContext& aPresContext,
960 const Element* aFocusedElement) {
961 if (!sFocusedPresContext || &aPresContext != sFocusedPresContext) {
962 return false;
965 if (sFocusedElement == aFocusedElement) {
966 return true;
969 // If sFocusedElement is not nullptr, but aFocusedElement is nullptr, it does
970 // not have focus from point of view of IMEStateManager.
971 if (sFocusedElement) {
972 return false;
975 // If the caller does not think that nobody has focus, but we know there is
976 // a focused content, the caller must be called with wrong content.
977 if (!aFocusedElement) {
978 return false;
981 // If the aFocusedElement is in design mode, sFocusedElement may be nullptr.
982 if (aFocusedElement->IsInDesignMode()) {
983 MOZ_ASSERT(&aPresContext == sFocusedPresContext && !sFocusedElement);
984 return true;
987 // Otherwise, only when aFocusedElement is the root element, it can have
988 // focus, but IMEStateManager::OnChangeFocus is called with nullptr for
989 // aFocusedElement if it was not editable.
990 // XXX In this case, should the caller update sFocusedElement?
991 return aFocusedElement->IsEditable() && sFocusedPresContext->Document() &&
992 sFocusedPresContext->Document()->GetRootElement() == aFocusedElement;
995 // static
996 void IMEStateManager::OnFocusInEditor(nsPresContext& aPresContext,
997 Element* aElement,
998 EditorBase& aEditorBase) {
999 MOZ_LOG(sISMLog, LogLevel::Info,
1000 ("OnFocusInEditor(aPresContext=0x%p (available: %s), aElement=0x%p, "
1001 "aEditorBase=0x%p), sFocusedPresContext=0x%p, sFocusedElement=0x%p, "
1002 "sActiveIMEContentObserver=0x%p",
1003 &aPresContext, GetBoolName(CanHandleWith(&aPresContext)), aElement,
1004 &aEditorBase, sFocusedPresContext.get(), sFocusedElement.get(),
1005 sActiveIMEContentObserver.get()));
1007 if (!IsFocusedElement(aPresContext, aElement)) {
1008 MOZ_LOG(sISMLog, LogLevel::Debug,
1009 (" OnFocusInEditor(), "
1010 "an editor not managed by ISM gets focus"));
1011 return;
1013 MOZ_ASSERT(sTextInputHandlingWidget);
1015 // If the IMEContentObserver instance isn't observing the editable content for
1016 // aElement, we need to recreate the instance because the active observer is
1017 // observing different content even if aElement keeps being focused. E.g.,
1018 // it's an <input> and editing host but was not a text control, but now, it's
1019 // a text control.
1020 if (sActiveIMEContentObserver) {
1021 if (sActiveIMEContentObserver->IsObserving(aPresContext, aElement)) {
1022 MOZ_LOG(sISMLog, LogLevel::Debug,
1023 (" OnFocusInEditor(), "
1024 "the editable content for aEditorBase has already been being "
1025 "observed by sActiveIMEContentObserver"));
1026 return;
1028 // If the IMEContentObserver has not finished initializing itself yet,
1029 // we don't need to recreate it because the following
1030 // TryToFlushPendingNotifications call must make it initialized.
1031 const nsCOMPtr<nsIWidget> textInputHandlingWidget =
1032 sTextInputHandlingWidget;
1033 if (!sActiveIMEContentObserver->IsBeingInitializedFor(
1034 aPresContext, aElement, aEditorBase)) {
1035 DestroyIMEContentObserver();
1037 if (NS_WARN_IF(!IsFocusedElement(aPresContext, aElement)) ||
1038 NS_WARN_IF(!sTextInputHandlingWidget) ||
1039 NS_WARN_IF(sTextInputHandlingWidget != textInputHandlingWidget)) {
1040 MOZ_LOG(sISMLog, LogLevel::Error,
1041 (" OnFocusInEditor(), detected unexpected focus change with "
1042 "re-initializing active IMEContentObserver"));
1043 return;
1047 if (!sActiveIMEContentObserver && sTextInputHandlingWidget &&
1048 IsIMEObserverNeeded(
1049 sTextInputHandlingWidget->GetInputContext().mIMEState)) {
1050 CreateIMEContentObserver(aEditorBase, aElement);
1051 if (sActiveIMEContentObserver) {
1052 MOZ_LOG(sISMLog, LogLevel::Debug,
1053 (" OnFocusInEditor(), new IMEContentObserver is created (0x%p)",
1054 sActiveIMEContentObserver.get()));
1058 if (sActiveIMEContentObserver) {
1059 sActiveIMEContentObserver->TryToFlushPendingNotifications(false);
1060 MOZ_LOG(sISMLog, LogLevel::Debug,
1061 (" OnFocusInEditor(), trying to send pending notifications in "
1062 "the active IMEContentObserver (0x%p)...",
1063 sActiveIMEContentObserver.get()));
1067 // static
1068 void IMEStateManager::OnEditorInitialized(EditorBase& aEditorBase) {
1069 if (!sActiveIMEContentObserver ||
1070 !sActiveIMEContentObserver->WasInitializedWith(aEditorBase)) {
1071 return;
1074 MOZ_LOG(sISMLog, LogLevel::Info,
1075 ("OnEditorInitialized(aEditorBase=0x%p)", &aEditorBase));
1077 sActiveIMEContentObserver->UnsuppressNotifyingIME();
1080 // static
1081 void IMEStateManager::OnEditorDestroying(EditorBase& aEditorBase) {
1082 if (!sActiveIMEContentObserver ||
1083 !sActiveIMEContentObserver->WasInitializedWith(aEditorBase)) {
1084 return;
1087 MOZ_LOG(sISMLog, LogLevel::Info,
1088 ("OnEditorDestroying(aEditorBase=0x%p)", &aEditorBase));
1090 // The IMEContentObserver shouldn't notify IME of anything until reframing
1091 // is finished.
1092 sActiveIMEContentObserver->SuppressNotifyingIME();
1095 void IMEStateManager::OnReFocus(nsPresContext& aPresContext,
1096 Element& aElement) {
1097 MOZ_LOG(sISMLog, LogLevel::Info,
1098 ("OnReFocus(aPresContext=0x%p (available: %s), aElement=0x%p), "
1099 "sActiveIMEContentObserver=0x%p, aElement=0x%p",
1100 &aPresContext, GetBoolName(CanHandleWith(&aPresContext)), &aElement,
1101 sActiveIMEContentObserver.get(), sFocusedElement.get()));
1103 if (NS_WARN_IF(!sTextInputHandlingWidget) ||
1104 NS_WARN_IF(sTextInputHandlingWidget->Destroyed())) {
1105 return;
1108 // Check if IME has focus. If and only if IME has focus, we may need to
1109 // update IME state of the widget to re-open VKB. Otherwise, IME will open
1110 // VKB at getting focus.
1111 if (!sActiveIMEContentObserver ||
1112 !sActiveIMEContentObserver->IsObserving(aPresContext, &aElement)) {
1113 MOZ_LOG(sISMLog, LogLevel::Debug,
1114 (" OnReFocus(), editable content for aElement was not being "
1115 "observed by the sActiveIMEContentObserver"));
1116 return;
1119 MOZ_ASSERT(&aElement == sFocusedElement.get());
1121 if (!UserActivation::IsHandlingUserInput() ||
1122 UserActivation::IsHandlingKeyboardInput()) {
1123 return;
1126 const OwningNonNull<nsIWidget> textInputHandlingWidget =
1127 *sTextInputHandlingWidget;
1129 // Don't update IME state during composition.
1130 if (sTextCompositions) {
1131 if (const TextComposition* composition =
1132 sTextCompositions->GetCompositionFor(textInputHandlingWidget)) {
1133 if (composition->IsComposing()) {
1134 return;
1139 InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
1140 InputContextAction::FOCUS_NOT_CHANGED);
1141 IMEState newState = GetNewIMEState(aPresContext, &aElement);
1142 MOZ_ASSERT(newState.IsEditable());
1143 SetIMEState(newState, &aPresContext, &aElement, textInputHandlingWidget,
1144 action, sOrigin);
1147 // static
1148 void IMEStateManager::MaybeOnEditableStateDisabled(nsPresContext& aPresContext,
1149 dom::Element* aElement) {
1150 MOZ_LOG(
1151 sISMLog, LogLevel::Info,
1152 ("MaybeOnEditableStateDisabled(aPresContext=0x%p, aElement=0x%p), "
1153 "sFocusedPresContext=0x%p (available: %s), "
1154 "sFocusedElement=0x%p, sTextInputHandlingWidget=0x%p (available: %s), "
1155 "sActiveIMEContentObserver=0x%p, sIsGettingNewIMEState=%s",
1156 &aPresContext, aElement, sFocusedPresContext.get(),
1157 GetBoolName(CanHandleWith(sFocusedPresContext)), sFocusedElement.get(),
1158 sTextInputHandlingWidget,
1159 GetBoolName(sTextInputHandlingWidget &&
1160 !sTextInputHandlingWidget->Destroyed()),
1161 sActiveIMEContentObserver.get(), GetBoolName(sIsGettingNewIMEState)));
1163 if (sIsGettingNewIMEState) {
1164 MOZ_LOG(sISMLog, LogLevel::Debug,
1165 (" MaybeOnEditableStateDisabled(), "
1166 "does nothing because of called while getting new IME state"));
1167 return;
1170 if (&aPresContext != sFocusedPresContext || aElement != sFocusedElement) {
1171 MOZ_LOG(sISMLog, LogLevel::Debug,
1172 (" MaybeOnEditableStateDisabled(), "
1173 "does nothing because of another element already has focus"));
1174 return;
1177 if (NS_WARN_IF(!sTextInputHandlingWidget) ||
1178 NS_WARN_IF(sTextInputHandlingWidget->Destroyed())) {
1179 MOZ_LOG(sISMLog, LogLevel::Error,
1180 (" MaybeOnEditableStateDisabled(), FAILED due to "
1181 "the widget for the managing the nsPresContext has gone"));
1182 return;
1185 const OwningNonNull<nsIWidget> textInputHandlingWidget =
1186 *sTextInputHandlingWidget;
1187 MOZ_ASSERT_IF(sFocusedPresContext->GetTextInputHandlingWidget(),
1188 sFocusedPresContext->GetTextInputHandlingWidget() ==
1189 textInputHandlingWidget.get());
1191 const IMEState newIMEState = GetNewIMEState(aPresContext, aElement);
1192 // If IME state becomes editable, HTMLEditor should also be initialized with
1193 // same path as it gets focus. Therefore, IMEStateManager::UpdateIMEState
1194 // should be called by HTMLEditor instead.
1195 if (newIMEState.IsEditable()) {
1196 MOZ_LOG(sISMLog, LogLevel::Debug,
1197 (" MaybeOnEditableStateDisabled(), "
1198 "does nothing because IME state does not become disabled"));
1199 return;
1202 // Otherwise, disable IME on the widget and destroy active IMEContentObserver
1203 // if there is.
1204 const InputContext inputContext = textInputHandlingWidget->GetInputContext();
1205 if (inputContext.mIMEState.mEnabled == newIMEState.mEnabled) {
1206 MOZ_LOG(sISMLog, LogLevel::Debug,
1207 (" MaybeOnEditableStateDisabled(), "
1208 "does nothing because IME state is not changed"));
1209 return;
1212 if (sActiveIMEContentObserver) {
1213 DestroyIMEContentObserver();
1216 InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
1217 InputContextAction::FOCUS_NOT_CHANGED);
1218 SetIMEState(newIMEState, &aPresContext, aElement, textInputHandlingWidget,
1219 action, sOrigin);
1222 // static
1223 void IMEStateManager::UpdateIMEState(const IMEState& aNewIMEState,
1224 Element* aElement, EditorBase& aEditorBase,
1225 const UpdateIMEStateOptions& aOptions) {
1226 MOZ_LOG(
1227 sISMLog, LogLevel::Info,
1228 ("UpdateIMEState(aNewIMEState=%s, aElement=0x%p, aEditorBase=0x%p, "
1229 "aOptions=0x%0x), sFocusedPresContext=0x%p (available: %s), "
1230 "sFocusedElement=0x%p, sTextInputHandlingWidget=0x%p (available: %s), "
1231 "sActiveIMEContentObserver=0x%p, sIsGettingNewIMEState=%s",
1232 ToString(aNewIMEState).c_str(), aElement, &aEditorBase,
1233 aOptions.serialize(), sFocusedPresContext.get(),
1234 GetBoolName(CanHandleWith(sFocusedPresContext)), sFocusedElement.get(),
1235 sTextInputHandlingWidget,
1236 GetBoolName(sTextInputHandlingWidget &&
1237 !sTextInputHandlingWidget->Destroyed()),
1238 sActiveIMEContentObserver.get(), GetBoolName(sIsGettingNewIMEState)));
1240 if (sIsGettingNewIMEState) {
1241 MOZ_LOG(sISMLog, LogLevel::Debug,
1242 (" UpdateIMEState(), "
1243 "does nothing because of called while getting new IME state"));
1244 return;
1247 RefPtr<PresShell> presShell(aEditorBase.GetPresShell());
1248 if (NS_WARN_IF(!presShell)) {
1249 MOZ_LOG(sISMLog, LogLevel::Error,
1250 (" UpdateIMEState(), FAILED due to "
1251 "editor doesn't have PresShell"));
1252 return;
1255 const RefPtr<nsPresContext> presContext =
1256 aElement
1257 ? aElement->GetPresContext(Element::PresContextFor::eForComposedDoc)
1258 : aEditorBase.GetPresContext();
1259 if (NS_WARN_IF(!presContext)) {
1260 MOZ_LOG(sISMLog, LogLevel::Error,
1261 (" UpdateIMEState(), FAILED due to "
1262 "editor doesn't have PresContext"));
1263 return;
1266 // IMEStateManager::UpdateIMEState() should be called after
1267 // IMEStateManager::OnChangeFocus() is called for setting focus to aElement
1268 // and aEditorBase. However, when aEditorBase is an HTMLEditor, this may be
1269 // called by nsIEditor::PostCreate() before IMEStateManager::OnChangeFocus().
1270 // Similarly, when aEditorBase is a TextEditor, this may be called by
1271 // nsIEditor::SetFlags(). In such cases, this method should do nothing
1272 // because input context should be updated when
1273 // IMEStateManager::OnChangeFocus() is called later.
1274 if (sFocusedPresContext != presContext) {
1275 MOZ_LOG(sISMLog, LogLevel::Warning,
1276 (" UpdateIMEState(), does nothing due to "
1277 "the editor hasn't managed by IMEStateManager yet"));
1278 return;
1281 // If IMEStateManager doesn't manage any document, this cannot update IME
1282 // state of any widget.
1283 if (NS_WARN_IF(!sFocusedPresContext)) {
1284 MOZ_LOG(sISMLog, LogLevel::Error,
1285 (" UpdateIMEState(), FAILED due to "
1286 "no managing nsPresContext"));
1287 return;
1290 if (NS_WARN_IF(!sTextInputHandlingWidget) ||
1291 NS_WARN_IF(sTextInputHandlingWidget->Destroyed())) {
1292 MOZ_LOG(sISMLog, LogLevel::Error,
1293 (" UpdateIMEState(), FAILED due to "
1294 "the widget for the managing nsPresContext has gone"));
1295 return;
1298 const OwningNonNull<nsIWidget> textInputHandlingWidget =
1299 *sTextInputHandlingWidget;
1300 MOZ_ASSERT_IF(sFocusedPresContext->GetTextInputHandlingWidget(),
1301 sFocusedPresContext->GetTextInputHandlingWidget() ==
1302 textInputHandlingWidget.get());
1304 // TODO: Investigate if we could put off to initialize IMEContentObserver
1305 // later because a lot of callers need to be marked as
1306 // MOZ_CAN_RUN_SCRIPT otherwise.
1308 // If there is an active observer and we need an observer, we want to keep
1309 // using the observer as far as possible because it's already notified IME of
1310 // focus. However, between the call of OnChangeFocus() and UpdateIMEState(),
1311 // focus and/or IME state may have been changed. If so, we need to update
1312 // the observer for current situation.
1313 const bool hasActiveObserverAndNeedObserver =
1314 sActiveIMEContentObserver && IsIMEObserverNeeded(aNewIMEState);
1316 // If the active observer was not initialized with aEditorBase, it means
1317 // that the old editor lost focus but new editor gets focus **without**
1318 // focus move in the DOM tree. This may happen with changing the type of
1319 // <input> element, etc. In this case, we need to let IME know a focus move
1320 // with recreating IMEContentObserver because editable target has been
1321 // changed.
1322 const bool needToRecreateObserver =
1323 hasActiveObserverAndNeedObserver &&
1324 !sActiveIMEContentObserver->WasInitializedWith(aEditorBase);
1326 // If the active observer was initialized with same editor, we can/should
1327 // keep using it for avoiding to notify IME of a focus change. However, we
1328 // may need to re-initialize it because it may have temporarily stopped
1329 // observing the content.
1330 if (hasActiveObserverAndNeedObserver && !needToRecreateObserver) {
1331 MOZ_LOG(sISMLog, LogLevel::Debug,
1332 (" UpdateIMEState(), try to reinitialize the active "
1333 "IMEContentObserver"));
1334 OwningNonNull<IMEContentObserver> contentObserver =
1335 *sActiveIMEContentObserver;
1336 OwningNonNull<nsPresContext> presContext = *sFocusedPresContext;
1337 if (!contentObserver->MaybeReinitialize(
1338 textInputHandlingWidget, presContext, aElement, aEditorBase)) {
1339 MOZ_LOG(sISMLog, LogLevel::Error,
1340 (" UpdateIMEState(), failed to reinitialize the active "
1341 "IMEContentObserver"));
1343 if (NS_WARN_IF(textInputHandlingWidget->Destroyed())) {
1344 MOZ_LOG(sISMLog, LogLevel::Error,
1345 (" UpdateIMEState(), widget has gone during re-initializing "
1346 "the active IMEContentObserver"));
1347 return;
1351 // If there is no active IMEContentObserver or it isn't observing the
1352 // editable content for aElement, we should recreate an observer. E.g.,
1353 // aElement which is an editing host may have been changed from/to a text
1354 // control.
1355 const bool createNewObserver =
1356 IsIMEObserverNeeded(aNewIMEState) &&
1357 (!sActiveIMEContentObserver || needToRecreateObserver ||
1358 !sActiveIMEContentObserver->IsObserving(*sFocusedPresContext, aElement));
1359 // If we're recreating new IMEContentObserver or new state does not need
1360 // IMEContentObserver, destroy the active one.
1361 const bool destroyCurrentObserver =
1362 sActiveIMEContentObserver &&
1363 (createNewObserver || !IsIMEObserverNeeded(aNewIMEState));
1365 // If IME state is changed, e.g., from "enabled" or "password" to "disabled",
1366 // we cannot keep the composing state because the new "disabled" state does
1367 // not support composition, and also from "enabled" to "password" and vice
1368 // versa, IME may use different composing rules. Therefore, for avoiding
1369 // IME to be confused, we should fix the composition first.
1370 const bool updateIMEState =
1371 aOptions.contains(UpdateIMEStateOption::ForceUpdate) ||
1372 (textInputHandlingWidget->GetInputContext().mIMEState.mEnabled !=
1373 aNewIMEState.mEnabled);
1374 if (NS_WARN_IF(textInputHandlingWidget->Destroyed())) {
1375 MOZ_LOG(
1376 sISMLog, LogLevel::Error,
1377 (" UpdateIMEState(), widget has gone during getting input context"));
1378 return;
1381 if (updateIMEState &&
1382 !aOptions.contains(UpdateIMEStateOption::DontCommitComposition)) {
1383 NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, textInputHandlingWidget,
1384 sFocusedIMEBrowserParent);
1385 if (NS_WARN_IF(textInputHandlingWidget->Destroyed())) {
1386 MOZ_LOG(sISMLog, LogLevel::Error,
1387 (" UpdateIMEState(), widget has gone during committing "
1388 "composition"));
1389 return;
1391 // FIXME: If committing composition changes IME state recursively, we should
1392 // not keep updating IME state here. However, how can we manage it?
1393 // Is a generation of the state is required?
1396 // Notify IME of blur first.
1397 if (destroyCurrentObserver) {
1398 DestroyIMEContentObserver();
1399 if (NS_WARN_IF(textInputHandlingWidget->Destroyed()) ||
1400 NS_WARN_IF(sTextInputHandlingWidget != textInputHandlingWidget)) {
1401 MOZ_LOG(sISMLog, LogLevel::Error,
1402 (" UpdateIMEState(), has set input context, but the widget is "
1403 "not focused"));
1404 return;
1408 // Then, notify our IME handler of new IME state.
1409 if (updateIMEState) {
1410 InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
1411 InputContextAction::FOCUS_NOT_CHANGED);
1412 RefPtr<nsPresContext> editorPresContext = aEditorBase.GetPresContext();
1413 if (NS_WARN_IF(!editorPresContext)) {
1414 MOZ_LOG(sISMLog, LogLevel::Error,
1415 (" UpdateIMEState(), nsPresContext for editor has already been "
1416 "lost"));
1417 return;
1419 SetIMEState(aNewIMEState, editorPresContext, aElement,
1420 textInputHandlingWidget, action, sOrigin);
1421 if (NS_WARN_IF(textInputHandlingWidget->Destroyed()) ||
1422 NS_WARN_IF(sTextInputHandlingWidget != textInputHandlingWidget)) {
1423 MOZ_LOG(sISMLog, LogLevel::Error,
1424 (" UpdateIMEState(), has set input context, but the widget is "
1425 "not focused"));
1426 return;
1428 if (NS_WARN_IF(
1429 sTextInputHandlingWidget->GetInputContext().mIMEState.mEnabled !=
1430 aNewIMEState.mEnabled)) {
1431 MOZ_LOG(sISMLog, LogLevel::Error,
1432 (" UpdateIMEState(), has set input context, but IME enabled "
1433 "state was overridden by somebody else"));
1434 return;
1438 NS_ASSERTION(IsFocusedElement(*presContext, aElement),
1439 "aElement does not match with sFocusedElement");
1441 // Finally, create new observer if required.
1442 if (createNewObserver) {
1443 if (!sActiveIMEContentObserver && sFocusedPresContext &&
1444 sTextInputHandlingWidget) {
1445 // XXX In this case, it might not be enough safe to notify IME of
1446 // anything. So, don't try to flush pending notifications of
1447 // IMEContentObserver here.
1448 CreateIMEContentObserver(aEditorBase, aElement);
1449 } else {
1450 MOZ_LOG(sISMLog, LogLevel::Error,
1451 (" UpdateIMEState(), wanted to create IMEContentObserver, but "
1452 "lost focus"));
1457 // static
1458 IMEState IMEStateManager::GetNewIMEState(const nsPresContext& aPresContext,
1459 Element* aElement) {
1460 MOZ_LOG(
1461 sISMLog, LogLevel::Info,
1462 ("GetNewIMEState(aPresContext=0x%p, aElement=0x%p), "
1463 "sInstalledMenuKeyboardListener=%s",
1464 &aPresContext, aElement, GetBoolName(sInstalledMenuKeyboardListener)));
1466 if (!CanHandleWith(&aPresContext)) {
1467 MOZ_LOG(sISMLog, LogLevel::Debug,
1468 (" GetNewIMEState() returns IMEEnabled::Disabled because "
1469 "the nsPresContext has been destroyed"));
1470 return IMEState(IMEEnabled::Disabled);
1473 // On Printing or Print Preview, we don't need IME.
1474 if (aPresContext.Type() == nsPresContext::eContext_PrintPreview ||
1475 aPresContext.Type() == nsPresContext::eContext_Print) {
1476 MOZ_LOG(sISMLog, LogLevel::Debug,
1477 (" GetNewIMEState() returns IMEEnabled::Disabled because "
1478 "the nsPresContext is for print or print preview"));
1479 return IMEState(IMEEnabled::Disabled);
1482 if (sInstalledMenuKeyboardListener) {
1483 MOZ_LOG(sISMLog, LogLevel::Debug,
1484 (" GetNewIMEState() returns IMEEnabled::Disabled because "
1485 "menu keyboard listener was installed"));
1486 return IMEState(IMEEnabled::Disabled);
1489 if (!aElement) {
1490 // Even if there are no focused content, the focused document might be
1491 // editable, such case is design mode.
1492 if (aPresContext.Document() && aPresContext.Document()->IsInDesignMode()) {
1493 MOZ_LOG(sISMLog, LogLevel::Debug,
1494 (" GetNewIMEState() returns IMEEnabled::Enabled because "
1495 "design mode editor has focus"));
1496 return IMEState(IMEEnabled::Enabled);
1498 MOZ_LOG(sISMLog, LogLevel::Debug,
1499 (" GetNewIMEState() returns IMEEnabled::Disabled because "
1500 "no content has focus"));
1501 return IMEState(IMEEnabled::Disabled);
1504 // If aElement is in designMode, aElement should be the root node of the
1505 // document.
1506 if (aElement && aElement->IsInDesignMode()) {
1507 MOZ_LOG(sISMLog, LogLevel::Debug,
1508 (" GetNewIMEState() returns IMEEnabled::Enabled because "
1509 "a content node in design mode editor has focus"));
1510 return IMEState(IMEEnabled::Enabled);
1513 // nsIContent::GetDesiredIMEState() may cause a call of UpdateIMEState()
1514 // from EditorBase::PostCreate() because GetDesiredIMEState() needs to
1515 // retrieve an editor instance for the element if it's editable element.
1516 // For avoiding such nested IME state updates, we should set
1517 // sIsGettingNewIMEState here and UpdateIMEState() should check it.
1518 GettingNewIMEStateBlocker blocker;
1520 IMEState newIMEState = aElement->GetDesiredIMEState();
1521 MOZ_LOG(sISMLog, LogLevel::Debug,
1522 (" GetNewIMEState() returns %s", ToString(newIMEState).c_str()));
1523 return newIMEState;
1526 // static
1527 void IMEStateManager::ResetActiveChildInputContext() {
1528 sActiveChildInputContext.mIMEState.mEnabled = IMEEnabled::Unknown;
1531 // static
1532 bool IMEStateManager::HasActiveChildSetInputContext() {
1533 return sActiveChildInputContext.mIMEState.mEnabled != IMEEnabled::Unknown;
1536 // static
1537 void IMEStateManager::SetInputContextForChildProcess(
1538 BrowserParent* aBrowserParent, const InputContext& aInputContext,
1539 const InputContextAction& aAction) {
1540 MOZ_LOG(
1541 sISMLog, LogLevel::Info,
1542 ("SetInputContextForChildProcess(aBrowserParent=0x%p, "
1543 "aInputContext=%s , aAction={ mCause=%s, mAction=%s }), "
1544 "sFocusedPresContext=0x%p (available: %s), "
1545 "sTextInputHandlingWidget=0x%p (available: %s), "
1546 "BrowserParent::GetFocused()=0x%p, sInstalledMenuKeyboardListener=%s",
1547 aBrowserParent, ToString(aInputContext).c_str(),
1548 ToString(aAction.mCause).c_str(), ToString(aAction.mFocusChange).c_str(),
1549 sFocusedPresContext.get(),
1550 GetBoolName(CanHandleWith(sFocusedPresContext)),
1551 sTextInputHandlingWidget,
1552 GetBoolName(sTextInputHandlingWidget &&
1553 !sTextInputHandlingWidget->Destroyed()),
1554 BrowserParent::GetFocused(),
1555 GetBoolName(sInstalledMenuKeyboardListener)));
1557 if (aBrowserParent != BrowserParent::GetFocused()) {
1558 MOZ_LOG(sISMLog, LogLevel::Error,
1559 (" SetInputContextForChildProcess(), FAILED, "
1560 "because non-focused tab parent tries to set input context"));
1561 return;
1564 if (NS_WARN_IF(!CanHandleWith(sFocusedPresContext))) {
1565 MOZ_LOG(sISMLog, LogLevel::Error,
1566 (" SetInputContextForChildProcess(), FAILED, "
1567 "due to no focused presContext"));
1568 return;
1571 if (NS_WARN_IF(!sTextInputHandlingWidget) ||
1572 NS_WARN_IF(sTextInputHandlingWidget->Destroyed())) {
1573 MOZ_LOG(sISMLog, LogLevel::Error,
1574 (" SetInputContextForChildProcess(), FAILED, "
1575 "due to the widget for the nsPresContext has gone"));
1576 return;
1579 const OwningNonNull<nsIWidget> textInputHandlingWidget =
1580 *sTextInputHandlingWidget;
1581 MOZ_ASSERT_IF(sFocusedPresContext->GetTextInputHandlingWidget(),
1582 sFocusedPresContext->GetTextInputHandlingWidget() ==
1583 textInputHandlingWidget.get());
1584 MOZ_ASSERT(aInputContext.mOrigin == InputContext::ORIGIN_CONTENT);
1586 sActiveChildInputContext = aInputContext;
1587 MOZ_ASSERT(HasActiveChildSetInputContext());
1589 // If input context is changed in remote process while menu keyboard listener
1590 // is installed, this process shouldn't set input context now. When it's
1591 // uninstalled, input context should be restored from
1592 // sActiveChildInputContext.
1593 if (sInstalledMenuKeyboardListener) {
1594 MOZ_LOG(sISMLog, LogLevel::Info,
1595 (" SetInputContextForChildProcess(), waiting to set input context "
1596 "until menu keyboard listener is uninstalled"));
1597 return;
1600 SetInputContext(textInputHandlingWidget, aInputContext, aAction);
1603 MOZ_CAN_RUN_SCRIPT static bool IsNextFocusableElementTextControl(
1604 const Element* aInputContent) {
1605 RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager();
1606 if (MOZ_UNLIKELY(!fm)) {
1607 return false;
1609 nsCOMPtr<nsIContent> nextContent;
1610 const RefPtr<Element> inputContent = const_cast<Element*>(aInputContent);
1611 const nsCOMPtr<nsPIDOMWindowOuter> outerWindow =
1612 aInputContent->OwnerDoc()->GetWindow();
1613 nsresult rv = fm->DetermineElementToMoveFocus(
1614 outerWindow, inputContent, nsIFocusManager::MOVEFOCUS_FORWARD, true,
1615 false, getter_AddRefs(nextContent));
1616 if (NS_WARN_IF(NS_FAILED(rv)) || !nextContent) {
1617 return false;
1619 nextContent = nextContent->FindFirstNonChromeOnlyAccessContent();
1620 nsCOMPtr<nsIFormControl> nextControl = do_QueryInterface(nextContent);
1621 if (!nextControl || !nextControl->IsTextControl(false)) {
1622 return false;
1625 // XXX We don't consider next element is date/time control yet.
1626 nsGenericHTMLElement* nextElement =
1627 nsGenericHTMLElement::FromNodeOrNull(nextContent);
1628 if (!nextElement) {
1629 return false;
1632 // FIXME: Should probably use nsIFrame::IsFocusable if possible, to account
1633 // for things like visibility: hidden or so.
1634 if (!nextElement->IsFocusableWithoutStyle(false)) {
1635 return false;
1638 // Check readonly attribute.
1639 if (nextElement->IsHTMLElement(nsGkAtoms::textarea)) {
1640 auto* textAreaElement = HTMLTextAreaElement::FromNode(nextElement);
1641 return !textAreaElement->ReadOnly();
1644 // If neither textarea nor input, what element type?
1645 MOZ_DIAGNOSTIC_ASSERT(nextElement->IsHTMLElement(nsGkAtoms::input));
1647 auto* inputElement = HTMLInputElement::FromNode(nextElement);
1648 return !inputElement->ReadOnly();
1651 static void GetInputType(const IMEState& aState, const nsIContent& aContent,
1652 nsAString& aInputType) {
1653 // NOTE: If you change here, you may need to update
1654 // widget::InputContext::GatNativeKeyBindings too.
1655 if (aContent.IsHTMLElement(nsGkAtoms::input)) {
1656 const HTMLInputElement* inputElement =
1657 HTMLInputElement::FromNode(&aContent);
1658 if (inputElement->HasBeenTypePassword() && aState.IsEditable()) {
1659 aInputType.AssignLiteral("password");
1660 } else {
1661 inputElement->GetType(aInputType);
1663 } else if (aContent.IsHTMLElement(nsGkAtoms::textarea)) {
1664 aInputType.Assign(nsGkAtoms::textarea->GetUTF16String());
1668 MOZ_CAN_RUN_SCRIPT static void GetActionHint(const IMEState& aState,
1669 const nsIContent& aContent,
1670 nsAString& aActionHint) {
1671 MOZ_ASSERT(aContent.IsHTMLElement());
1673 if (aState.IsEditable()) {
1674 nsGenericHTMLElement::FromNode(&aContent)->GetEnterKeyHint(aActionHint);
1676 // If enterkeyhint is set, we don't infer action hint.
1677 if (!aActionHint.IsEmpty()) {
1678 return;
1682 if (!aContent.IsHTMLElement(nsGkAtoms::input)) {
1683 return;
1686 // Get the input content corresponding to the focused node,
1687 // which may be an anonymous child of the input content.
1688 MOZ_ASSERT(&aContent == aContent.FindFirstNonChromeOnlyAccessContent());
1689 const HTMLInputElement* inputElement = HTMLInputElement::FromNode(aContent);
1690 if (!inputElement) {
1691 return;
1694 // If we don't have an action hint and
1695 // return won't submit the form, use "maybenext".
1696 bool willSubmit = false;
1697 bool isLastElement = false;
1698 HTMLFormElement* formElement = inputElement->GetForm();
1699 // is this a form and does it have a default submit element?
1700 if (formElement) {
1701 if (formElement->IsLastActiveElement(inputElement)) {
1702 isLastElement = true;
1705 if (formElement->GetDefaultSubmitElement()) {
1706 willSubmit = true;
1707 // is this an html form...
1708 } else {
1709 // ... and does it only have a single text input element ?
1710 if (!formElement->ImplicitSubmissionIsDisabled() ||
1711 // ... or is this the last non-disabled element?
1712 isLastElement) {
1713 willSubmit = true;
1718 if (!isLastElement && formElement) {
1719 // If next tabbable content in form is text control, hint should be "next"
1720 // even there is submit in form.
1721 // MOZ_KnownLive(inputElement) because it's an alias of aContent.
1722 if (IsNextFocusableElementTextControl(MOZ_KnownLive(inputElement))) {
1723 // This is focusable text control
1724 // XXX What good hint for read only field?
1725 aActionHint.AssignLiteral("maybenext");
1726 return;
1730 if (!willSubmit) {
1731 aActionHint.Truncate();
1732 return;
1735 if (inputElement->ControlType() == FormControlType::InputSearch) {
1736 aActionHint.AssignLiteral("search");
1737 return;
1740 aActionHint.AssignLiteral("go");
1743 static void GetInputMode(const IMEState& aState, const nsIContent& aContent,
1744 nsAString& aInputMode) {
1745 if (aState.IsEditable()) {
1746 aContent.AsElement()->GetAttr(nsGkAtoms::inputmode, aInputMode);
1747 if (aContent.IsHTMLElement(nsGkAtoms::input) &&
1748 aInputMode.EqualsLiteral("mozAwesomebar")) {
1749 if (!nsContentUtils::IsChromeDoc(aContent.OwnerDoc())) {
1750 // mozAwesomebar should be allowed only in chrome
1751 aInputMode.Truncate();
1753 } else {
1754 ToLowerCase(aInputMode);
1759 static void GetAutocapitalize(const IMEState& aState, const Element& aElement,
1760 const InputContext& aInputContext,
1761 nsAString& aAutocapitalize) {
1762 if (aElement.IsHTMLElement() && aState.IsEditable() &&
1763 aInputContext.IsAutocapitalizeSupported()) {
1764 nsGenericHTMLElement::FromNode(&aElement)->GetAutocapitalize(
1765 aAutocapitalize);
1769 // static
1770 void IMEStateManager::SetIMEState(const IMEState& aState,
1771 const nsPresContext* aPresContext,
1772 Element* aElement, nsIWidget& aWidget,
1773 InputContextAction aAction,
1774 InputContext::Origin aOrigin) {
1775 MOZ_LOG(sISMLog, LogLevel::Info,
1776 ("SetIMEState(aState=%s, nsPresContext=0x%p, aElement=0x%p "
1777 "(BrowserParent=0x%p), aWidget=0x%p, aAction={ mCause=%s, "
1778 "mFocusChange=%s }, aOrigin=%s)",
1779 ToString(aState).c_str(), aPresContext, aElement,
1780 BrowserParent::GetFrom(aElement), &aWidget,
1781 ToString(aAction.mCause).c_str(),
1782 ToString(aAction.mFocusChange).c_str(), ToChar(aOrigin)));
1784 InputContext context;
1785 context.mIMEState = aState;
1786 if (aPresContext) {
1787 if (nsIURI* uri = aPresContext->Document()->GetDocumentURI()) {
1788 // We don't need to and should not expose special URLs such as:
1789 // about: Any apps like IME should work normally and constantly in any
1790 // default pages such as about:blank, about:home, etc in either
1791 // the main process or a content process.
1792 // blob: This may contain big data. If we copy it to the main process,
1793 // it may make the heap dirty which makes the process slower.
1794 // chrome: Same as about, any apps should work normally and constantly in
1795 // any chrome documents.
1796 // data: Any native apps in the environment shouldn't change the behavior
1797 // with the data URL's content and it may contain too big data.
1798 // file: The file path may contain private things and we shouldn't let
1799 // other apps like IME know which one is touched by the user because
1800 // malicious text services may like files which are explicitly used
1801 // by the user better.
1802 if (uri->SchemeIs("http") || uri->SchemeIs("https")) {
1803 // Note that we don't need to expose UserPass, Query and Reference to
1804 // IME since they may contain sensitive data, but non-malicious text
1805 // services must not require these data.
1806 nsCOMPtr<nsIURI> exposableURL;
1807 if (NS_SUCCEEDED(NS_MutateURI(uri)
1808 .SetQuery(""_ns)
1809 .SetRef(""_ns)
1810 .SetUserPass(""_ns)
1811 .Finalize(exposableURL))) {
1812 context.mURI = std::move(exposableURL);
1817 context.mOrigin = aOrigin;
1819 context.mHasHandledUserInput =
1820 aPresContext && aPresContext->PresShell()->HasHandledUserInput();
1822 context.mInPrivateBrowsing =
1823 aPresContext &&
1824 nsContentUtils::IsInPrivateBrowsing(aPresContext->Document());
1826 const RefPtr<Element> focusedElement =
1827 aElement ? Element::FromNodeOrNull(
1828 aElement->FindFirstNonChromeOnlyAccessContent())
1829 : nullptr;
1831 if (focusedElement && focusedElement->IsHTMLElement()) {
1832 GetInputType(aState, *focusedElement, context.mHTMLInputType);
1833 GetActionHint(aState, *focusedElement, context.mActionHint);
1834 GetInputMode(aState, *focusedElement, context.mHTMLInputMode);
1835 GetAutocapitalize(aState, *focusedElement, context,
1836 context.mAutocapitalize);
1839 if (aAction.mCause == InputContextAction::CAUSE_UNKNOWN &&
1840 nsContentUtils::LegacyIsCallerChromeOrNativeCode()) {
1841 aAction.mCause = InputContextAction::CAUSE_UNKNOWN_CHROME;
1844 if ((aAction.mCause == InputContextAction::CAUSE_UNKNOWN ||
1845 aAction.mCause == InputContextAction::CAUSE_UNKNOWN_CHROME) &&
1846 UserActivation::IsHandlingUserInput()) {
1847 aAction.mCause =
1848 UserActivation::IsHandlingKeyboardInput()
1849 ? InputContextAction::CAUSE_UNKNOWN_DURING_KEYBOARD_INPUT
1850 : InputContextAction::CAUSE_UNKNOWN_DURING_NON_KEYBOARD_INPUT;
1853 SetInputContext(aWidget, context, aAction);
1856 // static
1857 void IMEStateManager::SetInputContext(nsIWidget& aWidget,
1858 const InputContext& aInputContext,
1859 const InputContextAction& aAction) {
1860 MOZ_LOG(
1861 sISMLog, LogLevel::Info,
1862 ("SetInputContext(aWidget=0x%p, aInputContext=%s, "
1863 "aAction={ mCause=%s, mAction=%s }), BrowserParent::GetFocused()=0x%p",
1864 &aWidget, ToString(aInputContext).c_str(),
1865 ToString(aAction.mCause).c_str(), ToString(aAction.mFocusChange).c_str(),
1866 BrowserParent::GetFocused()));
1868 OwningNonNull<nsIWidget> widget = aWidget;
1869 widget->SetInputContext(aInputContext, aAction);
1870 sActiveInputContextWidget = widget;
1873 // static
1874 void IMEStateManager::EnsureTextCompositionArray() {
1875 if (sTextCompositions) {
1876 return;
1878 sTextCompositions = new TextCompositionArray();
1881 // static
1882 void IMEStateManager::DispatchCompositionEvent(
1883 nsINode* aEventTargetNode, nsPresContext* aPresContext,
1884 BrowserParent* aBrowserParent, WidgetCompositionEvent* aCompositionEvent,
1885 nsEventStatus* aStatus, EventDispatchingCallback* aCallBack,
1886 bool aIsSynthesized) {
1887 MOZ_LOG(
1888 sISMLog, LogLevel::Info,
1889 ("DispatchCompositionEvent(aNode=0x%p, "
1890 "aPresContext=0x%p, aCompositionEvent={ mMessage=%s, "
1891 "mNativeIMEContext={ mRawNativeIMEContext=0x%" PRIXPTR ", "
1892 "mOriginProcessID=0x%" PRIX64 " }, mWidget(0x%p)={ "
1893 "GetNativeIMEContext()={ mRawNativeIMEContext=0x%" PRIXPTR ", "
1894 "mOriginProcessID=0x%" PRIX64 " }, Destroyed()=%s }, "
1895 "mFlags={ mIsTrusted=%s, mPropagationStopped=%s } }, "
1896 "aIsSynthesized=%s), browserParent=%p",
1897 aEventTargetNode, aPresContext, ToChar(aCompositionEvent->mMessage),
1898 aCompositionEvent->mNativeIMEContext.mRawNativeIMEContext,
1899 aCompositionEvent->mNativeIMEContext.mOriginProcessID,
1900 aCompositionEvent->mWidget.get(),
1901 aCompositionEvent->mWidget->GetNativeIMEContext().mRawNativeIMEContext,
1902 aCompositionEvent->mWidget->GetNativeIMEContext().mOriginProcessID,
1903 GetBoolName(aCompositionEvent->mWidget->Destroyed()),
1904 GetBoolName(aCompositionEvent->mFlags.mIsTrusted),
1905 GetBoolName(aCompositionEvent->mFlags.mPropagationStopped),
1906 GetBoolName(aIsSynthesized), aBrowserParent));
1908 if (NS_WARN_IF(!aCompositionEvent->IsTrusted()) ||
1909 NS_WARN_IF(aCompositionEvent->PropagationStopped())) {
1910 return;
1913 MOZ_ASSERT(aCompositionEvent->mMessage != eCompositionUpdate,
1914 "compositionupdate event shouldn't be dispatched manually");
1916 EnsureTextCompositionArray();
1918 RefPtr<TextComposition> composition =
1919 sTextCompositions->GetCompositionFor(aCompositionEvent);
1920 if (!composition) {
1921 // If synthesized event comes after delayed native composition events
1922 // for request of commit or cancel, we should ignore it.
1923 if (NS_WARN_IF(aIsSynthesized)) {
1924 return;
1926 MOZ_LOG(sISMLog, LogLevel::Debug,
1927 (" DispatchCompositionEvent(), "
1928 "adding new TextComposition to the array"));
1929 MOZ_ASSERT(aCompositionEvent->mMessage == eCompositionStart);
1930 composition = new TextComposition(aPresContext, aEventTargetNode,
1931 aBrowserParent, aCompositionEvent);
1932 sTextCompositions->AppendElement(composition);
1934 #ifdef DEBUG
1935 else {
1936 MOZ_ASSERT(aCompositionEvent->mMessage != eCompositionStart);
1938 #endif // #ifdef DEBUG
1940 // Dispatch the event on composing target.
1941 composition->DispatchCompositionEvent(aCompositionEvent, aStatus, aCallBack,
1942 aIsSynthesized);
1944 // WARNING: the |composition| might have been destroyed already.
1946 // Remove the ended composition from the array.
1947 // NOTE: When TextComposition is synthesizing compositionend event for
1948 // emulating a commit, the instance shouldn't be removed from the array
1949 // because IME may perform it later. Then, we need to ignore the
1950 // following commit events in TextComposition::DispatchEvent().
1951 // However, if commit or cancel for a request is performed synchronously
1952 // during not safe to dispatch events, PresShell must have discarded
1953 // compositionend event. Then, the synthesized compositionend event is
1954 // the last event for the composition. In this case, we need to
1955 // destroy the TextComposition with synthesized compositionend event.
1956 if ((!aIsSynthesized ||
1957 composition->WasNativeCompositionEndEventDiscarded()) &&
1958 aCompositionEvent->CausesDOMCompositionEndEvent()) {
1959 TextCompositionArray::index_type i =
1960 sTextCompositions->IndexOf(aCompositionEvent->mWidget);
1961 if (i != TextCompositionArray::NoIndex) {
1962 MOZ_LOG(
1963 sISMLog, LogLevel::Debug,
1964 (" DispatchCompositionEvent(), "
1965 "removing TextComposition from the array since NS_COMPOSTION_END "
1966 "was dispatched"));
1967 sTextCompositions->ElementAt(i)->Destroy();
1968 sTextCompositions->RemoveElementAt(i);
1973 // static
1974 IMEContentObserver* IMEStateManager::GetActiveContentObserver() {
1975 return sActiveIMEContentObserver;
1978 // static
1979 nsIContent* IMEStateManager::GetRootContent(nsPresContext* aPresContext) {
1980 Document* doc = aPresContext->Document();
1981 if (NS_WARN_IF(!doc)) {
1982 return nullptr;
1984 return doc->GetRootElement();
1987 // static
1988 void IMEStateManager::HandleSelectionEvent(
1989 nsPresContext* aPresContext, nsIContent* aEventTargetContent,
1990 WidgetSelectionEvent* aSelectionEvent) {
1991 RefPtr<BrowserParent> browserParent = GetActiveBrowserParent();
1992 if (!browserParent) {
1993 browserParent = BrowserParent::GetFrom(aEventTargetContent
1994 ? aEventTargetContent
1995 : GetRootContent(aPresContext));
1998 MOZ_LOG(
1999 sISMLog, LogLevel::Info,
2000 ("HandleSelectionEvent(aPresContext=0x%p, "
2001 "aEventTargetContent=0x%p, aSelectionEvent={ mMessage=%s, "
2002 "mFlags={ mIsTrusted=%s } }), browserParent=%p",
2003 aPresContext, aEventTargetContent, ToChar(aSelectionEvent->mMessage),
2004 GetBoolName(aSelectionEvent->mFlags.mIsTrusted), browserParent.get()));
2006 if (!aSelectionEvent->IsTrusted()) {
2007 return;
2010 RefPtr<TextComposition> composition =
2011 sTextCompositions
2012 ? sTextCompositions->GetCompositionFor(aSelectionEvent->mWidget)
2013 : nullptr;
2014 if (composition) {
2015 // When there is a composition, TextComposition should guarantee that the
2016 // selection event will be handled in same target as composition events.
2017 composition->HandleSelectionEvent(aSelectionEvent);
2018 } else {
2019 // When there is no composition, the selection event should be handled
2020 // in the aPresContext or browserParent.
2021 TextComposition::HandleSelectionEvent(aPresContext, browserParent,
2022 aSelectionEvent);
2026 // static
2027 void IMEStateManager::OnCompositionEventDiscarded(
2028 WidgetCompositionEvent* aCompositionEvent) {
2029 // Note that this method is never called for synthesized events for emulating
2030 // commit or cancel composition.
2032 MOZ_LOG(
2033 sISMLog, LogLevel::Info,
2034 ("OnCompositionEventDiscarded(aCompositionEvent={ "
2035 "mMessage=%s, mNativeIMEContext={ mRawNativeIMEContext=0x%" PRIXPTR ", "
2036 "mOriginProcessID=0x%" PRIX64 " }, mWidget(0x%p)={ "
2037 "GetNativeIMEContext()={ mRawNativeIMEContext=0x%" PRIXPTR ", "
2038 "mOriginProcessID=0x%" PRIX64 " }, Destroyed()=%s }, "
2039 "mFlags={ mIsTrusted=%s } })",
2040 ToChar(aCompositionEvent->mMessage),
2041 aCompositionEvent->mNativeIMEContext.mRawNativeIMEContext,
2042 aCompositionEvent->mNativeIMEContext.mOriginProcessID,
2043 aCompositionEvent->mWidget.get(),
2044 aCompositionEvent->mWidget->GetNativeIMEContext().mRawNativeIMEContext,
2045 aCompositionEvent->mWidget->GetNativeIMEContext().mOriginProcessID,
2046 GetBoolName(aCompositionEvent->mWidget->Destroyed()),
2047 GetBoolName(aCompositionEvent->mFlags.mIsTrusted)));
2049 if (!aCompositionEvent->IsTrusted()) {
2050 return;
2053 // Ignore compositionstart for now because sTextCompositions may not have
2054 // been created yet.
2055 if (aCompositionEvent->mMessage == eCompositionStart) {
2056 return;
2059 RefPtr<TextComposition> composition =
2060 sTextCompositions->GetCompositionFor(aCompositionEvent->mWidget);
2061 if (!composition) {
2062 // If the PresShell has been being destroyed during composition,
2063 // a TextComposition instance for the composition was already removed from
2064 // the array and destroyed in OnDestroyPresContext(). Therefore, we may
2065 // fail to retrieve a TextComposition instance here.
2066 MOZ_LOG(sISMLog, LogLevel::Info,
2067 (" OnCompositionEventDiscarded(), "
2068 "TextComposition instance for the widget has already gone"));
2069 return;
2071 composition->OnCompositionEventDiscarded(aCompositionEvent);
2074 // static
2075 nsresult IMEStateManager::NotifyIME(IMEMessage aMessage, nsIWidget* aWidget,
2076 BrowserParent* aBrowserParent) {
2077 return IMEStateManager::NotifyIME(IMENotification(aMessage), aWidget,
2078 aBrowserParent);
2081 // static
2082 nsresult IMEStateManager::NotifyIME(const IMENotification& aNotification,
2083 nsIWidget* aWidget,
2084 BrowserParent* aBrowserParent) {
2085 MOZ_LOG(sISMLog, LogLevel::Info,
2086 ("NotifyIME(aNotification={ mMessage=%s }, "
2087 "aWidget=0x%p, aBrowserParent=0x%p), sFocusedIMEWidget=0x%p, "
2088 "BrowserParent::GetFocused()=0x%p, sFocusedIMEBrowserParent=0x%p, "
2089 "aBrowserParent == BrowserParent::GetFocused()=%s, "
2090 "aBrowserParent == sFocusedIMEBrowserParent=%s, "
2091 "CanSendNotificationToWidget()=%s",
2092 ToChar(aNotification.mMessage), aWidget, aBrowserParent,
2093 sFocusedIMEWidget, BrowserParent::GetFocused(),
2094 sFocusedIMEBrowserParent.get(),
2095 GetBoolName(aBrowserParent == BrowserParent::GetFocused()),
2096 GetBoolName(aBrowserParent == sFocusedIMEBrowserParent),
2097 GetBoolName(CanSendNotificationToWidget())));
2099 if (NS_WARN_IF(!aWidget)) {
2100 MOZ_LOG(sISMLog, LogLevel::Error,
2101 (" NotifyIME(), FAILED due to no widget"));
2102 return NS_ERROR_INVALID_ARG;
2105 switch (aNotification.mMessage) {
2106 case NOTIFY_IME_OF_FOCUS: {
2107 MOZ_ASSERT(CanSendNotificationToWidget());
2109 // If focus notification comes from a remote browser which already lost
2110 // focus, we shouldn't accept the focus notification. Then, following
2111 // notifications from the process will be ignored.
2112 if (aBrowserParent != BrowserParent::GetFocused()) {
2113 MOZ_LOG(sISMLog, LogLevel::Warning,
2114 (" NotifyIME(), WARNING, the received focus notification is "
2115 "ignored, because its associated BrowserParent did not match"
2116 "the focused BrowserParent."));
2117 return NS_OK;
2119 // If IME focus is already set, first blur the currently-focused
2120 // IME widget
2121 if (sFocusedIMEWidget) {
2122 // XXX Why don't we first request the previously-focused IME
2123 // widget to commit the composition?
2124 MOZ_ASSERT(
2125 sFocusedIMEBrowserParent || aBrowserParent,
2126 "This case shouldn't be caused by focus move in this process");
2127 MOZ_LOG(sISMLog, LogLevel::Warning,
2128 (" NotifyIME(), WARNING, received focus notification with ")
2129 "non-null sFocusedIMEWidget. How come "
2130 "OnFocusMovedBetweenBrowsers did not blur it already?");
2131 nsCOMPtr<nsIWidget> focusedIMEWidget(sFocusedIMEWidget);
2132 sFocusedIMEWidget = nullptr;
2133 sFocusedIMEBrowserParent = nullptr;
2134 focusedIMEWidget->NotifyIME(IMENotification(NOTIFY_IME_OF_BLUR));
2136 #ifdef DEBUG
2137 if (aBrowserParent) {
2138 nsCOMPtr<nsIWidget> browserParentWidget =
2139 aBrowserParent->GetTextInputHandlingWidget();
2140 MOZ_ASSERT(browserParentWidget == aWidget);
2141 } else {
2142 MOZ_ASSERT(sFocusedPresContext);
2143 MOZ_ASSERT_IF(
2144 sFocusedPresContext->GetTextInputHandlingWidget(),
2145 sFocusedPresContext->GetTextInputHandlingWidget() == aWidget);
2147 #endif
2148 sFocusedIMEBrowserParent = aBrowserParent;
2149 sFocusedIMEWidget = aWidget;
2150 nsCOMPtr<nsIWidget> widget(aWidget);
2151 MOZ_LOG(
2152 sISMLog, LogLevel::Info,
2153 (" NotifyIME(), about to call widget->NotifyIME() for IME focus"));
2154 return widget->NotifyIME(aNotification);
2156 case NOTIFY_IME_OF_BLUR: {
2157 if (aBrowserParent != sFocusedIMEBrowserParent) {
2158 MOZ_LOG(sISMLog, LogLevel::Warning,
2159 (" NotifyIME(), WARNING, the received blur notification is "
2160 "ignored "
2161 "because it's not from current focused IME browser"));
2162 return NS_OK;
2164 if (!sFocusedIMEWidget) {
2165 MOZ_LOG(
2166 sISMLog, LogLevel::Error,
2167 (" NotifyIME(), WARNING, received blur notification but there is "
2168 "no focused IME widget"));
2169 return NS_OK;
2171 if (NS_WARN_IF(sFocusedIMEWidget != aWidget)) {
2172 MOZ_LOG(sISMLog, LogLevel::Warning,
2173 (" NotifyIME(), WARNING, the received blur notification is "
2174 "ignored "
2175 "because it's not for current focused IME widget"));
2176 return NS_OK;
2178 nsCOMPtr<nsIWidget> focusedIMEWidget(sFocusedIMEWidget);
2179 sFocusedIMEWidget = nullptr;
2180 sFocusedIMEBrowserParent = nullptr;
2181 return CanSendNotificationToWidget()
2182 ? focusedIMEWidget->NotifyIME(
2183 IMENotification(NOTIFY_IME_OF_BLUR))
2184 : NS_OK;
2186 case NOTIFY_IME_OF_SELECTION_CHANGE:
2187 case NOTIFY_IME_OF_TEXT_CHANGE:
2188 case NOTIFY_IME_OF_POSITION_CHANGE:
2189 case NOTIFY_IME_OF_MOUSE_BUTTON_EVENT:
2190 case NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED: {
2191 if (aBrowserParent != sFocusedIMEBrowserParent) {
2192 MOZ_LOG(
2193 sISMLog, LogLevel::Warning,
2194 (" NotifyIME(), WARNING, the received content change notification "
2195 "is ignored because it's not from current focused IME browser"));
2196 return NS_OK;
2198 if (!sFocusedIMEWidget) {
2199 MOZ_LOG(
2200 sISMLog, LogLevel::Warning,
2201 (" NotifyIME(), WARNING, the received content change notification "
2202 "is ignored because there is no focused IME widget"));
2203 return NS_OK;
2205 if (NS_WARN_IF(sFocusedIMEWidget != aWidget)) {
2206 MOZ_LOG(
2207 sISMLog, LogLevel::Warning,
2208 (" NotifyIME(), WARNING, the received content change notification "
2209 "is ignored because it's not for current focused IME widget"));
2210 return NS_OK;
2212 if (!CanSendNotificationToWidget()) {
2213 return NS_OK;
2215 nsCOMPtr<nsIWidget> widget(aWidget);
2216 return widget->NotifyIME(aNotification);
2218 default:
2219 // Other notifications should be sent only when there is composition.
2220 // So, we need to handle the others below.
2221 break;
2224 if (!sTextCompositions) {
2225 MOZ_LOG(sISMLog, LogLevel::Info,
2226 (" NotifyIME(), the request to IME is ignored because "
2227 "there have been no compositions yet"));
2228 return NS_OK;
2231 RefPtr<TextComposition> composition =
2232 sTextCompositions->GetCompositionFor(aWidget);
2233 if (!composition) {
2234 MOZ_LOG(sISMLog, LogLevel::Info,
2235 (" NotifyIME(), the request to IME is ignored because "
2236 "there is no active composition"));
2237 return NS_OK;
2240 if (aBrowserParent != composition->GetBrowserParent()) {
2241 MOZ_LOG(
2242 sISMLog, LogLevel::Warning,
2243 (" NotifyIME(), WARNING, the request to IME is ignored because "
2244 "it does not come from the remote browser which has the composition "
2245 "on aWidget"));
2246 return NS_OK;
2249 switch (aNotification.mMessage) {
2250 case REQUEST_TO_COMMIT_COMPOSITION:
2251 return composition->RequestToCommit(aWidget, false);
2252 case REQUEST_TO_CANCEL_COMPOSITION:
2253 return composition->RequestToCommit(aWidget, true);
2254 default:
2255 MOZ_CRASH("Unsupported notification");
2257 MOZ_CRASH(
2258 "Failed to handle the notification for non-synthesized composition");
2259 return NS_ERROR_FAILURE;
2262 // static
2263 nsresult IMEStateManager::NotifyIME(IMEMessage aMessage,
2264 nsPresContext* aPresContext,
2265 BrowserParent* aBrowserParent) {
2266 MOZ_LOG(sISMLog, LogLevel::Info,
2267 ("NotifyIME(aMessage=%s, aPresContext=0x%p, aBrowserParent=0x%p)",
2268 ToChar(aMessage), aPresContext, aBrowserParent));
2269 // This assertion is just for clarify which message may be set, so feel free
2270 // to add other messages if you want.
2271 MOZ_ASSERT(aMessage == REQUEST_TO_CANCEL_COMPOSITION ||
2272 aMessage == REQUEST_TO_COMMIT_COMPOSITION ||
2273 aMessage == NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED);
2274 // However, these messages require additional information. Therefore, this
2275 // overload shouldn't be used for them.
2276 MOZ_ASSERT(aMessage != NOTIFY_IME_OF_FOCUS &&
2277 aMessage != NOTIFY_IME_OF_BLUR &&
2278 aMessage != NOTIFY_IME_OF_TEXT_CHANGE &&
2279 aMessage != NOTIFY_IME_OF_SELECTION_CHANGE &&
2280 aMessage != NOTIFY_IME_OF_MOUSE_BUTTON_EVENT);
2282 if (NS_WARN_IF(!CanHandleWith(aPresContext))) {
2283 return NS_ERROR_INVALID_ARG;
2286 nsCOMPtr<nsIWidget> widget =
2287 aPresContext == sFocusedPresContext && sTextInputHandlingWidget
2288 ? sTextInputHandlingWidget
2289 : aPresContext->GetTextInputHandlingWidget();
2290 if (NS_WARN_IF(!widget)) {
2291 MOZ_LOG(sISMLog, LogLevel::Error,
2292 (" NotifyIME(), FAILED due to no widget for the nsPresContext"));
2293 return NS_ERROR_NOT_AVAILABLE;
2295 return NotifyIME(aMessage, widget, aBrowserParent);
2298 // static
2299 bool IMEStateManager::IsEditable(nsINode* node) {
2300 if (node->IsEditable()) {
2301 return true;
2303 // |node| might be readwrite (for example, a text control)
2304 if (node->IsElement() &&
2305 node->AsElement()->State().HasState(ElementState::READWRITE)) {
2306 return true;
2308 return false;
2311 // static
2312 nsINode* IMEStateManager::GetRootEditableNode(const nsPresContext& aPresContext,
2313 const Element* aElement) {
2314 if (aElement) {
2315 // If the focused content is in design mode, return is composed document
2316 // because aElement may be in UA widget shadow tree.
2317 if (aElement->IsInDesignMode()) {
2318 return aElement->GetComposedDoc();
2321 nsINode* candidateRootNode = const_cast<Element*>(aElement);
2322 for (nsINode* node = candidateRootNode; node && IsEditable(node);
2323 node = node->GetParentNode()) {
2324 // If the node has independent selection like <input type="text"> or
2325 // <textarea>, the node should be the root editable node for aElement.
2326 // FYI: <select> element also has independent selection but IsEditable()
2327 // returns false.
2328 // XXX: If somebody adds new editable element which has independent
2329 // selection but doesn't own editor, we'll need more checks here.
2330 // XXX: If aElement is not in native anonymous subtree, checking
2331 // independent selection must be wrong, see bug 1731005.
2332 if (node->IsContent() && node->AsContent()->HasIndependentSelection()) {
2333 return node;
2335 candidateRootNode = node;
2337 return candidateRootNode;
2340 return aPresContext.Document() && aPresContext.Document()->IsInDesignMode()
2341 ? aPresContext.Document()
2342 : nullptr;
2345 // static
2346 bool IMEStateManager::IsIMEObserverNeeded(const IMEState& aState) {
2347 return aState.IsEditable();
2350 // static
2351 void IMEStateManager::DestroyIMEContentObserver() {
2352 if (!sActiveIMEContentObserver) {
2353 MOZ_LOG(sISMLog, LogLevel::Verbose,
2354 ("DestroyIMEContentObserver() does nothing"));
2355 return;
2358 MOZ_LOG(sISMLog, LogLevel::Info,
2359 ("DestroyIMEContentObserver(), destroying "
2360 "the active IMEContentObserver..."));
2361 RefPtr<IMEContentObserver> tsm = sActiveIMEContentObserver.get();
2362 sActiveIMEContentObserver = nullptr;
2363 tsm->Destroy();
2366 // static
2367 void IMEStateManager::CreateIMEContentObserver(EditorBase& aEditorBase,
2368 Element* aFocusedElement) {
2369 MOZ_ASSERT(!sActiveIMEContentObserver);
2370 MOZ_ASSERT(sTextInputHandlingWidget);
2371 MOZ_ASSERT(sFocusedPresContext);
2372 MOZ_ASSERT(IsIMEObserverNeeded(
2373 sTextInputHandlingWidget->GetInputContext().mIMEState));
2375 MOZ_LOG(sISMLog, LogLevel::Info,
2376 ("CreateIMEContentObserver(aEditorBase=0x%p, aFocusedElement=0x%p), "
2377 "sFocusedPresContext=0x%p, sFocusedElement=0x%p, "
2378 "sTextInputHandlingWidget=0x%p (available: %s), "
2379 "sActiveIMEContentObserver=0x%p, "
2380 "sActiveIMEContentObserver->IsObserving(sFocusedPresContext, "
2381 "sFocusedElement)=%s",
2382 &aEditorBase, aFocusedElement, sFocusedPresContext.get(),
2383 sFocusedElement.get(), sTextInputHandlingWidget,
2384 GetBoolName(sTextInputHandlingWidget &&
2385 !sTextInputHandlingWidget->Destroyed()),
2386 sActiveIMEContentObserver.get(),
2387 GetBoolName(sActiveIMEContentObserver && sFocusedPresContext &&
2388 sActiveIMEContentObserver->IsObserving(
2389 *sFocusedPresContext, sFocusedElement))));
2391 if (NS_WARN_IF(sTextInputHandlingWidget->Destroyed())) {
2392 MOZ_LOG(sISMLog, LogLevel::Error,
2393 (" CreateIMEContentObserver(), FAILED due to "
2394 "the widget for the nsPresContext has gone"));
2395 return;
2398 const OwningNonNull<nsIWidget> textInputHandlingWidget =
2399 *sTextInputHandlingWidget;
2400 MOZ_ASSERT_IF(sFocusedPresContext->GetTextInputHandlingWidget(),
2401 sFocusedPresContext->GetTextInputHandlingWidget() ==
2402 textInputHandlingWidget.get());
2404 MOZ_LOG(sISMLog, LogLevel::Debug,
2405 (" CreateIMEContentObserver() is creating an "
2406 "IMEContentObserver instance..."));
2407 sActiveIMEContentObserver = new IMEContentObserver();
2409 // IMEContentObserver::Init() might create another IMEContentObserver
2410 // instance. So, sActiveIMEContentObserver would be replaced with new one.
2411 // We should hold the current instance here.
2412 OwningNonNull<IMEContentObserver> activeIMEContentObserver =
2413 *sActiveIMEContentObserver;
2414 OwningNonNull<nsPresContext> focusedPresContext = *sFocusedPresContext;
2415 RefPtr<Element> focusedElement = aFocusedElement;
2416 activeIMEContentObserver->Init(textInputHandlingWidget, focusedPresContext,
2417 focusedElement, aEditorBase);
2420 // static
2421 nsresult IMEStateManager::GetFocusSelectionAndRootElement(
2422 Selection** aSelection, Element** aRootElement) {
2423 if (!sActiveIMEContentObserver) {
2424 return NS_ERROR_NOT_AVAILABLE;
2426 return sActiveIMEContentObserver->GetSelectionAndRoot(aSelection,
2427 aRootElement);
2430 // static
2431 TextComposition* IMEStateManager::GetTextCompositionFor(nsIWidget* aWidget) {
2432 return sTextCompositions ? sTextCompositions->GetCompositionFor(aWidget)
2433 : nullptr;
2436 // static
2437 TextComposition* IMEStateManager::GetTextCompositionFor(
2438 const WidgetCompositionEvent* aCompositionEvent) {
2439 return sTextCompositions
2440 ? sTextCompositions->GetCompositionFor(aCompositionEvent)
2441 : nullptr;
2444 // static
2445 TextComposition* IMEStateManager::GetTextCompositionFor(
2446 nsPresContext* aPresContext) {
2447 return sTextCompositions ? sTextCompositions->GetCompositionFor(aPresContext)
2448 : nullptr;
2451 } // namespace mozilla