Bug 1686668 [wpt PR 27185] - Update wpt metadata, a=testonly
[gecko.git] / dom / events / IMEContentObserver.cpp
blob9b806ed204d3b7eda5630bec542d55a1340cb376
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/Logging.h"
9 #include "ContentEventHandler.h"
10 #include "IMEContentObserver.h"
11 #include "mozilla/AsyncEventDispatcher.h"
12 #include "mozilla/AutoRestore.h"
13 #include "mozilla/EventStateManager.h"
14 #include "mozilla/IMEStateManager.h"
15 #include "mozilla/MouseEvents.h"
16 #include "mozilla/PresShell.h"
17 #include "mozilla/TextComposition.h"
18 #include "mozilla/TextEvents.h"
19 #include "mozilla/dom/Element.h"
20 #include "mozilla/dom/Selection.h"
21 #include "nsContentUtils.h"
22 #include "nsGkAtoms.h"
23 #include "nsAtom.h"
24 #include "nsDocShell.h"
25 #include "nsIContent.h"
26 #include "mozilla/dom/Document.h"
27 #include "nsIFrame.h"
28 #include "nsINode.h"
29 #include "nsISelectionController.h"
30 #include "nsISupports.h"
31 #include "nsIWeakReferenceUtils.h"
32 #include "nsIWidget.h"
33 #include "nsPresContext.h"
34 #include "nsRange.h"
35 #include "nsRefreshDriver.h"
36 #include "WritingModes.h"
38 namespace mozilla {
40 typedef ContentEventHandler::NodePosition NodePosition;
41 typedef ContentEventHandler::NodePositionBefore NodePositionBefore;
43 using namespace widget;
45 LazyLogModule sIMECOLog("IMEContentObserver");
47 static const char* ToChar(bool aBool) { return aBool ? "true" : "false"; }
49 // This method determines the node to use for the point before the current node.
50 // If you have the following aContent and aContainer, and want to represent the
51 // following point for `NodePosition` or `RangeBoundary`:
53 // <parent> {node} {node} | {node} </parent>
54 // ^ ^ ^
55 // aContainer point aContent
57 // This function will shift `aContent` to the left into the format which
58 // `NodePosition` and `RangeBoundary` use:
60 // <parent> {node} {node} | {node} </parent>
61 // ^ ^ ^
62 // aContainer result point
63 static nsIContent* PointBefore(nsINode* aContainer, nsIContent* aContent) {
64 if (aContent) {
65 return aContent->GetPreviousSibling();
67 return aContainer->GetLastChild();
70 /******************************************************************************
71 * mozilla::IMEContentObserver
72 ******************************************************************************/
74 NS_IMPL_CYCLE_COLLECTION_CLASS(IMEContentObserver)
76 // Note that we don't need to add mFirstAddedContainer nor
77 // mLastAddedContainer to cycle collection because they are non-null only
78 // during short time and shouldn't be touched while they are non-null.
80 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IMEContentObserver)
81 nsAutoScriptBlocker scriptBlocker;
83 tmp->NotifyIMEOfBlur();
84 tmp->UnregisterObservers();
86 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelection)
87 NS_IMPL_CYCLE_COLLECTION_UNLINK(mRootContent)
88 NS_IMPL_CYCLE_COLLECTION_UNLINK(mEditableNode)
89 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocShell)
90 NS_IMPL_CYCLE_COLLECTION_UNLINK(mEditorBase)
91 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentObserver)
92 NS_IMPL_CYCLE_COLLECTION_UNLINK(mEndOfAddedTextCache.mContainerNode)
93 NS_IMPL_CYCLE_COLLECTION_UNLINK(mStartOfRemovingTextRangeCache.mContainerNode)
94 NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE
96 tmp->mIMENotificationRequests = nullptr;
97 tmp->mESM = nullptr;
98 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
100 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IMEContentObserver)
101 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWidget)
102 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFocusedWidget)
103 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelection)
104 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRootContent)
105 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEditableNode)
106 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocShell)
107 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEditorBase)
108 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentObserver)
109 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEndOfAddedTextCache.mContainerNode)
110 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(
111 mStartOfRemovingTextRangeCache.mContainerNode)
112 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
114 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IMEContentObserver)
115 NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
116 NS_INTERFACE_MAP_ENTRY(nsIReflowObserver)
117 NS_INTERFACE_MAP_ENTRY(nsIScrollObserver)
118 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
119 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIReflowObserver)
120 NS_INTERFACE_MAP_END
122 NS_IMPL_CYCLE_COLLECTING_ADDREF(IMEContentObserver)
123 NS_IMPL_CYCLE_COLLECTING_RELEASE(IMEContentObserver)
125 IMEContentObserver::IMEContentObserver()
126 : mESM(nullptr),
127 mIMENotificationRequests(nullptr),
128 mSuppressNotifications(0),
129 mPreCharacterDataChangeLength(-1),
130 mSendingNotification(NOTIFY_IME_OF_NOTHING),
131 mIsObserving(false),
132 mIMEHasFocus(false),
133 mNeedsToNotifyIMEOfFocusSet(false),
134 mNeedsToNotifyIMEOfTextChange(false),
135 mNeedsToNotifyIMEOfSelectionChange(false),
136 mNeedsToNotifyIMEOfPositionChange(false),
137 mNeedsToNotifyIMEOfCompositionEventHandled(false),
138 mIsHandlingQueryContentEvent(false) {
139 #ifdef DEBUG
140 mTextChangeData.Test();
141 #endif
144 void IMEContentObserver::Init(nsIWidget& aWidget, nsPresContext& aPresContext,
145 nsIContent* aContent, EditorBase& aEditorBase) {
146 State state = GetState();
147 if (NS_WARN_IF(state == eState_Observing)) {
148 return; // Nothing to do.
151 bool firstInitialization = state != eState_StoppedObserving;
152 if (!firstInitialization) {
153 // If this is now trying to initialize with new contents, all observers
154 // should be registered again for simpler implementation.
155 UnregisterObservers();
156 Clear();
159 mESM = aPresContext.EventStateManager();
160 mESM->OnStartToObserveContent(this);
162 mWidget = &aWidget;
163 mIMENotificationRequests = &mWidget->IMENotificationRequestsRef();
165 if (!InitWithEditor(aPresContext, aContent, aEditorBase)) {
166 Clear();
167 return;
170 if (firstInitialization) {
171 // Now, try to send NOTIFY_IME_OF_FOCUS to IME via the widget.
172 MaybeNotifyIMEOfFocusSet();
173 // When this is called first time, IME has not received NOTIFY_IME_OF_FOCUS
174 // yet since NOTIFY_IME_OF_FOCUS will be sent to widget asynchronously.
175 // So, we need to do nothing here. After NOTIFY_IME_OF_FOCUS has been
176 // sent, OnIMEReceivedFocus() will be called and content, selection and/or
177 // position changes will be observed
178 return;
181 // When this is called after editor reframing (i.e., the root editable node
182 // is also recreated), IME has usually received NOTIFY_IME_OF_FOCUS. In this
183 // case, we need to restart to observe content, selection and/or position
184 // changes in new root editable node.
185 ObserveEditableNode();
187 if (!NeedsToNotifyIMEOfSomething()) {
188 return;
191 // Some change events may wait to notify IME because this was being
192 // initialized. It is the time to flush them.
193 FlushMergeableNotifications();
196 void IMEContentObserver::OnIMEReceivedFocus() {
197 // While Init() notifies IME of focus, pending layout may be flushed
198 // because the notification may cause querying content. Then, recursive
199 // call of Init() with the latest content may occur. In such case, we
200 // shouldn't keep first initialization which notified IME of focus.
201 if (GetState() != eState_Initializing) {
202 return;
205 // NOTIFY_IME_OF_FOCUS might cause recreating IMEContentObserver
206 // instance via IMEStateManager::UpdateIMEState(). So, this
207 // instance might already have been destroyed, check it.
208 if (!mRootContent) {
209 return;
212 // Start to observe which is needed by IME when IME actually has focus.
213 ObserveEditableNode();
215 if (!NeedsToNotifyIMEOfSomething()) {
216 return;
219 // Some change events may wait to notify IME because this was being
220 // initialized. It is the time to flush them.
221 FlushMergeableNotifications();
224 bool IMEContentObserver::InitWithEditor(nsPresContext& aPresContext,
225 nsIContent* aContent,
226 EditorBase& aEditorBase) {
227 mEditableNode = IMEStateManager::GetRootEditableNode(&aPresContext, aContent);
228 if (NS_WARN_IF(!mEditableNode)) {
229 return false;
232 mEditorBase = &aEditorBase;
234 RefPtr<PresShell> presShell = aPresContext.GetPresShell();
236 // get selection and root content
237 nsCOMPtr<nsISelectionController> selCon;
238 if (mEditableNode->IsContent()) {
239 nsIFrame* frame = mEditableNode->AsContent()->GetPrimaryFrame();
240 if (NS_WARN_IF(!frame)) {
241 return false;
244 frame->GetSelectionController(&aPresContext, getter_AddRefs(selCon));
245 } else {
246 // mEditableNode is a document
247 selCon = presShell;
250 if (NS_WARN_IF(!selCon)) {
251 return false;
254 mSelection = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL);
255 if (NS_WARN_IF(!mSelection)) {
256 return false;
259 if (const nsRange* selRange = mSelection->GetRangeAt(0)) {
260 if (NS_WARN_IF(!selRange->GetStartContainer())) {
261 return false;
264 nsCOMPtr<nsINode> startContainer = selRange->GetStartContainer();
265 mRootContent = startContainer->GetSelectionRootContent(presShell);
266 } else {
267 nsCOMPtr<nsINode> editableNode = mEditableNode;
268 mRootContent = editableNode->GetSelectionRootContent(presShell);
270 if (!mRootContent && mEditableNode->IsDocument()) {
271 // The document node is editable, but there are no contents, this document
272 // is not editable.
273 return false;
276 if (NS_WARN_IF(!mRootContent)) {
277 return false;
280 mDocShell = aPresContext.GetDocShell();
281 if (NS_WARN_IF(!mDocShell)) {
282 return false;
285 mDocumentObserver = new DocumentObserver(*this);
287 return true;
290 void IMEContentObserver::Clear() {
291 mEditorBase = nullptr;
292 mSelection = nullptr;
293 mEditableNode = nullptr;
294 mRootContent = nullptr;
295 mDocShell = nullptr;
296 // Should be safe to clear mDocumentObserver here even though it grabs
297 // this instance in most cases because this is called by Init() or Destroy().
298 // The callers of Init() grab this instance with local RefPtr.
299 // The caller of Destroy() also grabs this instance with local RefPtr.
300 // So, this won't cause refcount of this instance become 0.
301 mDocumentObserver = nullptr;
304 void IMEContentObserver::ObserveEditableNode() {
305 MOZ_RELEASE_ASSERT(mSelection);
306 MOZ_RELEASE_ASSERT(mRootContent);
307 MOZ_RELEASE_ASSERT(GetState() != eState_Observing);
309 // If this is called before sending NOTIFY_IME_OF_FOCUS (it's possible when
310 // the editor is reframed before sending NOTIFY_IME_OF_FOCUS asynchronously),
311 // the notification requests of mWidget may be different from after the widget
312 // receives NOTIFY_IME_OF_FOCUS. So, this should be called again by
313 // OnIMEReceivedFocus() which is called after sending NOTIFY_IME_OF_FOCUS.
314 if (!mIMEHasFocus) {
315 MOZ_ASSERT(!mWidget || mNeedsToNotifyIMEOfFocusSet ||
316 mSendingNotification == NOTIFY_IME_OF_FOCUS,
317 "Wow, OnIMEReceivedFocus() won't be called?");
318 return;
321 mIsObserving = true;
322 if (mEditorBase) {
323 mEditorBase->SetIMEContentObserver(this);
326 mRootContent->AddMutationObserver(this);
327 // If it's in a document (should be so), we can use document observer to
328 // reduce redundant computation of text change offsets.
329 dom::Document* doc = mRootContent->GetComposedDoc();
330 if (doc) {
331 RefPtr<DocumentObserver> documentObserver = mDocumentObserver;
332 documentObserver->Observe(doc);
335 if (mDocShell) {
336 // Add scroll position listener and reflow observer to detect position
337 // and size changes
338 mDocShell->AddWeakScrollObserver(this);
339 mDocShell->AddWeakReflowObserver(this);
343 void IMEContentObserver::NotifyIMEOfBlur() {
344 // Prevent any notifications to be sent IME.
345 nsCOMPtr<nsIWidget> widget;
346 mWidget.swap(widget);
347 mIMENotificationRequests = nullptr;
349 // If we hasn't been set focus, we shouldn't send blur notification to IME.
350 if (!mIMEHasFocus) {
351 return;
354 // mWidget must have been non-nullptr if IME has focus.
355 MOZ_RELEASE_ASSERT(widget);
357 RefPtr<IMEContentObserver> kungFuDeathGrip(this);
359 MOZ_LOG(sIMECOLog, LogLevel::Info,
360 ("0x%p IMEContentObserver::NotifyIMEOfBlur(), "
361 "sending NOTIFY_IME_OF_BLUR",
362 this));
364 // For now, we need to send blur notification in any condition because
365 // we don't have any simple ways to send blur notification asynchronously.
366 // After this call, Destroy() or Unlink() will stop observing the content
367 // and forget everything. Therefore, if it's not safe to send notification
368 // when script blocker is unlocked, we cannot send blur notification after
369 // that and before next focus notification.
370 // Anyway, as far as we know, IME doesn't try to query content when it loses
371 // focus. So, this may not cause any problem.
372 mIMEHasFocus = false;
373 IMEStateManager::NotifyIME(IMENotification(NOTIFY_IME_OF_BLUR), widget);
375 MOZ_LOG(sIMECOLog, LogLevel::Debug,
376 ("0x%p IMEContentObserver::NotifyIMEOfBlur(), "
377 "sent NOTIFY_IME_OF_BLUR",
378 this));
381 void IMEContentObserver::UnregisterObservers() {
382 if (!mIsObserving) {
383 return;
385 mIsObserving = false;
387 if (mEditorBase) {
388 mEditorBase->SetIMEContentObserver(nullptr);
391 if (mSelection) {
392 mSelectionData.Clear();
393 mFocusedWidget = nullptr;
396 if (mRootContent) {
397 mRootContent->RemoveMutationObserver(this);
400 if (mDocumentObserver) {
401 RefPtr<DocumentObserver> documentObserver = mDocumentObserver;
402 documentObserver->StopObserving();
405 if (mDocShell) {
406 mDocShell->RemoveWeakScrollObserver(this);
407 mDocShell->RemoveWeakReflowObserver(this);
411 nsPresContext* IMEContentObserver::GetPresContext() const {
412 return mESM ? mESM->GetPresContext() : nullptr;
415 void IMEContentObserver::Destroy() {
416 // WARNING: When you change this method, you have to check Unlink() too.
418 // Note that don't send any notifications later from here. I.e., notify
419 // IMEStateManager of the blur synchronously because IMEStateManager needs to
420 // stop notifying the main process if this is requested by the main process.
421 NotifyIMEOfBlur();
422 UnregisterObservers();
423 Clear();
425 mWidget = nullptr;
426 mIMENotificationRequests = nullptr;
428 if (mESM) {
429 mESM->OnStopObservingContent(this);
430 mESM = nullptr;
434 bool IMEContentObserver::Destroyed() const { return !mWidget; }
436 void IMEContentObserver::DisconnectFromEventStateManager() { mESM = nullptr; }
438 bool IMEContentObserver::MaybeReinitialize(nsIWidget& aWidget,
439 nsPresContext& aPresContext,
440 nsIContent* aContent,
441 EditorBase& aEditorBase) {
442 if (!IsObservingContent(&aPresContext, aContent)) {
443 return false;
446 if (GetState() == eState_StoppedObserving) {
447 Init(aWidget, aPresContext, aContent, aEditorBase);
449 return IsManaging(&aPresContext, aContent);
452 bool IMEContentObserver::IsManaging(nsPresContext* aPresContext,
453 nsIContent* aContent) const {
454 return GetState() == eState_Observing &&
455 IsObservingContent(aPresContext, aContent);
458 bool IMEContentObserver::IsManaging(const TextComposition* aComposition) const {
459 if (GetState() != eState_Observing) {
460 return false;
462 nsPresContext* presContext = aComposition->GetPresContext();
463 if (NS_WARN_IF(!presContext)) {
464 return false;
466 if (presContext != GetPresContext()) {
467 return false; // observing different document
469 nsINode* targetNode = aComposition->GetEventTargetNode();
470 nsIContent* targetContent =
471 targetNode && targetNode->IsContent() ? targetNode->AsContent() : nullptr;
472 return IsObservingContent(presContext, targetContent);
475 IMEContentObserver::State IMEContentObserver::GetState() const {
476 if (!mSelection || !mRootContent || !mEditableNode) {
477 return eState_NotObserving; // failed to initialize or finalized.
479 if (!mRootContent->IsInComposedDoc()) {
480 // the focused editor has already been reframed.
481 return eState_StoppedObserving;
483 return mIsObserving ? eState_Observing : eState_Initializing;
486 bool IMEContentObserver::IsObservingContent(nsPresContext* aPresContext,
487 nsIContent* aContent) const {
488 return mEditableNode ==
489 IMEStateManager::GetRootEditableNode(aPresContext, aContent);
492 bool IMEContentObserver::IsEditorHandlingEventForComposition() const {
493 if (!mWidget) {
494 return false;
496 RefPtr<TextComposition> composition =
497 IMEStateManager::GetTextCompositionFor(mWidget);
498 if (!composition) {
499 return false;
501 return composition->IsEditorHandlingEvent();
504 bool IMEContentObserver::IsEditorComposing() const {
505 // Note that don't use TextComposition here. The important thing is,
506 // whether the editor already started to handle composition because
507 // web contents can change selection, text content and/or something from
508 // compositionstart event listener which is run before EditorBase handles it.
509 if (NS_WARN_IF(!mEditorBase)) {
510 return false;
512 return mEditorBase->IsIMEComposing();
515 nsresult IMEContentObserver::GetSelectionAndRoot(
516 dom::Selection** aSelection, nsIContent** aRootContent) const {
517 if (!mEditableNode || !mSelection) {
518 return NS_ERROR_NOT_AVAILABLE;
521 NS_ASSERTION(mSelection && mRootContent, "uninitialized content observer");
522 NS_ADDREF(*aSelection = mSelection);
523 NS_ADDREF(*aRootContent = mRootContent);
524 return NS_OK;
527 void IMEContentObserver::OnSelectionChange(dom::Selection& aSelection) {
528 if (!mIsObserving) {
529 return;
532 if (aSelection.RangeCount() && mWidget) {
533 bool causedByComposition = IsEditorHandlingEventForComposition();
534 bool causedBySelectionEvent = TextComposition::IsHandlingSelectionEvent();
535 bool duringComposition = IsEditorComposing();
536 MaybeNotifyIMEOfSelectionChange(causedByComposition, causedBySelectionEvent,
537 duringComposition);
541 void IMEContentObserver::ScrollPositionChanged() {
542 if (!NeedsPositionChangeNotification()) {
543 return;
546 MaybeNotifyIMEOfPositionChange();
549 NS_IMETHODIMP
550 IMEContentObserver::Reflow(DOMHighResTimeStamp aStart,
551 DOMHighResTimeStamp aEnd) {
552 if (!NeedsPositionChangeNotification()) {
553 return NS_OK;
556 MaybeNotifyIMEOfPositionChange();
557 return NS_OK;
560 NS_IMETHODIMP
561 IMEContentObserver::ReflowInterruptible(DOMHighResTimeStamp aStart,
562 DOMHighResTimeStamp aEnd) {
563 if (!NeedsPositionChangeNotification()) {
564 return NS_OK;
567 MaybeNotifyIMEOfPositionChange();
568 return NS_OK;
571 nsresult IMEContentObserver::HandleQueryContentEvent(
572 WidgetQueryContentEvent* aEvent) {
573 // If the instance has normal selection cache and the query event queries
574 // normal selection's range, it should use the cached selection which was
575 // sent to the widget. However, if this instance has already received new
576 // selection change notification but hasn't updated the cache yet (i.e.,
577 // not sending selection change notification to IME, don't use the cached
578 // value. Note that don't update selection cache here since if you update
579 // selection cache here, IMENotificationSender won't notify IME of selection
580 // change because it looks like that the selection isn't actually changed.
581 bool isSelectionCacheAvailable = aEvent->mUseNativeLineBreak &&
582 mSelectionData.IsValid() &&
583 !mNeedsToNotifyIMEOfSelectionChange;
584 if (isSelectionCacheAvailable && aEvent->mMessage == eQuerySelectedText &&
585 aEvent->mInput.mSelectionType == SelectionType::eNormal) {
586 aEvent->EmplaceReply();
587 aEvent->mReply->mOffsetAndData.emplace(mSelectionData.mOffset,
588 mSelectionData.String(),
589 OffsetAndDataFor::SelectedString);
590 aEvent->mReply->mContentsRoot = mRootContent;
591 aEvent->mReply->mHasSelection = !mSelectionData.IsCollapsed();
592 aEvent->mReply->mWritingMode = mSelectionData.GetWritingMode();
593 aEvent->mReply->mReversed = mSelectionData.mReversed;
594 MOZ_LOG(sIMECOLog, LogLevel::Debug,
595 ("0x%p IMEContentObserver::HandleQueryContentEvent(aEvent={ "
596 "mMessage=%s, mReply=%s })",
597 this, ToChar(aEvent->mMessage), ToString(aEvent->mReply).c_str()));
598 return NS_OK;
601 MOZ_LOG(sIMECOLog, LogLevel::Debug,
602 ("0x%p IMEContentObserver::HandleQueryContentEvent(aEvent={ "
603 "mMessage=%s })",
604 this, ToChar(aEvent->mMessage)));
606 // If we can make the event's input offset absolute with TextComposition or
607 // mSelection, we should set it here for reducing the cost of computing
608 // selection start offset. If ContentEventHandler receives a
609 // WidgetQueryContentEvent whose input offset is relative to insertion point,
610 // it computes current selection start offset (this may be expensive) and
611 // make the offset absolute value itself.
612 // Note that calling MakeOffsetAbsolute() makes the event a query event with
613 // absolute offset. So, ContentEventHandler doesn't pay any additional cost
614 // after calling MakeOffsetAbsolute() here.
615 if (aEvent->mInput.mRelativeToInsertionPoint &&
616 aEvent->mInput.IsValidEventMessage(aEvent->mMessage)) {
617 RefPtr<TextComposition> composition =
618 IMEStateManager::GetTextCompositionFor(aEvent->mWidget);
619 if (composition) {
620 uint32_t compositionStart = composition->NativeOffsetOfStartComposition();
621 if (NS_WARN_IF(!aEvent->mInput.MakeOffsetAbsolute(compositionStart))) {
622 return NS_ERROR_FAILURE;
624 } else if (isSelectionCacheAvailable) {
625 uint32_t selectionStart = mSelectionData.mOffset;
626 if (NS_WARN_IF(!aEvent->mInput.MakeOffsetAbsolute(selectionStart))) {
627 return NS_ERROR_FAILURE;
632 AutoRestore<bool> handling(mIsHandlingQueryContentEvent);
633 mIsHandlingQueryContentEvent = true;
634 ContentEventHandler handler(GetPresContext());
635 nsresult rv = handler.HandleQueryContentEvent(aEvent);
636 if (NS_WARN_IF(Destroyed())) {
637 // If this has already destroyed during querying the content, the query
638 // is outdated even if it's succeeded. So, make the query fail.
639 aEvent->mReply.reset();
640 MOZ_LOG(sIMECOLog, LogLevel::Warning,
641 ("0x%p IMEContentObserver::HandleQueryContentEvent(), WARNING, "
642 "IMEContentObserver has been destroyed during the query, "
643 "making the query fail",
644 this));
645 return rv;
648 if (aEvent->Succeeded() &&
649 NS_WARN_IF(aEvent->mReply->mContentsRoot != mRootContent)) {
650 // Focus has changed unexpectedly, so make the query fail.
651 aEvent->mReply.reset();
653 return rv;
656 bool IMEContentObserver::OnMouseButtonEvent(nsPresContext* aPresContext,
657 WidgetMouseEvent* aMouseEvent) {
658 if (!mIMENotificationRequests ||
659 !mIMENotificationRequests->WantMouseButtonEventOnChar()) {
660 return false;
662 if (!aMouseEvent->IsTrusted() || aMouseEvent->DefaultPrevented() ||
663 !aMouseEvent->mWidget) {
664 return false;
666 // Now, we need to notify only mouse down and mouse up event.
667 switch (aMouseEvent->mMessage) {
668 case eMouseUp:
669 case eMouseDown:
670 break;
671 default:
672 return false;
674 if (NS_WARN_IF(!mWidget) || NS_WARN_IF(mWidget->Destroyed())) {
675 return false;
678 RefPtr<IMEContentObserver> kungFuDeathGrip(this);
680 WidgetQueryContentEvent queryCharAtPointEvent(true, eQueryCharacterAtPoint,
681 aMouseEvent->mWidget);
682 queryCharAtPointEvent.mRefPoint = aMouseEvent->mRefPoint;
683 ContentEventHandler handler(aPresContext);
684 handler.OnQueryCharacterAtPoint(&queryCharAtPointEvent);
685 if (NS_WARN_IF(queryCharAtPointEvent.Failed()) ||
686 queryCharAtPointEvent.DidNotFindChar()) {
687 return false;
690 // The widget might be destroyed during querying the content since it
691 // causes flushing layout.
692 if (!mWidget || NS_WARN_IF(mWidget->Destroyed())) {
693 return false;
696 // The result character rect is relative to the top level widget.
697 // We should notify it with offset in the widget.
698 nsIWidget* topLevelWidget = mWidget->GetTopLevelWidget();
699 if (topLevelWidget && topLevelWidget != mWidget) {
700 queryCharAtPointEvent.mReply->mRect.MoveBy(
701 topLevelWidget->WidgetToScreenOffset() -
702 mWidget->WidgetToScreenOffset());
704 // The refPt is relative to its widget.
705 // We should notify it with offset in the widget.
706 if (aMouseEvent->mWidget != mWidget) {
707 queryCharAtPointEvent.mRefPoint +=
708 aMouseEvent->mWidget->WidgetToScreenOffset() -
709 mWidget->WidgetToScreenOffset();
712 IMENotification notification(NOTIFY_IME_OF_MOUSE_BUTTON_EVENT);
713 notification.mMouseButtonEventData.mEventMessage = aMouseEvent->mMessage;
714 notification.mMouseButtonEventData.mOffset =
715 queryCharAtPointEvent.mReply->StartOffset();
716 notification.mMouseButtonEventData.mCursorPos.Set(
717 queryCharAtPointEvent.mRefPoint.ToUnknownPoint());
718 notification.mMouseButtonEventData.mCharRect.Set(
719 queryCharAtPointEvent.mReply->mRect.ToUnknownRect());
720 notification.mMouseButtonEventData.mButton = aMouseEvent->mButton;
721 notification.mMouseButtonEventData.mButtons = aMouseEvent->mButtons;
722 notification.mMouseButtonEventData.mModifiers = aMouseEvent->mModifiers;
724 nsresult rv = IMEStateManager::NotifyIME(notification, mWidget);
725 if (NS_WARN_IF(NS_FAILED(rv))) {
726 return false;
729 bool consumed = (rv == NS_SUCCESS_EVENT_CONSUMED);
730 if (consumed) {
731 aMouseEvent->PreventDefault();
733 return consumed;
736 void IMEContentObserver::CharacterDataWillChange(
737 nsIContent* aContent, const CharacterDataChangeInfo& aInfo) {
738 NS_ASSERTION(aContent->IsText(), "character data changed for non-text node");
739 MOZ_ASSERT(mPreCharacterDataChangeLength < 0,
740 "CharacterDataChanged() should've reset "
741 "mPreCharacterDataChangeLength");
743 if (!NeedsTextChangeNotification() ||
744 !nsContentUtils::IsInSameAnonymousTree(mRootContent, aContent)) {
745 return;
748 mEndOfAddedTextCache.Clear();
749 mStartOfRemovingTextRangeCache.Clear();
751 // Although we don't assume this change occurs while this is storing
752 // the range of added consecutive nodes, if it actually happens, we need to
753 // flush them since this change may occur before or in the range. So, it's
754 // safe to flush pending computation of mTextChangeData before handling this.
755 MaybeNotifyIMEOfAddedTextDuringDocumentChange();
757 mPreCharacterDataChangeLength = ContentEventHandler::GetNativeTextLength(
758 aContent, aInfo.mChangeStart, aInfo.mChangeEnd);
759 MOZ_ASSERT(
760 mPreCharacterDataChangeLength >= aInfo.mChangeEnd - aInfo.mChangeStart,
761 "The computed length must be same as or larger than XP length");
764 void IMEContentObserver::CharacterDataChanged(
765 nsIContent* aContent, const CharacterDataChangeInfo& aInfo) {
766 NS_ASSERTION(aContent->IsText(), "character data changed for non-text node");
768 if (!NeedsTextChangeNotification() ||
769 !nsContentUtils::IsInSameAnonymousTree(mRootContent, aContent)) {
770 return;
773 mEndOfAddedTextCache.Clear();
774 mStartOfRemovingTextRangeCache.Clear();
775 MOZ_ASSERT(
776 !HasAddedNodesDuringDocumentChange(),
777 "The stored range should be flushed before actually the data is changed");
779 int64_t removedLength = mPreCharacterDataChangeLength;
780 mPreCharacterDataChangeLength = -1;
782 MOZ_ASSERT(removedLength >= 0,
783 "mPreCharacterDataChangeLength should've been set by "
784 "CharacterDataWillChange()");
786 uint32_t offset = 0;
787 // get offsets of change and fire notification
788 nsresult rv = ContentEventHandler::GetFlatTextLengthInRange(
789 NodePosition(mRootContent, 0), NodePosition(aContent, aInfo.mChangeStart),
790 mRootContent, &offset, LINE_BREAK_TYPE_NATIVE);
791 if (NS_WARN_IF(NS_FAILED(rv))) {
792 return;
795 uint32_t newLength = ContentEventHandler::GetNativeTextLength(
796 aContent, aInfo.mChangeStart, aInfo.mChangeStart + aInfo.mReplaceLength);
798 uint32_t oldEnd = offset + static_cast<uint32_t>(removedLength);
799 uint32_t newEnd = offset + newLength;
801 TextChangeData data(offset, oldEnd, newEnd,
802 IsEditorHandlingEventForComposition(),
803 IsEditorComposing());
804 MaybeNotifyIMEOfTextChange(data);
807 void IMEContentObserver::NotifyContentAdded(nsINode* aContainer,
808 nsIContent* aFirstContent,
809 nsIContent* aLastContent) {
810 if (!NeedsTextChangeNotification() ||
811 !nsContentUtils::IsInSameAnonymousTree(mRootContent, aFirstContent)) {
812 return;
815 MOZ_ASSERT_IF(aFirstContent, aFirstContent->GetParentNode() == aContainer);
816 MOZ_ASSERT_IF(aLastContent, aLastContent->GetParentNode() == aContainer);
818 mStartOfRemovingTextRangeCache.Clear();
820 // If it's in a document change, nodes are added consecutively. Therefore,
821 // if we cache the first node and the last node, we need to compute the
822 // range once.
823 // FYI: This is not true if the change caused by an operation in the editor.
824 if (IsInDocumentChange()) {
825 // Now, mEndOfAddedTextCache may be invalid if node is added before
826 // the last node in mEndOfAddedTextCache. Clear it.
827 mEndOfAddedTextCache.Clear();
828 if (!HasAddedNodesDuringDocumentChange()) {
829 mFirstAddedContainer = mLastAddedContainer = aContainer;
830 mFirstAddedContent = aFirstContent;
831 mLastAddedContent = aLastContent;
832 MOZ_LOG(sIMECOLog, LogLevel::Debug,
833 ("0x%p IMEContentObserver::NotifyContentAdded(), starts to store "
834 "consecutive added nodes",
835 this));
836 return;
838 // If first node being added is not next node of the last node,
839 // notify IME of the previous range first, then, restart to cache the
840 // range.
841 if (NS_WARN_IF(!IsNextNodeOfLastAddedNode(aContainer, aFirstContent))) {
842 // Flush the old range first.
843 MaybeNotifyIMEOfAddedTextDuringDocumentChange();
844 mFirstAddedContainer = aContainer;
845 mFirstAddedContent = aFirstContent;
846 MOZ_LOG(sIMECOLog, LogLevel::Debug,
847 ("0x%p IMEContentObserver::NotifyContentAdded(), starts to store "
848 "consecutive added nodes",
849 this));
851 mLastAddedContainer = aContainer;
852 mLastAddedContent = aLastContent;
853 return;
855 MOZ_ASSERT(!HasAddedNodesDuringDocumentChange(),
856 "The cache should be cleared when document change finished");
858 uint32_t offset = 0;
859 nsresult rv = NS_OK;
860 if (!mEndOfAddedTextCache.Match(aContainer,
861 aFirstContent->GetPreviousSibling())) {
862 mEndOfAddedTextCache.Clear();
863 rv = ContentEventHandler::GetFlatTextLengthInRange(
864 NodePosition(mRootContent, 0),
865 NodePositionBefore(aContainer, PointBefore(aContainer, aFirstContent)),
866 mRootContent, &offset, LINE_BREAK_TYPE_NATIVE);
867 if (NS_WARN_IF(NS_FAILED((rv)))) {
868 return;
870 } else {
871 offset = mEndOfAddedTextCache.mFlatTextLength;
874 // get offset at the end of the last added node
875 uint32_t addingLength = 0;
876 rv = ContentEventHandler::GetFlatTextLengthInRange(
877 NodePositionBefore(aContainer, PointBefore(aContainer, aFirstContent)),
878 NodePosition(aContainer, aLastContent), mRootContent, &addingLength,
879 LINE_BREAK_TYPE_NATIVE);
880 if (NS_WARN_IF(NS_FAILED((rv)))) {
881 mEndOfAddedTextCache.Clear();
882 return;
885 // If multiple lines are being inserted in an HTML editor, next call of
886 // NotifyContentAdded() is for adding next node. Therefore, caching the text
887 // length can skip to compute the text length before the adding node and
888 // before of it.
889 mEndOfAddedTextCache.Cache(aContainer, aLastContent, offset + addingLength);
891 if (!addingLength) {
892 return;
895 TextChangeData data(offset, offset, offset + addingLength,
896 IsEditorHandlingEventForComposition(),
897 IsEditorComposing());
898 MaybeNotifyIMEOfTextChange(data);
901 void IMEContentObserver::ContentAppended(nsIContent* aFirstNewContent) {
902 nsIContent* parent = aFirstNewContent->GetParent();
903 MOZ_ASSERT(parent);
904 NotifyContentAdded(parent, aFirstNewContent, parent->GetLastChild());
907 void IMEContentObserver::ContentInserted(nsIContent* aChild) {
908 MOZ_ASSERT(aChild);
909 NotifyContentAdded(aChild->GetParentNode(), aChild, aChild);
912 void IMEContentObserver::ContentRemoved(nsIContent* aChild,
913 nsIContent* aPreviousSibling) {
914 if (!NeedsTextChangeNotification() ||
915 !nsContentUtils::IsInSameAnonymousTree(mRootContent, aChild)) {
916 return;
919 mEndOfAddedTextCache.Clear();
920 MaybeNotifyIMEOfAddedTextDuringDocumentChange();
922 nsINode* containerNode = aChild->GetParentNode();
923 MOZ_ASSERT(containerNode);
925 uint32_t offset = 0;
926 nsresult rv = NS_OK;
927 if (!mStartOfRemovingTextRangeCache.Match(containerNode, aPreviousSibling)) {
928 // At removing a child node of aContainer, we need the line break caused
929 // by open tag of aContainer. Be careful when aPreviousSibling is nullptr.
931 rv = ContentEventHandler::GetFlatTextLengthInRange(
932 NodePosition(mRootContent, 0),
933 NodePosition(containerNode, aPreviousSibling), mRootContent, &offset,
934 LINE_BREAK_TYPE_NATIVE);
935 if (NS_WARN_IF(NS_FAILED(rv))) {
936 mStartOfRemovingTextRangeCache.Clear();
937 return;
939 mStartOfRemovingTextRangeCache.Cache(containerNode, aPreviousSibling,
940 offset);
941 } else {
942 offset = mStartOfRemovingTextRangeCache.mFlatTextLength;
945 // get offset at the end of the deleted node
946 uint32_t textLength = 0;
947 if (aChild->IsText()) {
948 textLength = ContentEventHandler::GetNativeTextLength(aChild);
949 } else {
950 uint32_t nodeLength = static_cast<int32_t>(aChild->GetChildCount());
951 rv = ContentEventHandler::GetFlatTextLengthInRange(
952 NodePositionBefore(aChild, 0), NodePosition(aChild, nodeLength),
953 mRootContent, &textLength, LINE_BREAK_TYPE_NATIVE, true);
954 if (NS_WARN_IF(NS_FAILED(rv))) {
955 mStartOfRemovingTextRangeCache.Clear();
956 return;
960 if (!textLength) {
961 return;
964 TextChangeData data(offset, offset + textLength, offset,
965 IsEditorHandlingEventForComposition(),
966 IsEditorComposing());
967 MaybeNotifyIMEOfTextChange(data);
970 void IMEContentObserver::ClearAddedNodesDuringDocumentChange() {
971 mFirstAddedContainer = mLastAddedContainer = nullptr;
972 mFirstAddedContent = mLastAddedContent = nullptr;
973 MOZ_LOG(sIMECOLog, LogLevel::Debug,
974 ("0x%p IMEContentObserver::ClearAddedNodesDuringDocumentChange()"
975 ", finished storing consecutive nodes",
976 this));
979 bool IMEContentObserver::IsNextNodeOfLastAddedNode(nsINode* aParent,
980 nsIContent* aChild) const {
981 MOZ_ASSERT(aParent);
982 MOZ_ASSERT(aChild && aChild->GetParentNode() == aParent);
983 MOZ_ASSERT(mRootContent);
984 MOZ_ASSERT(HasAddedNodesDuringDocumentChange());
986 // If the parent node isn't changed, we can check that mLastAddedContent has
987 // aChild as its next sibling.
988 if (aParent == mLastAddedContainer) {
989 if (NS_WARN_IF(mLastAddedContent->GetNextSibling() != aChild)) {
990 return false;
992 return true;
995 // If the parent node is changed, that means that the recorded last added node
996 // shouldn't have a sibling.
997 if (NS_WARN_IF(mLastAddedContent->GetNextSibling())) {
998 return false;
1001 // If the node is aParent is a descendant of mLastAddedContainer,
1002 // aChild should be the first child in the new container.
1003 if (mLastAddedContainer == aParent->GetParent()) {
1004 if (NS_WARN_IF(aChild->GetPreviousSibling())) {
1005 return false;
1007 return true;
1010 // Otherwise, we need to check it even with slow path.
1011 nsIContent* nextContentOfLastAddedContent =
1012 mLastAddedContent->GetNextNode(mRootContent->GetParentNode());
1013 if (NS_WARN_IF(!nextContentOfLastAddedContent)) {
1014 return false;
1016 if (NS_WARN_IF(nextContentOfLastAddedContent != aChild)) {
1017 return false;
1019 return true;
1022 void IMEContentObserver::MaybeNotifyIMEOfAddedTextDuringDocumentChange() {
1023 if (!HasAddedNodesDuringDocumentChange()) {
1024 return;
1027 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1028 ("0x%p "
1029 "IMEContentObserver::MaybeNotifyIMEOfAddedTextDuringDocumentChange()"
1030 ", flushing stored consecutive nodes",
1031 this));
1033 // Notify IME of text change which is caused by added nodes now.
1035 // First, compute offset of start of first added node from start of the
1036 // editor.
1037 uint32_t offset;
1038 nsresult rv = ContentEventHandler::GetFlatTextLengthInRange(
1039 NodePosition(mRootContent, 0),
1040 NodePosition(mFirstAddedContainer,
1041 PointBefore(mFirstAddedContainer, mFirstAddedContent)),
1042 mRootContent, &offset, LINE_BREAK_TYPE_NATIVE);
1043 if (NS_WARN_IF(NS_FAILED(rv))) {
1044 ClearAddedNodesDuringDocumentChange();
1045 return;
1048 // Next, compute the text length of added nodes.
1049 uint32_t length;
1050 rv = ContentEventHandler::GetFlatTextLengthInRange(
1051 NodePosition(mFirstAddedContainer,
1052 PointBefore(mFirstAddedContainer, mFirstAddedContent)),
1053 NodePosition(mLastAddedContainer, mLastAddedContent), mRootContent,
1054 &length, LINE_BREAK_TYPE_NATIVE);
1055 if (NS_WARN_IF(NS_FAILED(rv))) {
1056 ClearAddedNodesDuringDocumentChange();
1057 return;
1060 // Finally, try to notify IME of the range.
1061 TextChangeData data(offset, offset, offset + length,
1062 IsEditorHandlingEventForComposition(),
1063 IsEditorComposing());
1064 MaybeNotifyIMEOfTextChange(data);
1065 ClearAddedNodesDuringDocumentChange();
1068 void IMEContentObserver::BeginDocumentUpdate() {
1069 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1070 ("0x%p IMEContentObserver::BeginDocumentUpdate(), "
1071 "HasAddedNodesDuringDocumentChange()=%s",
1072 this, ToChar(HasAddedNodesDuringDocumentChange())));
1074 // If we're not in a nested document update, this will return early,
1075 // otherwise, it will handle flusing any changes currently pending before
1076 // entering a nested document update.
1077 MaybeNotifyIMEOfAddedTextDuringDocumentChange();
1080 void IMEContentObserver::EndDocumentUpdate() {
1081 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1082 ("0x%p IMEContentObserver::EndDocumentUpdate(), "
1083 "HasAddedNodesDuringDocumentChange()=%s",
1084 this, ToChar(HasAddedNodesDuringDocumentChange())));
1086 MaybeNotifyIMEOfAddedTextDuringDocumentChange();
1089 void IMEContentObserver::SuppressNotifyingIME() {
1090 mSuppressNotifications++;
1092 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1093 ("0x%p IMEContentObserver::SuppressNotifyingIME(), "
1094 "mSuppressNotifications=%u",
1095 this, mSuppressNotifications));
1098 void IMEContentObserver::UnsuppressNotifyingIME() {
1099 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1100 ("0x%p IMEContentObserver::UnsuppressNotifyingIME(), "
1101 "mSuppressNotifications=%u",
1102 this, mSuppressNotifications));
1104 if (!mSuppressNotifications || --mSuppressNotifications) {
1105 return;
1107 FlushMergeableNotifications();
1110 void IMEContentObserver::OnEditActionHandled() {
1111 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1112 ("0x%p IMEContentObserver::EditAction()", this));
1114 mEndOfAddedTextCache.Clear();
1115 mStartOfRemovingTextRangeCache.Clear();
1116 FlushMergeableNotifications();
1119 void IMEContentObserver::BeforeEditAction() {
1120 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1121 ("0x%p IMEContentObserver::BeforeEditAction()", this));
1123 mEndOfAddedTextCache.Clear();
1124 mStartOfRemovingTextRangeCache.Clear();
1127 void IMEContentObserver::CancelEditAction() {
1128 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1129 ("0x%p IMEContentObserver::CancelEditAction()", this));
1131 mEndOfAddedTextCache.Clear();
1132 mStartOfRemovingTextRangeCache.Clear();
1133 FlushMergeableNotifications();
1136 void IMEContentObserver::PostFocusSetNotification() {
1137 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1138 ("0x%p IMEContentObserver::PostFocusSetNotification()", this));
1140 mNeedsToNotifyIMEOfFocusSet = true;
1143 void IMEContentObserver::PostTextChangeNotification() {
1144 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1145 ("0x%p IMEContentObserver::PostTextChangeNotification("
1146 "mTextChangeData=%s)",
1147 this, ToString(mTextChangeData).c_str()));
1149 MOZ_ASSERT(mTextChangeData.IsValid(),
1150 "mTextChangeData must have text change data");
1151 mNeedsToNotifyIMEOfTextChange = true;
1154 void IMEContentObserver::PostSelectionChangeNotification() {
1155 MOZ_LOG(
1156 sIMECOLog, LogLevel::Debug,
1157 ("0x%p IMEContentObserver::PostSelectionChangeNotification(), "
1158 "mSelectionData={ mCausedByComposition=%s, mCausedBySelectionEvent=%s }",
1159 this, ToChar(mSelectionData.mCausedByComposition),
1160 ToChar(mSelectionData.mCausedBySelectionEvent)));
1162 mNeedsToNotifyIMEOfSelectionChange = true;
1165 void IMEContentObserver::MaybeNotifyIMEOfFocusSet() {
1166 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1167 ("0x%p IMEContentObserver::MaybeNotifyIMEOfFocusSet()", this));
1169 PostFocusSetNotification();
1170 FlushMergeableNotifications();
1173 void IMEContentObserver::MaybeNotifyIMEOfTextChange(
1174 const TextChangeDataBase& aTextChangeData) {
1175 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1176 ("0x%p IMEContentObserver::MaybeNotifyIMEOfTextChange("
1177 "aTextChangeData=%s)",
1178 this, ToString(aTextChangeData).c_str()));
1180 mTextChangeData += aTextChangeData;
1181 PostTextChangeNotification();
1182 FlushMergeableNotifications();
1185 void IMEContentObserver::CancelNotifyingIMEOfTextChange() {
1186 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1187 ("0x%p IMEContentObserver::CancelNotifyingIMEOfTextChange()", this));
1188 mTextChangeData.Clear();
1189 mNeedsToNotifyIMEOfTextChange = false;
1192 void IMEContentObserver::MaybeNotifyIMEOfSelectionChange(
1193 bool aCausedByComposition, bool aCausedBySelectionEvent,
1194 bool aOccurredDuringComposition) {
1195 MOZ_LOG(
1196 sIMECOLog, LogLevel::Debug,
1197 ("0x%p IMEContentObserver::MaybeNotifyIMEOfSelectionChange("
1198 "aCausedByComposition=%s, aCausedBySelectionEvent=%s, "
1199 "aOccurredDuringComposition)",
1200 this, ToChar(aCausedByComposition), ToChar(aCausedBySelectionEvent)));
1202 mSelectionData.AssignReason(aCausedByComposition, aCausedBySelectionEvent,
1203 aOccurredDuringComposition);
1204 PostSelectionChangeNotification();
1205 FlushMergeableNotifications();
1208 void IMEContentObserver::MaybeNotifyIMEOfPositionChange() {
1209 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1210 ("0x%p IMEContentObserver::MaybeNotifyIMEOfPositionChange()", this));
1211 // If reflow is caused by ContentEventHandler during PositionChangeEvent
1212 // sending NOTIFY_IME_OF_POSITION_CHANGE, we don't need to notify IME of it
1213 // again since ContentEventHandler returns the result including this reflow's
1214 // result.
1215 if (mIsHandlingQueryContentEvent &&
1216 mSendingNotification == NOTIFY_IME_OF_POSITION_CHANGE) {
1217 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1218 ("0x%p IMEContentObserver::MaybeNotifyIMEOfPositionChange(), "
1219 "ignored since caused by ContentEventHandler during sending "
1220 "NOTIY_IME_OF_POSITION_CHANGE",
1221 this));
1222 return;
1224 PostPositionChangeNotification();
1225 FlushMergeableNotifications();
1228 void IMEContentObserver::CancelNotifyingIMEOfPositionChange() {
1229 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1230 ("0x%p IMEContentObserver::CancelNotifyIMEOfPositionChange()", this));
1231 mNeedsToNotifyIMEOfPositionChange = false;
1234 void IMEContentObserver::MaybeNotifyCompositionEventHandled() {
1235 MOZ_LOG(
1236 sIMECOLog, LogLevel::Debug,
1237 ("0x%p IMEContentObserver::MaybeNotifyCompositionEventHandled()", this));
1239 PostCompositionEventHandledNotification();
1240 FlushMergeableNotifications();
1243 bool IMEContentObserver::UpdateSelectionCache(bool aRequireFlush /* = true */) {
1244 MOZ_ASSERT(IsSafeToNotifyIME());
1246 mSelectionData.ClearSelectionData();
1248 // XXX Cannot we cache some information for reducing the cost to compute
1249 // selection offset and writing mode?
1250 WidgetQueryContentEvent querySelectedTextEvent(true, eQuerySelectedText,
1251 mWidget);
1252 querySelectedTextEvent.mNeedsToFlushLayout = aRequireFlush;
1253 ContentEventHandler handler(GetPresContext());
1254 handler.OnQuerySelectedText(&querySelectedTextEvent);
1255 if (NS_WARN_IF(querySelectedTextEvent.DidNotFindSelection()) ||
1256 NS_WARN_IF(querySelectedTextEvent.mReply->mContentsRoot !=
1257 mRootContent)) {
1258 return false;
1260 MOZ_ASSERT(querySelectedTextEvent.mReply->mOffsetAndData.isSome());
1262 mFocusedWidget = querySelectedTextEvent.mReply->mFocusedWidget;
1263 mSelectionData.mOffset = querySelectedTextEvent.mReply->StartOffset();
1264 *mSelectionData.mString = querySelectedTextEvent.mReply->DataRef();
1265 mSelectionData.SetWritingMode(
1266 querySelectedTextEvent.mReply->WritingModeRef());
1267 mSelectionData.mReversed = querySelectedTextEvent.mReply->mReversed;
1269 // WARNING: Don't modify the reason of selection change here.
1271 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1272 ("0x%p IMEContentObserver::UpdateSelectionCache(), "
1273 "mSelectionData=%s",
1274 this, ToString(mSelectionData).c_str()));
1276 return mSelectionData.IsValid();
1279 void IMEContentObserver::PostPositionChangeNotification() {
1280 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1281 ("0x%p IMEContentObserver::PostPositionChangeNotification()", this));
1283 mNeedsToNotifyIMEOfPositionChange = true;
1286 void IMEContentObserver::PostCompositionEventHandledNotification() {
1287 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1288 ("0x%p IMEContentObserver::"
1289 "PostCompositionEventHandledNotification()",
1290 this));
1292 mNeedsToNotifyIMEOfCompositionEventHandled = true;
1295 bool IMEContentObserver::IsReflowLocked() const {
1296 nsPresContext* presContext = GetPresContext();
1297 if (NS_WARN_IF(!presContext)) {
1298 return false;
1300 PresShell* presShell = presContext->GetPresShell();
1301 if (NS_WARN_IF(!presShell)) {
1302 return false;
1304 // During reflow, we shouldn't notify IME because IME may query content
1305 // synchronously. Then, it causes ContentEventHandler will try to flush
1306 // pending notifications during reflow.
1307 return presShell->IsReflowLocked();
1310 bool IMEContentObserver::IsSafeToNotifyIME() const {
1311 // If this is already detached from the widget, this doesn't need to notify
1312 // anything.
1313 if (!mWidget) {
1314 return false;
1317 // Don't notify IME of anything if it's not good time to do it.
1318 if (mSuppressNotifications) {
1319 return false;
1322 if (!mESM || NS_WARN_IF(!GetPresContext())) {
1323 return false;
1326 // If it's in reflow, we should wait to finish the reflow.
1327 // FYI: This should be called again from Reflow() or ReflowInterruptible().
1328 if (IsReflowLocked()) {
1329 return false;
1332 // If we're in handling an edit action, this method will be called later.
1333 if (mEditorBase && mEditorBase->IsInEditSubAction()) {
1334 return false;
1337 return true;
1340 void IMEContentObserver::FlushMergeableNotifications() {
1341 if (!IsSafeToNotifyIME()) {
1342 // So, if this is already called, this should do nothing.
1343 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1344 ("0x%p IMEContentObserver::FlushMergeableNotifications(), "
1345 "FAILED, due to unsafe to notify IME",
1346 this));
1347 return;
1350 // Notifying something may cause nested call of this method. For example,
1351 // when somebody notified one of the notifications may dispatch query content
1352 // event. Then, it causes flushing layout which may cause another layout
1353 // change notification.
1355 if (mQueuedSender) {
1356 // So, if this is already called, this should do nothing.
1357 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1358 ("0x%p IMEContentObserver::FlushMergeableNotifications(), "
1359 "FAILED, due to already flushing pending notifications",
1360 this));
1361 return;
1364 // If text change notification and/or position change notification becomes
1365 // unnecessary, let's cancel them.
1366 if (mNeedsToNotifyIMEOfTextChange && !NeedsTextChangeNotification()) {
1367 CancelNotifyingIMEOfTextChange();
1369 if (mNeedsToNotifyIMEOfPositionChange && !NeedsPositionChangeNotification()) {
1370 CancelNotifyingIMEOfPositionChange();
1373 if (!NeedsToNotifyIMEOfSomething()) {
1374 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1375 ("0x%p IMEContentObserver::FlushMergeableNotifications(), "
1376 "FAILED, due to no pending notifications",
1377 this));
1378 return;
1381 // NOTE: Reset each pending flag because sending notification may cause
1382 // another change.
1384 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1385 ("0x%p IMEContentObserver::FlushMergeableNotifications(), "
1386 "creating IMENotificationSender...",
1387 this));
1389 // If contents in selection range is modified, the selection range still
1390 // has removed node from the tree. In such case, ContentIterator won't
1391 // work well. Therefore, we shouldn't use AddScriptRunnder() here since
1392 // it may kick runnable event immediately after DOM tree is changed but
1393 // the selection range isn't modified yet.
1394 mQueuedSender = new IMENotificationSender(this);
1395 mQueuedSender->Dispatch(mDocShell);
1396 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1397 ("0x%p IMEContentObserver::FlushMergeableNotifications(), "
1398 "finished",
1399 this));
1402 void IMEContentObserver::TryToFlushPendingNotifications(bool aAllowAsync) {
1403 if (!mQueuedSender || mSendingNotification != NOTIFY_IME_OF_NOTHING ||
1404 (XRE_IsContentProcess() && aAllowAsync)) {
1405 return;
1408 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1409 ("0x%p IMEContentObserver::TryToFlushPendingNotifications(), "
1410 "performing queued IMENotificationSender forcibly",
1411 this));
1412 RefPtr<IMENotificationSender> queuedSender = mQueuedSender;
1413 queuedSender->Run();
1416 /******************************************************************************
1417 * mozilla::IMEContentObserver::AChangeEvent
1418 ******************************************************************************/
1420 bool IMEContentObserver::AChangeEvent::CanNotifyIME(
1421 ChangeEventType aChangeEventType) const {
1422 RefPtr<IMEContentObserver> observer = GetObserver();
1423 if (NS_WARN_IF(!observer)) {
1424 return false;
1427 if (aChangeEventType == eChangeEventType_CompositionEventHandled) {
1428 return observer->mWidget != nullptr;
1430 State state = observer->GetState();
1431 // If it's not initialized, we should do nothing.
1432 if (state == eState_NotObserving) {
1433 return false;
1435 // If setting focus, just check the state.
1436 if (aChangeEventType == eChangeEventType_Focus) {
1437 return !NS_WARN_IF(observer->mIMEHasFocus);
1439 // If we've not notified IME of focus yet, we shouldn't notify anything.
1440 if (!observer->mIMEHasFocus) {
1441 return false;
1444 // If IME has focus, IMEContentObserver must hold the widget.
1445 MOZ_ASSERT(observer->mWidget);
1447 return true;
1450 bool IMEContentObserver::AChangeEvent::IsSafeToNotifyIME(
1451 ChangeEventType aChangeEventType) const {
1452 if (NS_WARN_IF(!nsContentUtils::IsSafeToRunScript())) {
1453 return false;
1456 RefPtr<IMEContentObserver> observer = GetObserver();
1457 if (!observer) {
1458 return false;
1461 // While we're sending a notification, we shouldn't send another notification
1462 // recursively.
1463 if (observer->mSendingNotification != NOTIFY_IME_OF_NOTHING) {
1464 MOZ_LOG(
1465 sIMECOLog, LogLevel::Debug,
1466 ("0x%p IMEContentObserver::AChangeEvent::IsSafeToNotifyIME(), "
1467 "putting off sending notification due to detecting recursive call, "
1468 "mIMEContentObserver={ mSendingNotification=%s }",
1469 this, ToChar(observer->mSendingNotification)));
1470 return false;
1472 State state = observer->GetState();
1473 if (aChangeEventType == eChangeEventType_Focus) {
1474 if (NS_WARN_IF(state != eState_Initializing && state != eState_Observing)) {
1475 return false;
1477 } else if (aChangeEventType == eChangeEventType_CompositionEventHandled) {
1478 // It doesn't need to check the observing status.
1479 } else if (state != eState_Observing) {
1480 return false;
1482 return observer->IsSafeToNotifyIME();
1485 /******************************************************************************
1486 * mozilla::IMEContentObserver::IMENotificationSender
1487 ******************************************************************************/
1489 void IMEContentObserver::IMENotificationSender::Dispatch(
1490 nsIDocShell* aDocShell) {
1491 if (XRE_IsContentProcess() && aDocShell) {
1492 RefPtr<nsPresContext> presContext = aDocShell->GetPresContext();
1493 if (presContext) {
1494 nsRefreshDriver* refreshDriver = presContext->RefreshDriver();
1495 if (refreshDriver) {
1496 refreshDriver->AddEarlyRunner(this);
1497 return;
1502 nsIScriptGlobalObject* globalObject =
1503 aDocShell ? aDocShell->GetScriptGlobalObject() : nullptr;
1504 if (globalObject) {
1505 RefPtr<IMENotificationSender> queuedSender = this;
1506 globalObject->Dispatch(TaskCategory::Other, queuedSender.forget());
1507 } else {
1508 NS_DispatchToCurrentThread(this);
1512 NS_IMETHODIMP
1513 IMEContentObserver::IMENotificationSender::Run() {
1514 if (NS_WARN_IF(mIsRunning)) {
1515 MOZ_LOG(sIMECOLog, LogLevel::Error,
1516 ("0x%p IMEContentObserver::IMENotificationSender::Run(), FAILED, "
1517 "called recursively",
1518 this));
1519 return NS_OK;
1522 RefPtr<IMEContentObserver> observer = GetObserver();
1523 if (!observer) {
1524 return NS_OK;
1527 AutoRestore<bool> running(mIsRunning);
1528 mIsRunning = true;
1530 // This instance was already performed forcibly.
1531 if (observer->mQueuedSender != this) {
1532 return NS_OK;
1535 // NOTE: Reset each pending flag because sending notification may cause
1536 // another change.
1538 if (observer->mNeedsToNotifyIMEOfFocusSet) {
1539 observer->mNeedsToNotifyIMEOfFocusSet = false;
1540 SendFocusSet();
1541 observer->mQueuedSender = nullptr;
1542 // If it's not safe to notify IME of focus, SendFocusSet() sets
1543 // mNeedsToNotifyIMEOfFocusSet true again. For guaranteeing to send the
1544 // focus notification later, we should put a new sender into the queue but
1545 // this case must be rare. Note that if mIMEContentObserver is already
1546 // destroyed, mNeedsToNotifyIMEOfFocusSet is never set true again.
1547 if (observer->mNeedsToNotifyIMEOfFocusSet) {
1548 MOZ_ASSERT(!observer->mIMEHasFocus);
1549 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1550 ("0x%p IMEContentObserver::IMENotificationSender::Run(), "
1551 "posting IMENotificationSender to current thread",
1552 this));
1553 observer->mQueuedSender = new IMENotificationSender(observer);
1554 observer->mQueuedSender->Dispatch(observer->mDocShell);
1555 return NS_OK;
1557 // This is the first notification to IME. So, we don't need to notify
1558 // anymore since IME starts to query content after it gets focus.
1559 observer->ClearPendingNotifications();
1560 return NS_OK;
1563 if (observer->mNeedsToNotifyIMEOfTextChange) {
1564 observer->mNeedsToNotifyIMEOfTextChange = false;
1565 SendTextChange();
1566 // Even if the observer hasn't received selection change, let's try to send
1567 // selection change notification to IME because selection start offset may
1568 // be changed if the previous contents of selection start are changed. For
1569 // example, when previous `<p>` element of another `<p>` element which
1570 // contains caret is removed by a DOM mutation, selection change event
1571 // won't be fired, but selection start offset should be decreased by the
1572 // length of removed `<p>` element.
1573 observer->mNeedsToNotifyIMEOfSelectionChange = true;
1576 // If a text change notification causes another text change again, we should
1577 // notify IME of that before sending a selection change notification.
1578 if (!observer->mNeedsToNotifyIMEOfTextChange) {
1579 // Be aware, PuppetWidget depends on the order of this. A selection change
1580 // notification should not be sent before a text change notification because
1581 // PuppetWidget shouldn't query new text content every selection change.
1582 if (observer->mNeedsToNotifyIMEOfSelectionChange) {
1583 observer->mNeedsToNotifyIMEOfSelectionChange = false;
1584 SendSelectionChange();
1588 // If a text change notification causes another text change again or a
1589 // selection change notification causes either a text change or another
1590 // selection change, we should notify IME of those before sending a position
1591 // change notification.
1592 if (!observer->mNeedsToNotifyIMEOfTextChange &&
1593 !observer->mNeedsToNotifyIMEOfSelectionChange) {
1594 if (observer->mNeedsToNotifyIMEOfPositionChange) {
1595 observer->mNeedsToNotifyIMEOfPositionChange = false;
1596 SendPositionChange();
1600 // Composition event handled notification should be sent after all the
1601 // other notifications because this notifies widget of finishing all pending
1602 // events are handled completely.
1603 if (!observer->mNeedsToNotifyIMEOfTextChange &&
1604 !observer->mNeedsToNotifyIMEOfSelectionChange &&
1605 !observer->mNeedsToNotifyIMEOfPositionChange) {
1606 if (observer->mNeedsToNotifyIMEOfCompositionEventHandled) {
1607 observer->mNeedsToNotifyIMEOfCompositionEventHandled = false;
1608 SendCompositionEventHandled();
1612 observer->mQueuedSender = nullptr;
1614 // If notifications caused some new change, we should notify them now.
1615 if (observer->NeedsToNotifyIMEOfSomething()) {
1616 if (observer->GetState() == eState_StoppedObserving) {
1617 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1618 ("0x%p IMEContentObserver::IMENotificationSender::Run(), "
1619 "waiting IMENotificationSender to be reinitialized",
1620 this));
1621 } else {
1622 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1623 ("0x%p IMEContentObserver::IMENotificationSender::Run(), "
1624 "posting IMENotificationSender to current thread",
1625 this));
1626 observer->mQueuedSender = new IMENotificationSender(observer);
1627 observer->mQueuedSender->Dispatch(observer->mDocShell);
1630 return NS_OK;
1633 void IMEContentObserver::IMENotificationSender::SendFocusSet() {
1634 RefPtr<IMEContentObserver> observer = GetObserver();
1635 if (!observer) {
1636 return;
1639 if (!CanNotifyIME(eChangeEventType_Focus)) {
1640 // If IMEContentObserver has already gone, we don't need to notify IME of
1641 // focus.
1642 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1643 ("0x%p IMEContentObserver::IMENotificationSender::"
1644 "SendFocusSet(), FAILED, due to impossible to notify IME of focus",
1645 this));
1646 observer->ClearPendingNotifications();
1647 return;
1650 if (!IsSafeToNotifyIME(eChangeEventType_Focus)) {
1651 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1652 ("0x%p IMEContentObserver::IMENotificationSender::"
1653 "SendFocusSet(), retrying to send NOTIFY_IME_OF_FOCUS...",
1654 this));
1655 observer->PostFocusSetNotification();
1656 return;
1659 observer->mIMEHasFocus = true;
1660 // Initialize selection cache with the first selection data.
1661 #ifdef XP_MACOSX
1662 // We need to flush layout only on macOS because character coordinates are
1663 // cached by cocoa with this call, but we don't have a way to update them
1664 // after that. Therefore, we need the latest layout information right now.
1665 observer->UpdateSelectionCache(true);
1666 #else
1667 // We avoid flushing for focus in the general case.
1668 observer->UpdateSelectionCache(false);
1669 #endif // #ifdef XP_MACOSX #else
1670 MOZ_LOG(sIMECOLog, LogLevel::Info,
1671 ("0x%p IMEContentObserver::IMENotificationSender::"
1672 "SendFocusSet(), sending NOTIFY_IME_OF_FOCUS...",
1673 this));
1675 MOZ_RELEASE_ASSERT(observer->mSendingNotification == NOTIFY_IME_OF_NOTHING);
1676 observer->mSendingNotification = NOTIFY_IME_OF_FOCUS;
1677 IMEStateManager::NotifyIME(IMENotification(NOTIFY_IME_OF_FOCUS),
1678 observer->mWidget);
1679 observer->mSendingNotification = NOTIFY_IME_OF_NOTHING;
1681 // IMENotificationRequests referred by ObserveEditableNode() may be different
1682 // before or after widget receives NOTIFY_IME_OF_FOCUS. Therefore, we need
1683 // to guarantee to call ObserveEditableNode() after sending
1684 // NOTIFY_IME_OF_FOCUS.
1685 observer->OnIMEReceivedFocus();
1687 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1688 ("0x%p IMEContentObserver::IMENotificationSender::"
1689 "SendFocusSet(), sent NOTIFY_IME_OF_FOCUS",
1690 this));
1693 void IMEContentObserver::IMENotificationSender::SendSelectionChange() {
1694 RefPtr<IMEContentObserver> observer = GetObserver();
1695 if (!observer) {
1696 return;
1699 if (!CanNotifyIME(eChangeEventType_Selection)) {
1700 MOZ_LOG(
1701 sIMECOLog, LogLevel::Debug,
1702 ("0x%p IMEContentObserver::IMENotificationSender::"
1703 "SendSelectionChange(), FAILED, due to impossible to notify IME of "
1704 "selection change",
1705 this));
1706 return;
1709 if (!IsSafeToNotifyIME(eChangeEventType_Selection)) {
1710 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1711 ("0x%p IMEContentObserver::IMENotificationSender::"
1712 "SendSelectionChange(), retrying to send "
1713 "NOTIFY_IME_OF_SELECTION_CHANGE...",
1714 this));
1715 observer->PostSelectionChangeNotification();
1716 return;
1719 SelectionChangeData lastSelChangeData = observer->mSelectionData;
1720 if (NS_WARN_IF(!observer->UpdateSelectionCache())) {
1721 MOZ_LOG(
1722 sIMECOLog, LogLevel::Error,
1723 ("0x%p IMEContentObserver::IMENotificationSender::"
1724 "SendSelectionChange(), FAILED, due to UpdateSelectionCache() failure",
1725 this));
1726 return;
1729 // The state may be changed since querying content causes flushing layout.
1730 if (!CanNotifyIME(eChangeEventType_Selection)) {
1731 MOZ_LOG(
1732 sIMECOLog, LogLevel::Debug,
1733 ("0x%p IMEContentObserver::IMENotificationSender::"
1734 "SendSelectionChange(), FAILED, due to flushing layout having changed "
1735 "something",
1736 this));
1737 return;
1740 // If the selection isn't changed actually, we shouldn't notify IME of
1741 // selection change.
1742 SelectionChangeData& newSelChangeData = observer->mSelectionData;
1743 if (lastSelChangeData.IsValid() &&
1744 lastSelChangeData.mOffset == newSelChangeData.mOffset &&
1745 lastSelChangeData.String() == newSelChangeData.String() &&
1746 lastSelChangeData.GetWritingMode() == newSelChangeData.GetWritingMode() &&
1747 lastSelChangeData.mReversed == newSelChangeData.mReversed) {
1748 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1749 ("0x%p IMEContentObserver::IMENotificationSender::"
1750 "SendSelectionChange(), not notifying IME of "
1751 "NOTIFY_IME_OF_SELECTION_CHANGE due to not changed actually",
1752 this));
1753 return;
1756 MOZ_LOG(sIMECOLog, LogLevel::Info,
1757 ("0x%p IMEContentObserver::IMENotificationSender::"
1758 "SendSelectionChange(), sending NOTIFY_IME_OF_SELECTION_CHANGE... "
1759 "newSelChangeData=%s",
1760 this, ToString(newSelChangeData).c_str()));
1762 IMENotification notification(NOTIFY_IME_OF_SELECTION_CHANGE);
1763 notification.SetData(observer->mSelectionData);
1765 MOZ_RELEASE_ASSERT(observer->mSendingNotification == NOTIFY_IME_OF_NOTHING);
1766 observer->mSendingNotification = NOTIFY_IME_OF_SELECTION_CHANGE;
1767 IMEStateManager::NotifyIME(notification, observer->mWidget);
1768 observer->mSendingNotification = NOTIFY_IME_OF_NOTHING;
1770 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1771 ("0x%p IMEContentObserver::IMENotificationSender::"
1772 "SendSelectionChange(), sent NOTIFY_IME_OF_SELECTION_CHANGE",
1773 this));
1776 void IMEContentObserver::IMENotificationSender::SendTextChange() {
1777 RefPtr<IMEContentObserver> observer = GetObserver();
1778 if (!observer) {
1779 return;
1782 if (!CanNotifyIME(eChangeEventType_Text)) {
1783 MOZ_LOG(
1784 sIMECOLog, LogLevel::Debug,
1785 ("0x%p IMEContentObserver::IMENotificationSender::"
1786 "SendTextChange(), FAILED, due to impossible to notify IME of text "
1787 "change",
1788 this));
1789 return;
1792 if (!IsSafeToNotifyIME(eChangeEventType_Text)) {
1793 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1794 ("0x%p IMEContentObserver::IMENotificationSender::"
1795 "SendTextChange(), retrying to send NOTIFY_IME_OF_TEXT_CHANGE...",
1796 this));
1797 observer->PostTextChangeNotification();
1798 return;
1801 // If text change notification is unnecessary anymore, just cancel it.
1802 if (!observer->NeedsTextChangeNotification()) {
1803 MOZ_LOG(sIMECOLog, LogLevel::Warning,
1804 ("0x%p IMEContentObserver::IMENotificationSender::"
1805 "SendTextChange(), canceling sending NOTIFY_IME_OF_TEXT_CHANGE",
1806 this));
1807 observer->CancelNotifyingIMEOfTextChange();
1808 return;
1811 MOZ_LOG(sIMECOLog, LogLevel::Info,
1812 ("0x%p IMEContentObserver::IMENotificationSender::"
1813 "SendTextChange(), sending NOTIFY_IME_OF_TEXT_CHANGE... "
1814 "mIMEContentObserver={ mTextChangeData=%s }",
1815 this, ToString(observer->mTextChangeData).c_str()));
1817 IMENotification notification(NOTIFY_IME_OF_TEXT_CHANGE);
1818 notification.SetData(observer->mTextChangeData);
1819 observer->mTextChangeData.Clear();
1821 MOZ_RELEASE_ASSERT(observer->mSendingNotification == NOTIFY_IME_OF_NOTHING);
1822 observer->mSendingNotification = NOTIFY_IME_OF_TEXT_CHANGE;
1823 IMEStateManager::NotifyIME(notification, observer->mWidget);
1824 observer->mSendingNotification = NOTIFY_IME_OF_NOTHING;
1826 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1827 ("0x%p IMEContentObserver::IMENotificationSender::"
1828 "SendTextChange(), sent NOTIFY_IME_OF_TEXT_CHANGE",
1829 this));
1832 void IMEContentObserver::IMENotificationSender::SendPositionChange() {
1833 RefPtr<IMEContentObserver> observer = GetObserver();
1834 if (!observer) {
1835 return;
1838 if (!CanNotifyIME(eChangeEventType_Position)) {
1839 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1840 ("0x%p IMEContentObserver::IMENotificationSender::"
1841 "SendPositionChange(), FAILED, due to impossible to notify IME of "
1842 "position change",
1843 this));
1844 return;
1847 if (!IsSafeToNotifyIME(eChangeEventType_Position)) {
1848 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1849 ("0x%p IMEContentObserver::IMENotificationSender::"
1850 "SendPositionChange(), retrying to send "
1851 "NOTIFY_IME_OF_POSITION_CHANGE...",
1852 this));
1853 observer->PostPositionChangeNotification();
1854 return;
1857 // If position change notification is unnecessary anymore, just cancel it.
1858 if (!observer->NeedsPositionChangeNotification()) {
1859 MOZ_LOG(sIMECOLog, LogLevel::Warning,
1860 ("0x%p IMEContentObserver::IMENotificationSender::"
1861 "SendPositionChange(), canceling sending "
1862 "NOTIFY_IME_OF_POSITION_CHANGE",
1863 this));
1864 observer->CancelNotifyingIMEOfPositionChange();
1865 return;
1868 MOZ_LOG(sIMECOLog, LogLevel::Info,
1869 ("0x%p IMEContentObserver::IMENotificationSender::"
1870 "SendPositionChange(), sending NOTIFY_IME_OF_POSITION_CHANGE...",
1871 this));
1873 MOZ_RELEASE_ASSERT(observer->mSendingNotification == NOTIFY_IME_OF_NOTHING);
1874 observer->mSendingNotification = NOTIFY_IME_OF_POSITION_CHANGE;
1875 IMEStateManager::NotifyIME(IMENotification(NOTIFY_IME_OF_POSITION_CHANGE),
1876 observer->mWidget);
1877 observer->mSendingNotification = NOTIFY_IME_OF_NOTHING;
1879 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1880 ("0x%p IMEContentObserver::IMENotificationSender::"
1881 "SendPositionChange(), sent NOTIFY_IME_OF_POSITION_CHANGE",
1882 this));
1885 void IMEContentObserver::IMENotificationSender::SendCompositionEventHandled() {
1886 RefPtr<IMEContentObserver> observer = GetObserver();
1887 if (!observer) {
1888 return;
1891 if (!CanNotifyIME(eChangeEventType_CompositionEventHandled)) {
1892 MOZ_LOG(
1893 sIMECOLog, LogLevel::Debug,
1894 ("0x%p IMEContentObserver::IMENotificationSender::"
1895 "SendCompositionEventHandled(), FAILED, due to impossible to notify "
1896 "IME of composition event handled",
1897 this));
1898 return;
1901 if (!IsSafeToNotifyIME(eChangeEventType_CompositionEventHandled)) {
1902 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1903 ("0x%p IMEContentObserver::IMENotificationSender::"
1904 "SendCompositionEventHandled(), retrying to send "
1905 "NOTIFY_IME_OF_POSITION_CHANGE...",
1906 this));
1907 observer->PostCompositionEventHandledNotification();
1908 return;
1911 MOZ_LOG(sIMECOLog, LogLevel::Info,
1912 ("0x%p IMEContentObserver::IMENotificationSender::"
1913 "SendCompositionEventHandled(), sending "
1914 "NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED...",
1915 this));
1917 MOZ_RELEASE_ASSERT(observer->mSendingNotification == NOTIFY_IME_OF_NOTHING);
1918 observer->mSendingNotification = NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED;
1919 IMEStateManager::NotifyIME(
1920 IMENotification(NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED),
1921 observer->mWidget);
1922 observer->mSendingNotification = NOTIFY_IME_OF_NOTHING;
1924 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1925 ("0x%p IMEContentObserver::IMENotificationSender::"
1926 "SendCompositionEventHandled(), sent "
1927 "NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED",
1928 this));
1931 /******************************************************************************
1932 * mozilla::IMEContentObserver::DocumentObservingHelper
1933 ******************************************************************************/
1935 NS_IMPL_CYCLE_COLLECTION_CLASS(IMEContentObserver::DocumentObserver)
1937 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IMEContentObserver::DocumentObserver)
1938 // StopObserving() releases mIMEContentObserver and mDocument.
1939 tmp->StopObserving();
1940 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1942 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IMEContentObserver::DocumentObserver)
1943 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIMEContentObserver)
1944 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
1945 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1947 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IMEContentObserver::DocumentObserver)
1948 NS_INTERFACE_MAP_ENTRY(nsIDocumentObserver)
1949 NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
1950 NS_INTERFACE_MAP_ENTRY(nsISupports)
1951 NS_INTERFACE_MAP_END
1953 NS_IMPL_CYCLE_COLLECTING_ADDREF(IMEContentObserver::DocumentObserver)
1954 NS_IMPL_CYCLE_COLLECTING_RELEASE(IMEContentObserver::DocumentObserver)
1956 void IMEContentObserver::DocumentObserver::Observe(dom::Document* aDocument) {
1957 MOZ_ASSERT(aDocument);
1959 // Guarantee that aDocument won't be destroyed during a call of
1960 // StopObserving().
1961 RefPtr<dom::Document> newDocument = aDocument;
1963 StopObserving();
1965 mDocument = std::move(newDocument);
1966 mDocument->AddObserver(this);
1969 void IMEContentObserver::DocumentObserver::StopObserving() {
1970 if (!IsObserving()) {
1971 return;
1974 // Grab IMEContentObserver which could be destroyed during method calls.
1975 RefPtr<IMEContentObserver> observer = std::move(mIMEContentObserver);
1977 // Stop observing the document first.
1978 RefPtr<dom::Document> document = std::move(mDocument);
1979 document->RemoveObserver(this);
1981 // Notify IMEContentObserver of ending of document updates if this already
1982 // notified it of beginning of document updates.
1983 for (; IsUpdating(); --mDocumentUpdating) {
1984 // FYI: IsUpdating() returns true until mDocumentUpdating becomes 0.
1985 // However, IsObserving() returns false now because mDocument was
1986 // already cleared above. Therefore, this method won't be called
1987 // recursively.
1988 observer->EndDocumentUpdate();
1992 void IMEContentObserver::DocumentObserver::Destroy() {
1993 StopObserving();
1994 mIMEContentObserver = nullptr;
1997 void IMEContentObserver::DocumentObserver::BeginUpdate(
1998 dom::Document* aDocument) {
1999 if (NS_WARN_IF(Destroyed()) || NS_WARN_IF(!IsObserving())) {
2000 return;
2002 mDocumentUpdating++;
2003 mIMEContentObserver->BeginDocumentUpdate();
2006 void IMEContentObserver::DocumentObserver::EndUpdate(dom::Document* aDocument) {
2007 if (NS_WARN_IF(Destroyed()) || NS_WARN_IF(!IsObserving()) ||
2008 NS_WARN_IF(!IsUpdating())) {
2009 return;
2011 mDocumentUpdating--;
2012 mIMEContentObserver->EndDocumentUpdate();
2015 } // namespace mozilla