IMEStateManager.cpp (110850B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "IMEStateManager.h" 8 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/dom/BrowserBridgeChild.h" 26 #include "mozilla/dom/BrowserParent.h" 27 #include "mozilla/dom/Document.h" 28 #include "mozilla/dom/Element.h" 29 #include "mozilla/dom/HTMLFormElement.h" 30 #include "mozilla/dom/HTMLInputElement.h" 31 #include "mozilla/dom/HTMLTextAreaElement.h" 32 #include "mozilla/dom/MouseEventBinding.h" 33 #include "mozilla/dom/UserActivation.h" 34 #include "mozilla/widget/IMEData.h" 35 #include "nsCOMPtr.h" 36 #include "nsContentUtils.h" 37 #include "nsFocusManager.h" 38 #include "nsIContent.h" 39 #include "nsIContentInlines.h" 40 #include "nsIFormControl.h" 41 #include "nsINode.h" 42 #include "nsISupports.h" 43 #include "nsIURI.h" 44 #include "nsIURIMutator.h" 45 #include "nsPresContext.h" 46 #include "nsTextControlFrame.h" 47 #include "nsThreadUtils.h" 48 49 namespace mozilla { 50 51 using namespace dom; 52 using namespace widget; 53 54 /** 55 * When a method is called, log its arguments and/or related static variables 56 * with LogLevel::Info. However, if it puts too many logs like 57 * OnDestroyPresContext(), should long only when the method actually does 58 * something. In this case, the log should start with "<method name>". 59 * 60 * When a method quits due to unexpected situation, log the reason with 61 * LogLevel::Error. In this case, the log should start with 62 * "<method name>(), FAILED". The indent makes the log look easier. 63 * 64 * When a method does something only in some situations and it may be important 65 * for debug, log the information with LogLevel::Debug. In this case, the log 66 * should start with " <method name>(),". 67 */ 68 LazyLogModule sISMLog("IMEStateManager"); 69 70 StaticRefPtr<Element> IMEStateManager::sFocusedElement; 71 StaticRefPtr<nsPresContext> IMEStateManager::sFocusedPresContext; 72 nsIWidget* IMEStateManager::sTextInputHandlingWidget = nullptr; 73 nsIWidget* IMEStateManager::sFocusedIMEWidget = nullptr; 74 StaticRefPtr<BrowserParent> IMEStateManager::sFocusedIMEBrowserParent; 75 nsIWidget* IMEStateManager::sActiveInputContextWidget = nullptr; 76 StaticRefPtr<IMEContentObserver> IMEStateManager::sActiveIMEContentObserver; 77 TextCompositionArray* IMEStateManager::sTextCompositions = nullptr; 78 InputContext::Origin IMEStateManager::sOrigin = InputContext::ORIGIN_MAIN; 79 MOZ_RUNINIT InputContext IMEStateManager::sActiveChildInputContext; 80 bool IMEStateManager::sInstalledMenuKeyboardListener = false; 81 bool IMEStateManager::sIsGettingNewIMEState = false; 82 bool IMEStateManager::sCleaningUpForStoppingIMEStateManagement = false; 83 bool IMEStateManager::sIsActive = false; 84 MOZ_RUNINIT Maybe<IMEStateManager::PendingFocusedBrowserSwitchingData> 85 IMEStateManager::sPendingFocusedBrowserSwitchingData; 86 87 class PseudoFocusChangeRunnable : public Runnable { 88 public: 89 explicit PseudoFocusChangeRunnable(bool aInstallingMenuKeyboardListener) 90 : Runnable("PseudoFocusChangeRunnable"), 91 mFocusedPresContext(IMEStateManager::sFocusedPresContext), 92 mFocusedElement(IMEStateManager::sFocusedElement), 93 mInstallMenuKeyboardListener(aInstallingMenuKeyboardListener) {} 94 95 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD Run() override { 96 IMEStateManager::SetMenubarPseudoFocus(this, mInstallMenuKeyboardListener, 97 mFocusedPresContext); 98 return NS_OK; 99 } 100 101 private: 102 const RefPtr<nsPresContext> mFocusedPresContext; 103 const RefPtr<Element> mFocusedElement; 104 const bool mInstallMenuKeyboardListener; 105 }; 106 107 StaticRefPtr<PseudoFocusChangeRunnable> 108 IMEStateManager::sPseudoFocusChangeRunnable; 109 110 // static 111 void IMEStateManager::Init() { 112 sOrigin = XRE_IsParentProcess() ? InputContext::ORIGIN_MAIN 113 : InputContext::ORIGIN_CONTENT; 114 ResetActiveChildInputContext(); 115 } 116 117 // static 118 void IMEStateManager::Shutdown() { 119 MOZ_LOG( 120 sISMLog, LogLevel::Info, 121 ("Shutdown(), sTextCompositions=0x%p, sTextCompositions->Length()=%zu, " 122 "sPendingFocusedBrowserSwitchingData.isSome()=%s", 123 sTextCompositions, sTextCompositions ? sTextCompositions->Length() : 0, 124 TrueOrFalse(sPendingFocusedBrowserSwitchingData.isSome()))); 125 MOZ_LOG(sISMLog, LogLevel::Debug, 126 (" Shutdown(), sFocusedElement=0x%p, sFocusedPresContext=0x%p, " 127 "sTextInputHandlingWidget=0x%p, sFocusedIMEWidget=0x%p, " 128 "sFocusedIMEBrowserParent=0x%p, sActiveInputContextWidget=0x%p, " 129 "sActiveIMEContentObserver=0x%p", 130 sFocusedElement.get(), sFocusedPresContext.get(), 131 sTextInputHandlingWidget, sFocusedIMEWidget, 132 sFocusedIMEBrowserParent.get(), sActiveInputContextWidget, 133 sActiveIMEContentObserver.get())); 134 135 sPendingFocusedBrowserSwitchingData.reset(); 136 MOZ_ASSERT(!sTextCompositions || !sTextCompositions->Length()); 137 delete sTextCompositions; 138 sTextCompositions = nullptr; 139 // All string instances in the global space need to be empty after XPCOM 140 // shutdown. 141 sActiveChildInputContext.ShutDown(); 142 } 143 144 // static 145 void IMEStateManager::OnFocusMovedBetweenBrowsers(BrowserParent* aBlur, 146 BrowserParent* aFocus) { 147 MOZ_ASSERT(aBlur != aFocus); 148 MOZ_ASSERT(XRE_IsParentProcess()); 149 150 if (sPendingFocusedBrowserSwitchingData.isSome()) { 151 MOZ_ASSERT(aBlur == 152 sPendingFocusedBrowserSwitchingData.ref().mBrowserParentFocused); 153 // If focus is not changed between browsers actually, we need to do 154 // nothing here. Let's cancel handling what this method does. 155 if (sPendingFocusedBrowserSwitchingData.ref().mBrowserParentBlurred == 156 aFocus) { 157 sPendingFocusedBrowserSwitchingData.reset(); 158 MOZ_LOG(sISMLog, LogLevel::Info, 159 (" OnFocusMovedBetweenBrowsers(), canceled all pending focus " 160 "moves between browsers")); 161 return; 162 } 163 aBlur = sPendingFocusedBrowserSwitchingData.ref().mBrowserParentBlurred; 164 sPendingFocusedBrowserSwitchingData.ref().mBrowserParentFocused = aFocus; 165 MOZ_ASSERT(aBlur != aFocus); 166 } 167 168 // If application was inactive, but is now activated, and the last focused 169 // this is called by BrowserParent::UnsetTopLevelWebFocusAll() from 170 // nsFocusManager::WindowRaised(). If a content has focus in a remote 171 // process and it has composition, it may get focus back later and the 172 // composition shouldn't be commited now. Therefore, we should put off to 173 // handle this until getting another call of this method or a call of 174 //`OnFocusChangeInternal()`. 175 if (aBlur && !aFocus && !sIsActive && sTextInputHandlingWidget && 176 sTextCompositions && 177 sTextCompositions->GetCompositionFor(sTextInputHandlingWidget)) { 178 if (sPendingFocusedBrowserSwitchingData.isNothing()) { 179 sPendingFocusedBrowserSwitchingData.emplace(aBlur, aFocus); 180 } 181 MOZ_LOG(sISMLog, LogLevel::Debug, 182 (" OnFocusMovedBetweenBrowsers(), put off to handle it until " 183 "next OnFocusChangeInternal() call")); 184 return; 185 } 186 sPendingFocusedBrowserSwitchingData.reset(); 187 188 const nsCOMPtr<nsIWidget> oldWidget = sTextInputHandlingWidget; 189 // In the chrome-process case, we'll get sTextInputHandlingWidget from a 190 // PresShell later. 191 sTextInputHandlingWidget = 192 aFocus ? nsCOMPtr<nsIWidget>(aFocus->GetTextInputHandlingWidget()).get() 193 : nullptr; 194 if (oldWidget && sTextCompositions) { 195 RefPtr<TextComposition> composition = 196 sTextCompositions->GetCompositionFor(oldWidget); 197 if (composition) { 198 MOZ_LOG( 199 sISMLog, LogLevel::Debug, 200 (" OnFocusMovedBetweenBrowsers(), requesting to commit " 201 "composition to " 202 "the (previous) focused widget (would request=%s)", 203 TrueOrFalse( 204 !oldWidget->IMENotificationRequestsRef().WantDuringDeactive()))); 205 NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, oldWidget, 206 composition->GetBrowserParent()); 207 } 208 } 209 210 // The manager check is to avoid telling the content process to stop 211 // IME state management after focus has already moved there between 212 // two same-process-hosted out-of-process iframes. 213 if (aBlur && (!aFocus || (aBlur->Manager() != aFocus->Manager()))) { 214 MOZ_LOG(sISMLog, LogLevel::Debug, 215 (" OnFocusMovedBetweenBrowsers(), notifying previous " 216 "focused child process of parent process or another child process " 217 "getting focus")); 218 aBlur->StopIMEStateManagement(); 219 } 220 221 if (sActiveIMEContentObserver) { 222 DestroyIMEContentObserver(); 223 } 224 225 if (sFocusedIMEWidget) { 226 // sFocusedIMEBrowserParent can be null, if IME focus hasn't been 227 // taken before BrowserParent blur. 228 // aBlur can be null when keyboard focus moves not actually 229 // between tabs but an open menu is involved. 230 MOZ_ASSERT(!sFocusedIMEBrowserParent || !aBlur || 231 (sFocusedIMEBrowserParent == aBlur)); 232 MOZ_LOG(sISMLog, LogLevel::Debug, 233 (" OnFocusMovedBetweenBrowsers(), notifying IME of blur")); 234 NotifyIME(NOTIFY_IME_OF_BLUR, sFocusedIMEWidget, sFocusedIMEBrowserParent); 235 236 MOZ_ASSERT(!sFocusedIMEBrowserParent); 237 MOZ_ASSERT(!sFocusedIMEWidget); 238 239 } else { 240 MOZ_ASSERT(!sFocusedIMEBrowserParent); 241 } 242 243 // We deliberately don't null out sFocusedElement or sFocusedPresContext here. 244 // When focus is in remote content, as far as layout in the chrome process is 245 // concerned, the corresponding content is the top-level XUL browser. Changes 246 // among out-of-process iframes don't change that, so dropping the pointer to 247 // the XUL browser upon such a change would break IME handling. 248 } 249 250 // static 251 void IMEStateManager::WidgetDestroyed(nsIWidget* aWidget) { 252 MOZ_LOG(sISMLog, LogLevel::Debug, 253 ("WidgetDestroyed(aWidget=0x%p), sFocusedIMEWidget=0x%p, " 254 "sActiveInputContextWidget=0x%p, sFocusedIMEBrowserParent=0x%p", 255 aWidget, sFocusedIMEWidget, sActiveInputContextWidget, 256 sFocusedIMEBrowserParent.get())); 257 if (sTextInputHandlingWidget == aWidget) { 258 sTextInputHandlingWidget = nullptr; 259 } 260 if (sFocusedIMEWidget == aWidget) { 261 if (sFocusedIMEBrowserParent) { 262 OnFocusMovedBetweenBrowsers(sFocusedIMEBrowserParent, nullptr); 263 MOZ_ASSERT(!sFocusedIMEBrowserParent); 264 } 265 sFocusedIMEWidget = nullptr; 266 } 267 if (sActiveInputContextWidget == aWidget) { 268 sActiveInputContextWidget = nullptr; 269 } 270 } 271 272 // static 273 void IMEStateManager::WidgetOnQuit(nsIWidget* aWidget) { 274 if (sFocusedIMEWidget == aWidget) { 275 MOZ_LOG( 276 sISMLog, LogLevel::Debug, 277 ("WidgetOnQuit(aWidget=0x%p (available %s)), sFocusedIMEWidget=0x%p", 278 aWidget, TrueOrFalse(aWidget && !aWidget->Destroyed()), 279 sFocusedIMEWidget)); 280 // Notify IME of blur (which is done by IMEContentObserver::Destroy 281 // automatically) when the widget still has IME focus before forgetting the 282 // focused widget because the focused widget is required to clean up native 283 // IME handler with sending blur notification. Fortunately, the widget 284 // has not been destroyed yet here since some methods to sending blur 285 // notification won't work with destroyed widget. 286 IMEStateManager::DestroyIMEContentObserver(); 287 // Finally, clean up the widget and related objects for avoiding to leak. 288 IMEStateManager::WidgetDestroyed(aWidget); 289 } 290 } 291 292 // static 293 void IMEStateManager::StopIMEStateManagement() { 294 MOZ_ASSERT(XRE_IsContentProcess()); 295 MOZ_LOG(sISMLog, LogLevel::Info, ("StopIMEStateManagement()")); 296 297 // NOTE: Don't set input context from here since this has already lost 298 // the rights to change input context. 299 300 // The requestee of this method in the main process must destroy its 301 // active IMEContentObserver for making existing composition end and 302 // make it be possible to start new composition in new focused process. 303 // Therefore, we shouldn't notify the main process of any changes which 304 // occurred after here. 305 AutoRestore<bool> restoreStoppingIMEStateManagementState( 306 sCleaningUpForStoppingIMEStateManagement); 307 sCleaningUpForStoppingIMEStateManagement = true; 308 309 if (sTextCompositions && sFocusedPresContext) { 310 NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, sFocusedPresContext, nullptr); 311 } 312 sActiveInputContextWidget = nullptr; 313 sFocusedPresContext = nullptr; 314 sFocusedElement = nullptr; 315 sIsActive = false; 316 DestroyIMEContentObserver(); 317 } 318 319 // static 320 void IMEStateManager::MaybeStartOffsetUpdatedInChild(nsIWidget* aWidget, 321 uint32_t aStartOffset) { 322 if (NS_WARN_IF(!sTextCompositions)) { 323 MOZ_LOG(sISMLog, LogLevel::Warning, 324 ("MaybeStartOffsetUpdatedInChild(aWidget=0x%p, aStartOffset=%u), " 325 "called when there is no composition", 326 aWidget, aStartOffset)); 327 return; 328 } 329 330 TextComposition* const composition = GetTextCompositionFor(aWidget); 331 if (NS_WARN_IF(!composition)) { 332 MOZ_LOG(sISMLog, LogLevel::Warning, 333 ("MaybeStartOffsetUpdatedInChild(aWidget=0x%p, aStartOffset=%u), " 334 "called when there is no composition", 335 aWidget, aStartOffset)); 336 return; 337 } 338 339 if (composition->NativeOffsetOfStartComposition() == aStartOffset) { 340 return; 341 } 342 343 MOZ_LOG( 344 sISMLog, LogLevel::Info, 345 ("MaybeStartOffsetUpdatedInChild(aWidget=0x%p, aStartOffset=%u), " 346 "old offset=%u", 347 aWidget, aStartOffset, composition->NativeOffsetOfStartComposition())); 348 composition->OnStartOffsetUpdatedInChild(aStartOffset); 349 } 350 351 // static 352 nsresult IMEStateManager::OnDestroyPresContext(nsPresContext& aPresContext) { 353 // First, if there is a composition in the aPresContext, clean up it. 354 if (sTextCompositions) { 355 TextCompositionArray::index_type i = 356 sTextCompositions->IndexOf(&aPresContext); 357 if (i != TextCompositionArray::NoIndex) { 358 MOZ_LOG(sISMLog, LogLevel::Debug, 359 (" OnDestroyPresContext(), " 360 "removing TextComposition instance from the array (index=%zu)", 361 i)); 362 // there should be only one composition per presContext object. 363 sTextCompositions->ElementAt(i)->Destroy(); 364 sTextCompositions->RemoveElementAt(i); 365 if (sTextCompositions->IndexOf(&aPresContext) != 366 TextCompositionArray::NoIndex) { 367 MOZ_LOG(sISMLog, LogLevel::Error, 368 (" OnDestroyPresContext(), FAILED to remove " 369 "TextComposition instance from the array")); 370 MOZ_CRASH("Failed to remove TextComposition instance from the array"); 371 } 372 } 373 } 374 375 if (&aPresContext != sFocusedPresContext) { 376 return NS_OK; 377 } 378 379 MOZ_LOG( 380 sISMLog, LogLevel::Info, 381 ("OnDestroyPresContext(aPresContext=0x%p), " 382 "sFocusedPresContext=0x%p, sFocusedElement=0x%p, sTextCompositions=0x%p", 383 &aPresContext, sFocusedPresContext.get(), sFocusedElement.get(), 384 sTextCompositions)); 385 386 DestroyIMEContentObserver(); 387 388 if (sTextInputHandlingWidget) { 389 IMEState newState = GetNewIMEState(*sFocusedPresContext, nullptr); 390 InputContextAction action(InputContextAction::CAUSE_UNKNOWN, 391 InputContextAction::LOST_FOCUS); 392 InputContext::Origin origin = 393 BrowserParent::GetFocused() ? InputContext::ORIGIN_CONTENT : sOrigin; 394 OwningNonNull<nsIWidget> textInputHandlingWidget = 395 *sTextInputHandlingWidget; 396 SetIMEState(newState, nullptr, nullptr, textInputHandlingWidget, action, 397 origin); 398 } 399 sTextInputHandlingWidget = nullptr; 400 sFocusedElement = nullptr; 401 sFocusedPresContext = nullptr; 402 return NS_OK; 403 } 404 405 // static 406 nsresult IMEStateManager::OnRemoveContent(nsPresContext& aPresContext, 407 Element& aElement) { 408 // First, if there is a composition in the aElement, clean up it. 409 if (sTextCompositions) { 410 const RefPtr<TextComposition> compositionInContent = 411 sTextCompositions->GetCompositionInContent(&aPresContext, &aElement); 412 413 if (compositionInContent) { 414 MOZ_LOG(sISMLog, LogLevel::Debug, 415 (" OnRemoveContent(), composition is in the content")); 416 417 // Try resetting the native IME state. Be aware, typically, this method 418 // is called during the content being removed. Then, the native 419 // composition events which are caused by following APIs are ignored due 420 // to unsafe to run script (in PresShell::HandleEvent()). 421 nsresult rv = 422 compositionInContent->NotifyIME(REQUEST_TO_CANCEL_COMPOSITION); 423 if (NS_FAILED(rv)) { 424 compositionInContent->NotifyIME(REQUEST_TO_COMMIT_COMPOSITION); 425 } 426 } 427 } 428 429 if (!sFocusedPresContext || 430 // If focused element is a text control or an editing host, we need to 431 // emulate "blur" on it when it's removed. 432 (sFocusedElement && sFocusedElement != &aElement) || 433 // If it is (or was) in design mode, we need to emulate "blur" on the 434 // document when the observing element (typically, <body>) is removed. 435 (!sFocusedElement && 436 (!sActiveIMEContentObserver || 437 sActiveIMEContentObserver 438 ->GetObservingEditingHostOrTextControlElement() != 439 &aElement))) { 440 return NS_OK; 441 } 442 MOZ_ASSERT(sFocusedPresContext == &aPresContext); 443 444 MOZ_LOG( 445 sISMLog, LogLevel::Info, 446 ("OnRemoveContent(aPresContext=0x%p, aElement=0x%p), " 447 "sFocusedPresContext=0x%p, sFocusedElement=0x%p, sTextCompositions=0x%p", 448 &aPresContext, &aElement, sFocusedPresContext.get(), 449 sFocusedElement.get(), sTextCompositions)); 450 451 DestroyIMEContentObserver(); 452 453 // FYI: Don't clear sTextInputHandlingWidget and sFocusedPresContext because 454 // the window/document keeps having focus. 455 sFocusedElement = nullptr; 456 457 // Current IME transaction should commit 458 if (!sTextInputHandlingWidget) { 459 return NS_OK; 460 } 461 462 IMEState newState = GetNewIMEState(*sFocusedPresContext, nullptr); 463 InputContextAction action(InputContextAction::CAUSE_UNKNOWN, 464 InputContextAction::LOST_FOCUS); 465 InputContext::Origin origin = 466 BrowserParent::GetFocused() ? InputContext::ORIGIN_CONTENT : sOrigin; 467 OwningNonNull<nsIWidget> textInputHandlingWidget = *sTextInputHandlingWidget; 468 SetIMEState(newState, &aPresContext, nullptr, textInputHandlingWidget, action, 469 origin); 470 if (sFocusedPresContext != &aPresContext || sFocusedElement) { 471 return NS_OK; // Somebody already has focus, don't steal it. 472 } 473 474 if (IsIMEObserverNeeded(newState)) { 475 // Initializing IMEContentObserver instance requires Selection, but its 476 // ranges have not been adjusted for this removal. Therefore, we need to 477 // wait a moment. 478 nsContentUtils::AddScriptRunner(NS_NewRunnableFunction( 479 "IMEStateManager::RecreateIMEContentObserverWhenContentRemoved", 480 [presContext = OwningNonNull{aPresContext}]() { 481 MOZ_ASSERT(sFocusedPresContext == presContext); 482 MOZ_ASSERT(!sFocusedElement); 483 if (RefPtr<HTMLEditor> htmlEditor = 484 nsContentUtils::GetHTMLEditor(presContext)) { 485 CreateIMEContentObserver(*htmlEditor, nullptr); 486 } 487 })); 488 } 489 490 return NS_OK; 491 } 492 493 // static 494 void IMEStateManager::OnParentChainChangedOfObservingElement( 495 IMEContentObserver& aObserver, nsIContent& aContent) { 496 if (!sFocusedPresContext || sActiveIMEContentObserver != &aObserver) { 497 return; 498 } 499 if (Element* const textControlElement = 500 aObserver.GetObservingTextControlElement()) { 501 // If a text control has focus, the parent chain changed is notified when 502 // the anonymous <div> is removed since aObserver is observing it. However, 503 // we want to be notified only when the focused element, i.e., the text 504 // control itself, is removed. So, when the text control element is not an 505 // inclusive descendant of aContent, we don't need to handle this. 506 MOZ_ASSERT(textControlElement->IsTextControlElement()); 507 if (!textControlElement->IsInclusiveDescendantOf(&aContent)) { 508 return; 509 } 510 } 511 const RefPtr<nsPresContext> presContext = aObserver.GetPresContext(); 512 const RefPtr<Element> editingHostOrTextControlElement = 513 aObserver.GetObservingEditingHostOrTextControlElement(); 514 if (NS_WARN_IF(!presContext) || 515 NS_WARN_IF(!editingHostOrTextControlElement)) { 516 return; 517 } 518 MOZ_LOG(sISMLog, LogLevel::Info, 519 ("OnParentChainChangedOfObservingElement(aObserver=0x%p), " 520 "sFocusedPresContext=0x%p, sFocusedElement=0x%p, " 521 "aObserver->GetPresContext()=0x%p, " 522 "aObserver->GetObservingEditingHostOrTextControlElement()=0x%p", 523 &aObserver, sFocusedPresContext.get(), sFocusedElement.get(), 524 presContext.get(), editingHostOrTextControlElement.get())); 525 OnRemoveContent(*presContext, *editingHostOrTextControlElement); 526 } 527 528 // static 529 void IMEStateManager::OnUpdateHTMLEditorRootElement(HTMLEditor& aHTMLEditor, 530 Element* aNewRootElement) { 531 MOZ_LOG( 532 sISMLog, LogLevel::Info, 533 ("OnUpdateHTMLEditorRootElement(aHTMLEditor=0x%p, aNewRootElement=%s), " 534 "sFocusedPresContext=0x%p, sFocusedElement=%s, " 535 "sActiveIMEContentObserver=0x%p (GetObservingElement()=%s), " 536 "sTextInputHandlingWidget=0x%p, aHTMLEditor.GetPresContext()=0x%p", 537 &aHTMLEditor, 538 aNewRootElement ? ToString(*aNewRootElement).c_str() : "nullptr", 539 sFocusedPresContext.get(), 540 ToString(RefPtr<Element>(sFocusedElement)).c_str(), 541 sActiveIMEContentObserver.get(), 542 sActiveIMEContentObserver 543 ? ToString(RefPtr<Element>( 544 sActiveIMEContentObserver->GetObservingElement())) 545 .c_str() 546 : "N/A", 547 sTextInputHandlingWidget, aHTMLEditor.GetPresContext())); 548 549 if ( 550 // Nothing to do if nobody has focus. 551 !sFocusedPresContext || !sTextInputHandlingWidget || 552 // Nothing to do if an element has focus because we need to handle this 553 // case only when no element has focus in the design mode. 554 sFocusedElement || 555 // Nothing to do if the editable document does not have focus. 556 sFocusedPresContext != aHTMLEditor.GetPresContext() || 557 // If it's not in the design mode, any mutation should be handled with a 558 // focus change. 559 !aHTMLEditor.IsInDesignMode() || 560 // Nothing to do if the active IMEContentObserver has already been 561 // observing the new root element. 562 (aNewRootElement && sActiveIMEContentObserver && 563 sActiveIMEContentObserver->GetObservingElement() == aNewRootElement)) { 564 return; 565 } 566 567 OwningNonNull<nsPresContext> presContext = *sFocusedPresContext; 568 569 DestroyIMEContentObserver(); 570 571 if (!aNewRootElement) { 572 // When there is no element in the document, let's disable IME. 573 IMEState newState = GetNewIMEState(*presContext, nullptr); 574 MOZ_ASSERT(newState.mEnabled == IMEEnabled::Disabled); 575 InputContextAction action(InputContextAction::CAUSE_UNKNOWN, 576 InputContextAction::LOST_FOCUS); 577 InputContext::Origin origin = 578 BrowserParent::GetFocused() ? InputContext::ORIGIN_CONTENT : sOrigin; 579 OwningNonNull<nsIWidget> textInputHandlingWidget = 580 *sTextInputHandlingWidget; 581 SetIMEState(newState, presContext, nullptr, textInputHandlingWidget, action, 582 origin); 583 return; 584 } 585 586 MOZ_ASSERT(aNewRootElement); 587 const IMEState newState = GetNewIMEState(*presContext, nullptr); 588 // The caller wants to enable IME if there is at least one element in the most 589 // cases. However, IME may be disabled, e.g., the menubar key listener is now 590 // installed, etc. Therefore, if the new state is not "enabled", we should 591 // not update the state in unexpected situations. 592 if (MOZ_UNLIKELY(newState.mEnabled != IMEEnabled::Enabled)) { 593 MOZ_LOG(sISMLog, LogLevel::Warning, 594 (" OnUpdateHTMLEditorRootElement(): WARNING, Not updating IME " 595 "state because of the new IME state is not \"enabled\"")); 596 return; 597 } 598 InputContextAction action(InputContextAction::CAUSE_UNKNOWN, 599 InputContextAction::GOT_FOCUS); 600 InputContext::Origin origin = 601 BrowserParent::GetFocused() ? InputContext::ORIGIN_CONTENT : sOrigin; 602 OwningNonNull<nsIWidget> textInputHandlingWidget = *sTextInputHandlingWidget; 603 SetIMEState(newState, presContext, nullptr, textInputHandlingWidget, action, 604 origin); 605 // Somebody moved focus, don't keep handling this since we lost the rights 606 // to touch IME state. 607 if (sFocusedElement || sActiveIMEContentObserver) { 608 MOZ_LOG(sISMLog, LogLevel::Warning, 609 ("OnUpdateHTMLEditorRootElement(), WARNING: Somebody update focus " 610 "during setting IME state, sFocusedElement=%s, " 611 "sActiveIMEContentObserver=0x%p", 612 ToString(RefPtr<Element>(sFocusedElement)).c_str(), 613 sActiveIMEContentObserver.get())); 614 return; 615 } 616 617 if (IsIMEObserverNeeded(newState)) { 618 MOZ_ASSERT(sFocusedPresContext == presContext); 619 MOZ_ASSERT(!sFocusedElement); 620 CreateIMEContentObserver(aHTMLEditor, nullptr); 621 } 622 } 623 624 // static 625 bool IMEStateManager::CanHandleWith(const nsPresContext* aPresContext) { 626 return aPresContext && aPresContext->GetPresShell() && 627 !aPresContext->PresShell()->IsDestroying(); 628 } 629 630 // static 631 nsresult IMEStateManager::OnChangeFocus(nsPresContext* aPresContext, 632 Element* aElement, 633 InputContextAction::Cause aCause) { 634 MOZ_LOG(sISMLog, LogLevel::Info, 635 ("OnChangeFocus(aPresContext=0x%p, aElement=0x%p, aCause=%s)", 636 aPresContext, aElement, ToString(aCause).c_str())); 637 638 InputContextAction action(aCause); 639 return OnChangeFocusInternal(aPresContext, aElement, action); 640 } 641 642 // static 643 nsresult IMEStateManager::OnChangeFocusInternal(nsPresContext* aPresContext, 644 Element* aElement, 645 InputContextAction aAction) { 646 NS_ASSERTION(!aElement || aElement->GetPresContext( 647 Element::PresContextFor::eForComposedDoc) == 648 aPresContext, 649 "aPresContext does not match with one of aElement"); 650 651 bool remoteHasFocus = EventStateManager::IsRemoteTarget(aElement); 652 // If we've handled focused content, we were inactive but now active, 653 // a remote process has focus, and setting focus to same content in the main 654 // process, it means that we're restoring focus without changing DOM focus 655 // both in the main process and the remote process. 656 const bool restoringContextForRemoteContent = 657 XRE_IsParentProcess() && remoteHasFocus && !sIsActive && aPresContext && 658 sFocusedPresContext && sFocusedElement && 659 sFocusedPresContext.get() == aPresContext && 660 sFocusedElement.get() == aElement && 661 aAction.mFocusChange != InputContextAction::MENU_GOT_PSEUDO_FOCUS; 662 663 MOZ_LOG( 664 sISMLog, LogLevel::Info, 665 ("OnChangeFocusInternal(aPresContext=0x%p (available: %s), " 666 "aElement=0x%p (remote: %s), aAction={ mCause=%s, " 667 "mFocusChange=%s }), sFocusedPresContext=0x%p (available: %s), " 668 "sFocusedElement=0x%p, sTextInputHandlingWidget=0x%p (available: %s), " 669 "BrowserParent::GetFocused()=0x%p, sActiveIMEContentObserver=0x%p, " 670 "sInstalledMenuKeyboardListener=%s, sIsActive=%s, " 671 "restoringContextForRemoteContent=%s", 672 aPresContext, TrueOrFalse(CanHandleWith(aPresContext)), aElement, 673 TrueOrFalse(remoteHasFocus), ToString(aAction.mCause).c_str(), 674 ToString(aAction.mFocusChange).c_str(), sFocusedPresContext.get(), 675 TrueOrFalse(CanHandleWith(sFocusedPresContext)), sFocusedElement.get(), 676 sTextInputHandlingWidget, 677 TrueOrFalse(sTextInputHandlingWidget && 678 !sTextInputHandlingWidget->Destroyed()), 679 BrowserParent::GetFocused(), sActiveIMEContentObserver.get(), 680 TrueOrFalse(sInstalledMenuKeyboardListener), TrueOrFalse(sIsActive), 681 TrueOrFalse(restoringContextForRemoteContent))); 682 if (aElement) { 683 MOZ_LOG(sISMLog, LogLevel::Debug, 684 (" aElement: %s", ToString(*aElement).c_str())); 685 } 686 if (sFocusedElement) { 687 MOZ_LOG(sISMLog, LogLevel::Debug, 688 (" sFocusedElement: %s", ToString(*sFocusedElement).c_str())); 689 } 690 691 sIsActive = !!aPresContext; 692 if (sPendingFocusedBrowserSwitchingData.isSome()) { 693 MOZ_ASSERT(XRE_IsParentProcess()); 694 RefPtr<Element> focusedElement = sFocusedElement; 695 RefPtr<nsPresContext> focusedPresContext = sFocusedPresContext; 696 RefPtr<BrowserParent> browserParentBlurred = 697 sPendingFocusedBrowserSwitchingData.ref().mBrowserParentBlurred; 698 RefPtr<BrowserParent> browserParentFocused = 699 sPendingFocusedBrowserSwitchingData.ref().mBrowserParentFocused; 700 OnFocusMovedBetweenBrowsers(browserParentBlurred, browserParentFocused); 701 // If another call of this method happens during the 702 // OnFocusMovedBetweenBrowsers call, we shouldn't take back focus to 703 // the old one. 704 if (focusedElement != sFocusedElement.get() || 705 focusedPresContext != sFocusedPresContext.get()) { 706 MOZ_LOG(sISMLog, LogLevel::Debug, 707 (" OnChangeFocusInternal(aPresContext=0x%p, aElement=0x%p) " 708 "stoped handling it because the focused content was changed to " 709 "sFocusedPresContext=0x%p, sFocusedElement=0x%p by another call", 710 aPresContext, aElement, sFocusedPresContext.get(), 711 sFocusedElement.get())); 712 return NS_OK; 713 } 714 } 715 716 // If new aPresShell has been destroyed, this should handle the focus change 717 // as nobody is getting focus. 718 MOZ_ASSERT_IF(!aPresContext, !aElement); 719 if (NS_WARN_IF(aPresContext && !CanHandleWith(aPresContext))) { 720 MOZ_LOG(sISMLog, LogLevel::Warning, 721 (" OnChangeFocusInternal(), called with destroyed PresShell, " 722 "handling this call as nobody getting focus")); 723 aPresContext = nullptr; 724 aElement = nullptr; 725 } else if (!aPresContext) { 726 aElement = nullptr; 727 } 728 729 const nsCOMPtr<nsIWidget> oldWidget = sTextInputHandlingWidget; 730 const nsCOMPtr<nsIWidget> newWidget = 731 aPresContext ? aPresContext->GetTextInputHandlingWidget() : nullptr; 732 const bool focusActuallyChanging = 733 (sFocusedElement != aElement || sFocusedPresContext != aPresContext || 734 oldWidget != newWidget || 735 (remoteHasFocus && !restoringContextForRemoteContent && 736 (aAction.mFocusChange != InputContextAction::MENU_GOT_PSEUDO_FOCUS))); 737 738 // If old widget has composition, we may need to commit composition since 739 // a native IME context is shared on all editors on some widgets or all 740 // widgets (it depends on platforms). 741 if (oldWidget && focusActuallyChanging && sTextCompositions) { 742 RefPtr<TextComposition> composition = 743 sTextCompositions->GetCompositionFor(oldWidget); 744 if (composition) { 745 // However, don't commit the composition if we're being inactivated 746 // but the composition should be kept even during deactive. 747 // Note that oldWidget and sFocusedIMEWidget may be different here (in 748 // such case, sFocusedIMEWidget is perhaps nullptr). For example, IME 749 // may receive only blur notification but still has composition. 750 // We need to clean up only the oldWidget's composition state here. 751 if (aPresContext || 752 !oldWidget->IMENotificationRequestsRef().WantDuringDeactive()) { 753 MOZ_LOG( 754 sISMLog, LogLevel::Info, 755 (" OnChangeFocusInternal(), requesting to commit composition to " 756 "the (previous) focused widget")); 757 NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, oldWidget, 758 composition->GetBrowserParent()); 759 } 760 } 761 } 762 763 if (sActiveIMEContentObserver) { 764 MOZ_ASSERT(!remoteHasFocus || XRE_IsContentProcess(), 765 "IMEContentObserver should have been destroyed by " 766 "OnFocusMovedBetweenBrowsers."); 767 if (!aPresContext) { 768 if (!sActiveIMEContentObserver->KeepAliveDuringDeactive()) { 769 DestroyIMEContentObserver(); 770 } 771 } 772 // Otherwise, i.e., new focused content is in this process, let's check 773 // whether the editable content for aElement has already been being observed 774 // by the active IMEContentObserver. If not, let's stop observing the 775 // editable content which is being blurred. 776 else if (!sActiveIMEContentObserver->IsObserving(*aPresContext, aElement)) { 777 DestroyIMEContentObserver(); 778 } 779 } 780 781 if (!aPresContext) { 782 MOZ_LOG(sISMLog, LogLevel::Debug, 783 (" OnChangeFocusInternal(), no nsPresContext is being activated")); 784 return NS_OK; 785 } 786 787 if (NS_WARN_IF(!newWidget)) { 788 MOZ_LOG(sISMLog, LogLevel::Error, 789 (" OnChangeFocusInternal(), FAILED due to no widget to manage its " 790 "IME state")); 791 return NS_OK; 792 } 793 794 // Update the cached widget since root view of the presContext may be 795 // changed to different view. 796 sTextInputHandlingWidget = newWidget; 797 798 // If a child process has focus, we should disable IME state until the child 799 // process actually gets focus because if user types keys before that they 800 // are handled by IME. 801 IMEState newState = remoteHasFocus ? IMEState(IMEEnabled::Disabled) 802 : GetNewIMEState(*aPresContext, aElement); 803 bool setIMEState = true; 804 805 const auto CanSkipSettingContext = [&](const InputContext& aOldContext) { 806 const auto IsChangingBrowsingMode = [&]() { 807 const bool willBeInPrivateBrowsingMode = 808 aPresContext && aPresContext->Document() && 809 aPresContext->Document()->IsInPrivateBrowsing(); 810 return willBeInPrivateBrowsingMode != aOldContext.mInPrivateBrowsing; 811 }; 812 const auto IsChangingURI = [&]() { 813 const nsCOMPtr<nsIURI> newURI = 814 IMEStateManager::GetExposableURL(aPresContext); 815 if (!newURI != !aOldContext.mURI) { 816 return true; // One is not exposable URI. 817 } 818 if (!newURI) { 819 MOZ_ASSERT(!aOldContext.mURI); 820 return false; // Moved in non-exposable URIs. 821 } 822 bool same = false; 823 return NS_FAILED(newURI->Equals(aOldContext.mURI, &same)) || !same; 824 }; 825 return !IsChangingBrowsingMode() && !IsChangingURI(); 826 }; 827 828 if (remoteHasFocus && XRE_IsParentProcess()) { 829 if (aAction.mFocusChange == InputContextAction::MENU_GOT_PSEUDO_FOCUS) { 830 // If menu keyboard listener is installed, we need to disable IME now. 831 setIMEState = true; 832 } else if (aAction.mFocusChange == 833 InputContextAction::MENU_LOST_PSEUDO_FOCUS) { 834 // If menu keyboard listener is uninstalled, we need to restore 835 // input context which was set by the remote process. However, if 836 // the remote process hasn't been set input context yet, we need to 837 // wait next SetInputContextForChildProcess() call. 838 if (HasActiveChildSetInputContext()) { 839 setIMEState = true; 840 newState = sActiveChildInputContext.mIMEState; 841 } else { 842 setIMEState = false; 843 } 844 } else if (focusActuallyChanging) { 845 InputContext context = newWidget->GetInputContext(); 846 if (context.mIMEState.mEnabled == IMEEnabled::Disabled && 847 context.mOrigin == InputContext::ORIGIN_CONTENT && 848 CanSkipSettingContext(context)) { 849 setIMEState = false; 850 MOZ_LOG(sISMLog, LogLevel::Debug, 851 (" OnChangeFocusInternal(), doesn't set IME state because " 852 "focused element (or document) is in a child process and the " 853 "IME state is already disabled by a remote process")); 854 } else { 855 // When new remote process gets focus, we should forget input context 856 // coming from old focused remote process. 857 ResetActiveChildInputContext(); 858 MOZ_LOG(sISMLog, LogLevel::Debug, 859 (" OnChangeFocusInternal(), will disable IME until new " 860 "focused element (or document) in the child process will get " 861 "focus actually")); 862 } 863 } else if (newWidget->GetInputContext().mOrigin != 864 InputContext::ORIGIN_MAIN && 865 CanSkipSettingContext(newWidget->GetInputContext())) { 866 // When focus is NOT changed actually, we shouldn't set IME state if 867 // current input context was set by a remote process since that means 868 // that the window is being activated and the child process may have 869 // composition. Then, we shouldn't commit the composition with making 870 // IME state disabled. 871 setIMEState = false; 872 MOZ_LOG( 873 sISMLog, LogLevel::Debug, 874 (" OnChangeFocusInternal(), doesn't set IME state because focused " 875 "element (or document) is already in the child process")); 876 } 877 } else { 878 // When this process gets focus, we should forget input context coming 879 // from remote process. 880 ResetActiveChildInputContext(); 881 } 882 883 if (setIMEState) { 884 if (!focusActuallyChanging) { 885 // actual focus isn't changing, but if IME enabled state is changing, 886 // we should do it. 887 InputContext context = newWidget->GetInputContext(); 888 if (context.mIMEState.mEnabled == newState.mEnabled && 889 CanSkipSettingContext(context)) { 890 MOZ_LOG(sISMLog, LogLevel::Debug, 891 (" OnChangeFocusInternal(), neither focus nor IME state is " 892 "changing")); 893 return NS_OK; 894 } 895 aAction.mFocusChange = InputContextAction::FOCUS_NOT_CHANGED; 896 897 // Even if focus isn't changing actually, we should commit current 898 // composition here since the IME state is changing. 899 if (sFocusedPresContext && oldWidget) { 900 NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, oldWidget, 901 sFocusedIMEBrowserParent); 902 } 903 } else if (aAction.mFocusChange == InputContextAction::FOCUS_NOT_CHANGED) { 904 // If aElement isn't null or aElement is null but editable, somebody gets 905 // focus. 906 bool gotFocus = aElement || (newState.mEnabled == IMEEnabled::Enabled); 907 aAction.mFocusChange = gotFocus ? InputContextAction::GOT_FOCUS 908 : InputContextAction::LOST_FOCUS; 909 } 910 911 if (remoteHasFocus && HasActiveChildSetInputContext() && 912 aAction.mFocusChange == InputContextAction::MENU_LOST_PSEUDO_FOCUS) { 913 // Restore the input context in the active remote process when 914 // menu keyboard listener is uninstalled and active remote tab has 915 // focus. 916 SetInputContext(*newWidget, sActiveChildInputContext, aAction); 917 } else { 918 // Update IME state for new focus widget 919 SetIMEState(newState, aPresContext, aElement, *newWidget, aAction, 920 remoteHasFocus ? InputContext::ORIGIN_CONTENT : sOrigin); 921 } 922 } 923 924 sFocusedPresContext = aPresContext; 925 sFocusedElement = aElement; 926 927 // Don't call CreateIMEContentObserver() here because it will be called from 928 // the focus event handler of focused editor. 929 930 MOZ_LOG(sISMLog, LogLevel::Debug, 931 (" OnChangeFocusInternal(), modified IME state for " 932 "sFocusedPresContext=0x%p, sFocusedElement=0x%p", 933 sFocusedPresContext.get(), sFocusedElement.get())); 934 935 return NS_OK; 936 } 937 938 // static 939 void IMEStateManager::OnInstalledMenuKeyboardListener(bool aInstalling) { 940 MOZ_LOG( 941 sISMLog, LogLevel::Info, 942 ("OnInstalledMenuKeyboardListener(aInstalling=%s), " 943 "nsContentUtils::IsSafeToRunScript()=%s, " 944 "sInstalledMenuKeyboardListener=%s, BrowserParent::GetFocused()=0x%p, " 945 "sActiveChildInputContext=%s, sFocusedPresContext=0x%p, " 946 "sFocusedElement=0x%p, sPseudoFocusChangeRunnable=0x%p", 947 TrueOrFalse(aInstalling), 948 TrueOrFalse(nsContentUtils::IsSafeToRunScript()), 949 TrueOrFalse(sInstalledMenuKeyboardListener), BrowserParent::GetFocused(), 950 ToString(sActiveChildInputContext).c_str(), sFocusedPresContext.get(), 951 sFocusedElement.get(), sPseudoFocusChangeRunnable.get())); 952 953 // Update the state whether the menubar has pseudo focus or not immediately. 954 // This will be referred by the runner which is created below. 955 sInstalledMenuKeyboardListener = aInstalling; 956 // However, this may be called when it's not safe to run script. Therefore, 957 // we need to create a runnable to notify IME of the pseudo focus change. 958 if (sPseudoFocusChangeRunnable) { 959 return; 960 } 961 sPseudoFocusChangeRunnable = new PseudoFocusChangeRunnable(aInstalling); 962 nsContentUtils::AddScriptRunner(sPseudoFocusChangeRunnable); 963 } 964 965 // static 966 void IMEStateManager::SetMenubarPseudoFocus( 967 PseudoFocusChangeRunnable* aCaller, bool aSetPseudoFocus, 968 nsPresContext* aFocusedPresContextAtRequested) { 969 MOZ_LOG( 970 sISMLog, LogLevel::Info, 971 ("SetMenubarPseudoFocus(aCaller=0x%p, aSetPseudoFocus=%s, " 972 "aFocusedPresContextAtRequested=0x%p), " 973 "sInstalledMenuKeyboardListener=%s, sFocusedPresContext=0x%p, " 974 "sFocusedElement=0x%p, sPseudoFocusChangeRunnable=0x%p", 975 aCaller, TrueOrFalse(aSetPseudoFocus), aFocusedPresContextAtRequested, 976 TrueOrFalse(sInstalledMenuKeyboardListener), sFocusedPresContext.get(), 977 sFocusedElement.get(), sPseudoFocusChangeRunnable.get())); 978 979 MOZ_ASSERT(sPseudoFocusChangeRunnable.get() == aCaller); 980 981 // Clear the runnable first for nested call of 982 // OnInstalledMenuKeyboardListener(). 983 RefPtr<PseudoFocusChangeRunnable> runningOne = 984 sPseudoFocusChangeRunnable.forget(); 985 MOZ_ASSERT(!sPseudoFocusChangeRunnable); 986 987 // If the requested state is still same, let's make the menubar get 988 // pseudo focus with the latest focused PresContext and Element. 989 // Note that this is no problem even after the focused element is changed 990 // after aCaller is created because only sInstalledMenuKeyboardListener 991 // manages the state and current focused PresContext and element should be 992 // used only for restoring the focus from the menubar. So, restoring 993 // focus with the lasted one does make sense. 994 if (sInstalledMenuKeyboardListener == aSetPseudoFocus) { 995 InputContextAction action(InputContextAction::CAUSE_UNKNOWN, 996 aSetPseudoFocus 997 ? InputContextAction::MENU_GOT_PSEUDO_FOCUS 998 : InputContextAction::MENU_LOST_PSEUDO_FOCUS); 999 RefPtr<nsPresContext> focusedPresContext = sFocusedPresContext; 1000 RefPtr<Element> focusedElement = sFocusedElement; 1001 OnChangeFocusInternal(focusedPresContext, focusedElement, action); 1002 return; 1003 } 1004 1005 // If the requested state is different from current state, we don't need to 1006 // make the menubar get and lose pseudo focus because of redundant. However, 1007 // if there is a composition on the original focused element, we need to 1008 // commit it because pseudo focus move should've caused it. 1009 if (!aFocusedPresContextAtRequested) { 1010 return; 1011 } 1012 RefPtr<TextComposition> composition = 1013 GetTextCompositionFor(aFocusedPresContextAtRequested); 1014 if (!composition) { 1015 return; 1016 } 1017 if (nsCOMPtr<nsIWidget> widget = 1018 aFocusedPresContextAtRequested->GetTextInputHandlingWidget()) { 1019 composition->RequestToCommit(widget, false); 1020 } 1021 } 1022 1023 // static 1024 bool IMEStateManager::OnMouseButtonEventInEditor( 1025 nsPresContext& aPresContext, Element* aElement, 1026 WidgetMouseEvent& aMouseEvent) { 1027 MOZ_LOG(sISMLog, LogLevel::Info, 1028 ("OnMouseButtonEventInEditor(aPresContext=0x%p (available: %s), " 1029 "aElement=0x%p, aMouseEvent=0x%p), sFocusedPresContext=0x%p, " 1030 "sFocusedElement=0x%p", 1031 &aPresContext, TrueOrFalse(CanHandleWith(&aPresContext)), aElement, 1032 &aMouseEvent, sFocusedPresContext.get(), sFocusedElement.get())); 1033 1034 if (sFocusedPresContext != &aPresContext || sFocusedElement != aElement) { 1035 MOZ_LOG(sISMLog, LogLevel::Debug, 1036 (" OnMouseButtonEventInEditor(), " 1037 "the mouse event isn't fired on the editor managed by ISM")); 1038 return false; 1039 } 1040 1041 if (!sActiveIMEContentObserver) { 1042 MOZ_LOG(sISMLog, LogLevel::Debug, 1043 (" OnMouseButtonEventInEditor(), " 1044 "there is no active IMEContentObserver")); 1045 return false; 1046 } 1047 1048 if (!sActiveIMEContentObserver->IsObserving(aPresContext, aElement)) { 1049 MOZ_LOG(sISMLog, LogLevel::Debug, 1050 (" OnMouseButtonEventInEditor(), " 1051 "the active IMEContentObserver isn't managing the editor")); 1052 return false; 1053 } 1054 1055 OwningNonNull<IMEContentObserver> observer = *sActiveIMEContentObserver; 1056 bool consumed = observer->OnMouseButtonEvent(aPresContext, aMouseEvent); 1057 MOZ_LOG(sISMLog, LogLevel::Info, 1058 (" OnMouseButtonEventInEditor(), " 1059 "mouse event (mMessage=%s, mButton=%d) is %s", 1060 ToChar(aMouseEvent.mMessage), aMouseEvent.mButton, 1061 consumed ? "consumed" : "not consumed")); 1062 return consumed; 1063 } 1064 1065 // static 1066 void IMEStateManager::OnClickInEditor(nsPresContext& aPresContext, 1067 Element* aElement, 1068 const WidgetMouseEvent& aMouseEvent) { 1069 MOZ_LOG(sISMLog, LogLevel::Info, 1070 ("OnClickInEditor(aPresContext=0x%p (available: %s), aElement=0x%p, " 1071 "aMouseEvent=0x%p), sFocusedPresContext=0x%p, sFocusedElement=0x%p, " 1072 "sTextInputHandlingWidget=0x%p (available: %s)", 1073 &aPresContext, TrueOrFalse(CanHandleWith(&aPresContext)), aElement, 1074 &aMouseEvent, sFocusedPresContext.get(), sFocusedElement.get(), 1075 sTextInputHandlingWidget, 1076 TrueOrFalse(sTextInputHandlingWidget && 1077 !sTextInputHandlingWidget->Destroyed()))); 1078 1079 if (sFocusedPresContext != &aPresContext || sFocusedElement != aElement || 1080 NS_WARN_IF(!sFocusedPresContext) || 1081 NS_WARN_IF(!sTextInputHandlingWidget) || 1082 NS_WARN_IF(sTextInputHandlingWidget->Destroyed())) { 1083 MOZ_LOG(sISMLog, LogLevel::Debug, 1084 (" OnClickInEditor(), " 1085 "the mouse event isn't fired on the editor managed by ISM")); 1086 return; 1087 } 1088 1089 const OwningNonNull<nsIWidget> textInputHandlingWidget = 1090 *sTextInputHandlingWidget; 1091 MOZ_ASSERT_IF(sFocusedPresContext->GetTextInputHandlingWidget(), 1092 sFocusedPresContext->GetTextInputHandlingWidget() == 1093 textInputHandlingWidget.get()); 1094 1095 if (!aMouseEvent.IsTrusted()) { 1096 MOZ_LOG(sISMLog, LogLevel::Debug, 1097 (" OnClickInEditor(), " 1098 "the mouse event isn't a trusted event")); 1099 return; // ignore untrusted event. 1100 } 1101 1102 if (aMouseEvent.mButton) { 1103 MOZ_LOG(sISMLog, LogLevel::Debug, 1104 (" OnClickInEditor(), " 1105 "the mouse event isn't a left mouse button event")); 1106 return; // not a left click event. 1107 } 1108 1109 if (aMouseEvent.mClickCount != 1) { 1110 MOZ_LOG(sISMLog, LogLevel::Debug, 1111 (" OnClickInEditor(), " 1112 "the mouse event isn't a single click event")); 1113 return; // should notify only first click event. 1114 } 1115 1116 MOZ_ASSERT_IF(aElement, !EventStateManager::IsRemoteTarget(aElement)); 1117 InputContextAction::Cause cause = 1118 aMouseEvent.mInputSource == MouseEvent_Binding::MOZ_SOURCE_TOUCH 1119 ? InputContextAction::CAUSE_TOUCH 1120 : InputContextAction::CAUSE_MOUSE; 1121 1122 InputContextAction action(cause, InputContextAction::FOCUS_NOT_CHANGED); 1123 IMEState newState = GetNewIMEState(aPresContext, aElement); 1124 // If the state is not editable, there should be no active IMEContentObserver. 1125 // However, if this click sets focus to the editor, IMEContentObserver may 1126 // have not been created yet. Instead, if there is active IMEContentObserver, 1127 // it should be editable. 1128 MOZ_ASSERT_IF(!newState.IsEditable(), !sActiveIMEContentObserver); 1129 MOZ_ASSERT_IF(sActiveIMEContentObserver, newState.IsEditable()); 1130 SetIMEState(newState, &aPresContext, aElement, textInputHandlingWidget, 1131 action, sOrigin); 1132 } 1133 1134 // static 1135 Element* IMEStateManager::GetFocusedElement() { return sFocusedElement; } 1136 1137 // static 1138 bool IMEStateManager::IsFocusedElement(const nsPresContext& aPresContext, 1139 const Element* aFocusedElement) { 1140 if (!sFocusedPresContext || &aPresContext != sFocusedPresContext) { 1141 return false; 1142 } 1143 1144 if (sFocusedElement == aFocusedElement) { 1145 return true; 1146 } 1147 1148 // If sFocusedElement is not nullptr, but aFocusedElement is nullptr, it does 1149 // not have focus from point of view of IMEStateManager. 1150 if (sFocusedElement) { 1151 return false; 1152 } 1153 1154 // If the caller does not think that nobody has focus, but we know there is 1155 // a focused content, the caller must be called with wrong content. 1156 if (!aFocusedElement) { 1157 return false; 1158 } 1159 1160 // If the aFocusedElement is in design mode, sFocusedElement may be nullptr. 1161 if (aFocusedElement->IsInDesignMode()) { 1162 MOZ_ASSERT(&aPresContext == sFocusedPresContext && !sFocusedElement); 1163 return true; 1164 } 1165 1166 // Otherwise, only when aFocusedElement is the root element, it can have 1167 // focus, but IMEStateManager::OnChangeFocus is called with nullptr for 1168 // aFocusedElement if it was not editable. 1169 // XXX In this case, should the caller update sFocusedElement? 1170 return aFocusedElement->IsEditable() && sFocusedPresContext->Document() && 1171 sFocusedPresContext->Document()->GetRootElement() == aFocusedElement; 1172 } 1173 1174 // static 1175 void IMEStateManager::OnFocusInEditor(nsPresContext& aPresContext, 1176 Element* aElement, 1177 EditorBase& aEditorBase) { 1178 MOZ_LOG(sISMLog, LogLevel::Info, 1179 ("OnFocusInEditor(aPresContext=0x%p (available: %s), aElement=0x%p, " 1180 "aEditorBase=0x%p), sFocusedPresContext=0x%p, sFocusedElement=0x%p, " 1181 "sActiveIMEContentObserver=0x%p", 1182 &aPresContext, TrueOrFalse(CanHandleWith(&aPresContext)), aElement, 1183 &aEditorBase, sFocusedPresContext.get(), sFocusedElement.get(), 1184 sActiveIMEContentObserver.get())); 1185 if (aElement) { 1186 MOZ_LOG(sISMLog, LogLevel::Debug, 1187 (" aElement: %s", ToString(*aElement).c_str())); 1188 } 1189 if (sFocusedElement) { 1190 MOZ_LOG(sISMLog, LogLevel::Debug, 1191 (" sFocusedElement: %s", ToString(*sFocusedElement).c_str())); 1192 } 1193 1194 if (!IsFocusedElement(aPresContext, aElement)) { 1195 MOZ_LOG(sISMLog, LogLevel::Debug, 1196 (" OnFocusInEditor(), " 1197 "an editor not managed by ISM gets focus")); 1198 return; 1199 } 1200 MOZ_ASSERT(sTextInputHandlingWidget); 1201 1202 // If the IMEContentObserver instance isn't observing the editable content for 1203 // aElement, we need to recreate the instance because the active observer is 1204 // observing different content even if aElement keeps being focused. E.g., 1205 // it's an <input> and editing host but was not a text control, but now, it's 1206 // a text control. 1207 if (sActiveIMEContentObserver) { 1208 if (sActiveIMEContentObserver->IsObserving(aPresContext, aElement)) { 1209 MOZ_LOG(sISMLog, LogLevel::Debug, 1210 (" OnFocusInEditor(), " 1211 "the editable content for aEditorBase has already been being " 1212 "observed by sActiveIMEContentObserver")); 1213 return; 1214 } 1215 // If the IMEContentObserver has not finished initializing itself yet, 1216 // we don't need to recreate it because the following 1217 // TryToFlushPendingNotifications call must make it initialized. 1218 const nsCOMPtr<nsIWidget> textInputHandlingWidget = 1219 sTextInputHandlingWidget; 1220 if (!sActiveIMEContentObserver->IsBeingInitializedFor( 1221 aPresContext, aElement, aEditorBase)) { 1222 DestroyIMEContentObserver(); 1223 } 1224 if (NS_WARN_IF(!IsFocusedElement(aPresContext, aElement)) || 1225 NS_WARN_IF(!sTextInputHandlingWidget) || 1226 NS_WARN_IF(sTextInputHandlingWidget != textInputHandlingWidget)) { 1227 MOZ_LOG(sISMLog, LogLevel::Error, 1228 (" OnFocusInEditor(), detected unexpected focus change with " 1229 "re-initializing active IMEContentObserver")); 1230 return; 1231 } 1232 } 1233 1234 if (!sActiveIMEContentObserver && sTextInputHandlingWidget && 1235 IsIMEObserverNeeded( 1236 sTextInputHandlingWidget->GetInputContext().mIMEState)) { 1237 CreateIMEContentObserver(aEditorBase, aElement); 1238 if (sActiveIMEContentObserver) { 1239 MOZ_LOG(sISMLog, LogLevel::Debug, 1240 (" OnFocusInEditor(), new IMEContentObserver is created (0x%p)", 1241 sActiveIMEContentObserver.get())); 1242 } 1243 } 1244 1245 if (sActiveIMEContentObserver) { 1246 sActiveIMEContentObserver->TryToFlushPendingNotifications(false); 1247 MOZ_LOG(sISMLog, LogLevel::Debug, 1248 (" OnFocusInEditor(), trying to send pending notifications in " 1249 "the active IMEContentObserver (0x%p)...", 1250 sActiveIMEContentObserver.get())); 1251 } 1252 } 1253 1254 // static 1255 void IMEStateManager::OnEditorInitialized(EditorBase& aEditorBase) { 1256 if (!sActiveIMEContentObserver || 1257 !sActiveIMEContentObserver->WasInitializedWith(aEditorBase)) { 1258 return; 1259 } 1260 1261 MOZ_LOG(sISMLog, LogLevel::Info, 1262 ("OnEditorInitialized(aEditorBase=0x%p)", &aEditorBase)); 1263 1264 sActiveIMEContentObserver->UnsuppressNotifyingIME(); 1265 } 1266 1267 // static 1268 void IMEStateManager::OnEditorDestroying(EditorBase& aEditorBase) { 1269 if (!sActiveIMEContentObserver || 1270 !sActiveIMEContentObserver->WasInitializedWith(aEditorBase)) { 1271 return; 1272 } 1273 1274 MOZ_LOG(sISMLog, LogLevel::Info, 1275 ("OnEditorDestroying(aEditorBase=0x%p)", &aEditorBase)); 1276 1277 // The IMEContentObserver shouldn't notify IME of anything until reframing 1278 // is finished. 1279 sActiveIMEContentObserver->SuppressNotifyingIME(); 1280 } 1281 1282 void IMEStateManager::OnReFocus(nsPresContext& aPresContext, 1283 Element& aElement) { 1284 MOZ_LOG(sISMLog, LogLevel::Info, 1285 ("OnReFocus(aPresContext=0x%p (available: %s), aElement=0x%p), " 1286 "sActiveIMEContentObserver=0x%p, sFocusedElement=0x%p", 1287 &aPresContext, TrueOrFalse(CanHandleWith(&aPresContext)), &aElement, 1288 sActiveIMEContentObserver.get(), sFocusedElement.get())); 1289 MOZ_LOG(sISMLog, LogLevel::Debug, 1290 (" aElement: %s", ToString(aElement).c_str())); 1291 if (sFocusedElement) { 1292 MOZ_LOG(sISMLog, LogLevel::Debug, 1293 (" sFocusedElement: %s", ToString(*sFocusedElement).c_str())); 1294 } 1295 1296 if (NS_WARN_IF(!sTextInputHandlingWidget) || 1297 NS_WARN_IF(sTextInputHandlingWidget->Destroyed())) { 1298 return; 1299 } 1300 1301 // Check if IME has focus. If and only if IME has focus, we may need to 1302 // update IME state of the widget to re-open VKB. Otherwise, IME will open 1303 // VKB at getting focus. 1304 if (!sActiveIMEContentObserver || 1305 !sActiveIMEContentObserver->IsObserving(aPresContext, &aElement)) { 1306 MOZ_LOG(sISMLog, LogLevel::Debug, 1307 (" OnReFocus(), editable content for aElement was not being " 1308 "observed by the sActiveIMEContentObserver")); 1309 return; 1310 } 1311 1312 MOZ_ASSERT(&aElement == sFocusedElement.get()); 1313 1314 if (!UserActivation::IsHandlingUserInput() || 1315 UserActivation::IsHandlingKeyboardInput()) { 1316 return; 1317 } 1318 1319 const OwningNonNull<nsIWidget> textInputHandlingWidget = 1320 *sTextInputHandlingWidget; 1321 1322 // Don't update IME state during composition. 1323 if (sTextCompositions) { 1324 if (const TextComposition* composition = 1325 sTextCompositions->GetCompositionFor(textInputHandlingWidget)) { 1326 if (composition->IsComposing()) { 1327 return; 1328 } 1329 } 1330 } 1331 1332 const InputContextAction action(InputContextAction::CAUSE_UNKNOWN, 1333 InputContextAction::FOCUS_NOT_CHANGED); 1334 const IMEState newState = GetNewIMEState(aPresContext, &aElement); 1335 // If aElement has not had a primary frame for it, 1336 if (MOZ_UNLIKELY(!newState.IsEditable())) { 1337 if (sActiveIMEContentObserver->EditorIsTextEditor()) { 1338 TextControlElement* const textControlElement = 1339 TextControlElement::FromNode(aElement); 1340 MOZ_ASSERT(textControlElement); 1341 if (textControlElement && 1342 textControlElement->IsSingleLineTextControlOrTextArea()) { 1343 nsTextControlFrame* const boundFrame = 1344 textControlElement->GetTextControlState()->GetBoundFrame(); 1345 MOZ_ASSERT(!boundFrame); 1346 MOZ_LOG( 1347 sISMLog, LogLevel::Warning, 1348 (" OnReFocus(), Temporarily disabling IME for the focused element " 1349 "because probably the TextControlState could not return " 1350 "TextEditor (textControlFrame: %p, textEditor: %p)", 1351 boundFrame, 1352 textControlElement->GetTextControlState()->GetExtantTextEditor())); 1353 } 1354 } else { 1355 HTMLEditor* const htmlEditor = 1356 nsContentUtils::GetHTMLEditor(&aPresContext); 1357 #ifdef DEBUG 1358 MOZ_ASSERT(htmlEditor); 1359 Result<IMEState, nsresult> stateOrError = 1360 htmlEditor->GetPreferredIMEState(); 1361 MOZ_ASSERT(stateOrError.isOk()); 1362 MOZ_ASSERT(!stateOrError.inspect().IsEditable()); 1363 #endif // #ifdef DEBUG 1364 MOZ_LOG(sISMLog, LogLevel::Warning, 1365 (" OnRefocus(), Disabling IME for the focused element, " 1366 "HTMLEditor=%p { IsReadonly()=%s }", 1367 htmlEditor, 1368 htmlEditor ? TrueOrFalse(htmlEditor->IsReadonly()) : "N/A")); 1369 } 1370 } 1371 SetIMEState(newState, &aPresContext, &aElement, textInputHandlingWidget, 1372 action, sOrigin); 1373 } 1374 1375 // static 1376 void IMEStateManager::MaybeOnEditableStateDisabled(nsPresContext& aPresContext, 1377 dom::Element* aElement) { 1378 MOZ_LOG( 1379 sISMLog, LogLevel::Info, 1380 ("MaybeOnEditableStateDisabled(aPresContext=0x%p, aElement=0x%p), " 1381 "sFocusedPresContext=0x%p (available: %s), " 1382 "sFocusedElement=0x%p, sTextInputHandlingWidget=0x%p (available: %s), " 1383 "sActiveIMEContentObserver=0x%p, sIsGettingNewIMEState=%s", 1384 &aPresContext, aElement, sFocusedPresContext.get(), 1385 TrueOrFalse(CanHandleWith(sFocusedPresContext)), sFocusedElement.get(), 1386 sTextInputHandlingWidget, 1387 TrueOrFalse(sTextInputHandlingWidget && 1388 !sTextInputHandlingWidget->Destroyed()), 1389 sActiveIMEContentObserver.get(), TrueOrFalse(sIsGettingNewIMEState))); 1390 1391 if (sIsGettingNewIMEState) { 1392 MOZ_LOG(sISMLog, LogLevel::Debug, 1393 (" MaybeOnEditableStateDisabled(), " 1394 "does nothing because of called while getting new IME state")); 1395 return; 1396 } 1397 1398 if (&aPresContext != sFocusedPresContext || aElement != sFocusedElement) { 1399 MOZ_LOG(sISMLog, LogLevel::Debug, 1400 (" MaybeOnEditableStateDisabled(), " 1401 "does nothing because of another element already has focus")); 1402 return; 1403 } 1404 1405 if (NS_WARN_IF(!sTextInputHandlingWidget) || 1406 NS_WARN_IF(sTextInputHandlingWidget->Destroyed())) { 1407 MOZ_LOG(sISMLog, LogLevel::Error, 1408 (" MaybeOnEditableStateDisabled(), FAILED due to " 1409 "the widget for the managing the nsPresContext has gone")); 1410 return; 1411 } 1412 1413 const OwningNonNull<nsIWidget> textInputHandlingWidget = 1414 *sTextInputHandlingWidget; 1415 MOZ_ASSERT_IF(sFocusedPresContext->GetTextInputHandlingWidget(), 1416 sFocusedPresContext->GetTextInputHandlingWidget() == 1417 textInputHandlingWidget.get()); 1418 1419 const IMEState newIMEState = GetNewIMEState(aPresContext, aElement); 1420 // If IME state becomes editable, HTMLEditor should also be initialized with 1421 // same path as it gets focus. Therefore, IMEStateManager::UpdateIMEState 1422 // should be called by HTMLEditor instead. 1423 if (newIMEState.IsEditable()) { 1424 MOZ_LOG(sISMLog, LogLevel::Debug, 1425 (" MaybeOnEditableStateDisabled(), " 1426 "does nothing because IME state does not become disabled")); 1427 return; 1428 } 1429 1430 // Otherwise, disable IME on the widget and destroy active IMEContentObserver 1431 // if there is. 1432 const InputContext inputContext = textInputHandlingWidget->GetInputContext(); 1433 if (inputContext.mIMEState.mEnabled == newIMEState.mEnabled) { 1434 MOZ_LOG(sISMLog, LogLevel::Debug, 1435 (" MaybeOnEditableStateDisabled(), " 1436 "does nothing because IME state is not changed")); 1437 return; 1438 } 1439 1440 if (sActiveIMEContentObserver) { 1441 DestroyIMEContentObserver(); 1442 } 1443 1444 InputContextAction action(InputContextAction::CAUSE_UNKNOWN, 1445 InputContextAction::FOCUS_NOT_CHANGED); 1446 SetIMEState(newIMEState, &aPresContext, aElement, textInputHandlingWidget, 1447 action, sOrigin); 1448 } 1449 1450 // static 1451 void IMEStateManager::UpdateIMEState(const IMEState& aNewIMEState, 1452 Element* aElement, EditorBase& aEditorBase, 1453 const UpdateIMEStateOptions& aOptions) { 1454 MOZ_LOG( 1455 sISMLog, LogLevel::Info, 1456 ("UpdateIMEState(aNewIMEState=%s, aElement=0x%p, aEditorBase=0x%p, " 1457 "aOptions=0x%0x), sFocusedPresContext=0x%p (available: %s), " 1458 "sFocusedElement=0x%p, sTextInputHandlingWidget=0x%p (available: %s), " 1459 "sActiveIMEContentObserver=0x%p, sIsGettingNewIMEState=%s", 1460 ToString(aNewIMEState).c_str(), aElement, &aEditorBase, 1461 aOptions.serialize(), sFocusedPresContext.get(), 1462 TrueOrFalse(CanHandleWith(sFocusedPresContext)), sFocusedElement.get(), 1463 sTextInputHandlingWidget, 1464 TrueOrFalse(sTextInputHandlingWidget && 1465 !sTextInputHandlingWidget->Destroyed()), 1466 sActiveIMEContentObserver.get(), TrueOrFalse(sIsGettingNewIMEState))); 1467 if (aElement) { 1468 MOZ_LOG(sISMLog, LogLevel::Debug, 1469 (" aElement: %s", ToString(*aElement).c_str())); 1470 } 1471 if (sFocusedElement) { 1472 MOZ_LOG(sISMLog, LogLevel::Debug, 1473 (" sFocusedElement: %s", ToString(*sFocusedElement).c_str())); 1474 } 1475 1476 if (sIsGettingNewIMEState) { 1477 MOZ_LOG(sISMLog, LogLevel::Debug, 1478 (" UpdateIMEState(), " 1479 "does nothing because of called while getting new IME state")); 1480 return; 1481 } 1482 1483 RefPtr<PresShell> presShell(aEditorBase.GetPresShell()); 1484 if (NS_WARN_IF(!presShell)) { 1485 MOZ_LOG(sISMLog, LogLevel::Error, 1486 (" UpdateIMEState(), FAILED due to " 1487 "editor doesn't have PresShell")); 1488 return; 1489 } 1490 1491 const RefPtr<nsPresContext> presContext = 1492 aElement 1493 ? aElement->GetPresContext(Element::PresContextFor::eForComposedDoc) 1494 : aEditorBase.GetPresContext(); 1495 if (NS_WARN_IF(!presContext)) { 1496 MOZ_LOG(sISMLog, LogLevel::Error, 1497 (" UpdateIMEState(), FAILED due to " 1498 "editor doesn't have PresContext")); 1499 return; 1500 } 1501 1502 // IMEStateManager::UpdateIMEState() should be called after 1503 // IMEStateManager::OnChangeFocus() is called for setting focus to aElement 1504 // and aEditorBase. However, when aEditorBase is an HTMLEditor, this may be 1505 // called by nsIEditor::PostCreate() before IMEStateManager::OnChangeFocus(). 1506 // Similarly, when aEditorBase is a TextEditor, this may be called by 1507 // nsIEditor::SetFlags(). In such cases, this method should do nothing 1508 // because input context should be updated when 1509 // IMEStateManager::OnChangeFocus() is called later. 1510 if (sFocusedPresContext != presContext) { 1511 MOZ_LOG(sISMLog, LogLevel::Warning, 1512 (" UpdateIMEState(), does nothing due to " 1513 "the editor hasn't managed by IMEStateManager yet")); 1514 return; 1515 } 1516 1517 // If IMEStateManager doesn't manage any document, this cannot update IME 1518 // state of any widget. 1519 if (NS_WARN_IF(!sFocusedPresContext)) { 1520 MOZ_LOG(sISMLog, LogLevel::Error, 1521 (" UpdateIMEState(), FAILED due to " 1522 "no managing nsPresContext")); 1523 return; 1524 } 1525 1526 if (NS_WARN_IF(!sTextInputHandlingWidget) || 1527 NS_WARN_IF(sTextInputHandlingWidget->Destroyed())) { 1528 MOZ_LOG(sISMLog, LogLevel::Error, 1529 (" UpdateIMEState(), FAILED due to " 1530 "the widget for the managing nsPresContext has gone")); 1531 return; 1532 } 1533 1534 const OwningNonNull<nsIWidget> textInputHandlingWidget = 1535 *sTextInputHandlingWidget; 1536 MOZ_ASSERT_IF(sFocusedPresContext->GetTextInputHandlingWidget(), 1537 sFocusedPresContext->GetTextInputHandlingWidget() == 1538 textInputHandlingWidget.get()); 1539 1540 // TODO: Investigate if we could put off to initialize IMEContentObserver 1541 // later because a lot of callers need to be marked as 1542 // MOZ_CAN_RUN_SCRIPT otherwise. 1543 1544 // If there is an active observer and we need an observer, we want to keep 1545 // using the observer as far as possible because it's already notified IME of 1546 // focus. However, between the call of OnChangeFocus() and UpdateIMEState(), 1547 // focus and/or IME state may have been changed. If so, we need to update 1548 // the observer for current situation. 1549 const bool hasActiveObserverAndNeedObserver = 1550 sActiveIMEContentObserver && IsIMEObserverNeeded(aNewIMEState); 1551 1552 // If the active observer was not initialized with aEditorBase, it means 1553 // that the old editor lost focus but new editor gets focus **without** 1554 // focus move in the DOM tree. This may happen with changing the type of 1555 // <input> element, etc. In this case, we need to let IME know a focus move 1556 // with recreating IMEContentObserver because editable target has been 1557 // changed. 1558 const bool needToRecreateObserver = 1559 hasActiveObserverAndNeedObserver && 1560 !sActiveIMEContentObserver->WasInitializedWith(aEditorBase); 1561 1562 // If the active observer was initialized with same editor, we can/should 1563 // keep using it for avoiding to notify IME of a focus change. However, we 1564 // may need to re-initialize it because it may have temporarily stopped 1565 // observing the content. 1566 if (hasActiveObserverAndNeedObserver && !needToRecreateObserver) { 1567 MOZ_LOG(sISMLog, LogLevel::Debug, 1568 (" UpdateIMEState(), try to reinitialize the active " 1569 "IMEContentObserver")); 1570 OwningNonNull<IMEContentObserver> contentObserver = 1571 *sActiveIMEContentObserver; 1572 OwningNonNull<nsPresContext> presContext = *sFocusedPresContext; 1573 if (!contentObserver->MaybeReinitialize( 1574 textInputHandlingWidget, presContext, aElement, aEditorBase)) { 1575 MOZ_LOG(sISMLog, LogLevel::Error, 1576 (" UpdateIMEState(), failed to reinitialize the active " 1577 "IMEContentObserver")); 1578 } 1579 if (NS_WARN_IF(textInputHandlingWidget->Destroyed())) { 1580 MOZ_LOG(sISMLog, LogLevel::Error, 1581 (" UpdateIMEState(), widget has gone during re-initializing " 1582 "the active IMEContentObserver")); 1583 return; 1584 } 1585 } 1586 1587 // If there is no active IMEContentObserver or it isn't observing the 1588 // editable content for aElement, we should recreate an observer. E.g., 1589 // aElement which is an editing host may have been changed from/to a text 1590 // control. 1591 const bool createNewObserver = 1592 IsIMEObserverNeeded(aNewIMEState) && 1593 (!sActiveIMEContentObserver || needToRecreateObserver || 1594 !sActiveIMEContentObserver->IsObserving(*sFocusedPresContext, aElement)); 1595 // If we're recreating new IMEContentObserver or new state does not need 1596 // IMEContentObserver, destroy the active one. 1597 const bool destroyCurrentObserver = 1598 sActiveIMEContentObserver && 1599 (createNewObserver || !IsIMEObserverNeeded(aNewIMEState)); 1600 1601 // If IME state is changed, e.g., from "enabled" or "password" to "disabled", 1602 // we cannot keep the composing state because the new "disabled" state does 1603 // not support composition, and also from "enabled" to "password" and vice 1604 // versa, IME may use different composing rules. Therefore, for avoiding 1605 // IME to be confused, we should fix the composition first. 1606 const bool updateIMEState = 1607 aOptions.contains(UpdateIMEStateOption::ForceUpdate) || 1608 (textInputHandlingWidget->GetInputContext().mIMEState.mEnabled != 1609 aNewIMEState.mEnabled); 1610 if (NS_WARN_IF(textInputHandlingWidget->Destroyed())) { 1611 MOZ_LOG( 1612 sISMLog, LogLevel::Error, 1613 (" UpdateIMEState(), widget has gone during getting input context")); 1614 return; 1615 } 1616 1617 if (updateIMEState && 1618 !aOptions.contains(UpdateIMEStateOption::DontCommitComposition)) { 1619 NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, textInputHandlingWidget, 1620 sFocusedIMEBrowserParent); 1621 if (NS_WARN_IF(textInputHandlingWidget->Destroyed())) { 1622 MOZ_LOG(sISMLog, LogLevel::Error, 1623 (" UpdateIMEState(), widget has gone during committing " 1624 "composition")); 1625 return; 1626 } 1627 // FIXME: If committing composition changes IME state recursively, we should 1628 // not keep updating IME state here. However, how can we manage it? 1629 // Is a generation of the state is required? 1630 } 1631 1632 // Notify IME of blur first. 1633 if (destroyCurrentObserver) { 1634 DestroyIMEContentObserver(); 1635 if (NS_WARN_IF(textInputHandlingWidget->Destroyed()) || 1636 NS_WARN_IF(sTextInputHandlingWidget != textInputHandlingWidget)) { 1637 MOZ_LOG(sISMLog, LogLevel::Error, 1638 (" UpdateIMEState(), has set input context, but the widget is " 1639 "not focused")); 1640 return; 1641 } 1642 } 1643 1644 // Then, notify our IME handler of new IME state. 1645 if (updateIMEState) { 1646 InputContextAction action(InputContextAction::CAUSE_UNKNOWN, 1647 InputContextAction::FOCUS_NOT_CHANGED); 1648 RefPtr<nsPresContext> editorPresContext = aEditorBase.GetPresContext(); 1649 if (NS_WARN_IF(!editorPresContext)) { 1650 MOZ_LOG(sISMLog, LogLevel::Error, 1651 (" UpdateIMEState(), nsPresContext for editor has already been " 1652 "lost")); 1653 return; 1654 } 1655 SetIMEState(aNewIMEState, editorPresContext, aElement, 1656 textInputHandlingWidget, action, sOrigin); 1657 if (NS_WARN_IF(textInputHandlingWidget->Destroyed()) || 1658 NS_WARN_IF(sTextInputHandlingWidget != textInputHandlingWidget)) { 1659 MOZ_LOG(sISMLog, LogLevel::Error, 1660 (" UpdateIMEState(), has set input context, but the widget is " 1661 "not focused")); 1662 return; 1663 } 1664 if (NS_WARN_IF( 1665 sTextInputHandlingWidget->GetInputContext().mIMEState.mEnabled != 1666 aNewIMEState.mEnabled)) { 1667 MOZ_LOG(sISMLog, LogLevel::Error, 1668 (" UpdateIMEState(), has set input context, but IME enabled " 1669 "state was overridden by somebody else")); 1670 return; 1671 } 1672 } 1673 1674 NS_ASSERTION(IsFocusedElement(*presContext, aElement), 1675 "aElement does not match with sFocusedElement"); 1676 1677 // Finally, create new observer if required. 1678 if (createNewObserver) { 1679 if (!sActiveIMEContentObserver && sFocusedPresContext && 1680 sTextInputHandlingWidget) { 1681 // XXX In this case, it might not be enough safe to notify IME of 1682 // anything. So, don't try to flush pending notifications of 1683 // IMEContentObserver here. 1684 CreateIMEContentObserver(aEditorBase, aElement); 1685 } else { 1686 MOZ_LOG(sISMLog, LogLevel::Error, 1687 (" UpdateIMEState(), wanted to create IMEContentObserver, but " 1688 "lost focus")); 1689 } 1690 } 1691 } 1692 1693 // static 1694 IMEState IMEStateManager::GetNewIMEState(const nsPresContext& aPresContext, 1695 Element* aElement) { 1696 MOZ_LOG( 1697 sISMLog, LogLevel::Info, 1698 ("GetNewIMEState(aPresContext=0x%p, aElement=0x%p), " 1699 "sInstalledMenuKeyboardListener=%s", 1700 &aPresContext, aElement, TrueOrFalse(sInstalledMenuKeyboardListener))); 1701 1702 if (!CanHandleWith(&aPresContext)) { 1703 MOZ_LOG(sISMLog, LogLevel::Debug, 1704 (" GetNewIMEState() returns IMEEnabled::Disabled because " 1705 "the nsPresContext has been destroyed")); 1706 return IMEState(IMEEnabled::Disabled); 1707 } 1708 1709 // On Printing or Print Preview, we don't need IME. 1710 if (aPresContext.Type() == nsPresContext::eContext_PrintPreview || 1711 aPresContext.Type() == nsPresContext::eContext_Print) { 1712 MOZ_LOG(sISMLog, LogLevel::Debug, 1713 (" GetNewIMEState() returns IMEEnabled::Disabled because " 1714 "the nsPresContext is for print or print preview")); 1715 return IMEState(IMEEnabled::Disabled); 1716 } 1717 1718 if (sInstalledMenuKeyboardListener) { 1719 MOZ_LOG(sISMLog, LogLevel::Debug, 1720 (" GetNewIMEState() returns IMEEnabled::Disabled because " 1721 "menu keyboard listener was installed")); 1722 return IMEState(IMEEnabled::Disabled); 1723 } 1724 1725 if (!aElement) { 1726 // Even if there are no focused content, the focused document might be 1727 // editable, such case is design mode. 1728 if (aPresContext.Document() && aPresContext.Document()->IsInDesignMode()) { 1729 if (aPresContext.Document()->GetRootElement()) { 1730 MOZ_LOG(sISMLog, LogLevel::Debug, 1731 (" GetNewIMEState() returns IMEEnabled::Enabled because " 1732 "design mode editor has focus")); 1733 return IMEState(IMEEnabled::Enabled); 1734 } 1735 MOZ_LOG(sISMLog, LogLevel::Debug, 1736 (" GetNewIMEState() returns IMEEnabled::Disabled because " 1737 "document is in the design mode but has no element")); 1738 return IMEState(IMEEnabled::Disabled); 1739 } 1740 MOZ_LOG(sISMLog, LogLevel::Debug, 1741 (" GetNewIMEState() returns IMEEnabled::Disabled because " 1742 "no content has focus")); 1743 return IMEState(IMEEnabled::Disabled); 1744 } 1745 1746 // If aElement is in designMode, aElement should be the root node of the 1747 // document. 1748 if (aElement && aElement->IsInDesignMode()) { 1749 MOZ_LOG(sISMLog, LogLevel::Debug, 1750 (" GetNewIMEState() returns IMEEnabled::Enabled because " 1751 "a content node in design mode editor has focus")); 1752 return IMEState(IMEEnabled::Enabled); 1753 } 1754 1755 // nsIContent::GetDesiredIMEState() may cause a call of UpdateIMEState() 1756 // from EditorBase::PostCreate() because GetDesiredIMEState() needs to 1757 // retrieve an editor instance for the element if it's editable element. 1758 // For avoiding such nested IME state updates, we should set 1759 // sIsGettingNewIMEState here and UpdateIMEState() should check it. 1760 GettingNewIMEStateBlocker blocker; 1761 1762 IMEState newIMEState = aElement->GetDesiredIMEState(); 1763 MOZ_LOG(sISMLog, LogLevel::Debug, 1764 (" GetNewIMEState() returns %s", ToString(newIMEState).c_str())); 1765 return newIMEState; 1766 } 1767 1768 // static 1769 void IMEStateManager::ResetActiveChildInputContext() { 1770 sActiveChildInputContext.mIMEState.mEnabled = IMEEnabled::Unknown; 1771 } 1772 1773 // static 1774 bool IMEStateManager::HasActiveChildSetInputContext() { 1775 return sActiveChildInputContext.mIMEState.mEnabled != IMEEnabled::Unknown; 1776 } 1777 1778 // static 1779 void IMEStateManager::SetInputContextForChildProcess( 1780 BrowserParent* aBrowserParent, const InputContext& aInputContext, 1781 const InputContextAction& aAction) { 1782 MOZ_LOG( 1783 sISMLog, LogLevel::Info, 1784 ("SetInputContextForChildProcess(aBrowserParent=0x%p, " 1785 "aInputContext=%s , aAction={ mCause=%s, mAction=%s }), " 1786 "sFocusedPresContext=0x%p (available: %s), " 1787 "sTextInputHandlingWidget=0x%p (available: %s), " 1788 "BrowserParent::GetFocused()=0x%p, sInstalledMenuKeyboardListener=%s", 1789 aBrowserParent, ToString(aInputContext).c_str(), 1790 ToString(aAction.mCause).c_str(), ToString(aAction.mFocusChange).c_str(), 1791 sFocusedPresContext.get(), 1792 TrueOrFalse(CanHandleWith(sFocusedPresContext)), 1793 sTextInputHandlingWidget, 1794 TrueOrFalse(sTextInputHandlingWidget && 1795 !sTextInputHandlingWidget->Destroyed()), 1796 BrowserParent::GetFocused(), 1797 TrueOrFalse(sInstalledMenuKeyboardListener))); 1798 1799 if (aBrowserParent != BrowserParent::GetFocused()) { 1800 MOZ_LOG(sISMLog, LogLevel::Error, 1801 (" SetInputContextForChildProcess(), FAILED, " 1802 "because non-focused tab parent tries to set input context")); 1803 return; 1804 } 1805 1806 if (NS_WARN_IF(!CanHandleWith(sFocusedPresContext))) { 1807 MOZ_LOG(sISMLog, LogLevel::Error, 1808 (" SetInputContextForChildProcess(), FAILED, " 1809 "due to no focused presContext")); 1810 return; 1811 } 1812 1813 if (NS_WARN_IF(!sTextInputHandlingWidget) || 1814 NS_WARN_IF(sTextInputHandlingWidget->Destroyed())) { 1815 MOZ_LOG(sISMLog, LogLevel::Error, 1816 (" SetInputContextForChildProcess(), FAILED, " 1817 "due to the widget for the nsPresContext has gone")); 1818 return; 1819 } 1820 1821 const OwningNonNull<nsIWidget> textInputHandlingWidget = 1822 *sTextInputHandlingWidget; 1823 MOZ_ASSERT_IF(sFocusedPresContext->GetTextInputHandlingWidget(), 1824 sFocusedPresContext->GetTextInputHandlingWidget() == 1825 textInputHandlingWidget.get()); 1826 MOZ_ASSERT(aInputContext.mOrigin == InputContext::ORIGIN_CONTENT); 1827 1828 sActiveChildInputContext = aInputContext; 1829 MOZ_ASSERT(HasActiveChildSetInputContext()); 1830 1831 // If input context is changed in remote process while menu keyboard listener 1832 // is installed, this process shouldn't set input context now. When it's 1833 // uninstalled, input context should be restored from 1834 // sActiveChildInputContext. 1835 if (sInstalledMenuKeyboardListener) { 1836 MOZ_LOG(sISMLog, LogLevel::Info, 1837 (" SetInputContextForChildProcess(), waiting to set input context " 1838 "until menu keyboard listener is uninstalled")); 1839 return; 1840 } 1841 1842 SetInputContext(textInputHandlingWidget, aInputContext, aAction); 1843 } 1844 1845 MOZ_CAN_RUN_SCRIPT static bool IsNextFocusableElementTextControl( 1846 const Element* aInputContent) { 1847 RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager(); 1848 if (MOZ_UNLIKELY(!fm)) { 1849 return false; 1850 } 1851 nsCOMPtr<nsIContent> nextContent; 1852 const RefPtr<Element> inputContent = const_cast<Element*>(aInputContent); 1853 const nsCOMPtr<nsPIDOMWindowOuter> outerWindow = 1854 aInputContent->OwnerDoc()->GetWindow(); 1855 nsresult rv = fm->DetermineElementToMoveFocus( 1856 outerWindow, inputContent, nsIFocusManager::MOVEFOCUS_FORWARD, true, 1857 false, getter_AddRefs(nextContent)); 1858 if (NS_WARN_IF(NS_FAILED(rv)) || !nextContent) { 1859 return false; 1860 } 1861 nextContent = nextContent->FindFirstNonChromeOnlyAccessContent(); 1862 const auto* nextControl = nsIFormControl::FromNode(nextContent); 1863 if (!nextControl || !nextControl->IsTextControl(false)) { 1864 return false; 1865 } 1866 1867 // XXX We don't consider next element is date/time control yet. 1868 nsGenericHTMLElement* nextElement = 1869 nsGenericHTMLElement::FromNodeOrNull(nextContent); 1870 if (!nextElement) { 1871 return false; 1872 } 1873 1874 // FIXME: Should probably use nsIFrame::IsFocusable if possible, to account 1875 // for things like visibility: hidden or so. 1876 if (!nextElement->IsFocusableWithoutStyle()) { 1877 return false; 1878 } 1879 1880 // Check readonly attribute. 1881 if (nextElement->IsHTMLElement(nsGkAtoms::textarea)) { 1882 auto* textAreaElement = HTMLTextAreaElement::FromNode(nextElement); 1883 return !textAreaElement->ReadOnly(); 1884 } 1885 1886 // If neither textarea nor input, what element type? 1887 MOZ_DIAGNOSTIC_ASSERT(nextElement->IsHTMLElement(nsGkAtoms::input)); 1888 1889 auto* inputElement = HTMLInputElement::FromNode(nextElement); 1890 return !inputElement->ReadOnly(); 1891 } 1892 1893 static void GetInputType(const IMEState& aState, const nsIContent& aContent, 1894 nsAString& aInputType) { 1895 // NOTE: If you change here, you may need to update 1896 // widget::InputContext::GatNativeKeyBindings too. 1897 if (aContent.IsHTMLElement(nsGkAtoms::input)) { 1898 const HTMLInputElement* inputElement = 1899 HTMLInputElement::FromNode(&aContent); 1900 if (inputElement->HasBeenTypePassword() && aState.IsEditable()) { 1901 aInputType.AssignLiteral("password"); 1902 } else { 1903 inputElement->GetType(aInputType); 1904 } 1905 } else if (aContent.IsHTMLElement(nsGkAtoms::textarea)) { 1906 aInputType.Assign(nsGkAtoms::textarea->GetUTF16String()); 1907 } 1908 } 1909 1910 MOZ_CAN_RUN_SCRIPT static void GetActionHint(const IMEState& aState, 1911 const nsIContent& aContent, 1912 nsAString& aActionHint) { 1913 MOZ_ASSERT(aContent.IsHTMLElement()); 1914 1915 if (aState.IsEditable()) { 1916 nsGenericHTMLElement::FromNode(&aContent)->GetEnterKeyHint(aActionHint); 1917 1918 // If enterkeyhint is set, we don't infer action hint. 1919 if (!aActionHint.IsEmpty()) { 1920 return; 1921 } 1922 } 1923 1924 if (!aContent.IsHTMLElement(nsGkAtoms::input)) { 1925 return; 1926 } 1927 1928 // Get the input content corresponding to the focused node, 1929 // which may be an anonymous child of the input content. 1930 MOZ_ASSERT(&aContent == aContent.FindFirstNonChromeOnlyAccessContent()); 1931 const HTMLInputElement* inputElement = HTMLInputElement::FromNode(aContent); 1932 if (!inputElement) { 1933 return; 1934 } 1935 1936 // If we don't have an action hint and 1937 // return won't submit the form, use "maybenext". 1938 bool willSubmit = false; 1939 bool isLastElement = false; 1940 HTMLFormElement* formElement = inputElement->GetForm(); 1941 // is this a form and does it have a default submit element? 1942 if (formElement) { 1943 if (formElement->IsLastActiveElement(inputElement)) { 1944 isLastElement = true; 1945 } 1946 1947 if (formElement->GetDefaultSubmitElement()) { 1948 willSubmit = true; 1949 // is this an html form... 1950 } else { 1951 // ... and does it only have a single text input element ? 1952 if (!formElement->ImplicitSubmissionIsDisabled() || 1953 // ... or is this the last non-disabled element? 1954 isLastElement) { 1955 willSubmit = true; 1956 } 1957 } 1958 } 1959 1960 if (!isLastElement && formElement) { 1961 // If next tabbable content in form is text control, hint should be "next" 1962 // even there is submit in form. 1963 // MOZ_KnownLive(inputElement) because it's an alias of aContent. 1964 if (IsNextFocusableElementTextControl(MOZ_KnownLive(inputElement))) { 1965 // This is focusable text control 1966 // XXX What good hint for read only field? 1967 aActionHint.AssignLiteral("maybenext"); 1968 return; 1969 } 1970 } 1971 1972 if (!willSubmit) { 1973 aActionHint.Truncate(); 1974 return; 1975 } 1976 1977 if (inputElement->ControlType() == FormControlType::InputSearch) { 1978 aActionHint.AssignLiteral("search"); 1979 return; 1980 } 1981 1982 aActionHint.AssignLiteral("go"); 1983 } 1984 1985 static void GetInputMode(const IMEState& aState, const nsIContent& aContent, 1986 nsAString& aInputMode) { 1987 if (aState.IsEditable()) { 1988 aContent.AsElement()->GetAttr(nsGkAtoms::inputmode, aInputMode); 1989 if (aContent.IsHTMLElement(nsGkAtoms::input) && 1990 aInputMode.EqualsLiteral("mozAwesomebar")) { 1991 if (!nsContentUtils::IsChromeDoc(aContent.OwnerDoc())) { 1992 // mozAwesomebar should be allowed only in chrome 1993 aInputMode.Truncate(); 1994 } 1995 } else { 1996 ToLowerCase(aInputMode); 1997 } 1998 } 1999 } 2000 2001 static void GetAutocapitalize(const IMEState& aState, const Element& aElement, 2002 const InputContext& aInputContext, 2003 nsAString& aAutocapitalize) { 2004 if (aElement.IsHTMLElement() && aState.IsEditable() && 2005 aInputContext.IsAutocapitalizeSupported()) { 2006 nsGenericHTMLElement::FromNode(&aElement)->GetAutocapitalize( 2007 aAutocapitalize); 2008 } 2009 } 2010 2011 static bool GetAutocorrect(const IMEState& aState, const Element& aElement, 2012 const InputContext& aInputContext) { 2013 if (!StaticPrefs::dom_forms_autocorrect()) { 2014 #ifdef ANDROID 2015 // Autocorrect was on-by-default on Android by bug 806349, despite 2016 // autocorrect preference. 2017 return true; 2018 #else 2019 return false; 2020 #endif 2021 } 2022 2023 if (aElement.IsHTMLElement() && aState.IsEditable()) { 2024 if (nsContentUtils::IsChromeDoc(aElement.OwnerDoc()) && 2025 !aElement.HasAttr(nsGkAtoms::autocorrect)) { 2026 // Since Chrome UI may not want to enable autocorrect by default such as 2027 // bug 1881783. 2028 return false; 2029 } 2030 2031 return nsGenericHTMLElement::FromNode(&aElement)->Autocorrect(); 2032 } 2033 return true; 2034 } 2035 2036 // static 2037 already_AddRefed<nsIURI> IMEStateManager::GetExposableURL( 2038 const nsPresContext* aPresContext) { 2039 if (!aPresContext) { 2040 return nullptr; 2041 } 2042 nsIURI* uri = aPresContext->Document()->GetDocumentURI(); 2043 if (!uri) { 2044 return nullptr; 2045 } 2046 // We don't need to and should not expose special URLs such as: 2047 // about: Any apps like IME should work normally and constantly in any 2048 // default pages such as about:blank, about:home, etc in either 2049 // the main process or a content process. 2050 // blob: This may contain big data. If we copy it to the main process, 2051 // it may make the heap dirty which makes the process slower. 2052 // chrome: Same as about, any apps should work normally and constantly in 2053 // any chrome documents. 2054 // data: Any native apps in the environment shouldn't change the behavior 2055 // with the data URL's content and it may contain too big data. 2056 // file: The file path may contain private things and we shouldn't let 2057 // other apps like IME know which one is touched by the user because 2058 // malicious text services may like files which are explicitly used 2059 // by the user better. 2060 if (!net::SchemeIsHttpOrHttps(uri)) { 2061 return nullptr; 2062 } 2063 // Note that we don't need to expose UserPass, Query and Reference to 2064 // IME since they may contain sensitive data, but non-malicious text 2065 // services must not require these data. 2066 nsCOMPtr<nsIURI> exposableURL; 2067 if (NS_FAILED(NS_MutateURI(uri) 2068 .SetQuery(""_ns) 2069 .SetRef(""_ns) 2070 .SetUserPass(""_ns) 2071 .Finalize(exposableURL))) { 2072 return nullptr; 2073 } 2074 2075 return exposableURL.forget(); 2076 } 2077 2078 // static 2079 void IMEStateManager::SetIMEState(const IMEState& aState, 2080 const nsPresContext* aPresContext, 2081 Element* aElement, nsIWidget& aWidget, 2082 InputContextAction aAction, 2083 InputContext::Origin aOrigin) { 2084 MOZ_LOG(sISMLog, LogLevel::Info, 2085 ("SetIMEState(aState=%s, nsPresContext=0x%p, aElement=0x%p " 2086 "(BrowserParent=0x%p), aWidget=0x%p, aAction={ mCause=%s, " 2087 "mFocusChange=%s }, aOrigin=%s)", 2088 ToString(aState).c_str(), aPresContext, aElement, 2089 BrowserParent::GetFrom(aElement), &aWidget, 2090 ToString(aAction.mCause).c_str(), 2091 ToString(aAction.mFocusChange).c_str(), ToChar(aOrigin))); 2092 2093 InputContext context; 2094 context.mIMEState = aState; 2095 context.mURI = IMEStateManager::GetExposableURL(aPresContext); 2096 context.mOrigin = aOrigin; 2097 2098 context.mHasHandledUserInput = 2099 aPresContext && aPresContext->PresShell()->HasHandledUserInput(); 2100 2101 context.mInPrivateBrowsing = aPresContext && aPresContext->Document() && 2102 aPresContext->Document()->IsInPrivateBrowsing(); 2103 2104 const RefPtr<Element> focusedElement = 2105 aElement ? Element::FromNodeOrNull( 2106 aElement->FindFirstNonChromeOnlyAccessContent()) 2107 : nullptr; 2108 2109 if (focusedElement && focusedElement->IsHTMLElement()) { 2110 GetInputType(aState, *focusedElement, context.mHTMLInputType); 2111 GetActionHint(aState, *focusedElement, context.mActionHint); 2112 GetInputMode(aState, *focusedElement, context.mHTMLInputMode); 2113 GetAutocapitalize(aState, *focusedElement, context, 2114 context.mAutocapitalize); 2115 context.mAutocorrect = GetAutocorrect(aState, *focusedElement, context); 2116 } 2117 2118 if (aAction.mCause == InputContextAction::CAUSE_UNKNOWN && 2119 nsContentUtils::LegacyIsCallerChromeOrNativeCode()) { 2120 aAction.mCause = InputContextAction::CAUSE_UNKNOWN_CHROME; 2121 } 2122 2123 if ((aAction.mCause == InputContextAction::CAUSE_UNKNOWN || 2124 aAction.mCause == InputContextAction::CAUSE_UNKNOWN_CHROME) && 2125 UserActivation::IsHandlingUserInput()) { 2126 aAction.mCause = 2127 UserActivation::IsHandlingKeyboardInput() 2128 ? InputContextAction::CAUSE_UNKNOWN_DURING_KEYBOARD_INPUT 2129 : InputContextAction::CAUSE_UNKNOWN_DURING_NON_KEYBOARD_INPUT; 2130 } 2131 2132 SetInputContext(aWidget, context, aAction); 2133 } 2134 2135 // static 2136 void IMEStateManager::SetInputContext(nsIWidget& aWidget, 2137 const InputContext& aInputContext, 2138 const InputContextAction& aAction) { 2139 MOZ_LOG( 2140 sISMLog, LogLevel::Info, 2141 ("SetInputContext(aWidget=0x%p, aInputContext=%s, " 2142 "aAction={ mCause=%s, mAction=%s }), BrowserParent::GetFocused()=0x%p", 2143 &aWidget, ToString(aInputContext).c_str(), 2144 ToString(aAction.mCause).c_str(), ToString(aAction.mFocusChange).c_str(), 2145 BrowserParent::GetFocused())); 2146 2147 OwningNonNull<nsIWidget> widget = aWidget; 2148 widget->SetInputContext(aInputContext, aAction); 2149 sActiveInputContextWidget = widget; 2150 } 2151 2152 // static 2153 void IMEStateManager::EnsureTextCompositionArray() { 2154 if (sTextCompositions) { 2155 return; 2156 } 2157 sTextCompositions = new TextCompositionArray(); 2158 } 2159 2160 // static 2161 void IMEStateManager::DispatchCompositionEvent( 2162 nsINode* aEventTargetNode, nsPresContext* aPresContext, 2163 BrowserParent* aBrowserParent, WidgetCompositionEvent* aCompositionEvent, 2164 nsEventStatus* aStatus, EventDispatchingCallback* aCallBack, 2165 bool aIsSynthesized) { 2166 MOZ_LOG( 2167 sISMLog, LogLevel::Info, 2168 ("DispatchCompositionEvent(aNode=0x%p, " 2169 "aPresContext=0x%p, aCompositionEvent={ mMessage=%s, " 2170 "mNativeIMEContext={ mRawNativeIMEContext=0x%" PRIXPTR ", " 2171 "mOriginProcessID=0x%" PRIX64 " }, mWidget(0x%p)={ " 2172 "GetNativeIMEContext()={ mRawNativeIMEContext=0x%" PRIXPTR ", " 2173 "mOriginProcessID=0x%" PRIX64 " }, Destroyed()=%s }, " 2174 "mFlags={ mIsTrusted=%s, mPropagationStopped=%s } }, " 2175 "aIsSynthesized=%s), browserParent=%p", 2176 aEventTargetNode, aPresContext, ToChar(aCompositionEvent->mMessage), 2177 aCompositionEvent->mNativeIMEContext.mRawNativeIMEContext, 2178 aCompositionEvent->mNativeIMEContext.mOriginProcessID, 2179 aCompositionEvent->mWidget.get(), 2180 aCompositionEvent->mWidget->GetNativeIMEContext().mRawNativeIMEContext, 2181 aCompositionEvent->mWidget->GetNativeIMEContext().mOriginProcessID, 2182 TrueOrFalse(aCompositionEvent->mWidget->Destroyed()), 2183 TrueOrFalse(aCompositionEvent->mFlags.mIsTrusted), 2184 TrueOrFalse(aCompositionEvent->mFlags.mPropagationStopped), 2185 TrueOrFalse(aIsSynthesized), aBrowserParent)); 2186 2187 if (NS_WARN_IF(!aCompositionEvent->IsTrusted()) || 2188 NS_WARN_IF(aCompositionEvent->PropagationStopped())) { 2189 return; 2190 } 2191 2192 MOZ_ASSERT(aCompositionEvent->mMessage != eCompositionUpdate, 2193 "compositionupdate event shouldn't be dispatched manually"); 2194 2195 EnsureTextCompositionArray(); 2196 2197 RefPtr<TextComposition> composition = 2198 sTextCompositions->GetCompositionFor(aCompositionEvent); 2199 if (!composition) { 2200 // If synthesized event comes after delayed native composition events 2201 // for request of commit or cancel, we should ignore it. 2202 if (NS_WARN_IF(aIsSynthesized)) { 2203 return; 2204 } 2205 MOZ_LOG(sISMLog, LogLevel::Debug, 2206 (" DispatchCompositionEvent(), " 2207 "adding new TextComposition to the array")); 2208 MOZ_ASSERT(aCompositionEvent->mMessage == eCompositionStart); 2209 composition = new TextComposition(aPresContext, aEventTargetNode, 2210 aBrowserParent, aCompositionEvent); 2211 sTextCompositions->AppendElement(composition); 2212 } 2213 #ifdef DEBUG 2214 else { 2215 MOZ_ASSERT(aCompositionEvent->mMessage != eCompositionStart); 2216 } 2217 #endif // #ifdef DEBUG 2218 2219 // Dispatch the event on composing target. 2220 composition->DispatchCompositionEvent(aCompositionEvent, aStatus, aCallBack, 2221 aIsSynthesized); 2222 2223 // WARNING: the |composition| might have been destroyed already. 2224 2225 // Remove the ended composition from the array. 2226 // NOTE: When TextComposition is synthesizing compositionend event for 2227 // emulating a commit, the instance shouldn't be removed from the array 2228 // because IME may perform it later. Then, we need to ignore the 2229 // following commit events in TextComposition::DispatchEvent(). 2230 // However, if commit or cancel for a request is performed synchronously 2231 // during not safe to dispatch events, PresShell must have discarded 2232 // compositionend event. Then, the synthesized compositionend event is 2233 // the last event for the composition. In this case, we need to 2234 // destroy the TextComposition with synthesized compositionend event. 2235 if ((!aIsSynthesized || 2236 composition->WasNativeCompositionEndEventDiscarded()) && 2237 aCompositionEvent->CausesDOMCompositionEndEvent()) { 2238 TextCompositionArray::index_type i = 2239 sTextCompositions->IndexOf(aCompositionEvent->mWidget); 2240 if (i != TextCompositionArray::NoIndex) { 2241 MOZ_LOG( 2242 sISMLog, LogLevel::Debug, 2243 (" DispatchCompositionEvent(), " 2244 "removing TextComposition from the array since NS_COMPOSTION_END " 2245 "was dispatched")); 2246 sTextCompositions->ElementAt(i)->Destroy(); 2247 sTextCompositions->RemoveElementAt(i); 2248 } 2249 } 2250 } 2251 2252 // static 2253 IMEContentObserver* IMEStateManager::GetActiveContentObserver() { 2254 return sActiveIMEContentObserver; 2255 } 2256 2257 // static 2258 nsIContent* IMEStateManager::GetRootContent(nsPresContext* aPresContext) { 2259 Document* doc = aPresContext->Document(); 2260 if (NS_WARN_IF(!doc)) { 2261 return nullptr; 2262 } 2263 return doc->GetRootElement(); 2264 } 2265 2266 // static 2267 void IMEStateManager::HandleSelectionEvent( 2268 nsPresContext* aPresContext, nsIContent* aEventTargetContent, 2269 WidgetSelectionEvent* aSelectionEvent) { 2270 RefPtr<BrowserParent> browserParent = GetActiveBrowserParent(); 2271 if (!browserParent) { 2272 browserParent = BrowserParent::GetFrom(aEventTargetContent 2273 ? aEventTargetContent 2274 : GetRootContent(aPresContext)); 2275 } 2276 2277 MOZ_LOG( 2278 sISMLog, LogLevel::Info, 2279 ("HandleSelectionEvent(aPresContext=0x%p, " 2280 "aEventTargetContent=0x%p, aSelectionEvent={ mMessage=%s, " 2281 "mFlags={ mIsTrusted=%s } }), browserParent=%p", 2282 aPresContext, aEventTargetContent, ToChar(aSelectionEvent->mMessage), 2283 TrueOrFalse(aSelectionEvent->mFlags.mIsTrusted), browserParent.get())); 2284 2285 if (!aSelectionEvent->IsTrusted()) { 2286 return; 2287 } 2288 2289 RefPtr<TextComposition> composition = 2290 sTextCompositions 2291 ? sTextCompositions->GetCompositionFor(aSelectionEvent->mWidget) 2292 : nullptr; 2293 if (composition) { 2294 // When there is a composition, TextComposition should guarantee that the 2295 // selection event will be handled in same target as composition events. 2296 composition->HandleSelectionEvent(aSelectionEvent); 2297 } else { 2298 // When there is no composition, the selection event should be handled 2299 // in the aPresContext or browserParent. 2300 TextComposition::HandleSelectionEvent(aPresContext, browserParent, 2301 aSelectionEvent); 2302 } 2303 } 2304 2305 // static 2306 void IMEStateManager::OnCompositionEventDiscarded( 2307 WidgetCompositionEvent* aCompositionEvent) { 2308 // Note that this method is never called for synthesized events for emulating 2309 // commit or cancel composition. 2310 2311 MOZ_LOG( 2312 sISMLog, LogLevel::Info, 2313 ("OnCompositionEventDiscarded(aCompositionEvent={ " 2314 "mMessage=%s, mNativeIMEContext={ mRawNativeIMEContext=0x%" PRIXPTR ", " 2315 "mOriginProcessID=0x%" PRIX64 " }, mWidget(0x%p)={ " 2316 "GetNativeIMEContext()={ mRawNativeIMEContext=0x%" PRIXPTR ", " 2317 "mOriginProcessID=0x%" PRIX64 " }, Destroyed()=%s }, " 2318 "mFlags={ mIsTrusted=%s } })", 2319 ToChar(aCompositionEvent->mMessage), 2320 aCompositionEvent->mNativeIMEContext.mRawNativeIMEContext, 2321 aCompositionEvent->mNativeIMEContext.mOriginProcessID, 2322 aCompositionEvent->mWidget.get(), 2323 aCompositionEvent->mWidget->GetNativeIMEContext().mRawNativeIMEContext, 2324 aCompositionEvent->mWidget->GetNativeIMEContext().mOriginProcessID, 2325 TrueOrFalse(aCompositionEvent->mWidget->Destroyed()), 2326 TrueOrFalse(aCompositionEvent->mFlags.mIsTrusted))); 2327 2328 if (!aCompositionEvent->IsTrusted()) { 2329 return; 2330 } 2331 2332 // Ignore compositionstart for now because sTextCompositions may not have 2333 // been created yet. 2334 if (aCompositionEvent->mMessage == eCompositionStart) { 2335 return; 2336 } 2337 2338 RefPtr<TextComposition> composition = 2339 sTextCompositions->GetCompositionFor(aCompositionEvent->mWidget); 2340 if (!composition) { 2341 // If the PresShell has been being destroyed during composition, 2342 // a TextComposition instance for the composition was already removed from 2343 // the array and destroyed in OnDestroyPresContext(). Therefore, we may 2344 // fail to retrieve a TextComposition instance here. 2345 MOZ_LOG(sISMLog, LogLevel::Info, 2346 (" OnCompositionEventDiscarded(), " 2347 "TextComposition instance for the widget has already gone")); 2348 return; 2349 } 2350 composition->OnCompositionEventDiscarded(aCompositionEvent); 2351 } 2352 2353 // static 2354 nsresult IMEStateManager::NotifyIME(IMEMessage aMessage, nsIWidget* aWidget, 2355 BrowserParent* aBrowserParent) { 2356 return IMEStateManager::NotifyIME(IMENotification(aMessage), aWidget, 2357 aBrowserParent); 2358 } 2359 2360 // static 2361 nsresult IMEStateManager::NotifyIME(const IMENotification& aNotification, 2362 nsIWidget* aWidget, 2363 BrowserParent* aBrowserParent) { 2364 MOZ_LOG(sISMLog, LogLevel::Info, 2365 ("NotifyIME(aNotification={ mMessage=%s }, " 2366 "aWidget=0x%p, aBrowserParent=0x%p), sFocusedIMEWidget=0x%p, " 2367 "BrowserParent::GetFocused()=0x%p, sFocusedIMEBrowserParent=0x%p, " 2368 "aBrowserParent == BrowserParent::GetFocused()=%s, " 2369 "aBrowserParent == sFocusedIMEBrowserParent=%s, " 2370 "CanSendNotificationToWidget()=%s", 2371 ToChar(aNotification.mMessage), aWidget, aBrowserParent, 2372 sFocusedIMEWidget, BrowserParent::GetFocused(), 2373 sFocusedIMEBrowserParent.get(), 2374 TrueOrFalse(aBrowserParent == BrowserParent::GetFocused()), 2375 TrueOrFalse(aBrowserParent == sFocusedIMEBrowserParent), 2376 TrueOrFalse(CanSendNotificationToWidget()))); 2377 2378 if (NS_WARN_IF(!aWidget)) { 2379 MOZ_LOG(sISMLog, LogLevel::Error, 2380 (" NotifyIME(), FAILED due to no widget")); 2381 return NS_ERROR_INVALID_ARG; 2382 } 2383 2384 switch (aNotification.mMessage) { 2385 case NOTIFY_IME_OF_FOCUS: { 2386 MOZ_ASSERT(CanSendNotificationToWidget()); 2387 2388 // If focus notification comes from a remote browser which already lost 2389 // focus, we shouldn't accept the focus notification. Then, following 2390 // notifications from the process will be ignored. 2391 if (aBrowserParent != BrowserParent::GetFocused()) { 2392 MOZ_LOG(sISMLog, LogLevel::Warning, 2393 (" NotifyIME(), WARNING, the received focus notification is " 2394 "ignored, because its associated BrowserParent did not match" 2395 "the focused BrowserParent.")); 2396 return NS_OK; 2397 } 2398 // If IME focus is already set, first blur the currently-focused 2399 // IME widget 2400 if (sFocusedIMEWidget) { 2401 // XXX Why don't we first request the previously-focused IME 2402 // widget to commit the composition? 2403 MOZ_ASSERT( 2404 sFocusedIMEBrowserParent || aBrowserParent, 2405 "This case shouldn't be caused by focus move in this process"); 2406 MOZ_LOG(sISMLog, LogLevel::Warning, 2407 (" NotifyIME(), WARNING, received focus notification with ") 2408 "non-null sFocusedIMEWidget. How come " 2409 "OnFocusMovedBetweenBrowsers did not blur it already?"); 2410 nsCOMPtr<nsIWidget> focusedIMEWidget(sFocusedIMEWidget); 2411 sFocusedIMEWidget = nullptr; 2412 sFocusedIMEBrowserParent = nullptr; 2413 focusedIMEWidget->NotifyIME(IMENotification(NOTIFY_IME_OF_BLUR)); 2414 } 2415 #ifdef DEBUG 2416 if (aBrowserParent) { 2417 nsCOMPtr<nsIWidget> browserParentWidget = 2418 aBrowserParent->GetTextInputHandlingWidget(); 2419 MOZ_ASSERT(browserParentWidget == aWidget); 2420 } else { 2421 MOZ_ASSERT(sFocusedPresContext); 2422 MOZ_ASSERT_IF( 2423 sFocusedPresContext->GetTextInputHandlingWidget(), 2424 sFocusedPresContext->GetTextInputHandlingWidget() == aWidget); 2425 } 2426 #endif 2427 sFocusedIMEBrowserParent = aBrowserParent; 2428 sFocusedIMEWidget = aWidget; 2429 nsCOMPtr<nsIWidget> widget(aWidget); 2430 MOZ_LOG( 2431 sISMLog, LogLevel::Info, 2432 (" NotifyIME(), about to call widget->NotifyIME() for IME focus")); 2433 return widget->NotifyIME(aNotification); 2434 } 2435 case NOTIFY_IME_OF_BLUR: { 2436 if (aBrowserParent != sFocusedIMEBrowserParent) { 2437 MOZ_LOG(sISMLog, LogLevel::Warning, 2438 (" NotifyIME(), WARNING, the received blur notification is " 2439 "ignored " 2440 "because it's not from current focused IME browser")); 2441 return NS_OK; 2442 } 2443 if (!sFocusedIMEWidget) { 2444 MOZ_LOG( 2445 sISMLog, LogLevel::Error, 2446 (" NotifyIME(), WARNING, received blur notification but there is " 2447 "no focused IME widget")); 2448 return NS_OK; 2449 } 2450 if (NS_WARN_IF(sFocusedIMEWidget != aWidget)) { 2451 MOZ_LOG(sISMLog, LogLevel::Warning, 2452 (" NotifyIME(), WARNING, the received blur notification is " 2453 "ignored " 2454 "because it's not for current focused IME widget")); 2455 return NS_OK; 2456 } 2457 nsCOMPtr<nsIWidget> focusedIMEWidget(sFocusedIMEWidget); 2458 sFocusedIMEWidget = nullptr; 2459 sFocusedIMEBrowserParent = nullptr; 2460 return CanSendNotificationToWidget() 2461 ? focusedIMEWidget->NotifyIME( 2462 IMENotification(NOTIFY_IME_OF_BLUR)) 2463 : NS_OK; 2464 } 2465 case NOTIFY_IME_OF_SELECTION_CHANGE: 2466 case NOTIFY_IME_OF_TEXT_CHANGE: 2467 case NOTIFY_IME_OF_POSITION_CHANGE: 2468 case NOTIFY_IME_OF_MOUSE_BUTTON_EVENT: 2469 case NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED: { 2470 if (aBrowserParent != sFocusedIMEBrowserParent) { 2471 MOZ_LOG( 2472 sISMLog, LogLevel::Warning, 2473 (" NotifyIME(), WARNING, the received content change notification " 2474 "is ignored because it's not from current focused IME browser")); 2475 return NS_OK; 2476 } 2477 if (!sFocusedIMEWidget) { 2478 MOZ_LOG( 2479 sISMLog, LogLevel::Warning, 2480 (" NotifyIME(), WARNING, the received content change notification " 2481 "is ignored because there is no focused IME widget")); 2482 return NS_OK; 2483 } 2484 if (NS_WARN_IF(sFocusedIMEWidget != aWidget)) { 2485 MOZ_LOG( 2486 sISMLog, LogLevel::Warning, 2487 (" NotifyIME(), WARNING, the received content change notification " 2488 "is ignored because it's not for current focused IME widget")); 2489 return NS_OK; 2490 } 2491 if (!CanSendNotificationToWidget()) { 2492 return NS_OK; 2493 } 2494 nsCOMPtr<nsIWidget> widget(aWidget); 2495 return widget->NotifyIME(aNotification); 2496 } 2497 default: 2498 // Other notifications should be sent only when there is composition. 2499 // So, we need to handle the others below. 2500 break; 2501 } 2502 2503 if (!sTextCompositions) { 2504 MOZ_LOG(sISMLog, LogLevel::Info, 2505 (" NotifyIME(), the request to IME is ignored because " 2506 "there have been no compositions yet")); 2507 return NS_OK; 2508 } 2509 2510 RefPtr<TextComposition> composition = 2511 sTextCompositions->GetCompositionFor(aWidget); 2512 if (!composition) { 2513 MOZ_LOG(sISMLog, LogLevel::Info, 2514 (" NotifyIME(), the request to IME is ignored because " 2515 "there is no active composition")); 2516 return NS_OK; 2517 } 2518 2519 if (aBrowserParent != composition->GetBrowserParent()) { 2520 MOZ_LOG( 2521 sISMLog, LogLevel::Warning, 2522 (" NotifyIME(), WARNING, the request to IME is ignored because " 2523 "it does not come from the remote browser which has the composition " 2524 "on aWidget")); 2525 return NS_OK; 2526 } 2527 2528 switch (aNotification.mMessage) { 2529 case REQUEST_TO_COMMIT_COMPOSITION: 2530 return composition->RequestToCommit(aWidget, false); 2531 case REQUEST_TO_CANCEL_COMPOSITION: 2532 return composition->RequestToCommit(aWidget, true); 2533 default: 2534 MOZ_CRASH("Unsupported notification"); 2535 } 2536 MOZ_CRASH( 2537 "Failed to handle the notification for non-synthesized composition"); 2538 return NS_ERROR_FAILURE; 2539 } 2540 2541 // static 2542 nsresult IMEStateManager::NotifyIME(IMEMessage aMessage, 2543 nsPresContext* aPresContext, 2544 BrowserParent* aBrowserParent) { 2545 MOZ_LOG(sISMLog, LogLevel::Info, 2546 ("NotifyIME(aMessage=%s, aPresContext=0x%p, aBrowserParent=0x%p)", 2547 ToChar(aMessage), aPresContext, aBrowserParent)); 2548 // This assertion is just for clarify which message may be set, so feel free 2549 // to add other messages if you want. 2550 MOZ_ASSERT(aMessage == REQUEST_TO_CANCEL_COMPOSITION || 2551 aMessage == REQUEST_TO_COMMIT_COMPOSITION || 2552 aMessage == NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED); 2553 // However, these messages require additional information. Therefore, this 2554 // overload shouldn't be used for them. 2555 MOZ_ASSERT(aMessage != NOTIFY_IME_OF_FOCUS && 2556 aMessage != NOTIFY_IME_OF_BLUR && 2557 aMessage != NOTIFY_IME_OF_TEXT_CHANGE && 2558 aMessage != NOTIFY_IME_OF_SELECTION_CHANGE && 2559 aMessage != NOTIFY_IME_OF_MOUSE_BUTTON_EVENT); 2560 2561 if (NS_WARN_IF(!CanHandleWith(aPresContext))) { 2562 return NS_ERROR_INVALID_ARG; 2563 } 2564 2565 nsCOMPtr<nsIWidget> widget = 2566 aPresContext == sFocusedPresContext && sTextInputHandlingWidget 2567 ? sTextInputHandlingWidget 2568 : aPresContext->GetTextInputHandlingWidget(); 2569 if (NS_WARN_IF(!widget)) { 2570 MOZ_LOG(sISMLog, LogLevel::Error, 2571 (" NotifyIME(), FAILED due to no widget for the nsPresContext")); 2572 return NS_ERROR_NOT_AVAILABLE; 2573 } 2574 return NotifyIME(aMessage, widget, aBrowserParent); 2575 } 2576 2577 // static 2578 bool IMEStateManager::IsIMEObserverNeeded(const IMEState& aState) { 2579 return aState.IsEditable(); 2580 } 2581 2582 // static 2583 void IMEStateManager::DestroyIMEContentObserver() { 2584 if (!sActiveIMEContentObserver) { 2585 MOZ_LOG(sISMLog, LogLevel::Verbose, 2586 ("DestroyIMEContentObserver() does nothing")); 2587 return; 2588 } 2589 2590 MOZ_LOG(sISMLog, LogLevel::Info, 2591 ("DestroyIMEContentObserver(), destroying " 2592 "the active IMEContentObserver...")); 2593 RefPtr<IMEContentObserver> tsm = sActiveIMEContentObserver.get(); 2594 sActiveIMEContentObserver = nullptr; 2595 tsm->Destroy(); 2596 } 2597 2598 // static 2599 void IMEStateManager::CreateIMEContentObserver(EditorBase& aEditorBase, 2600 Element* aFocusedElement) { 2601 MOZ_ASSERT(!sActiveIMEContentObserver); 2602 MOZ_ASSERT(sTextInputHandlingWidget); 2603 MOZ_ASSERT(sFocusedPresContext); 2604 MOZ_ASSERT(IsIMEObserverNeeded( 2605 sTextInputHandlingWidget->GetInputContext().mIMEState)); 2606 2607 MOZ_LOG(sISMLog, LogLevel::Info, 2608 ("CreateIMEContentObserver(aEditorBase=0x%p, aFocusedElement=0x%p), " 2609 "sFocusedPresContext=0x%p, sFocusedElement=0x%p, " 2610 "sTextInputHandlingWidget=0x%p (available: %s), " 2611 "sActiveIMEContentObserver=0x%p, " 2612 "sActiveIMEContentObserver->IsObserving(sFocusedPresContext, " 2613 "sFocusedElement)=%s", 2614 &aEditorBase, aFocusedElement, sFocusedPresContext.get(), 2615 sFocusedElement.get(), sTextInputHandlingWidget, 2616 TrueOrFalse(sTextInputHandlingWidget && 2617 !sTextInputHandlingWidget->Destroyed()), 2618 sActiveIMEContentObserver.get(), 2619 TrueOrFalse(sActiveIMEContentObserver && sFocusedPresContext && 2620 sActiveIMEContentObserver->IsObserving( 2621 *sFocusedPresContext, sFocusedElement)))); 2622 2623 if (NS_WARN_IF(sTextInputHandlingWidget->Destroyed())) { 2624 MOZ_LOG(sISMLog, LogLevel::Error, 2625 (" CreateIMEContentObserver(), FAILED due to " 2626 "the widget for the nsPresContext has gone")); 2627 return; 2628 } 2629 2630 const OwningNonNull<nsIWidget> textInputHandlingWidget = 2631 *sTextInputHandlingWidget; 2632 MOZ_ASSERT_IF(sFocusedPresContext->GetTextInputHandlingWidget(), 2633 sFocusedPresContext->GetTextInputHandlingWidget() == 2634 textInputHandlingWidget.get()); 2635 2636 MOZ_LOG(sISMLog, LogLevel::Debug, 2637 (" CreateIMEContentObserver() is creating an " 2638 "IMEContentObserver instance...")); 2639 sActiveIMEContentObserver = new IMEContentObserver(); 2640 2641 // IMEContentObserver::Init() might create another IMEContentObserver 2642 // instance. So, sActiveIMEContentObserver would be replaced with new one. 2643 // We should hold the current instance here. 2644 OwningNonNull<IMEContentObserver> activeIMEContentObserver = 2645 *sActiveIMEContentObserver; 2646 OwningNonNull<nsPresContext> focusedPresContext = *sFocusedPresContext; 2647 RefPtr<Element> focusedElement = aFocusedElement; 2648 activeIMEContentObserver->Init(textInputHandlingWidget, focusedPresContext, 2649 focusedElement, aEditorBase); 2650 } 2651 2652 // static 2653 nsresult IMEStateManager::GetFocusSelectionAndRootElement( 2654 Selection** aSelection, Element** aRootElement) { 2655 if (!sActiveIMEContentObserver) { 2656 return NS_ERROR_NOT_AVAILABLE; 2657 } 2658 return sActiveIMEContentObserver->GetSelectionAndRoot(aSelection, 2659 aRootElement); 2660 } 2661 2662 // static 2663 TextComposition* IMEStateManager::GetTextCompositionFor(nsIWidget* aWidget) { 2664 return sTextCompositions ? sTextCompositions->GetCompositionFor(aWidget) 2665 : nullptr; 2666 } 2667 2668 // static 2669 TextComposition* IMEStateManager::GetTextCompositionFor( 2670 const WidgetCompositionEvent* aCompositionEvent) { 2671 return sTextCompositions 2672 ? sTextCompositions->GetCompositionFor(aCompositionEvent) 2673 : nullptr; 2674 } 2675 2676 // static 2677 TextComposition* IMEStateManager::GetTextCompositionFor( 2678 nsPresContext* aPresContext) { 2679 return sTextCompositions ? sTextCompositions->GetCompositionFor(aPresContext) 2680 : nullptr; 2681 } 2682 2683 } // namespace mozilla