Bug 1776680 [wpt PR 34603] - [@container] Test invalidation of font-relative units...
[gecko.git] / dom / events / IMEContentObserver.h
blobfaa4ea9bcf87eb07a4e5b5e5f8982d48d49b6cb6
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 #ifndef mozilla_IMEContentObserver_h
8 #define mozilla_IMEContentObserver_h
10 #include "mozilla/Attributes.h"
11 #include "mozilla/EditorBase.h"
12 #include "mozilla/dom/Element.h"
13 #include "mozilla/dom/Selection.h"
14 #include "nsCOMPtr.h"
15 #include "nsCycleCollectionParticipant.h"
16 #include "nsIDocShell.h" // XXX Why does only this need to be included here?
17 #include "nsIReflowObserver.h"
18 #include "nsIScrollObserver.h"
19 #include "nsIWidget.h"
20 #include "nsStubDocumentObserver.h"
21 #include "nsStubMutationObserver.h"
22 #include "nsThreadUtils.h"
23 #include "nsWeakReference.h"
25 class nsIContent;
26 class nsINode;
27 class nsPresContext;
29 namespace mozilla {
31 class EventStateManager;
32 class TextComposition;
34 namespace dom {
35 class Selection;
36 } // namespace dom
38 // IMEContentObserver notifies widget of any text and selection changes
39 // in the currently focused editor
40 class IMEContentObserver final : public nsStubMutationObserver,
41 public nsIReflowObserver,
42 public nsIScrollObserver,
43 public nsSupportsWeakReference {
44 public:
45 using SelectionChangeData = widget::IMENotification::SelectionChangeData;
46 using TextChangeData = widget::IMENotification::TextChangeData;
47 using TextChangeDataBase = widget::IMENotification::TextChangeDataBase;
48 using IMENotificationRequests = widget::IMENotificationRequests;
49 using IMEMessage = widget::IMEMessage;
51 IMEContentObserver();
53 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
54 NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(IMEContentObserver,
55 nsIReflowObserver)
56 NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATAWILLCHANGE
57 NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED
58 NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
59 NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
60 NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
61 NS_DECL_NSIREFLOWOBSERVER
63 // nsIScrollObserver
64 virtual void ScrollPositionChanged() override;
66 /**
67 * OnSelectionChange() is called when selection is changed in the editor.
69 void OnSelectionChange(dom::Selection& aSelection);
71 MOZ_CAN_RUN_SCRIPT bool OnMouseButtonEvent(nsPresContext& aPresContext,
72 WidgetMouseEvent& aMouseEvent);
74 MOZ_CAN_RUN_SCRIPT nsresult
75 HandleQueryContentEvent(WidgetQueryContentEvent* aEvent);
77 /**
78 * Init() initializes the instance, i.e., retrieving necessary objects and
79 * starts to observe something.
80 * Be aware, callers of this method need to guarantee that the instance
81 * won't be released during calling this.
83 * @param aWidget The widget which can access native IME.
84 * @param aPresContext The PresContext which has aContent.
85 * @param aElement An editable element or nullptr if this will observe
86 * design mode document.
87 * @param aEditorBase The editor which is associated with aContent.
89 MOZ_CAN_RUN_SCRIPT void Init(nsIWidget& aWidget, nsPresContext& aPresContext,
90 dom::Element* aElement, EditorBase& aEditorBase);
92 /**
93 * Destroy() finalizes the instance, i.e., stops observing contents and
94 * clearing the members.
95 * Be aware, callers of this method need to guarantee that the instance
96 * won't be released during calling this.
98 void Destroy();
101 * Returns false if the instance refers some objects and observing them.
102 * Otherwise, true.
104 bool Destroyed() const;
107 * IMEContentObserver is stored by EventStateManager during observing.
108 * DisconnectFromEventStateManager() is called when EventStateManager stops
109 * storing the instance.
111 void DisconnectFromEventStateManager();
114 * MaybeReinitialize() tries to restart to observe the editor's root node.
115 * This is useful when the editor is reframed and all children are replaced
116 * with new node instances.
117 * Be aware, callers of this method need to guarantee that the instance
118 * won't be released during calling this.
120 * @return Returns true if the instance is managing the content.
121 * Otherwise, false.
123 MOZ_CAN_RUN_SCRIPT bool MaybeReinitialize(nsIWidget& aWidget,
124 nsPresContext& aPresContext,
125 dom::Element* aElement,
126 EditorBase& aEditorBase);
128 bool IsManaging(const nsPresContext& aPresContext,
129 const dom::Element* aElement) const;
130 bool IsBeingInitializedFor(const nsPresContext& aPresContext,
131 const dom::Element* aElement) const;
132 bool IsManaging(const TextComposition& aTextComposition) const;
133 bool WasInitializedWith(const EditorBase& aEditorBase) const {
134 return mEditorBase == &aEditorBase;
136 bool IsEditorHandlingEventForComposition() const;
137 bool KeepAliveDuringDeactive() const {
138 return mIMENotificationRequests &&
139 mIMENotificationRequests->WantDuringDeactive();
141 nsIWidget* GetWidget() const { return mWidget; }
142 void SuppressNotifyingIME();
143 void UnsuppressNotifyingIME();
144 nsPresContext* GetPresContext() const;
145 nsresult GetSelectionAndRoot(dom::Selection** aSelection,
146 dom::Element** aRootElement) const;
149 * TryToFlushPendingNotifications() should be called when pending events
150 * should be flushed. This tries to run the queued IMENotificationSender.
151 * Doesn't do anything in child processes where flushing happens
152 * asynchronously unless aAllowAsync is false.
154 void TryToFlushPendingNotifications(bool aAllowAsync);
157 * MaybeNotifyCompositionEventHandled() posts composition event handled
158 * notification into the pseudo queue.
160 void MaybeNotifyCompositionEventHandled();
163 * Following methods are called when the editor:
164 * - an edit action handled.
165 * - before handling an edit action.
166 * - canceled handling an edit action after calling BeforeEditAction().
168 void OnEditActionHandled();
169 void BeforeEditAction();
170 void CancelEditAction();
172 dom::Element* GetObservingElement() const {
173 return mIsObserving ? mRootElement.get() : nullptr;
176 private:
177 ~IMEContentObserver() = default;
179 enum State {
180 eState_NotObserving,
181 eState_Initializing,
182 eState_StoppedObserving,
183 eState_Observing
185 State GetState() const;
186 MOZ_CAN_RUN_SCRIPT bool InitWithEditor(nsPresContext& aPresContext,
187 dom::Element* aElement,
188 EditorBase& aEditorBase);
189 void OnIMEReceivedFocus();
190 void Clear();
191 [[nodiscard]] bool IsObservingContent(const nsPresContext& aPresContext,
192 const dom::Element* aElement) const;
193 [[nodiscard]] bool IsReflowLocked() const;
194 [[nodiscard]] bool IsSafeToNotifyIME() const;
195 [[nodiscard]] bool IsEditorComposing() const;
197 // Following methods are called by DocumentObserver when
198 // beginning to update the contents and ending updating the contents.
199 void BeginDocumentUpdate();
200 void EndDocumentUpdate();
202 // Following methods manages added nodes during a document change.
205 * MaybeNotifyIMEOfAddedTextDuringDocumentChange() may send text change
206 * notification caused by the nodes added between mFirstAddedContent in
207 * mFirstAddedContainer and mLastAddedContent in
208 * mLastAddedContainer and forgets the range.
210 void MaybeNotifyIMEOfAddedTextDuringDocumentChange();
213 * IsInDocumentChange() returns true while the DOM tree is being modified
214 * with mozAutoDocUpdate. E.g., it's being modified by setting innerHTML or
215 * insertAdjacentHTML(). This returns false when user types something in
216 * the focused editor editor.
218 bool IsInDocumentChange() const {
219 return mDocumentObserver && mDocumentObserver->IsUpdating();
223 * Forget the range of added nodes during a document change.
225 void ClearAddedNodesDuringDocumentChange();
228 * HasAddedNodesDuringDocumentChange() returns true when this stores range
229 * of nodes which were added into the DOM tree during a document change but
230 * have not been sent to IME. Note that this should always return false when
231 * IsInDocumentChange() returns false.
233 bool HasAddedNodesDuringDocumentChange() const {
234 return mFirstAddedContainer && mLastAddedContainer;
238 * Returns true if the passed-in node in aParent is the next node of
239 * mLastAddedContent in pre-order tree traversal of the DOM.
241 bool IsNextNodeOfLastAddedNode(nsINode* aParent, nsIContent* aChild) const;
243 void PostFocusSetNotification();
244 void MaybeNotifyIMEOfFocusSet();
245 void PostTextChangeNotification();
246 void MaybeNotifyIMEOfTextChange(const TextChangeDataBase& aTextChangeData);
247 void CancelNotifyingIMEOfTextChange();
248 void PostSelectionChangeNotification();
249 void MaybeNotifyIMEOfSelectionChange(bool aCausedByComposition,
250 bool aCausedBySelectionEvent,
251 bool aOccurredDuringComposition);
252 void PostPositionChangeNotification();
253 void MaybeNotifyIMEOfPositionChange();
254 void CancelNotifyingIMEOfPositionChange();
255 void PostCompositionEventHandledNotification();
257 void NotifyContentAdded(nsINode* aContainer, nsIContent* aFirstContent,
258 nsIContent* aLastContent);
259 void ObserveEditableNode();
261 * NotifyIMEOfBlur() notifies IME of blur.
263 void NotifyIMEOfBlur();
265 * UnregisterObservers() unregisters all listeners and observers.
267 void UnregisterObservers();
268 void FlushMergeableNotifications();
269 bool NeedsTextChangeNotification() const {
270 return mIMENotificationRequests &&
271 mIMENotificationRequests->WantTextChange();
273 bool NeedsPositionChangeNotification() const {
274 return mIMENotificationRequests &&
275 mIMENotificationRequests->WantPositionChanged();
277 void ClearPendingNotifications() {
278 mNeedsToNotifyIMEOfFocusSet = false;
279 mNeedsToNotifyIMEOfTextChange = false;
280 mNeedsToNotifyIMEOfSelectionChange = false;
281 mNeedsToNotifyIMEOfPositionChange = false;
282 mNeedsToNotifyIMEOfCompositionEventHandled = false;
283 mTextChangeData.Clear();
285 bool NeedsToNotifyIMEOfSomething() const {
286 return mNeedsToNotifyIMEOfFocusSet || mNeedsToNotifyIMEOfTextChange ||
287 mNeedsToNotifyIMEOfSelectionChange ||
288 mNeedsToNotifyIMEOfPositionChange ||
289 mNeedsToNotifyIMEOfCompositionEventHandled;
293 * UpdateSelectionCache() updates mSelectionData with the latest selection.
294 * This should be called only when IsSafeToNotifyIME() returns true.
296 MOZ_CAN_RUN_SCRIPT bool UpdateSelectionCache(bool aRequireFlush = true);
298 nsCOMPtr<nsIWidget> mWidget;
299 // mFocusedWidget has the editor observed by the instance. E.g., if the
300 // focused editor is in XUL panel, this should be the widget of the panel.
301 // On the other hand, mWidget is its parent which handles IME.
302 nsCOMPtr<nsIWidget> mFocusedWidget;
303 RefPtr<dom::Selection> mSelection;
304 RefPtr<dom::Element> mRootElement;
305 nsCOMPtr<nsINode> mEditableNode;
306 nsCOMPtr<nsIDocShell> mDocShell;
307 RefPtr<EditorBase> mEditorBase;
310 * Helper classes to notify IME.
313 class AChangeEvent : public Runnable {
314 protected:
315 enum ChangeEventType {
316 eChangeEventType_Focus,
317 eChangeEventType_Selection,
318 eChangeEventType_Text,
319 eChangeEventType_Position,
320 eChangeEventType_CompositionEventHandled
323 explicit AChangeEvent(const char* aName,
324 IMEContentObserver* aIMEContentObserver)
325 : Runnable(aName),
326 mIMEContentObserver(do_GetWeakReference(
327 static_cast<nsIReflowObserver*>(aIMEContentObserver))) {
328 MOZ_ASSERT(aIMEContentObserver);
331 already_AddRefed<IMEContentObserver> GetObserver() const {
332 nsCOMPtr<nsIReflowObserver> observer =
333 do_QueryReferent(mIMEContentObserver);
334 return observer.forget().downcast<IMEContentObserver>();
337 nsWeakPtr mIMEContentObserver;
340 * CanNotifyIME() checks if mIMEContentObserver can and should notify IME.
342 bool CanNotifyIME(ChangeEventType aChangeEventType) const;
345 * IsSafeToNotifyIME() checks if it's safe to noitify IME.
347 bool IsSafeToNotifyIME(ChangeEventType aChangeEventType) const;
350 class IMENotificationSender : public AChangeEvent {
351 public:
352 explicit IMENotificationSender(IMEContentObserver* aIMEContentObserver)
353 : AChangeEvent("IMENotificationSender", aIMEContentObserver),
354 mIsRunning(false) {}
355 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD Run() override;
357 void Dispatch(nsIDocShell* aDocShell);
359 private:
360 MOZ_CAN_RUN_SCRIPT void SendFocusSet();
361 MOZ_CAN_RUN_SCRIPT void SendSelectionChange();
362 void SendTextChange();
363 void SendPositionChange();
364 void SendCompositionEventHandled();
366 bool mIsRunning;
369 // mQueuedSender is, it was put into the event queue but not run yet.
370 RefPtr<IMENotificationSender> mQueuedSender;
373 * IMEContentObserver is a mutation observer of mRootContent. However,
374 * it needs to know the beginning of content changes and end of it too for
375 * reducing redundant computation of text offset with ContentEventHandler.
376 * Therefore, it needs helper class to listen only them since if
377 * both mutations were observed by IMEContentObserver directly, each
378 * methods need to check if the changing node is in mRootContent but it's
379 * too expensive.
381 class DocumentObserver final : public nsStubDocumentObserver {
382 public:
383 explicit DocumentObserver(IMEContentObserver& aIMEContentObserver)
384 : mIMEContentObserver(&aIMEContentObserver), mDocumentUpdating(0) {}
386 NS_DECL_CYCLE_COLLECTION_CLASS(DocumentObserver)
387 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
388 NS_DECL_NSIDOCUMENTOBSERVER_BEGINUPDATE
389 NS_DECL_NSIDOCUMENTOBSERVER_ENDUPDATE
391 void Observe(dom::Document*);
392 void StopObserving();
393 void Destroy();
395 bool Destroyed() const { return !mIMEContentObserver; }
396 bool IsObserving() const { return mDocument != nullptr; }
397 bool IsUpdating() const { return mDocumentUpdating != 0; }
399 private:
400 DocumentObserver() = delete;
401 virtual ~DocumentObserver() { Destroy(); }
403 RefPtr<IMEContentObserver> mIMEContentObserver;
404 RefPtr<dom::Document> mDocument;
405 uint32_t mDocumentUpdating;
407 RefPtr<DocumentObserver> mDocumentObserver;
410 * FlatTextCache stores flat text length from start of the content to
411 * mNodeOffset of mContainerNode.
413 struct FlatTextCache {
414 // mContainerNode and mNode represent a point in DOM tree. E.g.,
415 // if mContainerNode is a div element, mNode is a child.
416 nsCOMPtr<nsINode> mContainerNode;
417 // mNode points to the last child which participates in the current
418 // mFlatTextLength. If mNode is null, then that means that the end point for
419 // mFlatTextLength is immediately before the first child of mContainerNode.
420 nsCOMPtr<nsINode> mNode;
421 // Length of flat text generated from contents between the start of content
422 // and a child node whose index is mNodeOffset of mContainerNode.
423 uint32_t mFlatTextLength;
425 FlatTextCache() : mFlatTextLength(0) {}
427 void Clear() {
428 mContainerNode = nullptr;
429 mNode = nullptr;
430 mFlatTextLength = 0;
433 void Cache(nsINode* aContainer, nsINode* aNode, uint32_t aFlatTextLength) {
434 MOZ_ASSERT(aContainer, "aContainer must not be null");
435 MOZ_ASSERT(!aNode || aNode->GetParentNode() == aContainer,
436 "aNode must be either null or a child of aContainer");
437 mContainerNode = aContainer;
438 mNode = aNode;
439 mFlatTextLength = aFlatTextLength;
442 bool Match(nsINode* aContainer, nsINode* aNode) const {
443 return aContainer == mContainerNode && aNode == mNode;
446 // mEndOfAddedTextCache caches text length from the start of content to
447 // the end of the last added content only while an edit action is being
448 // handled by the editor and no other mutation (e.g., removing node)
449 // occur.
450 FlatTextCache mEndOfAddedTextCache;
451 // mStartOfRemovingTextRangeCache caches text length from the start of content
452 // to the start of the last removed content only while an edit action is being
453 // handled by the editor and no other mutation (e.g., adding node) occur.
454 FlatTextCache mStartOfRemovingTextRangeCache;
456 // mFirstAddedContainer is parent node of first added node in current
457 // document change. So, this is not nullptr only when a node was added
458 // during a document change and the change has not been included into
459 // mTextChangeData yet.
460 // Note that this shouldn't be in cycle collection since this is not nullptr
461 // only during a document change.
462 nsCOMPtr<nsINode> mFirstAddedContainer;
463 // mLastAddedContainer is parent node of last added node in current
464 // document change. So, this is not nullptr only when a node was added
465 // during a document change and the change has not been included into
466 // mTextChangeData yet.
467 // Note that this shouldn't be in cycle collection since this is not nullptr
468 // only during a document change.
469 nsCOMPtr<nsINode> mLastAddedContainer;
471 // mFirstAddedContent is the first node added in mFirstAddedContainer.
472 nsCOMPtr<nsIContent> mFirstAddedContent;
473 // mLastAddedContent is the last node added in mLastAddedContainer;
474 nsCOMPtr<nsIContent> mLastAddedContent;
476 TextChangeData mTextChangeData;
478 // mSelectionData is the last selection data which was notified. The
479 // selection information is modified by UpdateSelectionCache(). The reason
480 // of the selection change is modified by MaybeNotifyIMEOfSelectionChange().
481 SelectionChangeData mSelectionData;
483 EventStateManager* mESM;
485 const IMENotificationRequests* mIMENotificationRequests;
486 uint32_t mSuppressNotifications;
487 int64_t mPreCharacterDataChangeLength;
489 // mSendingNotification is a notification which is now sending from
490 // IMENotificationSender. When the value is NOTIFY_IME_OF_NOTHING, it's
491 // not sending any notification.
492 IMEMessage mSendingNotification;
494 bool mIsObserving;
495 bool mIMEHasFocus;
496 bool mNeedsToNotifyIMEOfFocusSet;
497 bool mNeedsToNotifyIMEOfTextChange;
498 bool mNeedsToNotifyIMEOfSelectionChange;
499 bool mNeedsToNotifyIMEOfPositionChange;
500 bool mNeedsToNotifyIMEOfCompositionEventHandled;
501 // mIsHandlingQueryContentEvent is true when IMEContentObserver is handling
502 // WidgetQueryContentEvent with ContentEventHandler.
503 bool mIsHandlingQueryContentEvent;
506 } // namespace mozilla
508 #endif // mozilla_IMEContentObserver_h