Merge mozilla-central to autoland on a CLOSED TREE
[gecko.git] / dom / events / IMEContentObserver.cpp
blob9950f0ae292edde04b56a1006a7841c243b22232
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 using NodePosition = ContentEventHandler::NodePosition;
41 using NodePositionBefore = ContentEventHandler::NodePositionBefore;
43 using namespace dom;
44 using namespace widget;
46 LazyLogModule sIMECOLog("IMEContentObserver");
48 static const char* ToChar(bool aBool) { return aBool ? "true" : "false"; }
50 // This method determines the node to use for the point before the current node.
51 // If you have the following aContent and aContainer, and want to represent the
52 // following point for `NodePosition` or `RangeBoundary`:
54 // <parent> {node} {node} | {node} </parent>
55 // ^ ^ ^
56 // aContainer point aContent
58 // This function will shift `aContent` to the left into the format which
59 // `NodePosition` and `RangeBoundary` use:
61 // <parent> {node} {node} | {node} </parent>
62 // ^ ^ ^
63 // aContainer result point
64 static nsIContent* PointBefore(nsINode* aContainer, nsIContent* aContent) {
65 if (aContent) {
66 return aContent->GetPreviousSibling();
68 return aContainer->GetLastChild();
71 /******************************************************************************
72 * mozilla::IMEContentObserver
73 ******************************************************************************/
75 NS_IMPL_CYCLE_COLLECTION_CLASS(IMEContentObserver)
77 // Note that we don't need to add mFirstAddedContainer nor
78 // mLastAddedContainer to cycle collection because they are non-null only
79 // during short time and shouldn't be touched while they are non-null.
81 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IMEContentObserver)
82 nsAutoScriptBlocker scriptBlocker;
84 tmp->NotifyIMEOfBlur();
85 tmp->UnregisterObservers();
87 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelection)
88 NS_IMPL_CYCLE_COLLECTION_UNLINK(mRootElement)
89 NS_IMPL_CYCLE_COLLECTION_UNLINK(mEditableNode)
90 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocShell)
91 NS_IMPL_CYCLE_COLLECTION_UNLINK(mEditorBase)
92 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentObserver)
93 NS_IMPL_CYCLE_COLLECTION_UNLINK(mEndOfAddedTextCache.mContainerNode)
94 NS_IMPL_CYCLE_COLLECTION_UNLINK(mStartOfRemovingTextRangeCache.mContainerNode)
95 NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE
97 tmp->mIMENotificationRequests = nullptr;
98 tmp->mESM = nullptr;
99 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
101 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IMEContentObserver)
102 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWidget)
103 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFocusedWidget)
104 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelection)
105 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRootElement)
106 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEditableNode)
107 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocShell)
108 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEditorBase)
109 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentObserver)
110 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEndOfAddedTextCache.mContainerNode)
111 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(
112 mStartOfRemovingTextRangeCache.mContainerNode)
113 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
115 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IMEContentObserver)
116 NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
117 NS_INTERFACE_MAP_ENTRY(nsIReflowObserver)
118 NS_INTERFACE_MAP_ENTRY(nsIScrollObserver)
119 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
120 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIReflowObserver)
121 NS_INTERFACE_MAP_END
123 NS_IMPL_CYCLE_COLLECTING_ADDREF(IMEContentObserver)
124 NS_IMPL_CYCLE_COLLECTING_RELEASE(IMEContentObserver)
126 IMEContentObserver::IMEContentObserver()
127 : mESM(nullptr),
128 mIMENotificationRequests(nullptr),
129 mSendingNotification(NOTIFY_IME_OF_NOTHING),
130 mIsObserving(false),
131 mIMEHasFocus(false),
132 mNeedsToNotifyIMEOfFocusSet(false),
133 mNeedsToNotifyIMEOfTextChange(false),
134 mNeedsToNotifyIMEOfSelectionChange(false),
135 mNeedsToNotifyIMEOfPositionChange(false),
136 mNeedsToNotifyIMEOfCompositionEventHandled(false),
137 mIsHandlingQueryContentEvent(false) {
138 #ifdef DEBUG
139 mTextChangeData.Test();
140 #endif
143 void IMEContentObserver::Init(nsIWidget& aWidget, nsPresContext& aPresContext,
144 Element* aElement, EditorBase& aEditorBase) {
145 State state = GetState();
146 if (NS_WARN_IF(state == eState_Observing)) {
147 return; // Nothing to do.
150 bool firstInitialization = state != eState_StoppedObserving;
151 if (!firstInitialization) {
152 // If this is now trying to initialize with new contents, all observers
153 // should be registered again for simpler implementation.
154 UnregisterObservers();
155 Clear();
158 mESM = aPresContext.EventStateManager();
159 mESM->OnStartToObserveContent(this);
161 mWidget = &aWidget;
162 mIMENotificationRequests = &mWidget->IMENotificationRequestsRef();
164 if (!InitWithEditor(aPresContext, aElement, aEditorBase)) {
165 MOZ_LOG(sIMECOLog, LogLevel::Error,
166 ("0x%p Init() FAILED, due to InitWithEditor() "
167 "failure",
168 this));
169 Clear();
170 return;
173 if (firstInitialization) {
174 // Now, try to send NOTIFY_IME_OF_FOCUS to IME via the widget.
175 MaybeNotifyIMEOfFocusSet();
176 // When this is called first time, IME has not received NOTIFY_IME_OF_FOCUS
177 // yet since NOTIFY_IME_OF_FOCUS will be sent to widget asynchronously.
178 // So, we need to do nothing here. After NOTIFY_IME_OF_FOCUS has been
179 // sent, OnIMEReceivedFocus() will be called and content, selection and/or
180 // position changes will be observed
181 return;
184 // When this is called after editor reframing (i.e., the root editable node
185 // is also recreated), IME has usually received NOTIFY_IME_OF_FOCUS. In this
186 // case, we need to restart to observe content, selection and/or position
187 // changes in new root editable node.
188 ObserveEditableNode();
190 if (!NeedsToNotifyIMEOfSomething()) {
191 return;
194 // Some change events may wait to notify IME because this was being
195 // initialized. It is the time to flush them.
196 FlushMergeableNotifications();
199 void IMEContentObserver::OnIMEReceivedFocus() {
200 // While Init() notifies IME of focus, pending layout may be flushed
201 // because the notification may cause querying content. Then, recursive
202 // call of Init() with the latest content may occur. In such case, we
203 // shouldn't keep first initialization which notified IME of focus.
204 if (GetState() != eState_Initializing) {
205 MOZ_LOG(sIMECOLog, LogLevel::Warning,
206 ("0x%p OnIMEReceivedFocus(), "
207 "but the state is not \"initializing\", so does nothing",
208 this));
209 return;
212 // NOTIFY_IME_OF_FOCUS might cause recreating IMEContentObserver
213 // instance via IMEStateManager::UpdateIMEState(). So, this
214 // instance might already have been destroyed, check it.
215 if (!mRootElement) {
216 MOZ_LOG(sIMECOLog, LogLevel::Warning,
217 ("0x%p OnIMEReceivedFocus(), "
218 "but mRootElement has already been cleared, so does nothing",
219 this));
220 return;
223 // Start to observe which is needed by IME when IME actually has focus.
224 ObserveEditableNode();
226 if (!NeedsToNotifyIMEOfSomething()) {
227 return;
230 // Some change events may wait to notify IME because this was being
231 // initialized. It is the time to flush them.
232 FlushMergeableNotifications();
235 bool IMEContentObserver::InitWithEditor(nsPresContext& aPresContext,
236 Element* aElement,
237 EditorBase& aEditorBase) {
238 mEditableNode = IMEStateManager::GetRootEditableNode(aPresContext, aElement);
239 if (NS_WARN_IF(!mEditableNode)) {
240 return false;
243 mEditorBase = &aEditorBase;
245 RefPtr<PresShell> presShell = aPresContext.GetPresShell();
247 // get selection and root content
248 nsCOMPtr<nsISelectionController> selCon;
249 if (mEditableNode->IsContent()) {
250 nsIFrame* frame = mEditableNode->AsContent()->GetPrimaryFrame();
251 if (NS_WARN_IF(!frame)) {
252 return false;
255 frame->GetSelectionController(&aPresContext, getter_AddRefs(selCon));
256 } else {
257 // mEditableNode is a document
258 selCon = presShell;
261 if (NS_WARN_IF(!selCon)) {
262 return false;
265 mSelection = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL);
266 if (NS_WARN_IF(!mSelection)) {
267 return false;
270 if (mEditorBase->IsTextEditor()) {
271 mRootElement = mEditorBase->GetRoot(); // The anonymous <div>
272 MOZ_ASSERT(mRootElement);
273 MOZ_ASSERT(mRootElement->GetFirstChild());
274 if (auto* text = Text::FromNodeOrNull(
275 mRootElement ? mRootElement->GetFirstChild() : nullptr)) {
276 mTextControlValueLength = ContentEventHandler::GetNativeTextLength(*text);
278 } else if (const nsRange* selRange = mSelection->GetRangeAt(0)) {
279 if (NS_WARN_IF(!selRange->GetStartContainer())) {
280 return false;
283 nsCOMPtr<nsINode> startContainer = selRange->GetStartContainer();
284 mRootElement = Element::FromNodeOrNull(
285 startContainer->GetSelectionRootContent(presShell));
286 } else {
287 nsCOMPtr<nsINode> editableNode = mEditableNode;
288 mRootElement = Element::FromNodeOrNull(
289 editableNode->GetSelectionRootContent(presShell));
291 if (!mRootElement && mEditableNode->IsDocument()) {
292 // The document node is editable, but there are no contents, this document
293 // is not editable.
294 return false;
297 if (NS_WARN_IF(!mRootElement)) {
298 return false;
301 mDocShell = aPresContext.GetDocShell();
302 if (NS_WARN_IF(!mDocShell)) {
303 return false;
306 mDocumentObserver = new DocumentObserver(*this);
308 return true;
311 void IMEContentObserver::Clear() {
312 mEditorBase = nullptr;
313 mSelection = nullptr;
314 mEditableNode = nullptr;
315 mRootElement = nullptr;
316 mDocShell = nullptr;
317 // Should be safe to clear mDocumentObserver here even though it grabs
318 // this instance in most cases because this is called by Init() or Destroy().
319 // The callers of Init() grab this instance with local RefPtr.
320 // The caller of Destroy() also grabs this instance with local RefPtr.
321 // So, this won't cause refcount of this instance become 0.
322 mDocumentObserver = nullptr;
325 void IMEContentObserver::ObserveEditableNode() {
326 MOZ_RELEASE_ASSERT(mSelection);
327 MOZ_RELEASE_ASSERT(mRootElement);
328 MOZ_RELEASE_ASSERT(GetState() != eState_Observing);
330 // If this is called before sending NOTIFY_IME_OF_FOCUS (it's possible when
331 // the editor is reframed before sending NOTIFY_IME_OF_FOCUS asynchronously),
332 // the notification requests of mWidget may be different from after the widget
333 // receives NOTIFY_IME_OF_FOCUS. So, this should be called again by
334 // OnIMEReceivedFocus() which is called after sending NOTIFY_IME_OF_FOCUS.
335 if (!mIMEHasFocus) {
336 MOZ_ASSERT(!mWidget || mNeedsToNotifyIMEOfFocusSet ||
337 mSendingNotification == NOTIFY_IME_OF_FOCUS,
338 "Wow, OnIMEReceivedFocus() won't be called?");
339 return;
342 mIsObserving = true;
343 if (mEditorBase) {
344 mEditorBase->SetIMEContentObserver(this);
347 mRootElement->AddMutationObserver(this);
348 // If it's in a document (should be so), we can use document observer to
349 // reduce redundant computation of text change offsets.
350 Document* doc = mRootElement->GetComposedDoc();
351 if (doc) {
352 RefPtr<DocumentObserver> documentObserver = mDocumentObserver;
353 documentObserver->Observe(doc);
356 if (mDocShell) {
357 // Add scroll position listener and reflow observer to detect position
358 // and size changes
359 mDocShell->AddWeakScrollObserver(this);
360 mDocShell->AddWeakReflowObserver(this);
364 void IMEContentObserver::NotifyIMEOfBlur() {
365 // Prevent any notifications to be sent IME.
366 nsCOMPtr<nsIWidget> widget;
367 mWidget.swap(widget);
368 mIMENotificationRequests = nullptr;
370 // If we hasn't been set focus, we shouldn't send blur notification to IME.
371 if (!mIMEHasFocus) {
372 return;
375 // mWidget must have been non-nullptr if IME has focus.
376 MOZ_RELEASE_ASSERT(widget);
378 RefPtr<IMEContentObserver> kungFuDeathGrip(this);
380 MOZ_LOG(sIMECOLog, LogLevel::Info,
381 ("0x%p NotifyIMEOfBlur(), sending NOTIFY_IME_OF_BLUR", this));
383 // For now, we need to send blur notification in any condition because
384 // we don't have any simple ways to send blur notification asynchronously.
385 // After this call, Destroy() or Unlink() will stop observing the content
386 // and forget everything. Therefore, if it's not safe to send notification
387 // when script blocker is unlocked, we cannot send blur notification after
388 // that and before next focus notification.
389 // Anyway, as far as we know, IME doesn't try to query content when it loses
390 // focus. So, this may not cause any problem.
391 mIMEHasFocus = false;
392 IMEStateManager::NotifyIME(IMENotification(NOTIFY_IME_OF_BLUR), widget);
394 MOZ_LOG(sIMECOLog, LogLevel::Debug,
395 ("0x%p NotifyIMEOfBlur(), sent NOTIFY_IME_OF_BLUR", this));
398 void IMEContentObserver::UnregisterObservers() {
399 if (!mIsObserving) {
400 return;
402 mIsObserving = false;
404 if (mEditorBase) {
405 mEditorBase->SetIMEContentObserver(nullptr);
408 if (mSelection) {
409 mSelectionData.Clear();
410 mFocusedWidget = nullptr;
413 if (mRootElement) {
414 mRootElement->RemoveMutationObserver(this);
417 if (mDocumentObserver) {
418 RefPtr<DocumentObserver> documentObserver = mDocumentObserver;
419 documentObserver->StopObserving();
422 if (mDocShell) {
423 mDocShell->RemoveWeakScrollObserver(this);
424 mDocShell->RemoveWeakReflowObserver(this);
428 nsPresContext* IMEContentObserver::GetPresContext() const {
429 return mESM ? mESM->GetPresContext() : nullptr;
432 void IMEContentObserver::Destroy() {
433 // WARNING: When you change this method, you have to check Unlink() too.
435 // Note that don't send any notifications later from here. I.e., notify
436 // IMEStateManager of the blur synchronously because IMEStateManager needs to
437 // stop notifying the main process if this is requested by the main process.
438 NotifyIMEOfBlur();
439 UnregisterObservers();
440 Clear();
442 mWidget = nullptr;
443 mIMENotificationRequests = nullptr;
445 if (mESM) {
446 mESM->OnStopObservingContent(this);
447 mESM = nullptr;
451 bool IMEContentObserver::Destroyed() const { return !mWidget; }
453 void IMEContentObserver::DisconnectFromEventStateManager() { mESM = nullptr; }
455 bool IMEContentObserver::MaybeReinitialize(nsIWidget& aWidget,
456 nsPresContext& aPresContext,
457 Element* aElement,
458 EditorBase& aEditorBase) {
459 if (!IsObservingContent(aPresContext, aElement)) {
460 return false;
463 if (GetState() == eState_StoppedObserving) {
464 Init(aWidget, aPresContext, aElement, aEditorBase);
466 return IsManaging(aPresContext, aElement);
469 bool IMEContentObserver::IsManaging(const nsPresContext& aPresContext,
470 const Element* aElement) const {
471 return GetState() == eState_Observing &&
472 IsObservingContent(aPresContext, aElement);
475 bool IMEContentObserver::IsBeingInitializedFor(
476 const nsPresContext& aPresContext, const Element* aElement) const {
477 return GetState() == eState_Initializing &&
478 IsObservingContent(aPresContext, aElement);
481 bool IMEContentObserver::IsManaging(
482 const TextComposition& aTextComposition) const {
483 if (GetState() != eState_Observing) {
484 return false;
486 nsPresContext* const presContext = aTextComposition.GetPresContext();
487 if (NS_WARN_IF(!presContext)) {
488 return false;
490 if (presContext != GetPresContext()) {
491 return false; // observing different document
493 return IsObservingContent(
494 *presContext,
495 Element::FromNodeOrNull(aTextComposition.GetEventTargetNode()));
498 IMEContentObserver::State IMEContentObserver::GetState() const {
499 if (!mSelection || !mRootElement || !mEditableNode) {
500 return eState_NotObserving; // failed to initialize or finalized.
502 if (!mRootElement->IsInComposedDoc()) {
503 // the focused editor has already been reframed.
504 return eState_StoppedObserving;
506 return mIsObserving ? eState_Observing : eState_Initializing;
509 bool IMEContentObserver::IsObservingContent(const nsPresContext& aPresContext,
510 const Element* aElement) const {
511 return mEditableNode ==
512 IMEStateManager::GetRootEditableNode(aPresContext, aElement);
515 bool IMEContentObserver::IsEditorHandlingEventForComposition() const {
516 if (!mWidget) {
517 return false;
519 RefPtr<TextComposition> composition =
520 IMEStateManager::GetTextCompositionFor(mWidget);
521 if (!composition) {
522 return false;
524 return composition->IsEditorHandlingEvent();
527 bool IMEContentObserver::IsEditorComposing() const {
528 // Note that don't use TextComposition here. The important thing is,
529 // whether the editor already started to handle composition because
530 // web contents can change selection, text content and/or something from
531 // compositionstart event listener which is run before EditorBase handles it.
532 if (NS_WARN_IF(!mEditorBase)) {
533 return false;
535 return mEditorBase->IsIMEComposing();
538 nsresult IMEContentObserver::GetSelectionAndRoot(Selection** aSelection,
539 Element** aRootElement) const {
540 if (!mEditableNode || !mSelection) {
541 return NS_ERROR_NOT_AVAILABLE;
544 NS_ASSERTION(mSelection && mRootElement, "uninitialized content observer");
545 NS_ADDREF(*aSelection = mSelection);
546 NS_ADDREF(*aRootElement = mRootElement);
547 return NS_OK;
550 void IMEContentObserver::OnSelectionChange(Selection& aSelection) {
551 if (!mIsObserving) {
552 return;
555 if (mWidget) {
556 bool causedByComposition = IsEditorHandlingEventForComposition();
557 bool causedBySelectionEvent = TextComposition::IsHandlingSelectionEvent();
558 bool duringComposition = IsEditorComposing();
559 MaybeNotifyIMEOfSelectionChange(causedByComposition, causedBySelectionEvent,
560 duringComposition);
564 void IMEContentObserver::ScrollPositionChanged() {
565 if (!NeedsPositionChangeNotification()) {
566 return;
569 MaybeNotifyIMEOfPositionChange();
572 NS_IMETHODIMP
573 IMEContentObserver::Reflow(DOMHighResTimeStamp aStart,
574 DOMHighResTimeStamp aEnd) {
575 if (!NeedsPositionChangeNotification()) {
576 return NS_OK;
579 MaybeNotifyIMEOfPositionChange();
580 return NS_OK;
583 NS_IMETHODIMP
584 IMEContentObserver::ReflowInterruptible(DOMHighResTimeStamp aStart,
585 DOMHighResTimeStamp aEnd) {
586 if (!NeedsPositionChangeNotification()) {
587 return NS_OK;
590 MaybeNotifyIMEOfPositionChange();
591 return NS_OK;
594 nsresult IMEContentObserver::HandleQueryContentEvent(
595 WidgetQueryContentEvent* aEvent) {
596 // If the instance has normal selection cache and the query event queries
597 // normal selection's range, it should use the cached selection which was
598 // sent to the widget. However, if this instance has already received new
599 // selection change notification but hasn't updated the cache yet (i.e.,
600 // not sending selection change notification to IME, don't use the cached
601 // value. Note that don't update selection cache here since if you update
602 // selection cache here, IMENotificationSender won't notify IME of selection
603 // change because it looks like that the selection isn't actually changed.
604 const bool isSelectionCacheAvailable = aEvent->mUseNativeLineBreak &&
605 mSelectionData.IsInitialized() &&
606 !mNeedsToNotifyIMEOfSelectionChange;
607 if (isSelectionCacheAvailable && aEvent->mMessage == eQuerySelectedText &&
608 aEvent->mInput.mSelectionType == SelectionType::eNormal) {
609 aEvent->EmplaceReply();
610 if (mSelectionData.HasRange()) {
611 aEvent->mReply->mOffsetAndData.emplace(mSelectionData.mOffset,
612 mSelectionData.String(),
613 OffsetAndDataFor::SelectedString);
614 aEvent->mReply->mReversed = mSelectionData.mReversed;
616 aEvent->mReply->mContentsRoot = mRootElement;
617 aEvent->mReply->mWritingMode = mSelectionData.GetWritingMode();
618 // The selection cache in IMEContentObserver must always have been in
619 // an editing host (or an editable anonymous <div> element). Therefore,
620 // we set mIsEditableContent to true here even though it's already been
621 // blurred or changed its editable state but the selection cache has not
622 // been invalidated yet.
623 aEvent->mReply->mIsEditableContent = true;
624 MOZ_LOG(sIMECOLog, LogLevel::Debug,
625 ("0x%p HandleQueryContentEvent(aEvent={ "
626 "mMessage=%s, mReply=%s })",
627 this, ToChar(aEvent->mMessage), ToString(aEvent->mReply).c_str()));
628 return NS_OK;
631 MOZ_LOG(sIMECOLog, LogLevel::Info,
632 ("0x%p HandleQueryContentEvent(aEvent={ mMessage=%s })", this,
633 ToChar(aEvent->mMessage)));
635 // If we can make the event's input offset absolute with TextComposition or
636 // mSelection, we should set it here for reducing the cost of computing
637 // selection start offset. If ContentEventHandler receives a
638 // WidgetQueryContentEvent whose input offset is relative to insertion point,
639 // it computes current selection start offset (this may be expensive) and
640 // make the offset absolute value itself.
641 // Note that calling MakeOffsetAbsolute() makes the event a query event with
642 // absolute offset. So, ContentEventHandler doesn't pay any additional cost
643 // after calling MakeOffsetAbsolute() here.
644 if (aEvent->mInput.mRelativeToInsertionPoint &&
645 aEvent->mInput.IsValidEventMessage(aEvent->mMessage)) {
646 RefPtr<TextComposition> composition =
647 IMEStateManager::GetTextCompositionFor(aEvent->mWidget);
648 if (composition) {
649 uint32_t compositionStart = composition->NativeOffsetOfStartComposition();
650 if (NS_WARN_IF(!aEvent->mInput.MakeOffsetAbsolute(compositionStart))) {
651 return NS_ERROR_FAILURE;
653 } else if (isSelectionCacheAvailable && mSelectionData.HasRange()) {
654 const uint32_t selectionStart = mSelectionData.mOffset;
655 if (NS_WARN_IF(!aEvent->mInput.MakeOffsetAbsolute(selectionStart))) {
656 return NS_ERROR_FAILURE;
661 AutoRestore<bool> handling(mIsHandlingQueryContentEvent);
662 mIsHandlingQueryContentEvent = true;
663 ContentEventHandler handler(GetPresContext());
664 nsresult rv = handler.HandleQueryContentEvent(aEvent);
665 if (NS_WARN_IF(Destroyed())) {
666 // If this has already destroyed during querying the content, the query
667 // is outdated even if it's succeeded. So, make the query fail.
668 aEvent->mReply.reset();
669 MOZ_LOG(sIMECOLog, LogLevel::Warning,
670 ("0x%p HandleQueryContentEvent(), WARNING, "
671 "IMEContentObserver has been destroyed during the query, "
672 "making the query fail",
673 this));
674 return rv;
677 if (aEvent->Succeeded() &&
678 NS_WARN_IF(aEvent->mReply->mContentsRoot != mRootElement)) {
679 // Focus has changed unexpectedly, so make the query fail.
680 aEvent->mReply.reset();
682 return rv;
685 nsresult IMEContentObserver::MaybeHandleSelectionEvent(
686 nsPresContext* aPresContext, WidgetSelectionEvent* aEvent) {
687 MOZ_ASSERT(aEvent);
688 MOZ_ASSERT(aEvent->mMessage == eSetSelection);
689 NS_ASSERTION(!mNeedsToNotifyIMEOfSelectionChange,
690 "Selection cache has not been updated yet");
692 MOZ_LOG(
693 sIMECOLog, LogLevel::Debug,
694 ("0x%p MaybeHandleSelectionEvent(aEvent={ "
695 "mMessage=%s, mOffset=%u, mLength=%u, mReversed=%s, "
696 "mExpandToClusterBoundary=%s, mUseNativeLineBreak=%s }), "
697 "mSelectionData=%s",
698 this, ToChar(aEvent->mMessage), aEvent->mOffset, aEvent->mLength,
699 ToChar(aEvent->mReversed), ToChar(aEvent->mExpandToClusterBoundary),
700 ToChar(aEvent->mUseNativeLineBreak), ToString(mSelectionData).c_str()));
702 // When we have Selection cache, and the caller wants to set same selection
703 // range, we shouldn't try to compute same range because it may be impossible
704 // if the range boundary is around element boundaries which won't be
705 // serialized with line breaks like close tags of inline elements. In that
706 // case, inserting new text at different point may be different from intention
707 // of users or web apps which set current selection.
708 // FIXME: We cache only selection data computed with native line breaker
709 // lengths. Perhaps, we should improve the struct to have both data of
710 // offset and length. E.g., adding line break counts for both offset and
711 // length.
712 if (!mNeedsToNotifyIMEOfSelectionChange && aEvent->mUseNativeLineBreak &&
713 mSelectionData.IsInitialized() && mSelectionData.HasRange() &&
714 mSelectionData.StartOffset() == aEvent->mOffset &&
715 mSelectionData.Length() == aEvent->mLength) {
716 if (RefPtr<Selection> selection = mSelection) {
717 selection->ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION,
718 ScrollAxis(), ScrollAxis(), 0);
720 aEvent->mSucceeded = true;
721 return NS_OK;
724 ContentEventHandler handler(aPresContext);
725 return handler.OnSelectionEvent(aEvent);
728 bool IMEContentObserver::OnMouseButtonEvent(nsPresContext& aPresContext,
729 WidgetMouseEvent& aMouseEvent) {
730 if (!mIMENotificationRequests ||
731 !mIMENotificationRequests->WantMouseButtonEventOnChar()) {
732 return false;
734 if (!aMouseEvent.IsTrusted() || aMouseEvent.DefaultPrevented() ||
735 !aMouseEvent.mWidget) {
736 return false;
738 // Now, we need to notify only mouse down and mouse up event.
739 switch (aMouseEvent.mMessage) {
740 case eMouseUp:
741 case eMouseDown:
742 break;
743 default:
744 return false;
746 if (NS_WARN_IF(!mWidget) || NS_WARN_IF(mWidget->Destroyed())) {
747 return false;
750 WidgetQueryContentEvent queryCharAtPointEvent(true, eQueryCharacterAtPoint,
751 aMouseEvent.mWidget);
752 queryCharAtPointEvent.mRefPoint = aMouseEvent.mRefPoint;
753 ContentEventHandler handler(&aPresContext);
754 handler.OnQueryCharacterAtPoint(&queryCharAtPointEvent);
755 if (NS_WARN_IF(queryCharAtPointEvent.Failed()) ||
756 queryCharAtPointEvent.DidNotFindChar()) {
757 return false;
760 // The widget might be destroyed during querying the content since it
761 // causes flushing layout.
762 if (!mWidget || NS_WARN_IF(mWidget->Destroyed())) {
763 return false;
766 // The result character rect is relative to the top level widget.
767 // We should notify it with offset in the widget.
768 nsIWidget* topLevelWidget = mWidget->GetTopLevelWidget();
769 if (topLevelWidget && topLevelWidget != mWidget) {
770 queryCharAtPointEvent.mReply->mRect.MoveBy(
771 topLevelWidget->WidgetToScreenOffset() -
772 mWidget->WidgetToScreenOffset());
774 // The refPt is relative to its widget.
775 // We should notify it with offset in the widget.
776 if (aMouseEvent.mWidget != mWidget) {
777 queryCharAtPointEvent.mRefPoint +=
778 aMouseEvent.mWidget->WidgetToScreenOffset() -
779 mWidget->WidgetToScreenOffset();
782 IMENotification notification(NOTIFY_IME_OF_MOUSE_BUTTON_EVENT);
783 notification.mMouseButtonEventData.mEventMessage = aMouseEvent.mMessage;
784 notification.mMouseButtonEventData.mOffset =
785 queryCharAtPointEvent.mReply->StartOffset();
786 notification.mMouseButtonEventData.mCursorPos =
787 queryCharAtPointEvent.mRefPoint;
788 notification.mMouseButtonEventData.mCharRect =
789 queryCharAtPointEvent.mReply->mRect;
790 notification.mMouseButtonEventData.mButton = aMouseEvent.mButton;
791 notification.mMouseButtonEventData.mButtons = aMouseEvent.mButtons;
792 notification.mMouseButtonEventData.mModifiers = aMouseEvent.mModifiers;
794 nsresult rv = IMEStateManager::NotifyIME(notification, mWidget);
795 if (NS_WARN_IF(NS_FAILED(rv))) {
796 return false;
799 bool consumed = (rv == NS_SUCCESS_EVENT_CONSUMED);
800 if (consumed) {
801 aMouseEvent.PreventDefault();
803 return consumed;
806 void IMEContentObserver::CharacterDataWillChange(
807 nsIContent* aContent, const CharacterDataChangeInfo& aInfo) {
808 if (!aContent->IsText()) {
809 return; // Ignore if it's a comment node or something other invisible data
810 // node.
812 MOZ_ASSERT(mPreCharacterDataChangeLength < 0,
813 "CharacterDataChanged() should've reset "
814 "mPreCharacterDataChangeLength");
816 if (!NeedsTextChangeNotification() ||
817 !nsContentUtils::IsInSameAnonymousTree(mRootElement, aContent)) {
818 return;
821 mEndOfAddedTextCache.Clear();
822 mStartOfRemovingTextRangeCache.Clear();
824 // Although we don't assume this change occurs while this is storing
825 // the range of added consecutive nodes, if it actually happens, we need to
826 // flush them since this change may occur before or in the range. So, it's
827 // safe to flush pending computation of mTextChangeData before handling this.
828 MaybeNotifyIMEOfAddedTextDuringDocumentChange();
830 mPreCharacterDataChangeLength = ContentEventHandler::GetNativeTextLength(
831 *aContent->AsText(), aInfo.mChangeStart, aInfo.mChangeEnd);
832 MOZ_ASSERT(
833 mPreCharacterDataChangeLength >= aInfo.mChangeEnd - aInfo.mChangeStart,
834 "The computed length must be same as or larger than XP length");
837 void IMEContentObserver::CharacterDataChanged(
838 nsIContent* aContent, const CharacterDataChangeInfo& aInfo) {
839 if (!aContent->IsText()) {
840 return; // Ignore if it's a comment node or something other invisible data
841 // node.
844 // Let TextComposition have a change to update composition string range in
845 // the text node if the change is caused by the web apps.
846 if (mWidget && !IsEditorHandlingEventForComposition()) {
847 if (RefPtr<TextComposition> composition =
848 IMEStateManager::GetTextCompositionFor(mWidget)) {
849 composition->OnCharacterDataChanged(*aContent->AsText(), aInfo);
853 if (!NeedsTextChangeNotification() ||
854 !nsContentUtils::IsInSameAnonymousTree(mRootElement, aContent)) {
855 return;
858 mEndOfAddedTextCache.Clear();
859 mStartOfRemovingTextRangeCache.Clear();
860 MOZ_ASSERT(
861 !HasAddedNodesDuringDocumentChange(),
862 "The stored range should be flushed before actually the data is changed");
864 int64_t removedLength = mPreCharacterDataChangeLength;
865 mPreCharacterDataChangeLength = -1;
867 MOZ_ASSERT(removedLength >= 0,
868 "mPreCharacterDataChangeLength should've been set by "
869 "CharacterDataWillChange()");
871 uint32_t offset = 0;
872 // get offsets of change and fire notification
873 nsresult rv = ContentEventHandler::GetFlatTextLengthInRange(
874 NodePosition(mRootElement, 0u),
875 NodePosition(aContent, aInfo.mChangeStart), mRootElement, &offset,
876 LINE_BREAK_TYPE_NATIVE);
877 if (NS_WARN_IF(NS_FAILED(rv))) {
878 return;
881 uint32_t newLength = ContentEventHandler::GetNativeTextLength(
882 *aContent->AsText(), aInfo.mChangeStart,
883 aInfo.mChangeStart + aInfo.mReplaceLength);
885 uint32_t oldEnd = offset + static_cast<uint32_t>(removedLength);
886 uint32_t newEnd = offset + newLength;
888 TextChangeData data(offset, oldEnd, newEnd,
889 IsEditorHandlingEventForComposition(),
890 IsEditorComposing());
891 MaybeNotifyIMEOfTextChange(data);
894 void IMEContentObserver::NotifyContentAdded(nsINode* aContainer,
895 nsIContent* aFirstContent,
896 nsIContent* aLastContent) {
897 if (!NeedsTextChangeNotification() ||
898 !nsContentUtils::IsInSameAnonymousTree(mRootElement, aFirstContent)) {
899 return;
902 MOZ_ASSERT_IF(aFirstContent, aFirstContent->GetParentNode() == aContainer);
903 MOZ_ASSERT_IF(aLastContent, aLastContent->GetParentNode() == aContainer);
905 mStartOfRemovingTextRangeCache.Clear();
907 // If it's in a document change, nodes are added consecutively. Therefore,
908 // if we cache the first node and the last node, we need to compute the
909 // range once.
910 // FYI: This is not true if the change caused by an operation in the editor.
911 if (IsInDocumentChange()) {
912 // Now, mEndOfAddedTextCache may be invalid if node is added before
913 // the last node in mEndOfAddedTextCache. Clear it.
914 mEndOfAddedTextCache.Clear();
915 if (!HasAddedNodesDuringDocumentChange()) {
916 mFirstAddedContainer = mLastAddedContainer = aContainer;
917 mFirstAddedContent = aFirstContent;
918 mLastAddedContent = aLastContent;
919 MOZ_LOG(sIMECOLog, LogLevel::Debug,
920 ("0x%p NotifyContentAdded(), starts to store consecutive added "
921 "nodes",
922 this));
923 return;
925 // If first node being added is not next node of the last node,
926 // notify IME of the previous range first, then, restart to cache the
927 // range.
928 if (NS_WARN_IF(!IsNextNodeOfLastAddedNode(aContainer, aFirstContent))) {
929 // Flush the old range first.
930 MaybeNotifyIMEOfAddedTextDuringDocumentChange();
931 mFirstAddedContainer = aContainer;
932 mFirstAddedContent = aFirstContent;
933 MOZ_LOG(sIMECOLog, LogLevel::Debug,
934 ("0x%p NotifyContentAdded(), starts to store consecutive added "
935 "nodes",
936 this));
938 mLastAddedContainer = aContainer;
939 mLastAddedContent = aLastContent;
940 return;
942 MOZ_ASSERT(!HasAddedNodesDuringDocumentChange(),
943 "The cache should be cleared when document change finished");
945 uint32_t offset = 0;
946 nsresult rv = NS_OK;
947 if (!mEndOfAddedTextCache.Match(aContainer,
948 aFirstContent->GetPreviousSibling())) {
949 mEndOfAddedTextCache.Clear();
950 rv = ContentEventHandler::GetFlatTextLengthInRange(
951 NodePosition(mRootElement, 0u),
952 NodePositionBefore(aContainer, PointBefore(aContainer, aFirstContent)),
953 mRootElement, &offset, LINE_BREAK_TYPE_NATIVE);
954 if (NS_WARN_IF(NS_FAILED((rv)))) {
955 return;
957 } else {
958 offset = mEndOfAddedTextCache.mFlatTextLength;
961 // get offset at the end of the last added node
962 uint32_t addingLength = 0;
963 rv = ContentEventHandler::GetFlatTextLengthInRange(
964 NodePositionBefore(aContainer, PointBefore(aContainer, aFirstContent)),
965 NodePosition(aContainer, aLastContent), mRootElement, &addingLength,
966 LINE_BREAK_TYPE_NATIVE);
967 if (NS_WARN_IF(NS_FAILED((rv)))) {
968 mEndOfAddedTextCache.Clear();
969 return;
972 // If multiple lines are being inserted in an HTML editor, next call of
973 // NotifyContentAdded() is for adding next node. Therefore, caching the text
974 // length can skip to compute the text length before the adding node and
975 // before of it.
976 mEndOfAddedTextCache.Cache(aContainer, aLastContent, offset + addingLength);
978 if (!addingLength) {
979 return;
982 TextChangeData data(offset, offset, offset + addingLength,
983 IsEditorHandlingEventForComposition(),
984 IsEditorComposing());
985 MaybeNotifyIMEOfTextChange(data);
988 void IMEContentObserver::ContentAppended(nsIContent* aFirstNewContent) {
989 nsIContent* parent = aFirstNewContent->GetParent();
990 MOZ_ASSERT(parent);
991 NotifyContentAdded(parent, aFirstNewContent, parent->GetLastChild());
994 void IMEContentObserver::ContentInserted(nsIContent* aChild) {
995 MOZ_ASSERT(aChild);
996 NotifyContentAdded(aChild->GetParentNode(), aChild, aChild);
999 void IMEContentObserver::ContentRemoved(nsIContent* aChild,
1000 nsIContent* aPreviousSibling) {
1001 if (!NeedsTextChangeNotification() ||
1002 !nsContentUtils::IsInSameAnonymousTree(mRootElement, aChild)) {
1003 return;
1006 mEndOfAddedTextCache.Clear();
1007 MaybeNotifyIMEOfAddedTextDuringDocumentChange();
1009 nsINode* containerNode = aChild->GetParentNode();
1010 MOZ_ASSERT(containerNode);
1012 uint32_t offset = 0;
1013 nsresult rv = NS_OK;
1014 if (!mStartOfRemovingTextRangeCache.Match(containerNode, aPreviousSibling)) {
1015 // At removing a child node of aContainer, we need the line break caused
1016 // by open tag of aContainer. Be careful when aPreviousSibling is nullptr.
1018 rv = ContentEventHandler::GetFlatTextLengthInRange(
1019 NodePosition(mRootElement, 0u),
1020 NodePosition(containerNode, aPreviousSibling), mRootElement, &offset,
1021 LINE_BREAK_TYPE_NATIVE);
1022 if (NS_WARN_IF(NS_FAILED(rv))) {
1023 mStartOfRemovingTextRangeCache.Clear();
1024 return;
1026 mStartOfRemovingTextRangeCache.Cache(containerNode, aPreviousSibling,
1027 offset);
1028 } else {
1029 offset = mStartOfRemovingTextRangeCache.mFlatTextLength;
1032 // get offset at the end of the deleted node
1033 uint32_t textLength = 0;
1034 if (const Text* textNode = Text::FromNode(aChild)) {
1035 textLength = ContentEventHandler::GetNativeTextLength(*textNode);
1036 } else {
1037 nsresult rv = ContentEventHandler::GetFlatTextLengthInRange(
1038 NodePositionBefore(aChild, 0u),
1039 NodePosition(aChild, aChild->GetChildCount()), mRootElement,
1040 &textLength, LINE_BREAK_TYPE_NATIVE, true);
1041 if (NS_WARN_IF(NS_FAILED(rv))) {
1042 mStartOfRemovingTextRangeCache.Clear();
1043 return;
1047 if (!textLength) {
1048 return;
1051 TextChangeData data(offset, offset + textLength, offset,
1052 IsEditorHandlingEventForComposition(),
1053 IsEditorComposing());
1054 MaybeNotifyIMEOfTextChange(data);
1057 void IMEContentObserver::ClearAddedNodesDuringDocumentChange() {
1058 mFirstAddedContainer = mLastAddedContainer = nullptr;
1059 mFirstAddedContent = mLastAddedContent = nullptr;
1060 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1061 ("0x%p ClearAddedNodesDuringDocumentChange(), finished storing "
1062 "consecutive nodes",
1063 this));
1066 bool IMEContentObserver::IsNextNodeOfLastAddedNode(nsINode* aParent,
1067 nsIContent* aChild) const {
1068 MOZ_ASSERT(aParent);
1069 MOZ_ASSERT(aChild && aChild->GetParentNode() == aParent);
1070 MOZ_ASSERT(mRootElement);
1071 MOZ_ASSERT(HasAddedNodesDuringDocumentChange());
1073 // If the parent node isn't changed, we can check that mLastAddedContent has
1074 // aChild as its next sibling.
1075 if (aParent == mLastAddedContainer) {
1076 return !NS_WARN_IF(mLastAddedContent->GetNextSibling() != aChild);
1079 // If the parent node is changed, that means that the recorded last added node
1080 // shouldn't have a sibling.
1081 if (NS_WARN_IF(mLastAddedContent->GetNextSibling())) {
1082 return false;
1085 // If the node is aParent is a descendant of mLastAddedContainer,
1086 // aChild should be the first child in the new container.
1087 if (mLastAddedContainer == aParent->GetParent()) {
1088 return !NS_WARN_IF(aChild->GetPreviousSibling());
1091 // Otherwise, we need to check it even with slow path.
1092 nsIContent* nextContentOfLastAddedContent =
1093 mLastAddedContent->GetNextNode(mRootElement->GetParentNode());
1094 if (NS_WARN_IF(!nextContentOfLastAddedContent)) {
1095 return false;
1097 if (NS_WARN_IF(nextContentOfLastAddedContent != aChild)) {
1098 return false;
1100 return true;
1103 void IMEContentObserver::MaybeNotifyIMEOfAddedTextDuringDocumentChange() {
1104 if (!HasAddedNodesDuringDocumentChange()) {
1105 return;
1108 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1109 ("0x%p "
1110 "IMEContentObserver::MaybeNotifyIMEOfAddedTextDuringDocumentChange()"
1111 ", flushing stored consecutive nodes",
1112 this));
1114 // Notify IME of text change which is caused by added nodes now.
1116 // First, compute offset of start of first added node from start of the
1117 // editor.
1118 uint32_t offset;
1119 nsresult rv = ContentEventHandler::GetFlatTextLengthInRange(
1120 NodePosition(mRootElement, 0u),
1121 NodePosition(mFirstAddedContainer,
1122 PointBefore(mFirstAddedContainer, mFirstAddedContent)),
1123 mRootElement, &offset, LINE_BREAK_TYPE_NATIVE);
1124 if (NS_WARN_IF(NS_FAILED(rv))) {
1125 ClearAddedNodesDuringDocumentChange();
1126 return;
1129 // Next, compute the text length of added nodes.
1130 uint32_t length;
1131 rv = ContentEventHandler::GetFlatTextLengthInRange(
1132 NodePosition(mFirstAddedContainer,
1133 PointBefore(mFirstAddedContainer, mFirstAddedContent)),
1134 NodePosition(mLastAddedContainer, mLastAddedContent), mRootElement,
1135 &length, LINE_BREAK_TYPE_NATIVE);
1136 if (NS_WARN_IF(NS_FAILED(rv))) {
1137 ClearAddedNodesDuringDocumentChange();
1138 return;
1141 // Finally, try to notify IME of the range.
1142 TextChangeData data(offset, offset, offset + length,
1143 IsEditorHandlingEventForComposition(),
1144 IsEditorComposing());
1145 MaybeNotifyIMEOfTextChange(data);
1146 ClearAddedNodesDuringDocumentChange();
1149 void IMEContentObserver::OnTextControlValueChangedWhileNotObservable(
1150 const nsAString& aNewValue) {
1151 MOZ_ASSERT(mEditorBase);
1152 MOZ_ASSERT(mEditorBase->IsTextEditor());
1153 uint32_t newLength = ContentEventHandler::GetNativeTextLength(aNewValue);
1154 TextChangeData data(0, mTextControlValueLength, newLength, false, false);
1155 MaybeNotifyIMEOfTextChange(data);
1158 void IMEContentObserver::BeginDocumentUpdate() {
1159 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1160 ("0x%p BeginDocumentUpdate(), HasAddedNodesDuringDocumentChange()=%s",
1161 this, ToChar(HasAddedNodesDuringDocumentChange())));
1163 // If we're not in a nested document update, this will return early,
1164 // otherwise, it will handle flusing any changes currently pending before
1165 // entering a nested document update.
1166 MaybeNotifyIMEOfAddedTextDuringDocumentChange();
1169 void IMEContentObserver::EndDocumentUpdate() {
1170 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1171 ("0x%p EndDocumentUpdate(), HasAddedNodesDuringDocumentChange()=%s",
1172 this, ToChar(HasAddedNodesDuringDocumentChange())));
1174 MaybeNotifyIMEOfAddedTextDuringDocumentChange();
1177 void IMEContentObserver::SuppressNotifyingIME() {
1178 mSuppressNotifications++;
1180 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1181 ("0x%p SuppressNotifyingIME(), mSuppressNotifications=%u", this,
1182 mSuppressNotifications));
1185 void IMEContentObserver::UnsuppressNotifyingIME() {
1186 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1187 ("0x%p UnsuppressNotifyingIME(), mSuppressNotifications=%u", this,
1188 mSuppressNotifications));
1190 if (!mSuppressNotifications || --mSuppressNotifications) {
1191 return;
1193 FlushMergeableNotifications();
1196 void IMEContentObserver::OnEditActionHandled() {
1197 MOZ_LOG(sIMECOLog, LogLevel::Debug, ("0x%p EditAction()", this));
1199 mEndOfAddedTextCache.Clear();
1200 mStartOfRemovingTextRangeCache.Clear();
1201 FlushMergeableNotifications();
1204 void IMEContentObserver::BeforeEditAction() {
1205 MOZ_LOG(sIMECOLog, LogLevel::Debug, ("0x%p BeforeEditAction()", this));
1207 mEndOfAddedTextCache.Clear();
1208 mStartOfRemovingTextRangeCache.Clear();
1211 void IMEContentObserver::CancelEditAction() {
1212 MOZ_LOG(sIMECOLog, LogLevel::Debug, ("0x%p CancelEditAction()", this));
1214 mEndOfAddedTextCache.Clear();
1215 mStartOfRemovingTextRangeCache.Clear();
1216 FlushMergeableNotifications();
1219 void IMEContentObserver::PostFocusSetNotification() {
1220 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1221 ("0x%p PostFocusSetNotification()", this));
1223 mNeedsToNotifyIMEOfFocusSet = true;
1226 void IMEContentObserver::PostTextChangeNotification() {
1227 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1228 ("0x%p PostTextChangeNotification(mTextChangeData=%s)", this,
1229 ToString(mTextChangeData).c_str()));
1231 MOZ_ASSERT(mTextChangeData.IsValid(),
1232 "mTextChangeData must have text change data");
1233 mNeedsToNotifyIMEOfTextChange = true;
1234 // Even if the observer hasn't received selection change, selection in the
1235 // flat text may have already been changed. For example, when previous `<p>`
1236 // element of another `<p>` element which contains caret is removed by a DOM
1237 // mutation, selection change event won't be fired, but selection start offset
1238 // should be decreased by the length of removed `<p>` element.
1239 // In such case, HandleQueryContentEvent shouldn't use the selection cache
1240 // anymore. Therefore, we also need to post selection change notification
1241 // too. eQuerySelectedText event may be dispatched at sending a text change
1242 // notification.
1243 mNeedsToNotifyIMEOfSelectionChange = true;
1246 void IMEContentObserver::PostSelectionChangeNotification() {
1247 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1248 ("0x%p PostSelectionChangeNotification(), mSelectionData={ "
1249 "mCausedByComposition=%s, mCausedBySelectionEvent=%s }",
1250 this, ToChar(mSelectionData.mCausedByComposition),
1251 ToChar(mSelectionData.mCausedBySelectionEvent)));
1253 mNeedsToNotifyIMEOfSelectionChange = true;
1256 void IMEContentObserver::MaybeNotifyIMEOfFocusSet() {
1257 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1258 ("0x%p MaybeNotifyIMEOfFocusSet()", this));
1260 PostFocusSetNotification();
1261 FlushMergeableNotifications();
1264 void IMEContentObserver::MaybeNotifyIMEOfTextChange(
1265 const TextChangeDataBase& aTextChangeData) {
1266 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1267 ("0x%p MaybeNotifyIMEOfTextChange(aTextChangeData=%s)", this,
1268 ToString(aTextChangeData).c_str()));
1270 if (mEditorBase && mEditorBase->IsTextEditor()) {
1271 MOZ_DIAGNOSTIC_ASSERT(static_cast<int64_t>(mTextControlValueLength) +
1272 aTextChangeData.Difference() >=
1274 mTextControlValueLength += aTextChangeData.Difference();
1277 mTextChangeData += aTextChangeData;
1278 PostTextChangeNotification();
1279 FlushMergeableNotifications();
1282 void IMEContentObserver::CancelNotifyingIMEOfTextChange() {
1283 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1284 ("0x%p CancelNotifyingIMEOfTextChange()", this));
1285 mTextChangeData.Clear();
1286 mNeedsToNotifyIMEOfTextChange = false;
1289 void IMEContentObserver::MaybeNotifyIMEOfSelectionChange(
1290 bool aCausedByComposition, bool aCausedBySelectionEvent,
1291 bool aOccurredDuringComposition) {
1292 MOZ_LOG(
1293 sIMECOLog, LogLevel::Debug,
1294 ("0x%p MaybeNotifyIMEOfSelectionChange(aCausedByComposition=%s, "
1295 "aCausedBySelectionEvent=%s, aOccurredDuringComposition)",
1296 this, ToChar(aCausedByComposition), ToChar(aCausedBySelectionEvent)));
1298 mSelectionData.AssignReason(aCausedByComposition, aCausedBySelectionEvent,
1299 aOccurredDuringComposition);
1300 PostSelectionChangeNotification();
1301 FlushMergeableNotifications();
1304 void IMEContentObserver::MaybeNotifyIMEOfPositionChange() {
1305 MOZ_LOG(sIMECOLog, LogLevel::Verbose,
1306 ("0x%p MaybeNotifyIMEOfPositionChange()", this));
1307 // If reflow is caused by ContentEventHandler during PositionChangeEvent
1308 // sending NOTIFY_IME_OF_POSITION_CHANGE, we don't need to notify IME of it
1309 // again since ContentEventHandler returns the result including this reflow's
1310 // result.
1311 if (mIsHandlingQueryContentEvent &&
1312 mSendingNotification == NOTIFY_IME_OF_POSITION_CHANGE) {
1313 MOZ_LOG(sIMECOLog, LogLevel::Verbose,
1314 ("0x%p MaybeNotifyIMEOfPositionChange(), ignored since caused by "
1315 "ContentEventHandler during sending NOTIFY_IME_OF_POSITION_CHANGE",
1316 this));
1317 return;
1319 PostPositionChangeNotification();
1320 FlushMergeableNotifications();
1323 void IMEContentObserver::CancelNotifyingIMEOfPositionChange() {
1324 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1325 ("0x%p CancelNotifyIMEOfPositionChange()", this));
1326 mNeedsToNotifyIMEOfPositionChange = false;
1329 void IMEContentObserver::MaybeNotifyCompositionEventHandled() {
1330 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1331 ("0x%p MaybeNotifyCompositionEventHandled()", this));
1333 PostCompositionEventHandledNotification();
1334 FlushMergeableNotifications();
1337 bool IMEContentObserver::UpdateSelectionCache(bool aRequireFlush /* = true */) {
1338 MOZ_ASSERT(IsSafeToNotifyIME());
1340 mSelectionData.ClearSelectionData();
1342 // XXX Cannot we cache some information for reducing the cost to compute
1343 // selection offset and writing mode?
1344 WidgetQueryContentEvent querySelectedTextEvent(true, eQuerySelectedText,
1345 mWidget);
1346 querySelectedTextEvent.mNeedsToFlushLayout = aRequireFlush;
1347 ContentEventHandler handler(GetPresContext());
1348 handler.OnQuerySelectedText(&querySelectedTextEvent);
1349 if (NS_WARN_IF(querySelectedTextEvent.Failed()) ||
1350 NS_WARN_IF(querySelectedTextEvent.mReply->mContentsRoot !=
1351 mRootElement)) {
1352 return false;
1355 mFocusedWidget = querySelectedTextEvent.mReply->mFocusedWidget;
1356 mSelectionData.Assign(querySelectedTextEvent);
1358 // WARNING: Don't set the reason of selection change here because it should be
1359 // set the reason at sending the notification.
1361 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1362 ("0x%p UpdateSelectionCache(), mSelectionData=%s", this,
1363 ToString(mSelectionData).c_str()));
1365 return true;
1368 void IMEContentObserver::PostPositionChangeNotification() {
1369 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1370 ("0x%p PostPositionChangeNotification()", this));
1372 mNeedsToNotifyIMEOfPositionChange = true;
1375 void IMEContentObserver::PostCompositionEventHandledNotification() {
1376 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1377 ("0x%p PostCompositionEventHandledNotification()", this));
1379 mNeedsToNotifyIMEOfCompositionEventHandled = true;
1382 bool IMEContentObserver::IsReflowLocked() const {
1383 nsPresContext* presContext = GetPresContext();
1384 if (NS_WARN_IF(!presContext)) {
1385 return false;
1387 PresShell* presShell = presContext->GetPresShell();
1388 if (NS_WARN_IF(!presShell)) {
1389 return false;
1391 // During reflow, we shouldn't notify IME because IME may query content
1392 // synchronously. Then, it causes ContentEventHandler will try to flush
1393 // pending notifications during reflow.
1394 return presShell->IsReflowLocked();
1397 bool IMEContentObserver::IsSafeToNotifyIME() const {
1398 // If this is already detached from the widget, this doesn't need to notify
1399 // anything.
1400 if (!mWidget) {
1401 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1402 ("0x%p IsSafeToNotifyIME(), it's not safe because of no widget",
1403 this));
1404 return false;
1407 // Don't notify IME of anything if it's not good time to do it.
1408 if (mSuppressNotifications) {
1409 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1410 ("0x%p IsSafeToNotifyIME(), it's not safe because of no widget",
1411 this));
1412 return false;
1415 if (!mESM || NS_WARN_IF(!GetPresContext())) {
1416 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1417 ("0x%p IsSafeToNotifyIME(), it's not safe because of no "
1418 "EventStateManager and/or PresContext",
1419 this));
1420 return false;
1423 // If it's in reflow, we should wait to finish the reflow.
1424 // FYI: This should be called again from Reflow() or ReflowInterruptible().
1425 if (IsReflowLocked()) {
1426 MOZ_LOG(
1427 sIMECOLog, LogLevel::Debug,
1428 ("0x%p IsSafeToNotifyIME(), it's not safe because of reflow locked",
1429 this));
1430 return false;
1433 // If we're in handling an edit action, this method will be called later.
1434 if (mEditorBase && mEditorBase->IsInEditSubAction()) {
1435 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1436 ("0x%p IsSafeToNotifyIME(), it's not safe because of focused "
1437 "editor handling somethings",
1438 this));
1439 return false;
1442 return true;
1445 void IMEContentObserver::FlushMergeableNotifications() {
1446 if (!IsSafeToNotifyIME()) {
1447 // So, if this is already called, this should do nothing.
1448 MOZ_LOG(sIMECOLog, LogLevel::Warning,
1449 ("0x%p FlushMergeableNotifications(), Warning, do nothing due to "
1450 "unsafe to notify IME",
1451 this));
1452 return;
1455 // Notifying something may cause nested call of this method. For example,
1456 // when somebody notified one of the notifications may dispatch query content
1457 // event. Then, it causes flushing layout which may cause another layout
1458 // change notification.
1460 if (mQueuedSender) {
1461 // So, if this is already called, this should do nothing.
1462 MOZ_LOG(sIMECOLog, LogLevel::Warning,
1463 ("0x%p FlushMergeableNotifications(), Warning, do nothing due to "
1464 "already flushing pending notifications",
1465 this));
1466 return;
1469 // If text change notification and/or position change notification becomes
1470 // unnecessary, let's cancel them.
1471 if (mNeedsToNotifyIMEOfTextChange && !NeedsTextChangeNotification()) {
1472 CancelNotifyingIMEOfTextChange();
1474 if (mNeedsToNotifyIMEOfPositionChange && !NeedsPositionChangeNotification()) {
1475 CancelNotifyingIMEOfPositionChange();
1478 if (!NeedsToNotifyIMEOfSomething()) {
1479 MOZ_LOG(sIMECOLog, LogLevel::Warning,
1480 ("0x%p FlushMergeableNotifications(), Warning, due to no pending "
1481 "notifications",
1482 this));
1483 return;
1486 // NOTE: Reset each pending flag because sending notification may cause
1487 // another change.
1489 MOZ_LOG(
1490 sIMECOLog, LogLevel::Info,
1491 ("0x%p FlushMergeableNotifications(), creating IMENotificationSender...",
1492 this));
1494 // If contents in selection range is modified, the selection range still
1495 // has removed node from the tree. In such case, ContentIterator won't
1496 // work well. Therefore, we shouldn't use AddScriptRunner() here since
1497 // it may kick runnable event immediately after DOM tree is changed but
1498 // the selection range isn't modified yet.
1499 mQueuedSender = new IMENotificationSender(this);
1500 mQueuedSender->Dispatch(mDocShell);
1501 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1502 ("0x%p FlushMergeableNotifications(), finished", this));
1505 void IMEContentObserver::TryToFlushPendingNotifications(bool aAllowAsync) {
1506 // If a sender instance is sending notifications, we shouldn't try to create
1507 // a new sender again because the sender will recreate by itself if there are
1508 // new pending notifications.
1509 if (mSendingNotification != NOTIFY_IME_OF_NOTHING) {
1510 return;
1513 // When the caller allows to put off notifying IME, we can wait the next
1514 // call of this method or to run the queued sender.
1515 if (mQueuedSender && XRE_IsContentProcess() && aAllowAsync) {
1516 return;
1519 if (!mQueuedSender) {
1520 // If it was not safe to dispatch notifications when the pending
1521 // notifications are posted, this may not have IMENotificationSender
1522 // instance because it couldn't dispatch it, e.g., when an edit sub-action
1523 // is being handled in the editor, we shouldn't do it even if it's safe to
1524 // run script. Therefore, we need to create the sender instance here in the
1525 // case.
1526 if (!NeedsToNotifyIMEOfSomething()) {
1527 return;
1529 mQueuedSender = new IMENotificationSender(this);
1532 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1533 ("0x%p TryToFlushPendingNotifications(), performing queued "
1534 "IMENotificationSender forcibly",
1535 this));
1536 RefPtr<IMENotificationSender> queuedSender = mQueuedSender;
1537 queuedSender->Run();
1540 /******************************************************************************
1541 * mozilla::IMEContentObserver::AChangeEvent
1542 ******************************************************************************/
1544 bool IMEContentObserver::AChangeEvent::CanNotifyIME(
1545 ChangeEventType aChangeEventType) const {
1546 RefPtr<IMEContentObserver> observer = GetObserver();
1547 if (NS_WARN_IF(!observer)) {
1548 return false;
1551 const LogLevel debugOrVerbose =
1552 aChangeEventType == ChangeEventType::eChangeEventType_Position
1553 ? LogLevel::Verbose
1554 : LogLevel::Debug;
1556 if (aChangeEventType == eChangeEventType_CompositionEventHandled) {
1557 if (observer->mWidget) {
1558 return true;
1560 MOZ_LOG(sIMECOLog, debugOrVerbose,
1561 ("0x%p AChangeEvent::CanNotifyIME(), Cannot notify IME of "
1562 "composition event handled because of no widget",
1563 this));
1564 return false;
1566 State state = observer->GetState();
1567 // If it's not initialized, we should do nothing.
1568 if (state == eState_NotObserving) {
1569 MOZ_LOG(sIMECOLog, debugOrVerbose,
1570 ("0x%p AChangeEvent::CanNotifyIME(), Cannot notify IME because "
1571 "of not observing",
1572 this));
1573 return false;
1575 // If setting focus, just check the state.
1576 if (aChangeEventType == eChangeEventType_Focus) {
1577 if (!observer->mIMEHasFocus) {
1578 return true;
1580 MOZ_LOG(sIMECOLog, debugOrVerbose,
1581 ("0x%p AChangeEvent::CanNotifyIME(), Cannot notify IME of focus "
1582 "change because of already focused",
1583 this));
1584 NS_WARNING("IME already has focus");
1585 return false;
1587 // If we've not notified IME of focus yet, we shouldn't notify anything.
1588 if (!observer->mIMEHasFocus) {
1589 MOZ_LOG(sIMECOLog, debugOrVerbose,
1590 ("0x%p AChangeEvent::CanNotifyIME(), Cannot notify IME because "
1591 "of not focused",
1592 this));
1593 return false;
1596 // If IME has focus, IMEContentObserver must hold the widget.
1597 MOZ_ASSERT(observer->mWidget);
1599 return true;
1602 bool IMEContentObserver::AChangeEvent::IsSafeToNotifyIME(
1603 ChangeEventType aChangeEventType) const {
1604 const LogLevel warningOrVerbose =
1605 aChangeEventType == ChangeEventType::eChangeEventType_Position
1606 ? LogLevel::Verbose
1607 : LogLevel::Warning;
1609 if (NS_WARN_IF(!nsContentUtils::IsSafeToRunScript())) {
1610 MOZ_LOG(sIMECOLog, warningOrVerbose,
1611 ("0x%p AChangeEvent::IsSafeToNotifyIME(), Warning, Cannot notify "
1612 "IME because of not safe to run script",
1613 this));
1614 return false;
1617 RefPtr<IMEContentObserver> observer = GetObserver();
1618 if (!observer) {
1619 MOZ_LOG(sIMECOLog, warningOrVerbose,
1620 ("0x%p AChangeEvent::IsSafeToNotifyIME(), Warning, Cannot notify "
1621 "IME because of no observer",
1622 this));
1623 return false;
1626 // While we're sending a notification, we shouldn't send another notification
1627 // recursively.
1628 if (observer->mSendingNotification != NOTIFY_IME_OF_NOTHING) {
1629 MOZ_LOG(sIMECOLog, warningOrVerbose,
1630 ("0x%p AChangeEvent::IsSafeToNotifyIME(), Warning, Cannot notify "
1631 "IME because of the observer sending another notification",
1632 this));
1633 return false;
1635 State state = observer->GetState();
1636 if (aChangeEventType == eChangeEventType_Focus) {
1637 if (NS_WARN_IF(state != eState_Initializing && state != eState_Observing)) {
1638 MOZ_LOG(sIMECOLog, warningOrVerbose,
1639 ("0x%p AChangeEvent::IsSafeToNotifyIME(), Warning, Cannot "
1640 "notify IME of focus because of not observing",
1641 this));
1642 return false;
1644 } else if (aChangeEventType == eChangeEventType_CompositionEventHandled) {
1645 // It doesn't need to check the observing status.
1646 } else if (state != eState_Observing) {
1647 MOZ_LOG(sIMECOLog, warningOrVerbose,
1648 ("0x%p AChangeEvent::IsSafeToNotifyIME(), Warning, Cannot notify "
1649 "IME because of not observing",
1650 this));
1651 return false;
1653 return observer->IsSafeToNotifyIME();
1656 /******************************************************************************
1657 * mozilla::IMEContentObserver::IMENotificationSender
1658 ******************************************************************************/
1660 void IMEContentObserver::IMENotificationSender::Dispatch(
1661 nsIDocShell* aDocShell) {
1662 if (XRE_IsContentProcess() && aDocShell) {
1663 RefPtr<nsPresContext> presContext = aDocShell->GetPresContext();
1664 if (presContext) {
1665 nsRefreshDriver* refreshDriver = presContext->RefreshDriver();
1666 if (refreshDriver) {
1667 refreshDriver->AddEarlyRunner(this);
1668 return;
1673 nsIScriptGlobalObject* globalObject =
1674 aDocShell ? aDocShell->GetScriptGlobalObject() : nullptr;
1675 if (globalObject) {
1676 RefPtr<IMENotificationSender> queuedSender = this;
1677 globalObject->Dispatch(TaskCategory::Other, queuedSender.forget());
1678 } else {
1679 NS_DispatchToCurrentThread(this);
1683 NS_IMETHODIMP
1684 IMEContentObserver::IMENotificationSender::Run() {
1685 if (NS_WARN_IF(mIsRunning)) {
1686 MOZ_LOG(
1687 sIMECOLog, LogLevel::Error,
1688 ("0x%p IMENotificationSender::Run(), FAILED, due to called recursively",
1689 this));
1690 return NS_OK;
1693 RefPtr<IMEContentObserver> observer = GetObserver();
1694 if (!observer) {
1695 return NS_OK;
1698 AutoRestore<bool> running(mIsRunning);
1699 mIsRunning = true;
1701 // This instance was already performed forcibly.
1702 if (observer->mQueuedSender != this) {
1703 return NS_OK;
1706 // NOTE: Reset each pending flag because sending notification may cause
1707 // another change.
1709 if (observer->mNeedsToNotifyIMEOfFocusSet) {
1710 observer->mNeedsToNotifyIMEOfFocusSet = false;
1711 SendFocusSet();
1712 observer->mQueuedSender = nullptr;
1713 // If it's not safe to notify IME of focus, SendFocusSet() sets
1714 // mNeedsToNotifyIMEOfFocusSet true again. For guaranteeing to send the
1715 // focus notification later, we should put a new sender into the queue but
1716 // this case must be rare. Note that if mIMEContentObserver is already
1717 // destroyed, mNeedsToNotifyIMEOfFocusSet is never set true again.
1718 if (observer->mNeedsToNotifyIMEOfFocusSet) {
1719 MOZ_ASSERT(!observer->mIMEHasFocus);
1720 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1721 ("0x%p IMENotificationSender::Run(), posting "
1722 "IMENotificationSender to current thread",
1723 this));
1724 observer->mQueuedSender = new IMENotificationSender(observer);
1725 observer->mQueuedSender->Dispatch(observer->mDocShell);
1726 return NS_OK;
1728 // This is the first notification to IME. So, we don't need to notify
1729 // anymore since IME starts to query content after it gets focus.
1730 observer->ClearPendingNotifications();
1731 return NS_OK;
1734 if (observer->mNeedsToNotifyIMEOfTextChange) {
1735 observer->mNeedsToNotifyIMEOfTextChange = false;
1736 SendTextChange();
1739 // If a text change notification causes another text change again, we should
1740 // notify IME of that before sending a selection change notification.
1741 if (!observer->mNeedsToNotifyIMEOfTextChange) {
1742 // Be aware, PuppetWidget depends on the order of this. A selection change
1743 // notification should not be sent before a text change notification because
1744 // PuppetWidget shouldn't query new text content every selection change.
1745 if (observer->mNeedsToNotifyIMEOfSelectionChange) {
1746 observer->mNeedsToNotifyIMEOfSelectionChange = false;
1747 SendSelectionChange();
1751 // If a text change notification causes another text change again or a
1752 // selection change notification causes either a text change or another
1753 // selection change, we should notify IME of those before sending a position
1754 // change notification.
1755 if (!observer->mNeedsToNotifyIMEOfTextChange &&
1756 !observer->mNeedsToNotifyIMEOfSelectionChange) {
1757 if (observer->mNeedsToNotifyIMEOfPositionChange) {
1758 observer->mNeedsToNotifyIMEOfPositionChange = false;
1759 SendPositionChange();
1763 // Composition event handled notification should be sent after all the
1764 // other notifications because this notifies widget of finishing all pending
1765 // events are handled completely.
1766 if (!observer->mNeedsToNotifyIMEOfTextChange &&
1767 !observer->mNeedsToNotifyIMEOfSelectionChange &&
1768 !observer->mNeedsToNotifyIMEOfPositionChange) {
1769 if (observer->mNeedsToNotifyIMEOfCompositionEventHandled) {
1770 observer->mNeedsToNotifyIMEOfCompositionEventHandled = false;
1771 SendCompositionEventHandled();
1775 observer->mQueuedSender = nullptr;
1777 // If notifications caused some new change, we should notify them now.
1778 if (observer->NeedsToNotifyIMEOfSomething()) {
1779 if (observer->GetState() == eState_StoppedObserving) {
1780 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1781 ("0x%p IMENotificationSender::Run(), waiting "
1782 "IMENotificationSender to be reinitialized",
1783 this));
1784 } else {
1785 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1786 ("0x%p IMENotificationSender::Run(), posting "
1787 "IMENotificationSender to current thread",
1788 this));
1789 observer->mQueuedSender = new IMENotificationSender(observer);
1790 observer->mQueuedSender->Dispatch(observer->mDocShell);
1793 return NS_OK;
1796 void IMEContentObserver::IMENotificationSender::SendFocusSet() {
1797 RefPtr<IMEContentObserver> observer = GetObserver();
1798 if (!observer) {
1799 return;
1802 if (!CanNotifyIME(eChangeEventType_Focus)) {
1803 // If IMEContentObserver has already gone, we don't need to notify IME of
1804 // focus.
1805 MOZ_LOG(sIMECOLog, LogLevel::Warning,
1806 ("0x%p IMENotificationSender::SendFocusSet(), Warning, does not "
1807 "send notification due to impossible to notify IME of focus",
1808 this));
1809 observer->ClearPendingNotifications();
1810 return;
1813 if (!IsSafeToNotifyIME(eChangeEventType_Focus)) {
1814 MOZ_LOG(
1815 sIMECOLog, LogLevel::Warning,
1816 ("0x%p IMENotificationSender::SendFocusSet(), Warning, does not send "
1817 "notification due to unsafe, retrying to send NOTIFY_IME_OF_FOCUS...",
1818 this));
1819 observer->PostFocusSetNotification();
1820 return;
1823 observer->mIMEHasFocus = true;
1824 // Initialize selection cache with the first selection data.
1825 #ifdef XP_MACOSX
1826 // We need to flush layout only on macOS because character coordinates are
1827 // cached by cocoa with this call, but we don't have a way to update them
1828 // after that. Therefore, we need the latest layout information right now.
1829 observer->UpdateSelectionCache(true);
1830 #else
1831 // We avoid flushing for focus in the general case.
1832 observer->UpdateSelectionCache(false);
1833 #endif // #ifdef XP_MACOSX #else
1834 MOZ_LOG(sIMECOLog, LogLevel::Info,
1835 ("0x%p IMENotificationSender::SendFocusSet(), sending "
1836 "NOTIFY_IME_OF_FOCUS...",
1837 this));
1839 MOZ_RELEASE_ASSERT(observer->mSendingNotification == NOTIFY_IME_OF_NOTHING);
1840 observer->mSendingNotification = NOTIFY_IME_OF_FOCUS;
1841 IMEStateManager::NotifyIME(IMENotification(NOTIFY_IME_OF_FOCUS),
1842 observer->mWidget);
1843 observer->mSendingNotification = NOTIFY_IME_OF_NOTHING;
1845 // IMENotificationRequests referred by ObserveEditableNode() may be different
1846 // before or after widget receives NOTIFY_IME_OF_FOCUS. Therefore, we need
1847 // to guarantee to call ObserveEditableNode() after sending
1848 // NOTIFY_IME_OF_FOCUS.
1849 observer->OnIMEReceivedFocus();
1851 MOZ_LOG(
1852 sIMECOLog, LogLevel::Debug,
1853 ("0x%p IMENotificationSender::SendFocusSet(), sent NOTIFY_IME_OF_FOCUS",
1854 this));
1857 void IMEContentObserver::IMENotificationSender::SendSelectionChange() {
1858 RefPtr<IMEContentObserver> observer = GetObserver();
1859 if (!observer) {
1860 return;
1863 if (!CanNotifyIME(eChangeEventType_Selection)) {
1864 MOZ_LOG(sIMECOLog, LogLevel::Warning,
1865 ("0x%p IMENotificationSender::SendSelectionChange(), Warning, "
1866 "does not send notification due to impossible to notify IME of "
1867 "selection change",
1868 this));
1869 return;
1872 if (!IsSafeToNotifyIME(eChangeEventType_Selection)) {
1873 MOZ_LOG(sIMECOLog, LogLevel::Warning,
1874 ("0x%p IMENotificationSender::SendSelectionChange(), Warning, "
1875 "does not send notification due to unsafe, retrying to send "
1876 "NOTIFY_IME_OF_SELECTION_CHANGE...",
1877 this));
1878 observer->PostSelectionChangeNotification();
1879 return;
1882 SelectionChangeData lastSelChangeData = observer->mSelectionData;
1883 if (NS_WARN_IF(!observer->UpdateSelectionCache())) {
1884 MOZ_LOG(sIMECOLog, LogLevel::Error,
1885 ("0x%p IMENotificationSender::SendSelectionChange(), FAILED, due "
1886 "to UpdateSelectionCache() failure",
1887 this));
1888 return;
1891 // The state may be changed since querying content causes flushing layout.
1892 if (!CanNotifyIME(eChangeEventType_Selection)) {
1893 MOZ_LOG(sIMECOLog, LogLevel::Error,
1894 ("0x%p IMENotificationSender::SendSelectionChange(), FAILED, due "
1895 "to flushing layout having changed something",
1896 this));
1897 return;
1900 // If the selection isn't changed actually, we shouldn't notify IME of
1901 // selection change.
1902 SelectionChangeData& newSelChangeData = observer->mSelectionData;
1903 if (lastSelChangeData.IsInitialized() &&
1904 lastSelChangeData.EqualsRangeAndDirectionAndWritingMode(
1905 newSelChangeData)) {
1906 MOZ_LOG(
1907 sIMECOLog, LogLevel::Debug,
1908 ("0x%p IMENotificationSender::SendSelectionChange(), not notifying IME "
1909 "of NOTIFY_IME_OF_SELECTION_CHANGE due to not changed actually",
1910 this));
1911 return;
1914 MOZ_LOG(sIMECOLog, LogLevel::Info,
1915 ("0x%p IMENotificationSender::SendSelectionChange(), sending "
1916 "NOTIFY_IME_OF_SELECTION_CHANGE... newSelChangeData=%s",
1917 this, ToString(newSelChangeData).c_str()));
1919 IMENotification notification(NOTIFY_IME_OF_SELECTION_CHANGE);
1920 notification.SetData(observer->mSelectionData);
1922 MOZ_RELEASE_ASSERT(observer->mSendingNotification == NOTIFY_IME_OF_NOTHING);
1923 observer->mSendingNotification = NOTIFY_IME_OF_SELECTION_CHANGE;
1924 IMEStateManager::NotifyIME(notification, observer->mWidget);
1925 observer->mSendingNotification = NOTIFY_IME_OF_NOTHING;
1927 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1928 ("0x%p IMENotificationSender::SendSelectionChange(), sent "
1929 "NOTIFY_IME_OF_SELECTION_CHANGE",
1930 this));
1933 void IMEContentObserver::IMENotificationSender::SendTextChange() {
1934 RefPtr<IMEContentObserver> observer = GetObserver();
1935 if (!observer) {
1936 return;
1939 if (!CanNotifyIME(eChangeEventType_Text)) {
1940 MOZ_LOG(
1941 sIMECOLog, LogLevel::Warning,
1942 ("0x%p IMENotificationSender::SendTextChange(), Warning, does not "
1943 "send notification due to impossible to notify IME of text change",
1944 this));
1945 return;
1948 if (!IsSafeToNotifyIME(eChangeEventType_Text)) {
1949 MOZ_LOG(sIMECOLog, LogLevel::Warning,
1950 ("0x%p IMENotificationSender::SendTextChange(), Warning, does "
1951 "not send notification due to unsafe, retrying to send "
1952 "NOTIFY_IME_OF_TEXT_CHANGE...",
1953 this));
1954 observer->PostTextChangeNotification();
1955 return;
1958 // If text change notification is unnecessary anymore, just cancel it.
1959 if (!observer->NeedsTextChangeNotification()) {
1960 MOZ_LOG(sIMECOLog, LogLevel::Warning,
1961 ("0x%p IMENotificationSender::SendTextChange(), Warning, "
1962 "canceling sending NOTIFY_IME_OF_TEXT_CHANGE",
1963 this));
1964 observer->CancelNotifyingIMEOfTextChange();
1965 return;
1968 MOZ_LOG(sIMECOLog, LogLevel::Info,
1969 ("0x%p IMENotificationSender::SendTextChange(), sending "
1970 "NOTIFY_IME_OF_TEXT_CHANGE... mIMEContentObserver={ "
1971 "mTextChangeData=%s }",
1972 this, ToString(observer->mTextChangeData).c_str()));
1974 IMENotification notification(NOTIFY_IME_OF_TEXT_CHANGE);
1975 notification.SetData(observer->mTextChangeData);
1976 observer->mTextChangeData.Clear();
1978 MOZ_RELEASE_ASSERT(observer->mSendingNotification == NOTIFY_IME_OF_NOTHING);
1979 observer->mSendingNotification = NOTIFY_IME_OF_TEXT_CHANGE;
1980 IMEStateManager::NotifyIME(notification, observer->mWidget);
1981 observer->mSendingNotification = NOTIFY_IME_OF_NOTHING;
1983 MOZ_LOG(sIMECOLog, LogLevel::Debug,
1984 ("0x%p IMENotificationSender::SendTextChange(), sent "
1985 "NOTIFY_IME_OF_TEXT_CHANGE",
1986 this));
1989 void IMEContentObserver::IMENotificationSender::SendPositionChange() {
1990 RefPtr<IMEContentObserver> observer = GetObserver();
1991 if (!observer) {
1992 return;
1995 if (!CanNotifyIME(eChangeEventType_Position)) {
1996 MOZ_LOG(sIMECOLog, LogLevel::Verbose,
1997 ("0x%p IMENotificationSender::SendPositionChange(), Warning, "
1998 "does not send notification due to impossible to notify IME of "
1999 "position change",
2000 this));
2001 return;
2004 if (!IsSafeToNotifyIME(eChangeEventType_Position)) {
2005 MOZ_LOG(sIMECOLog, LogLevel::Verbose,
2006 ("0x%p IMENotificationSender::SendPositionChange(), Warning, "
2007 "does not send notification due to unsafe, retrying to send "
2008 "NOTIFY_IME_OF_POSITION_CHANGE...",
2009 this));
2010 observer->PostPositionChangeNotification();
2011 return;
2014 // If position change notification is unnecessary anymore, just cancel it.
2015 if (!observer->NeedsPositionChangeNotification()) {
2016 MOZ_LOG(sIMECOLog, LogLevel::Verbose,
2017 ("0x%p IMENotificationSender::SendPositionChange(), Warning, "
2018 "canceling sending NOTIFY_IME_OF_POSITION_CHANGE",
2019 this));
2020 observer->CancelNotifyingIMEOfPositionChange();
2021 return;
2024 MOZ_LOG(sIMECOLog, LogLevel::Info,
2025 ("0x%p IMENotificationSender::SendPositionChange(), sending "
2026 "NOTIFY_IME_OF_POSITION_CHANGE...",
2027 this));
2029 MOZ_RELEASE_ASSERT(observer->mSendingNotification == NOTIFY_IME_OF_NOTHING);
2030 observer->mSendingNotification = NOTIFY_IME_OF_POSITION_CHANGE;
2031 IMEStateManager::NotifyIME(IMENotification(NOTIFY_IME_OF_POSITION_CHANGE),
2032 observer->mWidget);
2033 observer->mSendingNotification = NOTIFY_IME_OF_NOTHING;
2035 MOZ_LOG(sIMECOLog, LogLevel::Debug,
2036 ("0x%p IMENotificationSender::SendPositionChange(), sent "
2037 "NOTIFY_IME_OF_POSITION_CHANGE",
2038 this));
2041 void IMEContentObserver::IMENotificationSender::SendCompositionEventHandled() {
2042 RefPtr<IMEContentObserver> observer = GetObserver();
2043 if (!observer) {
2044 return;
2047 if (!CanNotifyIME(eChangeEventType_CompositionEventHandled)) {
2048 MOZ_LOG(sIMECOLog, LogLevel::Warning,
2049 ("0x%p IMENotificationSender::SendCompositionEventHandled(), "
2050 "Warning, does not send notification due to impossible to notify "
2051 "IME of composition event handled",
2052 this));
2053 return;
2056 if (!IsSafeToNotifyIME(eChangeEventType_CompositionEventHandled)) {
2057 MOZ_LOG(sIMECOLog, LogLevel::Warning,
2058 ("0x%p IMENotificationSender::SendCompositionEventHandled(), "
2059 "Warning, does not send notification due to unsafe, retrying to "
2060 "send NOTIFY_IME_OF_POSITION_CHANGE...",
2061 this));
2062 observer->PostCompositionEventHandledNotification();
2063 return;
2066 MOZ_LOG(sIMECOLog, LogLevel::Info,
2067 ("0x%p IMENotificationSender::SendCompositionEventHandled(), sending "
2068 "NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED...",
2069 this));
2071 MOZ_RELEASE_ASSERT(observer->mSendingNotification == NOTIFY_IME_OF_NOTHING);
2072 observer->mSendingNotification = NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED;
2073 IMEStateManager::NotifyIME(
2074 IMENotification(NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED),
2075 observer->mWidget);
2076 observer->mSendingNotification = NOTIFY_IME_OF_NOTHING;
2078 MOZ_LOG(sIMECOLog, LogLevel::Debug,
2079 ("0x%p IMENotificationSender::SendCompositionEventHandled(), sent "
2080 "NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED",
2081 this));
2084 /******************************************************************************
2085 * mozilla::IMEContentObserver::DocumentObservingHelper
2086 ******************************************************************************/
2088 NS_IMPL_CYCLE_COLLECTION_CLASS(IMEContentObserver::DocumentObserver)
2090 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IMEContentObserver::DocumentObserver)
2091 // StopObserving() releases mIMEContentObserver and mDocument.
2092 tmp->StopObserving();
2093 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
2095 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IMEContentObserver::DocumentObserver)
2096 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIMEContentObserver)
2097 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
2098 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
2100 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IMEContentObserver::DocumentObserver)
2101 NS_INTERFACE_MAP_ENTRY(nsIDocumentObserver)
2102 NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
2103 NS_INTERFACE_MAP_ENTRY(nsISupports)
2104 NS_INTERFACE_MAP_END
2106 NS_IMPL_CYCLE_COLLECTING_ADDREF(IMEContentObserver::DocumentObserver)
2107 NS_IMPL_CYCLE_COLLECTING_RELEASE(IMEContentObserver::DocumentObserver)
2109 void IMEContentObserver::DocumentObserver::Observe(Document* aDocument) {
2110 MOZ_ASSERT(aDocument);
2112 // Guarantee that aDocument won't be destroyed during a call of
2113 // StopObserving().
2114 RefPtr<Document> newDocument = aDocument;
2116 StopObserving();
2118 mDocument = std::move(newDocument);
2119 mDocument->AddObserver(this);
2122 void IMEContentObserver::DocumentObserver::StopObserving() {
2123 if (!IsObserving()) {
2124 return;
2127 // Grab IMEContentObserver which could be destroyed during method calls.
2128 RefPtr<IMEContentObserver> observer = std::move(mIMEContentObserver);
2130 // Stop observing the document first.
2131 RefPtr<Document> document = std::move(mDocument);
2132 document->RemoveObserver(this);
2134 // Notify IMEContentObserver of ending of document updates if this already
2135 // notified it of beginning of document updates.
2136 for (; IsUpdating(); --mDocumentUpdating) {
2137 // FYI: IsUpdating() returns true until mDocumentUpdating becomes 0.
2138 // However, IsObserving() returns false now because mDocument was
2139 // already cleared above. Therefore, this method won't be called
2140 // recursively.
2141 observer->EndDocumentUpdate();
2145 void IMEContentObserver::DocumentObserver::Destroy() {
2146 StopObserving();
2147 mIMEContentObserver = nullptr;
2150 void IMEContentObserver::DocumentObserver::BeginUpdate(Document* aDocument) {
2151 if (NS_WARN_IF(Destroyed()) || NS_WARN_IF(!IsObserving())) {
2152 return;
2154 mDocumentUpdating++;
2155 mIMEContentObserver->BeginDocumentUpdate();
2158 void IMEContentObserver::DocumentObserver::EndUpdate(Document* aDocument) {
2159 if (NS_WARN_IF(Destroyed()) || NS_WARN_IF(!IsObserving()) ||
2160 NS_WARN_IF(!IsUpdating())) {
2161 return;
2163 mDocumentUpdating--;
2164 mIMEContentObserver->EndDocumentUpdate();
2167 } // namespace mozilla