Bug 1492908 [wpt PR 13122] - Check completeness of images with and without srcset...
[gecko.git] / widget / ContentCache.h
blob8c9e99426c09884621ba0e8c459a7be05cfd461e
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 TabParent;
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
38 public:
39 typedef InfallibleTArray<LayoutDeviceIntRect> RectArray;
40 typedef widget::IMENotification IMENotification;
42 ContentCache();
44 protected:
45 // Whole text in the target
46 nsString mText;
48 // Start offset of the composition string.
49 uint32_t mCompositionStart;
51 enum
53 ePrevCharRect = 1,
54 eNextCharRect = 0
57 struct Selection final
59 // Following values are offset in "flat text".
60 uint32_t mAnchor;
61 uint32_t mFocus;
63 WritingMode mWritingMode;
65 // Character rects at previous and next character of mAnchor and mFocus.
66 // The reason why ContentCache needs to store each previous character of
67 // them is IME may query character rect of the last character of a line
68 // when caret is at the end of the line.
69 // Note that use ePrevCharRect and eNextCharRect for accessing each item.
70 LayoutDeviceIntRect mAnchorCharRects[2];
71 LayoutDeviceIntRect mFocusCharRects[2];
73 // Whole rect of selected text. This is empty if the selection is collapsed.
74 LayoutDeviceIntRect mRect;
76 Selection()
77 : mAnchor(UINT32_MAX)
78 , mFocus(UINT32_MAX)
82 void Clear()
84 mAnchor = mFocus = UINT32_MAX;
85 mWritingMode = WritingMode();
86 ClearAnchorCharRects();
87 ClearFocusCharRects();
88 mRect.SetEmpty();
91 void ClearAnchorCharRects()
93 for (size_t i = 0; i < ArrayLength(mAnchorCharRects); i++) {
94 mAnchorCharRects[i].SetEmpty();
97 void ClearFocusCharRects()
99 for (size_t i = 0; i < ArrayLength(mFocusCharRects); i++) {
100 mFocusCharRects[i].SetEmpty();
104 bool IsValid() const
106 return mAnchor != UINT32_MAX && mFocus != UINT32_MAX;
108 bool Collapsed() const
110 NS_ASSERTION(IsValid(),
111 "The caller should check if the selection is valid");
112 return mFocus == mAnchor;
114 bool Reversed() const
116 NS_ASSERTION(IsValid(),
117 "The caller should check if the selection is valid");
118 return mFocus < mAnchor;
120 uint32_t StartOffset() const
122 NS_ASSERTION(IsValid(),
123 "The caller should check if the selection is valid");
124 return Reversed() ? mFocus : mAnchor;
126 uint32_t EndOffset() const
128 NS_ASSERTION(IsValid(),
129 "The caller should check if the selection is valid");
130 return Reversed() ? mAnchor : mFocus;
132 uint32_t Length() const
134 NS_ASSERTION(IsValid(),
135 "The caller should check if the selection is valid");
136 return Reversed() ? mAnchor - mFocus : mFocus - mAnchor;
138 LayoutDeviceIntRect StartCharRect() const
140 NS_ASSERTION(IsValid(),
141 "The caller should check if the selection is valid");
142 return Reversed() ? mFocusCharRects[eNextCharRect] :
143 mAnchorCharRects[eNextCharRect];
145 LayoutDeviceIntRect EndCharRect() const
147 NS_ASSERTION(IsValid(),
148 "The caller should check if the selection is valid");
149 return Reversed() ? mAnchorCharRects[eNextCharRect] :
150 mFocusCharRects[eNextCharRect];
152 } mSelection;
154 bool IsSelectionValid() const
156 return mSelection.IsValid() && mSelection.EndOffset() <= mText.Length();
159 // Stores first char rect because Yosemite's Japanese IME sometimes tries
160 // to query it. If there is no text, this is caret rect.
161 LayoutDeviceIntRect mFirstCharRect;
163 struct Caret final
165 uint32_t mOffset;
166 LayoutDeviceIntRect mRect;
168 Caret()
169 : mOffset(UINT32_MAX)
173 void Clear()
175 mOffset = UINT32_MAX;
176 mRect.SetEmpty();
179 bool IsValid() const { return mOffset != UINT32_MAX; }
181 uint32_t Offset() const
183 NS_ASSERTION(IsValid(),
184 "The caller should check if the caret is valid");
185 return mOffset;
187 } mCaret;
189 struct TextRectArray final
191 uint32_t mStart;
192 RectArray mRects;
194 TextRectArray()
195 : mStart(UINT32_MAX)
199 void Clear()
201 mStart = UINT32_MAX;
202 mRects.Clear();
205 bool IsValid() const
207 if (mStart == UINT32_MAX) {
208 return false;
210 CheckedInt<uint32_t> endOffset =
211 CheckedInt<uint32_t>(mStart) + mRects.Length();
212 return endOffset.isValid();
214 bool HasRects() const
216 return IsValid() && !mRects.IsEmpty();
218 uint32_t StartOffset() const
220 NS_ASSERTION(IsValid(),
221 "The caller should check if the caret is valid");
222 return mStart;
224 uint32_t EndOffset() const
226 NS_ASSERTION(IsValid(),
227 "The caller should check if the caret is valid");
228 if (!IsValid()) {
229 return UINT32_MAX;
231 return mStart + mRects.Length();
233 bool InRange(uint32_t aOffset) const
235 return IsValid() &&
236 StartOffset() <= aOffset && aOffset < EndOffset();
238 bool InRange(uint32_t aOffset, uint32_t aLength) const
240 CheckedInt<uint32_t> endOffset =
241 CheckedInt<uint32_t>(aOffset) + aLength;
242 if (NS_WARN_IF(!endOffset.isValid())) {
243 return false;
245 return InRange(aOffset) && aOffset + aLength <= EndOffset();
247 bool IsOverlappingWith(uint32_t aOffset, uint32_t aLength) const
249 if (!HasRects() || aOffset == UINT32_MAX || !aLength) {
250 return false;
252 CheckedInt<uint32_t> endOffset =
253 CheckedInt<uint32_t>(aOffset) + aLength;
254 if (NS_WARN_IF(!endOffset.isValid())) {
255 return false;
257 return aOffset < EndOffset() && endOffset.value() > mStart;
259 LayoutDeviceIntRect GetRect(uint32_t aOffset) const;
260 LayoutDeviceIntRect GetUnionRect(uint32_t aOffset, uint32_t aLength) const;
261 LayoutDeviceIntRect GetUnionRectAsFarAsPossible(
262 uint32_t aOffset, uint32_t aLength,
263 bool aRoundToExistingOffset) const;
264 } mTextRectArray;
266 LayoutDeviceIntRect mEditorRect;
268 friend class ContentCacheInParent;
269 friend struct IPC::ParamTraits<ContentCache>;
272 class ContentCacheInChild final : public ContentCache
274 public:
275 ContentCacheInChild();
278 * When IME loses focus, this should be called and making this forget the
279 * content for reducing footprint.
281 void Clear();
284 * Cache*() retrieves the latest content information and store them.
285 * Be aware, CacheSelection() calls CacheTextRects(), and also CacheText()
286 * calls CacheSelection(). So, related data is also retrieved automatically.
288 bool CacheEditorRect(nsIWidget* aWidget,
289 const IMENotification* aNotification = nullptr);
290 bool CacheSelection(nsIWidget* aWidget,
291 const IMENotification* aNotification = nullptr);
292 bool CacheText(nsIWidget* aWidget,
293 const IMENotification* aNotification = nullptr);
295 bool CacheAll(nsIWidget* aWidget,
296 const IMENotification* aNotification = nullptr);
299 * SetSelection() modifies selection with specified raw data. And also this
300 * tries to retrieve text rects too.
302 void SetSelection(nsIWidget* aWidget,
303 uint32_t aStartOffset,
304 uint32_t aLength,
305 bool aReversed,
306 const WritingMode& aWritingMode);
308 private:
309 bool QueryCharRect(nsIWidget* aWidget,
310 uint32_t aOffset,
311 LayoutDeviceIntRect& aCharRect) const;
312 bool QueryCharRectArray(nsIWidget* aWidget,
313 uint32_t aOffset,
314 uint32_t aLength,
315 RectArray& aCharRectArray) const;
316 bool CacheCaret(nsIWidget* aWidget,
317 const IMENotification* aNotification = nullptr);
318 bool CacheTextRects(nsIWidget* aWidget,
319 const IMENotification* aNotification = nullptr);
322 class ContentCacheInParent final : public ContentCache
324 public:
325 explicit ContentCacheInParent(dom::TabParent& aTabParent);
328 * AssignContent() is called when TabParent receives ContentCache from
329 * the content process. This doesn't copy composition information because
330 * it's managed by TabParent itself.
332 void AssignContent(const ContentCache& aOther,
333 nsIWidget* aWidget,
334 const IMENotification* aNotification = nullptr);
337 * HandleQueryContentEvent() sets content data to aEvent.mReply.
339 * For eQuerySelectedText, fail if the cache doesn't contain the whole
340 * selected range. (This shouldn't happen because PuppetWidget should have
341 * already sent the whole selection.)
343 * For eQueryTextContent, fail only if the cache doesn't overlap with
344 * the queried range. Note the difference from above. We use
345 * this behavior because a normal eQueryTextContent event is allowed to
346 * have out-of-bounds offsets, so that widget can request content without
347 * knowing the exact length of text. It's up to widget to handle cases when
348 * the returned offset/length are different from the queried offset/length.
350 * For eQueryTextRect, fail if cached offset/length aren't equals to input.
351 * Cocoa widget always queries selected offset, so it works on it.
353 * For eQueryCaretRect, fail if cached offset isn't equals to input
355 * For eQueryEditorRect, always success
357 bool HandleQueryContentEvent(WidgetQueryContentEvent& aEvent,
358 nsIWidget* aWidget) const;
361 * OnCompositionEvent() should be called before sending composition string.
362 * This returns true if the event should be sent. Otherwise, false.
364 bool OnCompositionEvent(const WidgetCompositionEvent& aCompositionEvent);
367 * OnSelectionEvent() should be called before sending selection event.
369 void OnSelectionEvent(const WidgetSelectionEvent& aSelectionEvent);
372 * OnEventNeedingAckHandled() should be called after the child process
373 * handles a sent event which needs acknowledging.
375 * WARNING: This may send notifications to IME. That might cause destroying
376 * TabParent or aWidget. Therefore, the caller must not destroy
377 * this instance during a call of this method.
379 void OnEventNeedingAckHandled(nsIWidget* aWidget, EventMessage aMessage);
382 * RequestIMEToCommitComposition() requests aWidget to commit or cancel
383 * composition. If it's handled synchronously, this returns true.
385 * @param aWidget The widget to be requested to commit or cancel
386 * the composition.
387 * @param aCancel When the caller tries to cancel the composition, true.
388 * Otherwise, i.e., tries to commit the composition, false.
389 * @param aCommittedString The committed string (i.e., the last data of
390 * dispatched composition events during requesting
391 * IME to commit composition.
392 * @return Whether the composition is actually committed
393 * synchronously.
395 bool RequestIMEToCommitComposition(nsIWidget* aWidget,
396 bool aCancel,
397 nsAString& aCommittedString);
400 * MaybeNotifyIME() may notify IME of the notification. If child process
401 * hasn't been handled all sending events yet, this stores the notification
402 * and flush it later.
404 void MaybeNotifyIME(nsIWidget* aWidget,
405 const IMENotification& aNotification);
407 private:
408 IMENotification mPendingSelectionChange;
409 IMENotification mPendingTextChange;
410 IMENotification mPendingLayoutChange;
411 IMENotification mPendingCompositionUpdate;
413 #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
414 // Log of event messages to be output to crash report.
415 nsTArray<EventMessage> mDispatchedEventMessages;
416 nsTArray<EventMessage> mReceivedEventMessages;
417 // Log of RequestIMEToCommitComposition() in the last 2 compositions.
418 enum class RequestIMEToCommitCompositionResult : uint8_t
420 eToOldCompositionReceived,
421 eToCommittedCompositionReceived,
422 eReceivedAfterTabParentBlur,
423 eReceivedButNoTextComposition,
424 eHandledAsynchronously,
425 eHandledSynchronously,
427 const char* ToReadableText(RequestIMEToCommitCompositionResult aResult) const
429 switch (aResult) {
430 case RequestIMEToCommitCompositionResult::eToOldCompositionReceived:
431 return "Commit request is not handled because it's for "
432 "older composition";
433 case RequestIMEToCommitCompositionResult::eToCommittedCompositionReceived:
434 return "Commit request is not handled because TabParent has already "
435 "sent commit event for the composition";
436 case RequestIMEToCommitCompositionResult::eReceivedAfterTabParentBlur:
437 return "Commit request is handled with stored composition string "
438 "because TabParent has already lost focus";
439 case RequestIMEToCommitCompositionResult::eReceivedButNoTextComposition:
440 return "Commit request is not handled because there is no "
441 "TextCompsition instance";
442 case RequestIMEToCommitCompositionResult::eHandledAsynchronously:
443 return "Commit request is handled but IME doesn't commit current "
444 "composition synchronously";
445 case RequestIMEToCommitCompositionResult::eHandledSynchronously:
446 return "Commit reqeust is handled synchronously";
447 default:
448 return "Unknown reason";
451 nsTArray<RequestIMEToCommitCompositionResult>
452 mRequestIMEToCommitCompositionResults;
453 #endif // MOZ_DIAGNOSTIC_ASSERT_ENABLED
455 // mTabParent is owner of the instance.
456 dom::TabParent& MOZ_NON_OWNING_REF mTabParent;
457 // mCompositionString is composition string which were sent to the remote
458 // process but not yet committed in the remote process.
459 nsString mCompositionString;
460 // This is not nullptr only while the instance is requesting IME to
461 // composition. Then, data value of dispatched composition events should
462 // be stored into the instance.
463 nsAString* mCommitStringByRequest;
464 // mPendingEventsNeedingAck is increased before sending a composition event or
465 // a selection event and decreased after they are received in the child
466 // process.
467 uint32_t mPendingEventsNeedingAck;
468 // mCompositionStartInChild stores current composition start offset in the
469 // remote process.
470 uint32_t mCompositionStartInChild;
471 // mPendingCommitLength is commit string length of the first pending
472 // composition. This is used by relative offset query events when querying
473 // new composition start offset.
474 // Note that when mPendingCompositionCount is not 0, i.e., there are 2 or
475 // more pending compositions, this cache won't be used because in such case,
476 // anyway ContentCacheInParent cannot return proper character rect.
477 uint32_t mPendingCommitLength;
478 // mPendingCompositionCount is number of compositions which started in widget
479 // but not yet handled in the child process.
480 uint8_t mPendingCompositionCount;
481 // mPendingCommitCount is number of eCompositionCommit(AsIs) events which
482 // were sent to the child process but not yet handled in it.
483 uint8_t mPendingCommitCount;
484 // mWidgetHasComposition is true when the widget in this process thinks that
485 // IME has composition. So, this is set to true when eCompositionStart is
486 // dispatched and set to false when eCompositionCommit(AsIs) is dispatched.
487 bool mWidgetHasComposition;
488 // mIsChildIgnoringCompositionEvents is set to true if the child process
489 // requests commit composition whose commit has already been sent to it.
490 // Then, set to false when the child process ignores the commit event.
491 bool mIsChildIgnoringCompositionEvents;
493 ContentCacheInParent() = delete;
496 * When following methods' aRoundToExistingOffset is true, even if specified
497 * offset or range is out of bounds, the result is computed with the existing
498 * cache forcibly.
500 bool GetCaretRect(uint32_t aOffset,
501 bool aRoundToExistingOffset,
502 LayoutDeviceIntRect& aCaretRect) const;
503 bool GetTextRect(uint32_t aOffset,
504 bool aRoundToExistingOffset,
505 LayoutDeviceIntRect& aTextRect) const;
506 bool GetUnionTextRects(uint32_t aOffset,
507 uint32_t aLength,
508 bool aRoundToExistingOffset,
509 LayoutDeviceIntRect& aUnionTextRect) const;
511 void FlushPendingNotifications(nsIWidget* aWidget);
513 #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
515 * Remove unnecessary messages from mDispatchedEventMessages and
516 * mReceivedEventMessages.
518 void RemoveUnnecessaryEventMessageLog();
521 * Append event message log to aLog.
523 void AppendEventMessageLog(nsACString& aLog) const;
524 #endif // #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
527 } // namespace mozilla
529 #endif // mozilla_ContentCache_h