1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
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 #include "mozilla/ContentCache.h"
12 #include "mozilla/IMEStateManager.h"
13 #include "mozilla/IntegerPrintfMacros.h"
14 #include "mozilla/Logging.h"
15 #include "mozilla/RefPtr.h"
16 #include "mozilla/TextComposition.h"
17 #include "mozilla/TextEvents.h"
18 #include "mozilla/dom/BrowserParent.h"
19 #include "nsExceptionHandler.h"
20 #include "nsIWidget.h"
25 using namespace widget
;
27 static const char* GetBoolName(bool aBool
) { return aBool
? "true" : "false"; }
29 static const char* GetNotificationName(const IMENotification
* aNotification
) {
31 return "Not notification";
33 return ToChar(aNotification
->mMessage
);
36 class GetRectText
: public nsAutoCString
{
38 explicit GetRectText(const LayoutDeviceIntRect
& aRect
) {
39 AssignLiteral("{ x=");
41 AppendLiteral(", y=");
43 AppendLiteral(", width=");
44 AppendInt(aRect
.Width());
45 AppendLiteral(", height=");
46 AppendInt(aRect
.Height());
49 virtual ~GetRectText() = default;
52 class GetWritingModeName
: public nsAutoCString
{
54 explicit GetWritingModeName(const WritingMode
& aWritingMode
) {
55 if (!aWritingMode
.IsVertical()) {
56 AssignLiteral("Horizontal");
59 if (aWritingMode
.IsVerticalLR()) {
60 AssignLiteral("Vertical (LTR)");
63 AssignLiteral("Vertical (RTL)");
65 virtual ~GetWritingModeName() = default;
68 class GetEscapedUTF8String final
: public NS_ConvertUTF16toUTF8
{
70 explicit GetEscapedUTF8String(const nsAString
& aString
)
71 : NS_ConvertUTF16toUTF8(aString
) {
74 explicit GetEscapedUTF8String(const char16ptr_t aString
)
75 : NS_ConvertUTF16toUTF8(aString
) {
78 GetEscapedUTF8String(const char16ptr_t aString
, uint32_t aLength
)
79 : NS_ConvertUTF16toUTF8(aString
, aLength
) {
85 ReplaceSubstring("\r", "\\r");
86 ReplaceSubstring("\n", "\\n");
87 ReplaceSubstring("\t", "\\t");
91 /*****************************************************************************
92 * mozilla::ContentCache
93 *****************************************************************************/
95 LazyLogModule
sContentCacheLog("ContentCacheWidgets");
97 ContentCache::ContentCache() : mCompositionStart(UINT32_MAX
) {}
99 /*****************************************************************************
100 * mozilla::ContentCacheInChild
101 *****************************************************************************/
103 ContentCacheInChild::ContentCacheInChild() : ContentCache() {}
105 void ContentCacheInChild::Clear() {
106 MOZ_LOG(sContentCacheLog
, LogLevel::Info
, ("0x%p Clear()", this));
108 mCompositionStart
= UINT32_MAX
;
111 mFirstCharRect
.SetEmpty();
113 mTextRectArray
.Clear();
114 mEditorRect
.SetEmpty();
117 bool ContentCacheInChild::CacheAll(nsIWidget
* aWidget
,
118 const IMENotification
* aNotification
) {
119 MOZ_LOG(sContentCacheLog
, LogLevel::Info
,
120 ("0x%p CacheAll(aWidget=0x%p, aNotification=%s)", this, aWidget
,
121 GetNotificationName(aNotification
)));
123 if (NS_WARN_IF(!CacheText(aWidget
, aNotification
)) ||
124 NS_WARN_IF(!CacheEditorRect(aWidget
, aNotification
))) {
130 bool ContentCacheInChild::CacheSelection(nsIWidget
* aWidget
,
131 const IMENotification
* aNotification
) {
132 MOZ_LOG(sContentCacheLog
, LogLevel::Info
,
133 ("0x%p CacheSelection(aWidget=0x%p, aNotification=%s)", this, aWidget
,
134 GetNotificationName(aNotification
)));
139 nsEventStatus status
= nsEventStatus_eIgnore
;
140 WidgetQueryContentEvent
selection(true, eQuerySelectedText
, aWidget
);
141 aWidget
->DispatchEvent(&selection
, status
);
142 if (NS_WARN_IF(!selection
.mSucceeded
)) {
143 MOZ_LOG(sContentCacheLog
, LogLevel::Error
,
144 ("0x%p CacheSelection(), FAILED, "
145 "couldn't retrieve the selected text",
149 if (selection
.mReply
.mReversed
) {
151 selection
.mReply
.mOffset
+ selection
.mReply
.mString
.Length();
152 mSelection
.mFocus
= selection
.mReply
.mOffset
;
154 mSelection
.mAnchor
= selection
.mReply
.mOffset
;
156 selection
.mReply
.mOffset
+ selection
.mReply
.mString
.Length();
158 mSelection
.mWritingMode
= selection
.GetWritingMode();
160 return CacheCaret(aWidget
, aNotification
) &&
161 CacheTextRects(aWidget
, aNotification
);
164 bool ContentCacheInChild::CacheCaret(nsIWidget
* aWidget
,
165 const IMENotification
* aNotification
) {
166 MOZ_LOG(sContentCacheLog
, LogLevel::Info
,
167 ("0x%p CacheCaret(aWidget=0x%p, aNotification=%s)", this, aWidget
,
168 GetNotificationName(aNotification
)));
172 if (NS_WARN_IF(!mSelection
.IsValid())) {
176 // XXX Should be mSelection.mFocus?
177 mCaret
.mOffset
= mSelection
.StartOffset();
179 nsEventStatus status
= nsEventStatus_eIgnore
;
180 WidgetQueryContentEvent
caretRect(true, eQueryCaretRect
, aWidget
);
181 caretRect
.InitForQueryCaretRect(mCaret
.mOffset
);
182 aWidget
->DispatchEvent(&caretRect
, status
);
183 if (NS_WARN_IF(!caretRect
.mSucceeded
)) {
184 MOZ_LOG(sContentCacheLog
, LogLevel::Error
,
185 ("0x%p CacheCaret(), FAILED, "
186 "couldn't retrieve the caret rect at offset=%u",
187 this, mCaret
.mOffset
));
191 mCaret
.mRect
= caretRect
.mReply
.mRect
;
192 MOZ_LOG(sContentCacheLog
, LogLevel::Info
,
193 ("0x%p CacheCaret(), Succeeded, "
194 "mSelection={ mAnchor=%u, mFocus=%u, mWritingMode=%s }, "
195 "mCaret={ mOffset=%u, mRect=%s }",
196 this, mSelection
.mAnchor
, mSelection
.mFocus
,
197 GetWritingModeName(mSelection
.mWritingMode
).get(), mCaret
.mOffset
,
198 GetRectText(mCaret
.mRect
).get()));
202 bool ContentCacheInChild::CacheEditorRect(
203 nsIWidget
* aWidget
, const IMENotification
* aNotification
) {
204 MOZ_LOG(sContentCacheLog
, LogLevel::Info
,
205 ("0x%p CacheEditorRect(aWidget=0x%p, aNotification=%s)", this,
206 aWidget
, GetNotificationName(aNotification
)));
208 nsEventStatus status
= nsEventStatus_eIgnore
;
209 WidgetQueryContentEvent
editorRectEvent(true, eQueryEditorRect
, aWidget
);
210 aWidget
->DispatchEvent(&editorRectEvent
, status
);
211 if (NS_WARN_IF(!editorRectEvent
.mSucceeded
)) {
212 MOZ_LOG(sContentCacheLog
, LogLevel::Error
,
213 ("0x%p CacheEditorRect(), FAILED, "
214 "couldn't retrieve the editor rect",
218 mEditorRect
= editorRectEvent
.mReply
.mRect
;
219 MOZ_LOG(sContentCacheLog
, LogLevel::Info
,
220 ("0x%p CacheEditorRect(), Succeeded, "
222 this, GetRectText(mEditorRect
).get()));
226 bool ContentCacheInChild::CacheText(nsIWidget
* aWidget
,
227 const IMENotification
* aNotification
) {
228 MOZ_LOG(sContentCacheLog
, LogLevel::Info
,
229 ("0x%p CacheText(aWidget=0x%p, aNotification=%s)", this, aWidget
,
230 GetNotificationName(aNotification
)));
232 nsEventStatus status
= nsEventStatus_eIgnore
;
233 WidgetQueryContentEvent
queryText(true, eQueryTextContent
, aWidget
);
234 queryText
.InitForQueryTextContent(0, UINT32_MAX
);
235 aWidget
->DispatchEvent(&queryText
, status
);
236 if (NS_WARN_IF(!queryText
.mSucceeded
)) {
237 MOZ_LOG(sContentCacheLog
, LogLevel::Error
,
238 ("0x%p CacheText(), FAILED, couldn't retrieve whole text", this));
242 mText
= queryText
.mReply
.mString
;
244 sContentCacheLog
, LogLevel::Info
,
245 ("0x%p CacheText(), Succeeded, mText.Length()=%u", this, mText
.Length()));
247 return CacheSelection(aWidget
, aNotification
);
250 bool ContentCacheInChild::QueryCharRect(nsIWidget
* aWidget
, uint32_t aOffset
,
251 LayoutDeviceIntRect
& aCharRect
) const {
252 aCharRect
.SetEmpty();
254 nsEventStatus status
= nsEventStatus_eIgnore
;
255 WidgetQueryContentEvent
textRect(true, eQueryTextRect
, aWidget
);
256 textRect
.InitForQueryTextRect(aOffset
, 1);
257 aWidget
->DispatchEvent(&textRect
, status
);
258 if (NS_WARN_IF(!textRect
.mSucceeded
)) {
261 aCharRect
= textRect
.mReply
.mRect
;
263 // Guarantee the rect is not empty.
264 if (NS_WARN_IF(!aCharRect
.Height())) {
265 aCharRect
.SetHeight(1);
267 if (NS_WARN_IF(!aCharRect
.Width())) {
268 aCharRect
.SetWidth(1);
273 bool ContentCacheInChild::QueryCharRectArray(nsIWidget
* aWidget
,
274 uint32_t aOffset
, uint32_t aLength
,
275 RectArray
& aCharRectArray
) const {
276 nsEventStatus status
= nsEventStatus_eIgnore
;
277 WidgetQueryContentEvent
textRects(true, eQueryTextRectArray
, aWidget
);
278 textRects
.InitForQueryTextRectArray(aOffset
, aLength
);
279 aWidget
->DispatchEvent(&textRects
, status
);
280 if (NS_WARN_IF(!textRects
.mSucceeded
)) {
281 aCharRectArray
.Clear();
284 aCharRectArray
= std::move(textRects
.mReply
.mRectArray
);
288 bool ContentCacheInChild::CacheTextRects(nsIWidget
* aWidget
,
289 const IMENotification
* aNotification
) {
290 MOZ_LOG(sContentCacheLog
, LogLevel::Info
,
291 ("0x%p CacheTextRects(aWidget=0x%p, aNotification=%s), "
292 "mCaret={ mOffset=%u, IsValid()=%s }",
293 this, aWidget
, GetNotificationName(aNotification
), mCaret
.mOffset
,
294 GetBoolName(mCaret
.IsValid())));
296 mCompositionStart
= UINT32_MAX
;
297 mTextRectArray
.Clear();
298 mSelection
.ClearAnchorCharRects();
299 mSelection
.ClearFocusCharRects();
300 mSelection
.mRect
.SetEmpty();
301 mFirstCharRect
.SetEmpty();
303 if (NS_WARN_IF(!mSelection
.IsValid())) {
307 // Retrieve text rects in composition string if there is.
308 RefPtr
<TextComposition
> textComposition
=
309 IMEStateManager::GetTextCompositionFor(aWidget
);
310 if (textComposition
) {
311 // mCompositionStart may be updated by some composition event handlers.
312 // So, let's update it with the latest information.
313 mCompositionStart
= textComposition
->NativeOffsetOfStartComposition();
314 // Note that TextComposition::String() may not be modified here because
315 // it's modified after all edit action listeners are performed but this
316 // is called while some of them are performed.
317 // FYI: For supporting IME which commits composition and restart new
318 // composition immediately, we should cache next character of current
320 uint32_t length
= textComposition
->LastData().Length() + 1;
321 mTextRectArray
.mStart
= mCompositionStart
;
322 if (NS_WARN_IF(!QueryCharRectArray(aWidget
, mTextRectArray
.mStart
, length
,
323 mTextRectArray
.mRects
))) {
324 MOZ_LOG(sContentCacheLog
, LogLevel::Error
,
325 ("0x%p CacheTextRects(), FAILED, "
326 "couldn't retrieve text rect array of the composition string",
331 if (mTextRectArray
.InRange(mSelection
.mAnchor
) &&
332 (!mSelection
.mAnchor
|| mTextRectArray
.InRange(mSelection
.mAnchor
- 1))) {
333 mSelection
.mAnchorCharRects
[eNextCharRect
] =
334 mTextRectArray
.GetRect(mSelection
.mAnchor
);
335 if (mSelection
.mAnchor
) {
336 mSelection
.mAnchorCharRects
[ePrevCharRect
] =
337 mTextRectArray
.GetRect(mSelection
.mAnchor
- 1);
341 uint32_t startOffset
= mSelection
.mAnchor
? mSelection
.mAnchor
- 1 : 0;
342 uint32_t length
= mSelection
.mAnchor
? 2 : 1;
343 if (NS_WARN_IF(!QueryCharRectArray(aWidget
, startOffset
, length
, rects
))) {
345 sContentCacheLog
, LogLevel::Error
,
346 ("0x%p CacheTextRects(), FAILED, "
347 "couldn't retrieve text rect array around the selection anchor (%u)",
348 this, mSelection
.mAnchor
));
349 MOZ_ASSERT(mSelection
.mAnchorCharRects
[ePrevCharRect
].IsEmpty());
350 MOZ_ASSERT(mSelection
.mAnchorCharRects
[eNextCharRect
].IsEmpty());
352 if (rects
.Length() > 1) {
353 mSelection
.mAnchorCharRects
[ePrevCharRect
] = rects
[0];
354 mSelection
.mAnchorCharRects
[eNextCharRect
] = rects
[1];
355 } else if (rects
.Length()) {
356 mSelection
.mAnchorCharRects
[eNextCharRect
] = rects
[0];
357 MOZ_ASSERT(mSelection
.mAnchorCharRects
[ePrevCharRect
].IsEmpty());
362 if (mSelection
.Collapsed()) {
363 mSelection
.mFocusCharRects
[0] = mSelection
.mAnchorCharRects
[0];
364 mSelection
.mFocusCharRects
[1] = mSelection
.mAnchorCharRects
[1];
365 } else if (mTextRectArray
.InRange(mSelection
.mFocus
) &&
366 (!mSelection
.mFocus
||
367 mTextRectArray
.InRange(mSelection
.mFocus
- 1))) {
368 mSelection
.mFocusCharRects
[eNextCharRect
] =
369 mTextRectArray
.GetRect(mSelection
.mFocus
);
370 if (mSelection
.mFocus
) {
371 mSelection
.mFocusCharRects
[ePrevCharRect
] =
372 mTextRectArray
.GetRect(mSelection
.mFocus
- 1);
376 uint32_t startOffset
= mSelection
.mFocus
? mSelection
.mFocus
- 1 : 0;
377 uint32_t length
= mSelection
.mFocus
? 2 : 1;
378 if (NS_WARN_IF(!QueryCharRectArray(aWidget
, startOffset
, length
, rects
))) {
380 sContentCacheLog
, LogLevel::Error
,
381 ("0x%p CacheTextRects(), FAILED, "
382 "couldn't retrieve text rect array around the selection focus (%u)",
383 this, mSelection
.mFocus
));
384 MOZ_ASSERT(mSelection
.mFocusCharRects
[ePrevCharRect
].IsEmpty());
385 MOZ_ASSERT(mSelection
.mFocusCharRects
[eNextCharRect
].IsEmpty());
387 if (rects
.Length() > 1) {
388 mSelection
.mFocusCharRects
[ePrevCharRect
] = rects
[0];
389 mSelection
.mFocusCharRects
[eNextCharRect
] = rects
[1];
390 } else if (rects
.Length()) {
391 mSelection
.mFocusCharRects
[eNextCharRect
] = rects
[0];
392 MOZ_ASSERT(mSelection
.mFocusCharRects
[ePrevCharRect
].IsEmpty());
397 if (!mSelection
.Collapsed()) {
398 nsEventStatus status
= nsEventStatus_eIgnore
;
399 WidgetQueryContentEvent
textRect(true, eQueryTextRect
, aWidget
);
400 textRect
.InitForQueryTextRect(mSelection
.StartOffset(),
401 mSelection
.Length());
402 aWidget
->DispatchEvent(&textRect
, status
);
403 if (NS_WARN_IF(!textRect
.mSucceeded
)) {
404 MOZ_LOG(sContentCacheLog
, LogLevel::Error
,
405 ("0x%p CacheTextRects(), FAILED, "
406 "couldn't retrieve text rect of whole selected text",
409 mSelection
.mRect
= textRect
.mReply
.mRect
;
413 if (!mSelection
.mFocus
) {
414 mFirstCharRect
= mSelection
.mFocusCharRects
[eNextCharRect
];
415 } else if (mSelection
.mFocus
== 1) {
416 mFirstCharRect
= mSelection
.mFocusCharRects
[ePrevCharRect
];
417 } else if (!mSelection
.mAnchor
) {
418 mFirstCharRect
= mSelection
.mAnchorCharRects
[eNextCharRect
];
419 } else if (mSelection
.mAnchor
== 1) {
420 mFirstCharRect
= mSelection
.mFocusCharRects
[ePrevCharRect
];
421 } else if (mTextRectArray
.InRange(0)) {
422 mFirstCharRect
= mTextRectArray
.GetRect(0);
424 LayoutDeviceIntRect charRect
;
425 if (NS_WARN_IF(!QueryCharRect(aWidget
, 0, charRect
))) {
426 MOZ_LOG(sContentCacheLog
, LogLevel::Error
,
427 ("0x%p CacheTextRects(), FAILED, "
428 "couldn't retrieve first char rect",
431 mFirstCharRect
= charRect
;
436 sContentCacheLog
, LogLevel::Info
,
437 ("0x%p CacheTextRects(), Succeeded, "
438 "mText.Length()=%x, mTextRectArray={ mStart=%u, mRects.Length()=%zu"
439 " }, mSelection={ mAnchor=%u, mAnchorCharRects[eNextCharRect]=%s, "
440 "mAnchorCharRects[ePrevCharRect]=%s, mFocus=%u, "
441 "mFocusCharRects[eNextCharRect]=%s, mFocusCharRects[ePrevCharRect]=%s, "
442 "mRect=%s }, mFirstCharRect=%s",
443 this, mText
.Length(), mTextRectArray
.mStart
,
444 mTextRectArray
.mRects
.Length(), mSelection
.mAnchor
,
445 GetRectText(mSelection
.mAnchorCharRects
[eNextCharRect
]).get(),
446 GetRectText(mSelection
.mAnchorCharRects
[ePrevCharRect
]).get(),
448 GetRectText(mSelection
.mFocusCharRects
[eNextCharRect
]).get(),
449 GetRectText(mSelection
.mFocusCharRects
[ePrevCharRect
]).get(),
450 GetRectText(mSelection
.mRect
).get(), GetRectText(mFirstCharRect
).get()));
454 void ContentCacheInChild::SetSelection(nsIWidget
* aWidget
,
455 uint32_t aStartOffset
, uint32_t aLength
,
457 const WritingMode
& aWritingMode
) {
458 MOZ_LOG(sContentCacheLog
, LogLevel::Info
,
459 ("0x%p SetSelection(aStartOffset=%u, "
460 "aLength=%u, aReversed=%s, aWritingMode=%s), mText.Length()=%u",
461 this, aStartOffset
, aLength
, GetBoolName(aReversed
),
462 GetWritingModeName(aWritingMode
).get(), mText
.Length()));
465 mSelection
.mAnchor
= aStartOffset
;
466 mSelection
.mFocus
= aStartOffset
+ aLength
;
468 mSelection
.mAnchor
= aStartOffset
+ aLength
;
469 mSelection
.mFocus
= aStartOffset
;
471 mSelection
.mWritingMode
= aWritingMode
;
473 if (NS_WARN_IF(!CacheCaret(aWidget
))) {
476 Unused
<< NS_WARN_IF(!CacheTextRects(aWidget
));
479 /*****************************************************************************
480 * mozilla::ContentCacheInParent
481 *****************************************************************************/
483 ContentCacheInParent::ContentCacheInParent(BrowserParent
& aBrowserParent
)
485 mBrowserParent(aBrowserParent
),
486 mCommitStringByRequest(nullptr),
487 mPendingEventsNeedingAck(0),
488 mCompositionStartInChild(UINT32_MAX
),
489 mPendingCommitLength(0),
490 mPendingCompositionCount(0),
491 mPendingCommitCount(0),
492 mWidgetHasComposition(false),
493 mIsChildIgnoringCompositionEvents(false) {}
495 void ContentCacheInParent::AssignContent(const ContentCache
& aOther
,
497 const IMENotification
* aNotification
) {
498 mText
= aOther
.mText
;
499 mSelection
= aOther
.mSelection
;
500 mFirstCharRect
= aOther
.mFirstCharRect
;
501 mCaret
= aOther
.mCaret
;
502 mTextRectArray
= aOther
.mTextRectArray
;
503 mEditorRect
= aOther
.mEditorRect
;
505 // Only when there is one composition, the TextComposition instance in this
506 // process is managing the composition in the remote process. Therefore,
507 // we shouldn't update composition start offset of TextComposition with
508 // old composition which is still being handled by the child process.
509 if (mWidgetHasComposition
&& mPendingCompositionCount
== 1) {
510 IMEStateManager::MaybeStartOffsetUpdatedInChild(aWidget
, mCompositionStart
);
513 // When this instance allows to query content relative to composition string,
514 // we should modify mCompositionStart with the latest information in the
515 // remote process because now we have the information around the composition
517 mCompositionStartInChild
= aOther
.mCompositionStart
;
518 if (mWidgetHasComposition
|| mPendingCommitCount
) {
519 if (aOther
.mCompositionStart
!= UINT32_MAX
) {
520 if (mCompositionStart
!= aOther
.mCompositionStart
) {
521 mCompositionStart
= aOther
.mCompositionStart
;
522 mPendingCommitLength
= 0;
524 } else if (mCompositionStart
!= mSelection
.StartOffset()) {
525 mCompositionStart
= mSelection
.StartOffset();
526 mPendingCommitLength
= 0;
527 NS_WARNING_ASSERTION(mCompositionStart
!= UINT32_MAX
,
528 "mCompositionStart shouldn't be invalid offset when "
529 "the widget has composition");
534 sContentCacheLog
, LogLevel::Info
,
535 ("0x%p AssignContent(aNotification=%s), "
536 "Succeeded, mText.Length()=%u, mSelection={ mAnchor=%u, mFocus=%u, "
537 "mWritingMode=%s, mAnchorCharRects[eNextCharRect]=%s, "
538 "mAnchorCharRects[ePrevCharRect]=%s, mFocusCharRects[eNextCharRect]=%s, "
539 "mFocusCharRects[ePrevCharRect]=%s, mRect=%s }, "
540 "mFirstCharRect=%s, mCaret={ mOffset=%u, mRect=%s }, mTextRectArray={ "
541 "mStart=%u, mRects.Length()=%zu }, mWidgetHasComposition=%s, "
542 "mPendingCompositionCount=%u, mCompositionStart=%u, "
543 "mPendingCommitLength=%u, mEditorRect=%s",
544 this, GetNotificationName(aNotification
), mText
.Length(),
545 mSelection
.mAnchor
, mSelection
.mFocus
,
546 GetWritingModeName(mSelection
.mWritingMode
).get(),
547 GetRectText(mSelection
.mAnchorCharRects
[eNextCharRect
]).get(),
548 GetRectText(mSelection
.mAnchorCharRects
[ePrevCharRect
]).get(),
549 GetRectText(mSelection
.mFocusCharRects
[eNextCharRect
]).get(),
550 GetRectText(mSelection
.mFocusCharRects
[ePrevCharRect
]).get(),
551 GetRectText(mSelection
.mRect
).get(), GetRectText(mFirstCharRect
).get(),
552 mCaret
.mOffset
, GetRectText(mCaret
.mRect
).get(), mTextRectArray
.mStart
,
553 mTextRectArray
.mRects
.Length(), GetBoolName(mWidgetHasComposition
),
554 mPendingCompositionCount
, mCompositionStart
, mPendingCommitLength
,
555 GetRectText(mEditorRect
).get()));
558 bool ContentCacheInParent::HandleQueryContentEvent(
559 WidgetQueryContentEvent
& aEvent
, nsIWidget
* aWidget
) const {
562 aEvent
.mSucceeded
= false;
563 aEvent
.mReply
.mFocusedWidget
= aWidget
;
565 // ContentCache doesn't store offset of its start with XP linebreaks.
566 // So, we don't support to query contents relative to composition start
567 // offset with XP linebreaks.
568 if (NS_WARN_IF(!aEvent
.mUseNativeLineBreak
)) {
569 MOZ_LOG(sContentCacheLog
, LogLevel::Error
,
570 ("0x%p HandleQueryContentEvent(), FAILED due to query with XP "
576 if (NS_WARN_IF(!aEvent
.mInput
.IsValidOffset())) {
578 sContentCacheLog
, LogLevel::Error
,
579 ("0x%p HandleQueryContentEvent(), FAILED due to invalid offset", this));
583 if (NS_WARN_IF(!aEvent
.mInput
.IsValidEventMessage(aEvent
.mMessage
))) {
585 sContentCacheLog
, LogLevel::Error
,
586 ("0x%p HandleQueryContentEvent(), FAILED due to invalid event message",
591 bool isRelativeToInsertionPoint
= aEvent
.mInput
.mRelativeToInsertionPoint
;
592 if (isRelativeToInsertionPoint
) {
593 if (aWidget
->PluginHasFocus()) {
594 if (NS_WARN_IF(!aEvent
.mInput
.MakeOffsetAbsolute(0))) {
595 MOZ_LOG(sContentCacheLog
, LogLevel::Error
,
596 ("0x%p HandleQueryContentEvent(), FAILED due to "
597 "aEvent.mInput.MakeOffsetAbsolute(0) failure, aEvent={ "
599 "mInput={ mOffset=%" PRId64
", mLength=%" PRIu32
" } }",
600 this, ToChar(aEvent
.mMessage
), aEvent
.mInput
.mOffset
,
601 aEvent
.mInput
.mLength
));
604 } else if (mWidgetHasComposition
|| mPendingCommitCount
) {
605 if (NS_WARN_IF(!aEvent
.mInput
.MakeOffsetAbsolute(mCompositionStart
+
606 mPendingCommitLength
))) {
608 sContentCacheLog
, LogLevel::Error
,
609 ("0x%p HandleQueryContentEvent(), FAILED due to "
610 "aEvent.mInput.MakeOffsetAbsolute(mCompositionStart + "
611 "mPendingCommitLength) failure, "
612 "mCompositionStart=%" PRIu32
", mPendingCommitLength=%" PRIu32
", "
613 "aEvent={ mMessage=%s, mInput={ mOffset=%" PRId64
614 ", mLength=%" PRIu32
" } }",
615 this, mCompositionStart
, mPendingCommitLength
,
616 ToChar(aEvent
.mMessage
), aEvent
.mInput
.mOffset
,
617 aEvent
.mInput
.mLength
));
620 } else if (NS_WARN_IF(!mSelection
.IsValid())) {
621 MOZ_LOG(sContentCacheLog
, LogLevel::Error
,
622 ("0x%p HandleQueryContentEvent(), FAILED due to mSelection is "
626 } else if (NS_WARN_IF(!aEvent
.mInput
.MakeOffsetAbsolute(
627 mSelection
.StartOffset() + mPendingCommitLength
))) {
628 MOZ_LOG(sContentCacheLog
, LogLevel::Error
,
629 ("0x%p HandleQueryContentEvent(), FAILED due to "
630 "aEvent.mInput.MakeOffsetAbsolute(mSelection.StartOffset() + "
631 "mPendingCommitLength) failure, "
632 "mSelection={ StartOffset()=%d, Length()=%d }, "
633 "mPendingCommitLength=%" PRIu32
", aEvent={ mMessage=%s, "
634 "mInput={ mOffset=%" PRId64
", mLength=%" PRIu32
" } }",
635 this, mSelection
.StartOffset(), mSelection
.Length(),
636 mPendingCommitLength
, ToChar(aEvent
.mMessage
),
637 aEvent
.mInput
.mOffset
, aEvent
.mInput
.mLength
));
642 switch (aEvent
.mMessage
) {
643 case eQuerySelectedText
:
644 MOZ_LOG(sContentCacheLog
, LogLevel::Info
,
645 ("0x%p HandleQueryContentEvent("
646 "aEvent={ mMessage=eQuerySelectedText }, aWidget=0x%p)",
648 if (aWidget
->PluginHasFocus()) {
649 MOZ_LOG(sContentCacheLog
, LogLevel::Info
,
650 ("0x%p HandleQueryContentEvent(), "
651 "return emtpy selection becasue plugin has focus",
653 aEvent
.mSucceeded
= true;
654 aEvent
.mReply
.mOffset
= 0;
655 aEvent
.mReply
.mReversed
= false;
656 aEvent
.mReply
.mHasSelection
= false;
659 if (NS_WARN_IF(!IsSelectionValid())) {
660 // If content cache hasn't been initialized properly, make the query
662 MOZ_LOG(sContentCacheLog
, LogLevel::Error
,
663 ("0x%p HandleQueryContentEvent(), "
664 "FAILED because mSelection is not valid",
668 aEvent
.mReply
.mOffset
= mSelection
.StartOffset();
669 if (mSelection
.Collapsed()) {
670 aEvent
.mReply
.mString
.Truncate(0);
672 if (NS_WARN_IF(mSelection
.EndOffset() > mText
.Length())) {
673 MOZ_LOG(sContentCacheLog
, LogLevel::Error
,
674 ("0x%p HandleQueryContentEvent(), "
675 "FAILED because mSelection.EndOffset()=%u is larger than "
677 this, mSelection
.EndOffset(), mText
.Length()));
680 aEvent
.mReply
.mString
=
681 Substring(mText
, aEvent
.mReply
.mOffset
, mSelection
.Length());
683 aEvent
.mReply
.mReversed
= mSelection
.Reversed();
684 aEvent
.mReply
.mHasSelection
= true;
685 aEvent
.mReply
.mWritingMode
= mSelection
.mWritingMode
;
686 MOZ_LOG(sContentCacheLog
, LogLevel::Info
,
687 ("0x%p HandleQueryContentEvent(), "
688 "Succeeded, aEvent={ mReply={ mOffset=%u, mString=\"%s\", "
689 "mReversed=%s, mHasSelection=%s, mWritingMode=%s } }",
690 this, aEvent
.mReply
.mOffset
,
691 GetEscapedUTF8String(aEvent
.mReply
.mString
).get(),
692 GetBoolName(aEvent
.mReply
.mReversed
),
693 GetBoolName(aEvent
.mReply
.mHasSelection
),
694 GetWritingModeName(aEvent
.mReply
.mWritingMode
).get()));
696 case eQueryTextContent
: {
697 MOZ_LOG(sContentCacheLog
, LogLevel::Info
,
698 ("0x%p HandleQueryContentEvent("
699 "aEvent={ mMessage=eQueryTextContent, mInput={ mOffset=%" PRId64
700 ", mLength=%u } }, aWidget=0x%p), mText.Length()=%u",
701 this, aEvent
.mInput
.mOffset
, aEvent
.mInput
.mLength
, aWidget
,
703 uint32_t inputOffset
= aEvent
.mInput
.mOffset
;
704 uint32_t inputEndOffset
=
705 std::min(aEvent
.mInput
.EndOffset(), mText
.Length());
706 if (NS_WARN_IF(inputEndOffset
< inputOffset
)) {
708 sContentCacheLog
, LogLevel::Error
,
709 ("0x%p HandleQueryContentEvent(), "
710 "FAILED because inputOffset=%u is larger than inputEndOffset=%u",
711 this, inputOffset
, inputEndOffset
));
714 aEvent
.mReply
.mOffset
= inputOffset
;
715 aEvent
.mReply
.mString
=
716 Substring(mText
, inputOffset
, inputEndOffset
- inputOffset
);
718 sContentCacheLog
, LogLevel::Info
,
719 ("0x%p HandleQueryContentEvent(), "
720 "Succeeded, aEvent={ mReply={ mOffset=%u, mString.Length()=%u } }",
721 this, aEvent
.mReply
.mOffset
, aEvent
.mReply
.mString
.Length()));
725 MOZ_LOG(sContentCacheLog
, LogLevel::Info
,
726 ("0x%p HandleQueryContentEvent("
727 "aEvent={ mMessage=eQueryTextRect, mInput={ mOffset=%" PRId64
728 ", mLength=%u } }, aWidget=0x%p), mText.Length()=%u",
729 this, aEvent
.mInput
.mOffset
, aEvent
.mInput
.mLength
, aWidget
,
731 if (NS_WARN_IF(!IsSelectionValid())) {
732 // If content cache hasn't been initialized properly, make the query
734 MOZ_LOG(sContentCacheLog
, LogLevel::Error
,
735 ("0x%p HandleQueryContentEvent(), "
736 "FAILED because mSelection is not valid",
740 // Note that if the query is relative to insertion point, the query was
741 // probably requested by native IME. In such case, we should return
742 // non-empty rect since returning failure causes IME showing its window
744 if (aEvent
.mInput
.mLength
) {
745 if (NS_WARN_IF(!GetUnionTextRects(
746 aEvent
.mInput
.mOffset
, aEvent
.mInput
.mLength
,
747 isRelativeToInsertionPoint
, aEvent
.mReply
.mRect
))) {
748 // XXX We don't have cache for this request.
749 MOZ_LOG(sContentCacheLog
, LogLevel::Error
,
750 ("0x%p HandleQueryContentEvent(), "
751 "FAILED to get union rect",
756 // If the length is 0, we should return caret rect instead.
757 if (NS_WARN_IF(!GetCaretRect(aEvent
.mInput
.mOffset
,
758 isRelativeToInsertionPoint
,
759 aEvent
.mReply
.mRect
))) {
760 MOZ_LOG(sContentCacheLog
, LogLevel::Error
,
761 ("0x%p HandleQueryContentEvent(), "
762 "FAILED to get caret rect",
767 if (aEvent
.mInput
.mOffset
< mText
.Length()) {
768 aEvent
.mReply
.mString
= Substring(
769 mText
, aEvent
.mInput
.mOffset
,
770 mText
.Length() >= aEvent
.mInput
.EndOffset() ? aEvent
.mInput
.mLength
773 aEvent
.mReply
.mString
.Truncate(0);
775 aEvent
.mReply
.mOffset
= aEvent
.mInput
.mOffset
;
776 // XXX This may be wrong if storing range isn't in the selection range.
777 aEvent
.mReply
.mWritingMode
= mSelection
.mWritingMode
;
778 MOZ_LOG(sContentCacheLog
, LogLevel::Info
,
779 ("0x%p HandleQueryContentEvent(), "
780 "Succeeded, aEvent={ mReply={ mOffset=%u, mString=\"%s\", "
781 "mWritingMode=%s, mRect=%s } }",
782 this, aEvent
.mReply
.mOffset
,
783 GetEscapedUTF8String(aEvent
.mReply
.mString
).get(),
784 GetWritingModeName(aEvent
.mReply
.mWritingMode
).get(),
785 GetRectText(aEvent
.mReply
.mRect
).get()));
787 case eQueryCaretRect
:
788 MOZ_LOG(sContentCacheLog
, LogLevel::Info
,
789 ("0x%p HandleQueryContentEvent("
790 "aEvent={ mMessage=eQueryCaretRect, mInput={ mOffset=%" PRId64
792 "aWidget=0x%p), mText.Length()=%u",
793 this, aEvent
.mInput
.mOffset
, aWidget
, mText
.Length()));
794 if (NS_WARN_IF(!IsSelectionValid())) {
795 // If content cache hasn't been initialized properly, make the query
797 MOZ_LOG(sContentCacheLog
, LogLevel::Error
,
798 ("0x%p HandleQueryContentEvent(), "
799 "FAILED because mSelection is not valid",
803 // Note that if the query is relative to insertion point, the query was
804 // probably requested by native IME. In such case, we should return
805 // non-empty rect since returning failure causes IME showing its window
807 if (NS_WARN_IF(!GetCaretRect(aEvent
.mInput
.mOffset
,
808 isRelativeToInsertionPoint
,
809 aEvent
.mReply
.mRect
))) {
810 MOZ_LOG(sContentCacheLog
, LogLevel::Error
,
811 ("0x%p HandleQueryContentEvent(), "
812 "FAILED to get caret rect",
816 aEvent
.mReply
.mOffset
= aEvent
.mInput
.mOffset
;
817 MOZ_LOG(sContentCacheLog
, LogLevel::Info
,
818 ("0x%p HandleQueryContentEvent(), "
819 "Succeeded, aEvent={ mReply={ mOffset=%u, mRect=%s } }",
820 this, aEvent
.mReply
.mOffset
,
821 GetRectText(aEvent
.mReply
.mRect
).get()));
823 case eQueryEditorRect
:
824 MOZ_LOG(sContentCacheLog
, LogLevel::Info
,
825 ("0x%p HandleQueryContentEvent("
826 "aEvent={ mMessage=eQueryEditorRect }, aWidget=0x%p)",
828 aEvent
.mReply
.mRect
= mEditorRect
;
829 MOZ_LOG(sContentCacheLog
, LogLevel::Info
,
830 ("0x%p HandleQueryContentEvent(), "
831 "Succeeded, aEvent={ mReply={ mRect=%s } }",
832 this, GetRectText(aEvent
.mReply
.mRect
).get()));
837 aEvent
.mSucceeded
= true;
841 bool ContentCacheInParent::GetTextRect(uint32_t aOffset
,
842 bool aRoundToExistingOffset
,
843 LayoutDeviceIntRect
& aTextRect
) const {
844 MOZ_LOG(sContentCacheLog
, LogLevel::Info
,
845 ("0x%p GetTextRect(aOffset=%u, "
846 "aRoundToExistingOffset=%s), "
847 "mTextRectArray={ mStart=%u, mRects.Length()=%zu }, "
848 "mSelection={ mAnchor=%u, mFocus=%u }",
849 this, aOffset
, GetBoolName(aRoundToExistingOffset
),
850 mTextRectArray
.mStart
, mTextRectArray
.mRects
.Length(),
851 mSelection
.mAnchor
, mSelection
.mFocus
));
854 NS_WARNING_ASSERTION(!mFirstCharRect
.IsEmpty(), "empty rect");
855 aTextRect
= mFirstCharRect
;
856 return !aTextRect
.IsEmpty();
858 if (aOffset
== mSelection
.mAnchor
) {
859 NS_WARNING_ASSERTION(!mSelection
.mAnchorCharRects
[eNextCharRect
].IsEmpty(),
861 aTextRect
= mSelection
.mAnchorCharRects
[eNextCharRect
];
862 return !aTextRect
.IsEmpty();
864 if (mSelection
.mAnchor
&& aOffset
== mSelection
.mAnchor
- 1) {
865 NS_WARNING_ASSERTION(!mSelection
.mAnchorCharRects
[ePrevCharRect
].IsEmpty(),
867 aTextRect
= mSelection
.mAnchorCharRects
[ePrevCharRect
];
868 return !aTextRect
.IsEmpty();
870 if (aOffset
== mSelection
.mFocus
) {
871 NS_WARNING_ASSERTION(!mSelection
.mFocusCharRects
[eNextCharRect
].IsEmpty(),
873 aTextRect
= mSelection
.mFocusCharRects
[eNextCharRect
];
874 return !aTextRect
.IsEmpty();
876 if (mSelection
.mFocus
&& aOffset
== mSelection
.mFocus
- 1) {
877 NS_WARNING_ASSERTION(!mSelection
.mFocusCharRects
[ePrevCharRect
].IsEmpty(),
879 aTextRect
= mSelection
.mFocusCharRects
[ePrevCharRect
];
880 return !aTextRect
.IsEmpty();
883 uint32_t offset
= aOffset
;
884 if (!mTextRectArray
.InRange(aOffset
)) {
885 if (!aRoundToExistingOffset
) {
886 aTextRect
.SetEmpty();
889 if (!mTextRectArray
.IsValid()) {
890 // If there are no rects in mTextRectArray, we should refer the start of
891 // the selection because IME must query a char rect around it if there is
893 aTextRect
= mSelection
.StartCharRect();
894 return !aTextRect
.IsEmpty();
896 if (offset
< mTextRectArray
.StartOffset()) {
897 offset
= mTextRectArray
.StartOffset();
899 offset
= mTextRectArray
.EndOffset() - 1;
902 aTextRect
= mTextRectArray
.GetRect(offset
);
903 return !aTextRect
.IsEmpty();
906 bool ContentCacheInParent::GetUnionTextRects(
907 uint32_t aOffset
, uint32_t aLength
, bool aRoundToExistingOffset
,
908 LayoutDeviceIntRect
& aUnionTextRect
) const {
909 MOZ_LOG(sContentCacheLog
, LogLevel::Info
,
910 ("0x%p GetUnionTextRects(aOffset=%u, "
911 "aLength=%u, aRoundToExistingOffset=%s), mTextRectArray={ "
912 "mStart=%u, mRects.Length()=%zu }, "
913 "mSelection={ mAnchor=%u, mFocus=%u }",
914 this, aOffset
, aLength
, GetBoolName(aRoundToExistingOffset
),
915 mTextRectArray
.mStart
, mTextRectArray
.mRects
.Length(),
916 mSelection
.mAnchor
, mSelection
.mFocus
));
918 CheckedInt
<uint32_t> endOffset
= CheckedInt
<uint32_t>(aOffset
) + aLength
;
919 if (!endOffset
.isValid()) {
923 if (!mSelection
.Collapsed() && aOffset
== mSelection
.StartOffset() &&
924 aLength
== mSelection
.Length()) {
925 NS_WARNING_ASSERTION(!mSelection
.mRect
.IsEmpty(), "empty rect");
926 aUnionTextRect
= mSelection
.mRect
;
927 return !aUnionTextRect
.IsEmpty();
932 NS_WARNING_ASSERTION(!mFirstCharRect
.IsEmpty(), "empty rect");
933 aUnionTextRect
= mFirstCharRect
;
934 return !aUnionTextRect
.IsEmpty();
936 if (aOffset
== mSelection
.mAnchor
) {
937 NS_WARNING_ASSERTION(
938 !mSelection
.mAnchorCharRects
[eNextCharRect
].IsEmpty(), "empty rect");
939 aUnionTextRect
= mSelection
.mAnchorCharRects
[eNextCharRect
];
940 return !aUnionTextRect
.IsEmpty();
942 if (mSelection
.mAnchor
&& aOffset
== mSelection
.mAnchor
- 1) {
943 NS_WARNING_ASSERTION(
944 !mSelection
.mAnchorCharRects
[ePrevCharRect
].IsEmpty(), "empty rect");
945 aUnionTextRect
= mSelection
.mAnchorCharRects
[ePrevCharRect
];
946 return !aUnionTextRect
.IsEmpty();
948 if (aOffset
== mSelection
.mFocus
) {
949 NS_WARNING_ASSERTION(!mSelection
.mFocusCharRects
[eNextCharRect
].IsEmpty(),
951 aUnionTextRect
= mSelection
.mFocusCharRects
[eNextCharRect
];
952 return !aUnionTextRect
.IsEmpty();
954 if (mSelection
.mFocus
&& aOffset
== mSelection
.mFocus
- 1) {
955 NS_WARNING_ASSERTION(!mSelection
.mFocusCharRects
[ePrevCharRect
].IsEmpty(),
957 aUnionTextRect
= mSelection
.mFocusCharRects
[ePrevCharRect
];
958 return !aUnionTextRect
.IsEmpty();
962 // Even if some text rects are not cached of the queried range,
963 // we should return union rect when the first character's rect is cached
964 // since the first character rect is important and the others are not so
967 if (!aOffset
&& aOffset
!= mSelection
.mAnchor
&&
968 aOffset
!= mSelection
.mFocus
&& !mTextRectArray
.InRange(aOffset
)) {
969 // The first character rect isn't cached.
973 if ((aRoundToExistingOffset
&& mTextRectArray
.HasRects()) ||
974 mTextRectArray
.IsOverlappingWith(aOffset
, aLength
)) {
975 aUnionTextRect
= mTextRectArray
.GetUnionRectAsFarAsPossible(
976 aOffset
, aLength
, aRoundToExistingOffset
);
978 aUnionTextRect
.SetEmpty();
982 aUnionTextRect
= aUnionTextRect
.Union(mFirstCharRect
);
984 if (aOffset
<= mSelection
.mAnchor
&& mSelection
.mAnchor
< endOffset
.value()) {
986 aUnionTextRect
.Union(mSelection
.mAnchorCharRects
[eNextCharRect
]);
988 if (mSelection
.mAnchor
&& aOffset
<= mSelection
.mAnchor
- 1 &&
989 mSelection
.mAnchor
- 1 < endOffset
.value()) {
991 aUnionTextRect
.Union(mSelection
.mAnchorCharRects
[ePrevCharRect
]);
993 if (aOffset
<= mSelection
.mFocus
&& mSelection
.mFocus
< endOffset
.value()) {
995 aUnionTextRect
.Union(mSelection
.mFocusCharRects
[eNextCharRect
]);
997 if (mSelection
.mFocus
&& aOffset
<= mSelection
.mFocus
- 1 &&
998 mSelection
.mFocus
- 1 < endOffset
.value()) {
1000 aUnionTextRect
.Union(mSelection
.mFocusCharRects
[ePrevCharRect
]);
1003 return !aUnionTextRect
.IsEmpty();
1006 bool ContentCacheInParent::GetCaretRect(uint32_t aOffset
,
1007 bool aRoundToExistingOffset
,
1008 LayoutDeviceIntRect
& aCaretRect
) const {
1010 sContentCacheLog
, LogLevel::Info
,
1011 ("0x%p GetCaretRect(aOffset=%u, "
1012 "aRoundToExistingOffset=%s), "
1013 "mCaret={ mOffset=%u, mRect=%s, IsValid()=%s }, mTextRectArray={ "
1014 "mStart=%u, mRects.Length()=%zu }, mSelection={ mAnchor=%u, mFocus=%u, "
1015 "mWritingMode=%s, mAnchorCharRects[eNextCharRect]=%s, "
1016 "mAnchorCharRects[ePrevCharRect]=%s, mFocusCharRects[eNextCharRect]=%s, "
1017 "mFocusCharRects[ePrevCharRect]=%s }, mFirstCharRect=%s",
1018 this, aOffset
, GetBoolName(aRoundToExistingOffset
), mCaret
.mOffset
,
1019 GetRectText(mCaret
.mRect
).get(), GetBoolName(mCaret
.IsValid()),
1020 mTextRectArray
.mStart
, mTextRectArray
.mRects
.Length(),
1021 mSelection
.mAnchor
, mSelection
.mFocus
,
1022 GetWritingModeName(mSelection
.mWritingMode
).get(),
1023 GetRectText(mSelection
.mAnchorCharRects
[eNextCharRect
]).get(),
1024 GetRectText(mSelection
.mAnchorCharRects
[ePrevCharRect
]).get(),
1025 GetRectText(mSelection
.mFocusCharRects
[eNextCharRect
]).get(),
1026 GetRectText(mSelection
.mFocusCharRects
[ePrevCharRect
]).get(),
1027 GetRectText(mFirstCharRect
).get()));
1029 if (mCaret
.IsValid() && mCaret
.mOffset
== aOffset
) {
1030 aCaretRect
= mCaret
.mRect
;
1034 // Guess caret rect from the text rect if it's stored.
1035 if (!GetTextRect(aOffset
, aRoundToExistingOffset
, aCaretRect
)) {
1036 // There might be previous character rect in the cache. If so, we can
1037 // guess the caret rect with it.
1039 !GetTextRect(aOffset
- 1, aRoundToExistingOffset
, aCaretRect
)) {
1040 aCaretRect
.SetEmpty();
1044 if (mSelection
.mWritingMode
.IsVertical()) {
1045 aCaretRect
.MoveToY(aCaretRect
.YMost());
1047 // XXX bidi-unaware.
1048 aCaretRect
.MoveToX(aCaretRect
.XMost());
1052 // XXX This is not bidi aware because we don't cache each character's
1053 // direction. However, this is usually used by IME, so, assuming the
1054 // character is in LRT context must not cause any problem.
1055 if (mSelection
.mWritingMode
.IsVertical()) {
1056 aCaretRect
.SetHeight(mCaret
.IsValid() ? mCaret
.mRect
.Height() : 1);
1058 aCaretRect
.SetWidth(mCaret
.IsValid() ? mCaret
.mRect
.Width() : 1);
1063 bool ContentCacheInParent::OnCompositionEvent(
1064 const WidgetCompositionEvent
& aEvent
) {
1066 sContentCacheLog
, LogLevel::Info
,
1067 ("0x%p OnCompositionEvent(aEvent={ "
1068 "mMessage=%s, mData=\"%s\" (Length()=%u), mRanges->Length()=%zu }), "
1069 "mPendingEventsNeedingAck=%u, mWidgetHasComposition=%s, "
1070 "mPendingCompositionCount=%" PRIu8
", mPendingCommitCount=%" PRIu8
", "
1071 "mIsChildIgnoringCompositionEvents=%s, mCommitStringByRequest=0x%p",
1072 this, ToChar(aEvent
.mMessage
), GetEscapedUTF8String(aEvent
.mData
).get(),
1073 aEvent
.mData
.Length(), aEvent
.mRanges
? aEvent
.mRanges
->Length() : 0,
1074 mPendingEventsNeedingAck
, GetBoolName(mWidgetHasComposition
),
1075 mPendingCompositionCount
, mPendingCommitCount
,
1076 GetBoolName(mIsChildIgnoringCompositionEvents
), mCommitStringByRequest
));
1078 #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
1079 mDispatchedEventMessages
.AppendElement(aEvent
.mMessage
);
1080 #endif // #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
1082 // We must be able to simulate the selection because
1083 // we might not receive selection updates in time
1084 if (!mWidgetHasComposition
) {
1085 if (aEvent
.mWidget
&& aEvent
.mWidget
->PluginHasFocus()) {
1086 // If focus is on plugin, we cannot get selection range
1087 mCompositionStart
= 0;
1088 } else if (mCompositionStartInChild
!= UINT32_MAX
) {
1089 // If there is pending composition in the remote process, let's use
1090 // its start offset temporarily because this stores a lot of information
1091 // around it and the user must look around there, so, showing some UI
1092 // around it must make sense.
1093 mCompositionStart
= mCompositionStartInChild
;
1095 mCompositionStart
= mSelection
.StartOffset();
1097 MOZ_ASSERT(aEvent
.mMessage
== eCompositionStart
);
1098 MOZ_RELEASE_ASSERT(mPendingCompositionCount
< UINT8_MAX
);
1099 mPendingCompositionCount
++;
1102 mWidgetHasComposition
= !aEvent
.CausesDOMCompositionEndEvent();
1104 if (!mWidgetHasComposition
) {
1105 // mCompositionStart will be reset when commit event is completely handled
1106 // in the remote process.
1107 if (mPendingCompositionCount
== 1) {
1108 mPendingCommitLength
= aEvent
.mData
.Length();
1110 mPendingCommitCount
++;
1111 } else if (aEvent
.mMessage
!= eCompositionStart
) {
1112 mCompositionString
= aEvent
.mData
;
1115 // During REQUEST_TO_COMMIT_COMPOSITION or REQUEST_TO_CANCEL_COMPOSITION,
1116 // widget usually sends a eCompositionChange and/or eCompositionCommit event
1117 // to finalize or clear the composition, respectively. In this time,
1118 // we need to intercept all composition events here and pass the commit
1119 // string for returning to the remote process as a result of
1120 // RequestIMEToCommitComposition(). Then, eCommitComposition event will
1121 // be dispatched with the committed string in the remote process internally.
1122 if (mCommitStringByRequest
) {
1123 if (aEvent
.mMessage
== eCompositionCommitAsIs
) {
1124 *mCommitStringByRequest
= mCompositionString
;
1126 MOZ_ASSERT(aEvent
.mMessage
== eCompositionChange
||
1127 aEvent
.mMessage
== eCompositionCommit
);
1128 *mCommitStringByRequest
= aEvent
.mData
;
1130 // We need to wait eCompositionCommitRequestHandled from the remote process
1131 // in this case. Therefore, mPendingEventsNeedingAck needs to be
1132 // incremented here. Additionally, we stop sending eCompositionCommit(AsIs)
1133 // event. Therefore, we need to decrement mPendingCommitCount which has
1134 // been incremented above.
1135 if (!mWidgetHasComposition
) {
1136 mPendingEventsNeedingAck
++;
1137 MOZ_DIAGNOSTIC_ASSERT(mPendingCommitCount
);
1138 if (mPendingCommitCount
) {
1139 mPendingCommitCount
--;
1145 mPendingEventsNeedingAck
++;
1149 void ContentCacheInParent::OnSelectionEvent(
1150 const WidgetSelectionEvent
& aSelectionEvent
) {
1152 sContentCacheLog
, LogLevel::Info
,
1153 ("0x%p OnSelectionEvent(aEvent={ "
1154 "mMessage=%s, mOffset=%u, mLength=%u, mReversed=%s, "
1155 "mExpandToClusterBoundary=%s, mUseNativeLineBreak=%s }), "
1156 "mPendingEventsNeedingAck=%u, mWidgetHasComposition=%s, "
1157 "mPendingCompositionCount=%" PRIu8
", mPendingCommitCount=%" PRIu8
", "
1158 "mIsChildIgnoringCompositionEvents=%s",
1159 this, ToChar(aSelectionEvent
.mMessage
), aSelectionEvent
.mOffset
,
1160 aSelectionEvent
.mLength
, GetBoolName(aSelectionEvent
.mReversed
),
1161 GetBoolName(aSelectionEvent
.mExpandToClusterBoundary
),
1162 GetBoolName(aSelectionEvent
.mUseNativeLineBreak
),
1163 mPendingEventsNeedingAck
, GetBoolName(mWidgetHasComposition
),
1164 mPendingCompositionCount
, mPendingCommitCount
,
1165 GetBoolName(mIsChildIgnoringCompositionEvents
)));
1167 #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
1168 mDispatchedEventMessages
.AppendElement(aSelectionEvent
.mMessage
);
1169 #endif // MOZ_DIAGNOSTIC_ASSERT_ENABLED
1171 mPendingEventsNeedingAck
++;
1174 void ContentCacheInParent::OnEventNeedingAckHandled(nsIWidget
* aWidget
,
1175 EventMessage aMessage
) {
1176 // This is called when the child process receives WidgetCompositionEvent or
1177 // WidgetSelectionEvent.
1180 sContentCacheLog
, LogLevel::Info
,
1181 ("0x%p OnEventNeedingAckHandled(aWidget=0x%p, "
1182 "aMessage=%s), mPendingEventsNeedingAck=%u, "
1183 "mWidgetHasComposition=%s, mPendingCompositionCount=%" PRIu8
", "
1184 "mPendingCommitCount=%" PRIu8
", mIsChildIgnoringCompositionEvents=%s",
1185 this, aWidget
, ToChar(aMessage
), mPendingEventsNeedingAck
,
1186 GetBoolName(mWidgetHasComposition
), mPendingCompositionCount
,
1187 mPendingCommitCount
, GetBoolName(mIsChildIgnoringCompositionEvents
)));
1189 #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
1190 mReceivedEventMessages
.AppendElement(aMessage
);
1191 #endif // #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
1193 bool isCommittedInChild
=
1194 // Commit requester in the remote process has committed the composition.
1195 aMessage
== eCompositionCommitRequestHandled
||
1196 // The commit event has been handled normally in the remote process.
1197 (!mIsChildIgnoringCompositionEvents
&&
1198 WidgetCompositionEvent::IsFollowedByCompositionEnd(aMessage
));
1200 if (isCommittedInChild
) {
1201 #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
1202 if (mPendingCompositionCount
== 1) {
1203 RemoveUnnecessaryEventMessageLog();
1205 #endif // #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
1207 if (NS_WARN_IF(!mPendingCompositionCount
)) {
1208 #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
1209 nsPrintfCString
info(
1210 "\nThere is no pending composition but received %s "
1211 "message from the remote child\n\n",
1213 AppendEventMessageLog(info
);
1214 CrashReporter::AppendAppNotesToCrashReport(info
);
1215 MOZ_DIAGNOSTIC_ASSERT(
1216 false, "No pending composition but received unexpected commit event");
1217 #endif // #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
1219 // Prevent odd behavior in release channel.
1220 mPendingCompositionCount
= 1;
1223 mPendingCompositionCount
--;
1225 // Forget composition string only when the latest composition string is
1226 // handled in the remote process because if there is 2 or more pending
1227 // composition, this value shouldn't be referred.
1228 if (!mPendingCompositionCount
) {
1229 mCompositionString
.Truncate();
1232 // Forget pending commit string length if it's handled in the remote
1233 // process. Note that this doesn't care too old composition's commit
1234 // string because in such case, we cannot return proper information
1235 // to IME synchornously.
1236 mPendingCommitLength
= 0;
1239 if (WidgetCompositionEvent::IsFollowedByCompositionEnd(aMessage
)) {
1240 // After the remote process receives eCompositionCommit(AsIs) event,
1241 // it'll restart to handle composition events.
1242 mIsChildIgnoringCompositionEvents
= false;
1244 if (NS_WARN_IF(!mPendingCommitCount
)) {
1245 #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
1246 nsPrintfCString
info(
1247 "\nThere is no pending comment events but received "
1248 "%s message from the remote child\n\n",
1250 AppendEventMessageLog(info
);
1251 CrashReporter::AppendAppNotesToCrashReport(info
);
1252 MOZ_DIAGNOSTIC_ASSERT(
1254 "No pending commit events but received unexpected commit event");
1255 #endif // #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
1257 // Prevent odd behavior in release channel.
1258 mPendingCommitCount
= 1;
1261 mPendingCommitCount
--;
1262 } else if (aMessage
== eCompositionCommitRequestHandled
&&
1263 mPendingCommitCount
) {
1264 // If the remote process commits composition synchronously after
1265 // requesting commit composition and we've already sent commit composition,
1266 // it starts to ignore following composition events until receiving
1267 // eCompositionStart event.
1268 mIsChildIgnoringCompositionEvents
= true;
1271 // If neither widget (i.e., IME) nor the remote process has composition,
1272 // now, we can forget composition string informations.
1273 if (!mWidgetHasComposition
&& !mPendingCompositionCount
&&
1274 !mPendingCommitCount
) {
1275 mCompositionStart
= UINT32_MAX
;
1278 if (NS_WARN_IF(!mPendingEventsNeedingAck
)) {
1279 #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
1280 nsPrintfCString
info(
1281 "\nThere is no pending events but received %s "
1282 "message from the remote child\n\n",
1284 AppendEventMessageLog(info
);
1285 CrashReporter::AppendAppNotesToCrashReport(info
);
1286 #endif // #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
1287 MOZ_DIAGNOSTIC_ASSERT(
1288 false, "No pending event message but received unexpected event");
1289 mPendingEventsNeedingAck
= 1;
1291 if (--mPendingEventsNeedingAck
) {
1295 FlushPendingNotifications(aWidget
);
1298 bool ContentCacheInParent::RequestIMEToCommitComposition(
1299 nsIWidget
* aWidget
, bool aCancel
, nsAString
& aCommittedString
) {
1301 sContentCacheLog
, LogLevel::Info
,
1302 ("0x%p RequestToCommitComposition(aWidget=%p, "
1303 "aCancel=%s), mPendingCompositionCount=%" PRIu8
", "
1304 "mPendingCommitCount=%" PRIu8
", mIsChildIgnoringCompositionEvents=%s, "
1305 "IMEStateManager::DoesBrowserParentHaveIMEFocus(&mBrowserParent)=%s, "
1306 "mWidgetHasComposition=%s, mCommitStringByRequest=%p",
1307 this, aWidget
, GetBoolName(aCancel
), mPendingCompositionCount
,
1308 mPendingCommitCount
, GetBoolName(mIsChildIgnoringCompositionEvents
),
1310 IMEStateManager::DoesBrowserParentHaveIMEFocus(&mBrowserParent
)),
1311 GetBoolName(mWidgetHasComposition
), mCommitStringByRequest
));
1313 MOZ_ASSERT(!mCommitStringByRequest
);
1315 // If there are 2 or more pending compositions, we already sent
1316 // eCompositionCommit(AsIs) to the remote process. So, this request is
1317 // too late for IME. The remote process should wait following
1318 // composition events for cleaning up TextComposition and handle the
1319 // request as it's handled asynchronously.
1320 if (mPendingCompositionCount
> 1) {
1321 #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
1322 mRequestIMEToCommitCompositionResults
.AppendElement(
1323 RequestIMEToCommitCompositionResult::eToOldCompositionReceived
);
1324 #endif // #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
1328 // If there is no pending composition, we may have already sent
1329 // eCompositionCommit(AsIs) event for the active composition. If so, the
1330 // remote process will receive composition events which causes cleaning up
1331 // TextComposition. So, this shouldn't do nothing and TextComposition
1332 // should handle the request as it's handled asynchronously.
1333 // XXX Perhaps, this is wrong because TextComposition in child process
1334 // may commit the composition with current composition string in the
1335 // remote process. I.e., it may be different from actual commit string
1336 // which user typed. So, perhaps, we should return true and the commit
1338 if (mPendingCommitCount
) {
1339 #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
1340 mRequestIMEToCommitCompositionResults
.AppendElement(
1341 RequestIMEToCommitCompositionResult::eToCommittedCompositionReceived
);
1342 #endif // #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
1346 // If BrowserParent which has IME focus was already changed to different one,
1347 // the request shouldn't be sent to IME because it's too late.
1348 if (!IMEStateManager::DoesBrowserParentHaveIMEFocus(&mBrowserParent
)) {
1349 // Use the latest composition string which may not be handled in the
1350 // remote process for avoiding data loss.
1351 #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
1352 mRequestIMEToCommitCompositionResults
.AppendElement(
1353 RequestIMEToCommitCompositionResult::eReceivedAfterBrowserParentBlur
);
1354 #endif // #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
1355 aCommittedString
= mCompositionString
;
1356 // After we return true from here, i.e., without actually requesting IME
1357 // to commit composition, we will receive eCompositionCommitRequestHandled
1358 // pseudo event message from the remote process. So, we need to increment
1359 // mPendingEventsNeedingAck here.
1360 mPendingEventsNeedingAck
++;
1364 RefPtr
<TextComposition
> composition
=
1365 IMEStateManager::GetTextCompositionFor(aWidget
);
1366 if (NS_WARN_IF(!composition
)) {
1367 MOZ_LOG(sContentCacheLog
, LogLevel::Warning
,
1368 (" 0x%p RequestToCommitComposition(), "
1369 "does nothing due to no composition",
1371 #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
1372 mRequestIMEToCommitCompositionResults
.AppendElement(
1373 RequestIMEToCommitCompositionResult::eReceivedButNoTextComposition
);
1374 #endif // #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
1378 mCommitStringByRequest
= &aCommittedString
;
1380 // Request commit or cancel composition with TextComposition because we may
1381 // have already requested to commit or cancel the composition or we may
1382 // have already received eCompositionCommit(AsIs) event. Those status are
1383 // managed by composition. So, if we don't request commit composition,
1384 // we should do nothing with native IME here.
1385 composition
->RequestToCommit(aWidget
, aCancel
);
1387 mCommitStringByRequest
= nullptr;
1390 sContentCacheLog
, LogLevel::Info
,
1391 (" 0x%p RequestToCommitComposition(), "
1392 "mWidgetHasComposition=%s, the composition %s committed synchronously",
1393 this, GetBoolName(mWidgetHasComposition
),
1394 composition
->Destroyed() ? "WAS" : "has NOT been"));
1396 if (!composition
->Destroyed()) {
1397 // When the composition isn't committed synchronously, the remote process's
1398 // TextComposition instance will synthesize commit events and wait to
1399 // receive delayed composition events. When TextComposition instances both
1400 // in this process and the remote process will be destroyed when delayed
1401 // composition events received. TextComposition instance in the parent
1402 // process will dispatch following composition events and be destroyed
1403 // normally. On the other hand, TextComposition instance in the remote
1404 // process won't dispatch following composition events and will be
1405 // destroyed by IMEStateManager::DispatchCompositionEvent().
1406 #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
1407 mRequestIMEToCommitCompositionResults
.AppendElement(
1408 RequestIMEToCommitCompositionResult::eHandledAsynchronously
);
1409 #endif // #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
1413 // When the composition is committed synchronously, the commit string will be
1414 // returned to the remote process. Then, PuppetWidget will dispatch
1415 // eCompositionCommit event with the returned commit string (i.e., the value
1416 // is aCommittedString of this method) and that causes destroying
1417 // TextComposition instance in the remote process (Note that TextComposition
1418 // instance in this process was already destroyed).
1419 #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
1420 mRequestIMEToCommitCompositionResults
.AppendElement(
1421 RequestIMEToCommitCompositionResult::eHandledSynchronously
);
1422 #endif // #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
1426 void ContentCacheInParent::MaybeNotifyIME(
1427 nsIWidget
* aWidget
, const IMENotification
& aNotification
) {
1428 if (!mPendingEventsNeedingAck
) {
1429 IMEStateManager::NotifyIME(aNotification
, aWidget
, &mBrowserParent
);
1433 switch (aNotification
.mMessage
) {
1434 case NOTIFY_IME_OF_SELECTION_CHANGE
:
1435 mPendingSelectionChange
.MergeWith(aNotification
);
1437 case NOTIFY_IME_OF_TEXT_CHANGE
:
1438 mPendingTextChange
.MergeWith(aNotification
);
1440 case NOTIFY_IME_OF_POSITION_CHANGE
:
1441 mPendingLayoutChange
.MergeWith(aNotification
);
1443 case NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED
:
1444 mPendingCompositionUpdate
.MergeWith(aNotification
);
1447 MOZ_CRASH("Unsupported notification");
1452 void ContentCacheInParent::FlushPendingNotifications(nsIWidget
* aWidget
) {
1453 MOZ_ASSERT(!mPendingEventsNeedingAck
);
1455 // If the BrowserParent's widget has already gone, this can do nothing since
1456 // widget is necessary to notify IME of something.
1461 // New notifications which are notified during flushing pending notifications
1462 // should be merged again.
1463 mPendingEventsNeedingAck
++;
1465 nsCOMPtr
<nsIWidget
> widget
= aWidget
;
1467 // First, text change notification should be sent because selection change
1468 // notification notifies IME of current selection range in the latest content.
1469 // So, IME may need the latest content before that.
1470 if (mPendingTextChange
.HasNotification()) {
1471 IMENotification
notification(mPendingTextChange
);
1472 if (!widget
->Destroyed()) {
1473 mPendingTextChange
.Clear();
1474 IMEStateManager::NotifyIME(notification
, widget
, &mBrowserParent
);
1478 if (mPendingSelectionChange
.HasNotification()) {
1479 IMENotification
notification(mPendingSelectionChange
);
1480 if (!widget
->Destroyed()) {
1481 mPendingSelectionChange
.Clear();
1482 IMEStateManager::NotifyIME(notification
, widget
, &mBrowserParent
);
1486 // Layout change notification should be notified after selection change
1487 // notification because IME may want to query position of new caret position.
1488 if (mPendingLayoutChange
.HasNotification()) {
1489 IMENotification
notification(mPendingLayoutChange
);
1490 if (!widget
->Destroyed()) {
1491 mPendingLayoutChange
.Clear();
1492 IMEStateManager::NotifyIME(notification
, widget
, &mBrowserParent
);
1496 // Finally, send composition update notification because it notifies IME of
1497 // finishing handling whole sending events.
1498 if (mPendingCompositionUpdate
.HasNotification()) {
1499 IMENotification
notification(mPendingCompositionUpdate
);
1500 if (!widget
->Destroyed()) {
1501 mPendingCompositionUpdate
.Clear();
1502 IMEStateManager::NotifyIME(notification
, widget
, &mBrowserParent
);
1506 if (!--mPendingEventsNeedingAck
&& !widget
->Destroyed() &&
1507 (mPendingTextChange
.HasNotification() ||
1508 mPendingSelectionChange
.HasNotification() ||
1509 mPendingLayoutChange
.HasNotification() ||
1510 mPendingCompositionUpdate
.HasNotification())) {
1511 FlushPendingNotifications(widget
);
1515 #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
1517 void ContentCacheInParent::RemoveUnnecessaryEventMessageLog() {
1518 bool foundLastCompositionStart
= false;
1519 for (size_t i
= mDispatchedEventMessages
.Length(); i
> 1; i
--) {
1520 if (mDispatchedEventMessages
[i
- 1] != eCompositionStart
) {
1523 if (!foundLastCompositionStart
) {
1524 // Find previous eCompositionStart of the latest eCompositionStart.
1525 foundLastCompositionStart
= true;
1528 // Remove the messages before the last 2 sets of composition events.
1529 mDispatchedEventMessages
.RemoveElementsAt(0, i
- 1);
1532 uint32_t numberOfCompositionCommitRequestHandled
= 0;
1533 foundLastCompositionStart
= false;
1534 for (size_t i
= mReceivedEventMessages
.Length(); i
> 1; i
--) {
1535 if (mReceivedEventMessages
[i
- 1] == eCompositionCommitRequestHandled
) {
1536 numberOfCompositionCommitRequestHandled
++;
1538 if (mReceivedEventMessages
[i
- 1] != eCompositionStart
) {
1541 if (!foundLastCompositionStart
) {
1542 // Find previous eCompositionStart of the latest eCompositionStart.
1543 foundLastCompositionStart
= true;
1546 // Remove the messages before the last 2 sets of composition events.
1547 mReceivedEventMessages
.RemoveElementsAt(0, i
- 1);
1551 if (!numberOfCompositionCommitRequestHandled
) {
1552 // If there is no eCompositionCommitRequestHandled in
1553 // mReceivedEventMessages, we don't need to store log of
1554 // RequestIMEToCommmitComposition().
1555 mRequestIMEToCommitCompositionResults
.Clear();
1557 // We need to keep all reason of eCompositionCommitRequestHandled, which
1558 // is sent when mRequestIMEToCommitComposition() returns true.
1559 // So, we can discard older log than the first
1560 // eCompositionCommitRequestHandled in mReceivedEventMessages.
1561 for (size_t i
= mRequestIMEToCommitCompositionResults
.Length(); i
> 1;
1563 if (mRequestIMEToCommitCompositionResults
[i
- 1] ==
1564 RequestIMEToCommitCompositionResult::
1565 eReceivedAfterBrowserParentBlur
||
1566 mRequestIMEToCommitCompositionResults
[i
- 1] ==
1567 RequestIMEToCommitCompositionResult::eHandledSynchronously
) {
1568 --numberOfCompositionCommitRequestHandled
;
1569 if (!numberOfCompositionCommitRequestHandled
) {
1570 mRequestIMEToCommitCompositionResults
.RemoveElementsAt(0, i
- 1);
1578 void ContentCacheInParent::AppendEventMessageLog(nsACString
& aLog
) const {
1579 aLog
.AppendLiteral("Dispatched Event Message Log:\n");
1580 for (EventMessage message
: mDispatchedEventMessages
) {
1581 aLog
.AppendLiteral(" ");
1582 aLog
.Append(ToChar(message
));
1583 aLog
.AppendLiteral("\n");
1585 aLog
.AppendLiteral("\nReceived Event Message Log:\n");
1586 for (EventMessage message
: mReceivedEventMessages
) {
1587 aLog
.AppendLiteral(" ");
1588 aLog
.Append(ToChar(message
));
1589 aLog
.AppendLiteral("\n");
1591 aLog
.AppendLiteral("\nResult of RequestIMEToCommitComposition():\n");
1592 for (RequestIMEToCommitCompositionResult result
:
1593 mRequestIMEToCommitCompositionResults
) {
1594 aLog
.AppendLiteral(" ");
1595 aLog
.Append(ToReadableText(result
));
1596 aLog
.AppendLiteral("\n");
1598 aLog
.AppendLiteral("\n");
1601 #endif // #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
1603 /*****************************************************************************
1604 * mozilla::ContentCache::TextRectArray
1605 *****************************************************************************/
1607 LayoutDeviceIntRect
ContentCache::TextRectArray::GetRect(
1608 uint32_t aOffset
) const {
1609 LayoutDeviceIntRect rect
;
1610 if (InRange(aOffset
)) {
1611 rect
= mRects
[aOffset
- mStart
];
1616 LayoutDeviceIntRect
ContentCache::TextRectArray::GetUnionRect(
1617 uint32_t aOffset
, uint32_t aLength
) const {
1618 LayoutDeviceIntRect rect
;
1619 if (!InRange(aOffset
, aLength
)) {
1622 for (uint32_t i
= 0; i
< aLength
; i
++) {
1623 rect
= rect
.Union(mRects
[aOffset
- mStart
+ i
]);
1628 LayoutDeviceIntRect
ContentCache::TextRectArray::GetUnionRectAsFarAsPossible(
1629 uint32_t aOffset
, uint32_t aLength
, bool aRoundToExistingOffset
) const {
1630 LayoutDeviceIntRect rect
;
1632 (!aRoundToExistingOffset
&& !IsOverlappingWith(aOffset
, aLength
))) {
1635 uint32_t startOffset
= std::max(aOffset
, mStart
);
1636 if (aRoundToExistingOffset
&& startOffset
>= EndOffset()) {
1637 startOffset
= EndOffset() - 1;
1639 uint32_t endOffset
= std::min(aOffset
+ aLength
, EndOffset());
1640 if (aRoundToExistingOffset
&& endOffset
< mStart
+ 1) {
1641 endOffset
= mStart
+ 1;
1643 if (NS_WARN_IF(endOffset
< startOffset
)) {
1646 for (uint32_t i
= 0; i
< endOffset
- startOffset
; i
++) {
1647 rect
= rect
.Union(mRects
[startOffset
- mStart
+ i
]);
1652 } // namespace mozilla