Bug 1652470 [wpt PR 24579] - Update mozrunner to 8.0.0, a=testonly
[gecko.git] / widget / ContentCache.h
blob8c202fd27537dc6c08c8e5c86f6a68906a1cfad8
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: sw=2 ts=8 et :
3 */
4 /* This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #ifndef mozilla_ContentCache_h
9 #define mozilla_ContentCache_h
11 #include <stdint.h>
13 #include "mozilla/Assertions.h"
14 #include "mozilla/CheckedInt.h"
15 #include "mozilla/EventForwards.h"
16 #include "mozilla/WritingModes.h"
17 #include "nsIWidget.h"
18 #include "nsString.h"
19 #include "nsTArray.h"
20 #include "Units.h"
22 namespace mozilla {
24 class ContentCacheInParent;
26 namespace dom {
27 class BrowserParent;
28 } // namespace dom
30 /**
31 * ContentCache stores various information of the child content.
32 * This class has members which are necessary both in parent process and
33 * content process.
36 class ContentCache {
37 public:
38 typedef CopyableTArray<LayoutDeviceIntRect> RectArray;
39 typedef widget::IMENotification IMENotification;
41 ContentCache();
43 protected:
44 // Whole text in the target
45 nsString mText;
47 // Start offset of the composition string.
48 uint32_t mCompositionStart;
50 enum { ePrevCharRect = 1, eNextCharRect = 0 };
52 struct Selection final {
53 // Following values are offset in "flat text".
54 uint32_t mAnchor;
55 uint32_t mFocus;
57 WritingMode mWritingMode;
59 // Character rects at previous and next character of mAnchor and mFocus.
60 // The reason why ContentCache needs to store each previous character of
61 // them is IME may query character rect of the last character of a line
62 // when caret is at the end of the line.
63 // Note that use ePrevCharRect and eNextCharRect for accessing each item.
64 LayoutDeviceIntRect mAnchorCharRects[2];
65 LayoutDeviceIntRect mFocusCharRects[2];
67 // Whole rect of selected text. This is empty if the selection is collapsed.
68 LayoutDeviceIntRect mRect;
70 Selection() : mAnchor(UINT32_MAX), mFocus(UINT32_MAX) {}
72 void Clear() {
73 mAnchor = mFocus = UINT32_MAX;
74 mWritingMode = WritingMode();
75 ClearAnchorCharRects();
76 ClearFocusCharRects();
77 mRect.SetEmpty();
80 void ClearAnchorCharRects() {
81 for (size_t i = 0; i < ArrayLength(mAnchorCharRects); i++) {
82 mAnchorCharRects[i].SetEmpty();
85 void ClearFocusCharRects() {
86 for (size_t i = 0; i < ArrayLength(mFocusCharRects); i++) {
87 mFocusCharRects[i].SetEmpty();
91 bool IsValid() const {
92 return mAnchor != UINT32_MAX && mFocus != UINT32_MAX;
94 bool Collapsed() const {
95 NS_ASSERTION(IsValid(),
96 "The caller should check if the selection is valid");
97 return mFocus == mAnchor;
99 bool Reversed() const {
100 NS_ASSERTION(IsValid(),
101 "The caller should check if the selection is valid");
102 return mFocus < mAnchor;
104 uint32_t StartOffset() const {
105 NS_ASSERTION(IsValid(),
106 "The caller should check if the selection is valid");
107 return Reversed() ? mFocus : mAnchor;
109 uint32_t EndOffset() const {
110 NS_ASSERTION(IsValid(),
111 "The caller should check if the selection is valid");
112 return Reversed() ? mAnchor : mFocus;
114 uint32_t Length() const {
115 NS_ASSERTION(IsValid(),
116 "The caller should check if the selection is valid");
117 return Reversed() ? mAnchor - mFocus : mFocus - mAnchor;
119 LayoutDeviceIntRect StartCharRect() const {
120 NS_ASSERTION(IsValid(),
121 "The caller should check if the selection is valid");
122 return Reversed() ? mFocusCharRects[eNextCharRect]
123 : mAnchorCharRects[eNextCharRect];
125 LayoutDeviceIntRect EndCharRect() const {
126 NS_ASSERTION(IsValid(),
127 "The caller should check if the selection is valid");
128 return Reversed() ? mAnchorCharRects[eNextCharRect]
129 : mFocusCharRects[eNextCharRect];
131 } mSelection;
133 bool IsSelectionValid() const {
134 return mSelection.IsValid() && mSelection.EndOffset() <= mText.Length();
137 // Stores first char rect because Yosemite's Japanese IME sometimes tries
138 // to query it. If there is no text, this is caret rect.
139 LayoutDeviceIntRect mFirstCharRect;
141 struct Caret final {
142 uint32_t mOffset;
143 LayoutDeviceIntRect mRect;
145 Caret() : mOffset(UINT32_MAX) {}
147 void Clear() {
148 mOffset = UINT32_MAX;
149 mRect.SetEmpty();
152 bool IsValid() const { return mOffset != UINT32_MAX; }
154 uint32_t Offset() const {
155 NS_ASSERTION(IsValid(), "The caller should check if the caret is valid");
156 return mOffset;
158 } mCaret;
160 struct TextRectArray final {
161 uint32_t mStart;
162 RectArray mRects;
164 TextRectArray() : mStart(UINT32_MAX) {}
166 void Clear() {
167 mStart = UINT32_MAX;
168 mRects.Clear();
171 bool IsValid() const {
172 if (mStart == UINT32_MAX) {
173 return false;
175 CheckedInt<uint32_t> endOffset =
176 CheckedInt<uint32_t>(mStart) + mRects.Length();
177 return endOffset.isValid();
179 bool HasRects() const { return IsValid() && !mRects.IsEmpty(); }
180 uint32_t StartOffset() const {
181 NS_ASSERTION(IsValid(), "The caller should check if the caret is valid");
182 return mStart;
184 uint32_t EndOffset() const {
185 NS_ASSERTION(IsValid(), "The caller should check if the caret is valid");
186 if (!IsValid()) {
187 return UINT32_MAX;
189 return mStart + mRects.Length();
191 bool InRange(uint32_t aOffset) const {
192 return IsValid() && StartOffset() <= aOffset && aOffset < EndOffset();
194 bool InRange(uint32_t aOffset, uint32_t aLength) const {
195 CheckedInt<uint32_t> endOffset = CheckedInt<uint32_t>(aOffset) + aLength;
196 if (NS_WARN_IF(!endOffset.isValid())) {
197 return false;
199 return InRange(aOffset) && aOffset + aLength <= EndOffset();
201 bool IsOverlappingWith(uint32_t aOffset, uint32_t aLength) const {
202 if (!HasRects() || aOffset == UINT32_MAX || !aLength) {
203 return false;
205 CheckedInt<uint32_t> endOffset = CheckedInt<uint32_t>(aOffset) + aLength;
206 if (NS_WARN_IF(!endOffset.isValid())) {
207 return false;
209 return aOffset < EndOffset() && endOffset.value() > mStart;
211 LayoutDeviceIntRect GetRect(uint32_t aOffset) const;
212 LayoutDeviceIntRect GetUnionRect(uint32_t aOffset, uint32_t aLength) const;
213 LayoutDeviceIntRect GetUnionRectAsFarAsPossible(
214 uint32_t aOffset, uint32_t aLength, bool aRoundToExistingOffset) const;
215 } mTextRectArray;
217 LayoutDeviceIntRect mEditorRect;
219 friend class ContentCacheInParent;
220 friend struct IPC::ParamTraits<ContentCache>;
223 class ContentCacheInChild final : public ContentCache {
224 public:
225 ContentCacheInChild();
228 * When IME loses focus, this should be called and making this forget the
229 * content for reducing footprint.
231 void Clear();
234 * Cache*() retrieves the latest content information and store them.
235 * Be aware, CacheSelection() calls CacheTextRects(), and also CacheText()
236 * calls CacheSelection(). So, related data is also retrieved automatically.
238 bool CacheEditorRect(nsIWidget* aWidget,
239 const IMENotification* aNotification = nullptr);
240 bool CacheSelection(nsIWidget* aWidget,
241 const IMENotification* aNotification = nullptr);
242 bool CacheText(nsIWidget* aWidget,
243 const IMENotification* aNotification = nullptr);
245 bool CacheAll(nsIWidget* aWidget,
246 const IMENotification* aNotification = nullptr);
249 * SetSelection() modifies selection with specified raw data. And also this
250 * tries to retrieve text rects too.
252 void SetSelection(nsIWidget* aWidget, uint32_t aStartOffset, uint32_t aLength,
253 bool aReversed, const WritingMode& aWritingMode);
255 private:
256 bool QueryCharRect(nsIWidget* aWidget, uint32_t aOffset,
257 LayoutDeviceIntRect& aCharRect) const;
258 bool QueryCharRectArray(nsIWidget* aWidget, uint32_t aOffset,
259 uint32_t aLength, RectArray& aCharRectArray) const;
260 bool CacheCaret(nsIWidget* aWidget,
261 const IMENotification* aNotification = nullptr);
262 bool CacheTextRects(nsIWidget* aWidget,
263 const IMENotification* aNotification = nullptr);
266 class ContentCacheInParent final : public ContentCache {
267 public:
268 explicit ContentCacheInParent(dom::BrowserParent& aBrowserParent);
271 * AssignContent() is called when BrowserParent receives ContentCache from
272 * the content process. This doesn't copy composition information because
273 * it's managed by BrowserParent itself.
275 void AssignContent(const ContentCache& aOther, nsIWidget* aWidget,
276 const IMENotification* aNotification = nullptr);
279 * HandleQueryContentEvent() sets content data to aEvent.mReply.
281 * For eQuerySelectedText, fail if the cache doesn't contain the whole
282 * selected range. (This shouldn't happen because PuppetWidget should have
283 * already sent the whole selection.)
285 * For eQueryTextContent, fail only if the cache doesn't overlap with
286 * the queried range. Note the difference from above. We use
287 * this behavior because a normal eQueryTextContent event is allowed to
288 * have out-of-bounds offsets, so that widget can request content without
289 * knowing the exact length of text. It's up to widget to handle cases when
290 * the returned offset/length are different from the queried offset/length.
292 * For eQueryTextRect, fail if cached offset/length aren't equals to input.
293 * Cocoa widget always queries selected offset, so it works on it.
295 * For eQueryCaretRect, fail if cached offset isn't equals to input
297 * For eQueryEditorRect, always success
299 bool HandleQueryContentEvent(WidgetQueryContentEvent& aEvent,
300 nsIWidget* aWidget) const;
303 * OnCompositionEvent() should be called before sending composition string.
304 * This returns true if the event should be sent. Otherwise, false.
306 bool OnCompositionEvent(const WidgetCompositionEvent& aCompositionEvent);
309 * OnSelectionEvent() should be called before sending selection event.
311 void OnSelectionEvent(const WidgetSelectionEvent& aSelectionEvent);
314 * OnEventNeedingAckHandled() should be called after the child process
315 * handles a sent event which needs acknowledging.
317 * WARNING: This may send notifications to IME. That might cause destroying
318 * BrowserParent or aWidget. Therefore, the caller must not destroy
319 * this instance during a call of this method.
321 void OnEventNeedingAckHandled(nsIWidget* aWidget, EventMessage aMessage);
324 * RequestIMEToCommitComposition() requests aWidget to commit or cancel
325 * composition. If it's handled synchronously, this returns true.
327 * @param aWidget The widget to be requested to commit or cancel
328 * the composition.
329 * @param aCancel When the caller tries to cancel the composition, true.
330 * Otherwise, i.e., tries to commit the composition, false.
331 * @param aCommittedString The committed string (i.e., the last data of
332 * dispatched composition events during requesting
333 * IME to commit composition.
334 * @return Whether the composition is actually committed
335 * synchronously.
337 bool RequestIMEToCommitComposition(nsIWidget* aWidget, bool aCancel,
338 nsAString& aCommittedString);
341 * MaybeNotifyIME() may notify IME of the notification. If child process
342 * hasn't been handled all sending events yet, this stores the notification
343 * and flush it later.
345 void MaybeNotifyIME(nsIWidget* aWidget, const IMENotification& aNotification);
347 private:
348 IMENotification mPendingSelectionChange;
349 IMENotification mPendingTextChange;
350 IMENotification mPendingLayoutChange;
351 IMENotification mPendingCompositionUpdate;
353 #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
354 // Log of event messages to be output to crash report.
355 nsTArray<EventMessage> mDispatchedEventMessages;
356 nsTArray<EventMessage> mReceivedEventMessages;
357 // Log of RequestIMEToCommitComposition() in the last 2 compositions.
358 enum class RequestIMEToCommitCompositionResult : uint8_t {
359 eToOldCompositionReceived,
360 eToCommittedCompositionReceived,
361 eReceivedAfterBrowserParentBlur,
362 eReceivedButNoTextComposition,
363 eHandledAsynchronously,
364 eHandledSynchronously,
366 const char* ToReadableText(
367 RequestIMEToCommitCompositionResult aResult) const {
368 switch (aResult) {
369 case RequestIMEToCommitCompositionResult::eToOldCompositionReceived:
370 return "Commit request is not handled because it's for "
371 "older composition";
372 case RequestIMEToCommitCompositionResult::eToCommittedCompositionReceived:
373 return "Commit request is not handled because BrowserParent has "
374 "already "
375 "sent commit event for the composition";
376 case RequestIMEToCommitCompositionResult::eReceivedAfterBrowserParentBlur:
377 return "Commit request is handled with stored composition string "
378 "because BrowserParent has already lost focus";
379 case RequestIMEToCommitCompositionResult::eReceivedButNoTextComposition:
380 return "Commit request is not handled because there is no "
381 "TextCompsition instance";
382 case RequestIMEToCommitCompositionResult::eHandledAsynchronously:
383 return "Commit request is handled but IME doesn't commit current "
384 "composition synchronously";
385 case RequestIMEToCommitCompositionResult::eHandledSynchronously:
386 return "Commit reqeust is handled synchronously";
387 default:
388 return "Unknown reason";
391 nsTArray<RequestIMEToCommitCompositionResult>
392 mRequestIMEToCommitCompositionResults;
393 #endif // MOZ_DIAGNOSTIC_ASSERT_ENABLED
395 // mBrowserParent is owner of the instance.
396 dom::BrowserParent& MOZ_NON_OWNING_REF mBrowserParent;
397 // mCompositionString is composition string which were sent to the remote
398 // process but not yet committed in the remote process.
399 nsString mCompositionString;
400 // This is not nullptr only while the instance is requesting IME to
401 // composition. Then, data value of dispatched composition events should
402 // be stored into the instance.
403 nsAString* mCommitStringByRequest;
404 // mPendingEventsNeedingAck is increased before sending a composition event or
405 // a selection event and decreased after they are received in the child
406 // process.
407 uint32_t mPendingEventsNeedingAck;
408 // mCompositionStartInChild stores current composition start offset in the
409 // remote process.
410 uint32_t mCompositionStartInChild;
411 // mPendingCommitLength is commit string length of the first pending
412 // composition. This is used by relative offset query events when querying
413 // new composition start offset.
414 // Note that when mPendingCompositionCount is not 0, i.e., there are 2 or
415 // more pending compositions, this cache won't be used because in such case,
416 // anyway ContentCacheInParent cannot return proper character rect.
417 uint32_t mPendingCommitLength;
418 // mPendingCompositionCount is number of compositions which started in widget
419 // but not yet handled in the child process.
420 uint8_t mPendingCompositionCount;
421 // mPendingCommitCount is number of eCompositionCommit(AsIs) events which
422 // were sent to the child process but not yet handled in it.
423 uint8_t mPendingCommitCount;
424 // mWidgetHasComposition is true when the widget in this process thinks that
425 // IME has composition. So, this is set to true when eCompositionStart is
426 // dispatched and set to false when eCompositionCommit(AsIs) is dispatched.
427 bool mWidgetHasComposition;
428 // mIsChildIgnoringCompositionEvents is set to true if the child process
429 // requests commit composition whose commit has already been sent to it.
430 // Then, set to false when the child process ignores the commit event.
431 bool mIsChildIgnoringCompositionEvents;
433 ContentCacheInParent() = delete;
436 * When following methods' aRoundToExistingOffset is true, even if specified
437 * offset or range is out of bounds, the result is computed with the existing
438 * cache forcibly.
440 bool GetCaretRect(uint32_t aOffset, bool aRoundToExistingOffset,
441 LayoutDeviceIntRect& aCaretRect) const;
442 bool GetTextRect(uint32_t aOffset, bool aRoundToExistingOffset,
443 LayoutDeviceIntRect& aTextRect) const;
444 bool GetUnionTextRects(uint32_t aOffset, uint32_t aLength,
445 bool aRoundToExistingOffset,
446 LayoutDeviceIntRect& aUnionTextRect) const;
448 void FlushPendingNotifications(nsIWidget* aWidget);
450 #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
452 * Remove unnecessary messages from mDispatchedEventMessages and
453 * mReceivedEventMessages.
455 void RemoveUnnecessaryEventMessageLog();
458 * Append event message log to aLog.
460 void AppendEventMessageLog(nsACString& aLog) const;
461 #endif // #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
464 } // namespace mozilla
466 #endif // mozilla_ContentCache_h