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 /*****************************************************************************
37 * mozilla::ContentCache
38 *****************************************************************************/
40 LazyLogModule
sContentCacheLog("ContentCacheWidgets");
42 /*****************************************************************************
43 * mozilla::ContentCacheInChild
44 *****************************************************************************/
46 void ContentCacheInChild::Clear() {
47 MOZ_LOG(sContentCacheLog
, LogLevel::Info
, ("0x%p Clear()", this));
49 mCompositionStart
.reset();
53 mFirstCharRect
.SetEmpty();
55 mTextRectArray
.reset();
56 mLastCommitStringTextRectArray
.reset();
57 mEditorRect
.SetEmpty();
60 void ContentCacheInChild::OnCompositionEvent(
61 const WidgetCompositionEvent
& aCompositionEvent
) {
62 if (aCompositionEvent
.CausesDOMCompositionEndEvent()) {
63 RefPtr
<TextComposition
> composition
=
64 IMEStateManager::GetTextCompositionFor(aCompositionEvent
.mWidget
);
66 nsAutoString lastCommitString
;
67 if (aCompositionEvent
.mMessage
== eCompositionCommitAsIs
) {
68 lastCommitString
= composition
->CommitStringIfCommittedAsIs();
70 lastCommitString
= aCompositionEvent
.mData
;
72 // We don't need to store canceling information because this is required
73 // by undoing of last commit (Kakutei-Undo of Japanese IME).
74 if (!lastCommitString
.IsEmpty()) {
75 mLastCommit
= Some(OffsetAndData
<uint32_t>(
76 composition
->NativeOffsetOfStartComposition(), lastCommitString
));
78 sContentCacheLog
, LogLevel::Debug
,
79 ("0x%p OnCompositionEvent(), stored last composition string data "
80 "(aCompositionEvent={ mMessage=%s, mData=\"%s\"}, mLastCommit=%s)",
81 this, ToChar(aCompositionEvent
.mMessage
),
83 aCompositionEvent
.mData
,
84 PrintStringDetail::kMaxLengthForCompositionString
)
86 ToString(mLastCommit
).c_str()));
91 if (mLastCommit
.isSome()) {
93 sContentCacheLog
, LogLevel::Debug
,
94 ("0x%p OnCompositionEvent(), resetting the last composition string "
95 "data (aCompositionEvent={ mMessage=%s, mData=\"%s\"}, "
97 this, ToChar(aCompositionEvent
.mMessage
),
98 PrintStringDetail(aCompositionEvent
.mData
,
99 PrintStringDetail::kMaxLengthForCompositionString
)
101 ToString(mLastCommit
).c_str()));
106 bool ContentCacheInChild::CacheAll(nsIWidget
* aWidget
,
107 const IMENotification
* aNotification
) {
108 MOZ_LOG(sContentCacheLog
, LogLevel::Info
,
109 ("0x%p CacheAll(aWidget=0x%p, aNotification=%s)", this, aWidget
,
110 GetNotificationName(aNotification
)));
112 if (NS_WARN_IF(!CacheText(aWidget
, aNotification
)) ||
113 NS_WARN_IF(!CacheEditorRect(aWidget
, aNotification
))) {
119 bool ContentCacheInChild::CacheSelection(nsIWidget
* aWidget
,
120 const IMENotification
* aNotification
) {
121 MOZ_LOG(sContentCacheLog
, LogLevel::Info
,
122 ("0x%p CacheSelection(aWidget=0x%p, aNotification=%s)", this, aWidget
,
123 GetNotificationName(aNotification
)));
128 nsEventStatus status
= nsEventStatus_eIgnore
;
129 WidgetQueryContentEvent
querySelectedTextEvent(true, eQuerySelectedText
,
131 aWidget
->DispatchEvent(&querySelectedTextEvent
, status
);
132 if (NS_WARN_IF(querySelectedTextEvent
.DidNotFindSelection())) {
134 sContentCacheLog
, LogLevel::Error
,
135 ("0x%p CacheSelection(), FAILED, couldn't retrieve the selected text",
139 MOZ_ASSERT(querySelectedTextEvent
.mReply
->mOffsetAndData
.isSome());
140 if (querySelectedTextEvent
.mReply
->mReversed
) {
141 mSelection
.emplace(querySelectedTextEvent
.mReply
->EndOffset(),
142 querySelectedTextEvent
.mReply
->StartOffset(),
143 querySelectedTextEvent
.mReply
->WritingModeRef());
145 mSelection
.emplace(querySelectedTextEvent
.mReply
->StartOffset(),
146 querySelectedTextEvent
.mReply
->EndOffset(),
147 querySelectedTextEvent
.mReply
->WritingModeRef());
150 return CacheCaret(aWidget
, aNotification
) &&
151 CacheTextRects(aWidget
, aNotification
);
154 bool ContentCacheInChild::CacheCaret(nsIWidget
* aWidget
,
155 const IMENotification
* aNotification
) {
156 MOZ_LOG(sContentCacheLog
, LogLevel::Info
,
157 ("0x%p CacheCaret(aWidget=0x%p, aNotification=%s)", this, aWidget
,
158 GetNotificationName(aNotification
)));
162 if (NS_WARN_IF(mSelection
.isNothing())) {
166 // XXX Should be mSelection.mFocus?
167 uint32_t offset
= mSelection
->StartOffset();
169 nsEventStatus status
= nsEventStatus_eIgnore
;
170 WidgetQueryContentEvent
queryCaretRectEvet(true, eQueryCaretRect
, aWidget
);
171 queryCaretRectEvet
.InitForQueryCaretRect(offset
);
172 aWidget
->DispatchEvent(&queryCaretRectEvet
, status
);
173 if (NS_WARN_IF(queryCaretRectEvet
.Failed())) {
174 MOZ_LOG(sContentCacheLog
, LogLevel::Error
,
175 ("0x%p CacheCaret(), FAILED, couldn't retrieve the caret rect at "
180 mCaret
.emplace(offset
, queryCaretRectEvet
.mReply
->mRect
);
181 MOZ_LOG(sContentCacheLog
, LogLevel::Info
,
182 ("0x%p CacheCaret(), Succeeded, "
183 "mSelection=%s, mCaret=%s",
184 this, ToString(mSelection
).c_str(), ToString(mCaret
).c_str()));
188 bool ContentCacheInChild::CacheEditorRect(
189 nsIWidget
* aWidget
, const IMENotification
* aNotification
) {
190 MOZ_LOG(sContentCacheLog
, LogLevel::Info
,
191 ("0x%p CacheEditorRect(aWidget=0x%p, aNotification=%s)", this,
192 aWidget
, GetNotificationName(aNotification
)));
194 nsEventStatus status
= nsEventStatus_eIgnore
;
195 WidgetQueryContentEvent
queryEditorRectEvent(true, eQueryEditorRect
, aWidget
);
196 aWidget
->DispatchEvent(&queryEditorRectEvent
, status
);
197 if (NS_WARN_IF(queryEditorRectEvent
.Failed())) {
198 MOZ_LOG(sContentCacheLog
, LogLevel::Error
,
199 ("0x%p CacheEditorRect(), FAILED, "
200 "couldn't retrieve the editor rect",
204 mEditorRect
= queryEditorRectEvent
.mReply
->mRect
;
205 MOZ_LOG(sContentCacheLog
, LogLevel::Info
,
206 ("0x%p CacheEditorRect(), Succeeded, "
208 this, ToString(mEditorRect
).c_str()));
212 bool ContentCacheInChild::CacheText(nsIWidget
* aWidget
,
213 const IMENotification
* aNotification
) {
214 MOZ_LOG(sContentCacheLog
, LogLevel::Info
,
215 ("0x%p CacheText(aWidget=0x%p, aNotification=%s)", this, aWidget
,
216 GetNotificationName(aNotification
)));
218 nsEventStatus status
= nsEventStatus_eIgnore
;
219 WidgetQueryContentEvent
queryTextContentEvent(true, eQueryTextContent
,
221 queryTextContentEvent
.InitForQueryTextContent(0, UINT32_MAX
);
222 aWidget
->DispatchEvent(&queryTextContentEvent
, status
);
223 if (NS_WARN_IF(queryTextContentEvent
.Failed())) {
224 MOZ_LOG(sContentCacheLog
, LogLevel::Error
,
225 ("0x%p CacheText(), FAILED, couldn't retrieve whole text", this));
229 mText
= queryTextContentEvent
.mReply
->DataRef();
231 sContentCacheLog
, LogLevel::Info
,
232 ("0x%p CacheText(), Succeeded, mText.Length()=%u", this, mText
.Length()));
234 // Forget last commit range if string in the range is different from the
235 // last commit string.
236 if (mLastCommit
.isSome() &&
237 nsDependentSubstring(mText
, mLastCommit
->StartOffset(),
238 mLastCommit
->Length()) != mLastCommit
->DataRef()) {
239 MOZ_LOG(sContentCacheLog
, LogLevel::Debug
,
240 ("0x%p CacheText(), resetting the last composition string data "
241 "(mLastCommit=%s, current string=\"%s\")",
242 this, ToString(mLastCommit
).c_str(),
244 nsDependentSubstring(mText
, mLastCommit
->StartOffset(),
245 mLastCommit
->Length()),
246 PrintStringDetail::kMaxLengthForCompositionString
)
251 return CacheSelection(aWidget
, aNotification
);
254 bool ContentCacheInChild::QueryCharRect(nsIWidget
* aWidget
, uint32_t aOffset
,
255 LayoutDeviceIntRect
& aCharRect
) const {
256 aCharRect
.SetEmpty();
258 nsEventStatus status
= nsEventStatus_eIgnore
;
259 WidgetQueryContentEvent
queryTextRectEvent(true, eQueryTextRect
, aWidget
);
260 queryTextRectEvent
.InitForQueryTextRect(aOffset
, 1);
261 aWidget
->DispatchEvent(&queryTextRectEvent
, status
);
262 if (NS_WARN_IF(queryTextRectEvent
.Failed())) {
265 aCharRect
= queryTextRectEvent
.mReply
->mRect
;
267 // Guarantee the rect is not empty.
268 if (NS_WARN_IF(!aCharRect
.Height())) {
269 aCharRect
.SetHeight(1);
271 if (NS_WARN_IF(!aCharRect
.Width())) {
272 aCharRect
.SetWidth(1);
277 bool ContentCacheInChild::QueryCharRectArray(nsIWidget
* aWidget
,
278 uint32_t aOffset
, uint32_t aLength
,
279 RectArray
& aCharRectArray
) const {
280 nsEventStatus status
= nsEventStatus_eIgnore
;
281 WidgetQueryContentEvent
queryTextRectsEvent(true, eQueryTextRectArray
,
283 queryTextRectsEvent
.InitForQueryTextRectArray(aOffset
, aLength
);
284 aWidget
->DispatchEvent(&queryTextRectsEvent
, status
);
285 if (NS_WARN_IF(queryTextRectsEvent
.Failed())) {
286 aCharRectArray
.Clear();
289 aCharRectArray
= std::move(queryTextRectsEvent
.mReply
->mRectArray
);
293 bool ContentCacheInChild::CacheTextRects(nsIWidget
* aWidget
,
294 const IMENotification
* aNotification
) {
296 sContentCacheLog
, LogLevel::Info
,
297 ("0x%p CacheTextRects(aWidget=0x%p, aNotification=%s), mCaret=%s", this,
298 aWidget
, GetNotificationName(aNotification
), ToString(mCaret
).c_str()));
300 mCompositionStart
.reset();
301 mTextRectArray
.reset();
302 mLastCommitStringTextRectArray
.reset();
303 mFirstCharRect
.SetEmpty();
305 if (NS_WARN_IF(mSelection
.isNothing())) {
309 mSelection
->ClearRects();
311 // Retrieve text rects in composition string if there is.
312 RefPtr
<TextComposition
> textComposition
=
313 IMEStateManager::GetTextCompositionFor(aWidget
);
314 if (textComposition
) {
315 // mCompositionStart may be updated by some composition event handlers.
316 // So, let's update it with the latest information.
317 mCompositionStart
= Some(textComposition
->NativeOffsetOfStartComposition());
318 // Note that TextComposition::String() may not be modified here because
319 // it's modified after all edit action listeners are performed but this
320 // is called while some of them are performed.
321 // FYI: For supporting IME which commits composition and restart new
322 // composition immediately, we should cache next character of current
324 uint32_t length
= textComposition
->LastData().Length() + 1;
325 mTextRectArray
.emplace(mCompositionStart
.value());
326 if (NS_WARN_IF(!QueryCharRectArray(aWidget
, mTextRectArray
->mStart
, length
,
327 mTextRectArray
->mRects
))) {
328 MOZ_LOG(sContentCacheLog
, LogLevel::Error
,
329 ("0x%p CacheTextRects(), FAILED, "
330 "couldn't retrieve text rect array of the composition string",
332 mTextRectArray
.reset();
336 if (mTextRectArray
.isSome() &&
337 mTextRectArray
->IsOffsetInRange(mSelection
->mAnchor
) &&
338 (!mSelection
->mAnchor
||
339 mTextRectArray
->IsOffsetInRange(mSelection
->mAnchor
- 1))) {
340 mSelection
->mAnchorCharRects
[eNextCharRect
] =
341 mTextRectArray
->GetRect(mSelection
->mAnchor
);
342 if (mSelection
->mAnchor
) {
343 mSelection
->mAnchorCharRects
[ePrevCharRect
] =
344 mTextRectArray
->GetRect(mSelection
->mAnchor
- 1);
348 uint32_t startOffset
= mSelection
->mAnchor
? mSelection
->mAnchor
- 1 : 0;
349 uint32_t length
= mSelection
->mAnchor
? 2 : 1;
350 if (NS_WARN_IF(!QueryCharRectArray(aWidget
, startOffset
, length
, rects
))) {
352 sContentCacheLog
, LogLevel::Error
,
353 ("0x%p CacheTextRects(), FAILED, "
354 "couldn't retrieve text rect array around the selection anchor (%u)",
355 this, mSelection
->mAnchor
));
356 MOZ_ASSERT(mSelection
->mAnchorCharRects
[ePrevCharRect
].IsEmpty());
357 MOZ_ASSERT(mSelection
->mAnchorCharRects
[eNextCharRect
].IsEmpty());
359 if (rects
.Length() > 1) {
360 mSelection
->mAnchorCharRects
[ePrevCharRect
] = rects
[0];
361 mSelection
->mAnchorCharRects
[eNextCharRect
] = rects
[1];
362 } else if (rects
.Length()) {
363 mSelection
->mAnchorCharRects
[eNextCharRect
] = rects
[0];
364 MOZ_ASSERT(mSelection
->mAnchorCharRects
[ePrevCharRect
].IsEmpty());
369 if (mSelection
->Collapsed()) {
370 mSelection
->mFocusCharRects
[0] = mSelection
->mAnchorCharRects
[0];
371 mSelection
->mFocusCharRects
[1] = mSelection
->mAnchorCharRects
[1];
372 } else if (mTextRectArray
.isSome() &&
373 mTextRectArray
->IsOffsetInRange(mSelection
->mFocus
) &&
374 (!mSelection
->mFocus
||
375 mTextRectArray
->IsOffsetInRange(mSelection
->mFocus
- 1))) {
376 mSelection
->mFocusCharRects
[eNextCharRect
] =
377 mTextRectArray
->GetRect(mSelection
->mFocus
);
378 if (mSelection
->mFocus
) {
379 mSelection
->mFocusCharRects
[ePrevCharRect
] =
380 mTextRectArray
->GetRect(mSelection
->mFocus
- 1);
384 uint32_t startOffset
= mSelection
->mFocus
? mSelection
->mFocus
- 1 : 0;
385 uint32_t length
= mSelection
->mFocus
? 2 : 1;
386 if (NS_WARN_IF(!QueryCharRectArray(aWidget
, startOffset
, length
, rects
))) {
388 sContentCacheLog
, LogLevel::Error
,
389 ("0x%p CacheTextRects(), FAILED, "
390 "couldn't retrieve text rect array around the selection focus (%u)",
391 this, mSelection
->mFocus
));
392 MOZ_ASSERT(mSelection
->mFocusCharRects
[ePrevCharRect
].IsEmpty());
393 MOZ_ASSERT(mSelection
->mFocusCharRects
[eNextCharRect
].IsEmpty());
395 if (rects
.Length() > 1) {
396 mSelection
->mFocusCharRects
[ePrevCharRect
] = rects
[0];
397 mSelection
->mFocusCharRects
[eNextCharRect
] = rects
[1];
398 } else if (rects
.Length()) {
399 mSelection
->mFocusCharRects
[eNextCharRect
] = rects
[0];
400 MOZ_ASSERT(mSelection
->mFocusCharRects
[ePrevCharRect
].IsEmpty());
405 if (!mSelection
->Collapsed()) {
406 nsEventStatus status
= nsEventStatus_eIgnore
;
407 WidgetQueryContentEvent
queryTextRectEvent(true, eQueryTextRect
, aWidget
);
408 queryTextRectEvent
.InitForQueryTextRect(mSelection
->StartOffset(),
409 mSelection
->Length());
410 aWidget
->DispatchEvent(&queryTextRectEvent
, status
);
411 if (NS_WARN_IF(queryTextRectEvent
.Failed())) {
412 MOZ_LOG(sContentCacheLog
, LogLevel::Error
,
413 ("0x%p CacheTextRects(), FAILED, "
414 "couldn't retrieve text rect of whole selected text",
417 mSelection
->mRect
= queryTextRectEvent
.mReply
->mRect
;
421 if (!mSelection
->mFocus
) {
422 mFirstCharRect
= mSelection
->mFocusCharRects
[eNextCharRect
];
423 } else if (mSelection
->mFocus
== 1) {
424 mFirstCharRect
= mSelection
->mFocusCharRects
[ePrevCharRect
];
425 } else if (!mSelection
->mAnchor
) {
426 mFirstCharRect
= mSelection
->mAnchorCharRects
[eNextCharRect
];
427 } else if (mSelection
->mAnchor
== 1) {
428 mFirstCharRect
= mSelection
->mFocusCharRects
[ePrevCharRect
];
429 } else if (mTextRectArray
.isSome() && mTextRectArray
->IsOffsetInRange(0)) {
430 mFirstCharRect
= mTextRectArray
->GetRect(0);
432 LayoutDeviceIntRect charRect
;
433 if (NS_WARN_IF(!QueryCharRect(aWidget
, 0, charRect
))) {
434 MOZ_LOG(sContentCacheLog
, LogLevel::Error
,
435 ("0x%p CacheTextRects(), FAILED, "
436 "couldn't retrieve first char rect",
439 mFirstCharRect
= charRect
;
443 if (mLastCommit
.isSome()) {
444 mLastCommitStringTextRectArray
.emplace(mLastCommit
->StartOffset());
445 if (mLastCommit
->Length() == 1) {
446 MOZ_ASSERT(mSelection
->Collapsed());
447 MOZ_ASSERT(mSelection
->mAnchor
- 1 == mLastCommit
->StartOffset());
448 mLastCommitStringTextRectArray
->mRects
.AppendElement(
449 mSelection
->mAnchorCharRects
[ePrevCharRect
]);
450 } else if (NS_WARN_IF(!QueryCharRectArray(
451 aWidget
, mLastCommit
->StartOffset(), mLastCommit
->Length(),
452 mLastCommitStringTextRectArray
->mRects
))) {
453 MOZ_LOG(sContentCacheLog
, LogLevel::Error
,
454 ("0x%p CacheTextRects(), FAILED, "
455 "couldn't retrieve text rect array of the last commit string",
457 mLastCommitStringTextRectArray
.reset();
460 MOZ_ASSERT((mLastCommitStringTextRectArray
.isSome()
461 ? mLastCommitStringTextRectArray
->mRects
.Length()
462 : 0) == (mLastCommit
.isSome() ? mLastCommit
->Length() : 0));
465 MOZ_LOG(sContentCacheLog
, LogLevel::Info
,
466 ("0x%p CacheTextRects(), Succeeded, "
467 "mText.Length()=%x, mTextRectArray=%s, mSelection=%s, "
468 "mFirstCharRect=%s, mLastCommitStringTextRectArray=%s",
469 this, mText
.Length(), ToString(mTextRectArray
).c_str(),
470 ToString(mSelection
).c_str(), ToString(mFirstCharRect
).c_str(),
471 ToString(mLastCommitStringTextRectArray
).c_str()));
475 void ContentCacheInChild::SetSelection(nsIWidget
* aWidget
,
476 uint32_t aStartOffset
, uint32_t aLength
,
478 const WritingMode
& aWritingMode
) {
479 MOZ_LOG(sContentCacheLog
, LogLevel::Info
,
480 ("0x%p SetSelection(aStartOffset=%u, "
481 "aLength=%u, aReversed=%s, aWritingMode=%s), mText.Length()=%u",
482 this, aStartOffset
, aLength
, GetBoolName(aReversed
),
483 ToString(aWritingMode
).c_str(), mText
.Length()));
485 mSelection
= Some(Selection(
486 !aReversed
? aStartOffset
: aStartOffset
+ aLength
,
487 !aReversed
? aStartOffset
+ aLength
: aStartOffset
, aWritingMode
));
489 if (mLastCommit
.isSome()) {
490 // Forget last commit string range if selection is not collapsed
491 // at end of the last commit string.
492 if (!mSelection
->Collapsed() ||
493 mSelection
->mAnchor
!= mLastCommit
->EndOffset()) {
495 sContentCacheLog
, LogLevel::Debug
,
496 ("0x%p SetSelection(), forgetting last commit composition data "
497 "(mSelection=%s, mLastCommit=%s)",
498 this, ToString(mSelection
).c_str(), ToString(mLastCommit
).c_str()));
503 if (NS_WARN_IF(!CacheCaret(aWidget
))) {
506 Unused
<< NS_WARN_IF(!CacheTextRects(aWidget
));
509 /*****************************************************************************
510 * mozilla::ContentCacheInParent
511 *****************************************************************************/
513 ContentCacheInParent::ContentCacheInParent(BrowserParent
& aBrowserParent
)
515 mBrowserParent(aBrowserParent
),
516 mCommitStringByRequest(nullptr),
517 mPendingEventsNeedingAck(0),
518 mPendingCommitLength(0),
519 mPendingCompositionCount(0),
520 mPendingCommitCount(0),
521 mWidgetHasComposition(false),
522 mIsChildIgnoringCompositionEvents(false) {}
524 void ContentCacheInParent::AssignContent(const ContentCache
& aOther
,
526 const IMENotification
* aNotification
) {
527 mText
= aOther
.mText
;
528 mSelection
= aOther
.mSelection
;
529 mFirstCharRect
= aOther
.mFirstCharRect
;
530 mCaret
= aOther
.mCaret
;
531 mTextRectArray
= aOther
.mTextRectArray
;
532 mLastCommitStringTextRectArray
= aOther
.mLastCommitStringTextRectArray
;
533 mEditorRect
= aOther
.mEditorRect
;
535 // Only when there is one composition, the TextComposition instance in this
536 // process is managing the composition in the remote process. Therefore,
537 // we shouldn't update composition start offset of TextComposition with
538 // old composition which is still being handled by the child process.
539 if (mWidgetHasComposition
&& mPendingCompositionCount
== 1 &&
540 mCompositionStart
.isSome()) {
541 IMEStateManager::MaybeStartOffsetUpdatedInChild(aWidget
,
542 mCompositionStart
.value());
545 // When this instance allows to query content relative to composition string,
546 // we should modify mCompositionStart with the latest information in the
547 // remote process because now we have the information around the composition
549 mCompositionStartInChild
= aOther
.mCompositionStart
;
550 if (mWidgetHasComposition
|| mPendingCommitCount
) {
551 if (mCompositionStartInChild
.isSome()) {
552 if (mCompositionStart
.valueOr(UINT32_MAX
) !=
553 mCompositionStartInChild
.value()) {
554 mCompositionStart
= mCompositionStartInChild
;
555 mPendingCommitLength
= 0;
557 } else if (mCompositionStart
.isSome() && mSelection
.isSome() &&
558 mCompositionStart
.value() != mSelection
->StartOffset()) {
559 mCompositionStart
= Some(mSelection
->StartOffset());
560 mPendingCommitLength
= 0;
564 MOZ_LOG(sContentCacheLog
, LogLevel::Info
,
565 ("0x%p AssignContent(aNotification=%s), "
566 "Succeeded, mText.Length()=%u, mSelection=%s, mFirstCharRect=%s, "
567 "mCaret=%s, mTextRectArray=%s, mWidgetHasComposition=%s, "
568 "mPendingCompositionCount=%u, mCompositionStart=%s, "
569 "mPendingCommitLength=%u, mEditorRect=%s, "
570 "mLastCommitStringTextRectArray=%s",
571 this, GetNotificationName(aNotification
), mText
.Length(),
572 ToString(mSelection
).c_str(), ToString(mFirstCharRect
).c_str(),
573 ToString(mCaret
).c_str(), ToString(mTextRectArray
).c_str(),
574 GetBoolName(mWidgetHasComposition
), mPendingCompositionCount
,
575 ToString(mCompositionStart
).c_str(), mPendingCommitLength
,
576 ToString(mEditorRect
).c_str(),
577 ToString(mLastCommitStringTextRectArray
).c_str()));
580 bool ContentCacheInParent::HandleQueryContentEvent(
581 WidgetQueryContentEvent
& aEvent
, nsIWidget
* aWidget
) const {
584 // ContentCache doesn't store offset of its start with XP linebreaks.
585 // So, we don't support to query contents relative to composition start
586 // offset with XP linebreaks.
587 if (NS_WARN_IF(!aEvent
.mUseNativeLineBreak
)) {
588 MOZ_LOG(sContentCacheLog
, LogLevel::Error
,
589 ("0x%p HandleQueryContentEvent(), FAILED due to query with XP "
595 if (NS_WARN_IF(!aEvent
.mInput
.IsValidOffset())) {
597 sContentCacheLog
, LogLevel::Error
,
598 ("0x%p HandleQueryContentEvent(), FAILED due to invalid offset", this));
602 if (NS_WARN_IF(!aEvent
.mInput
.IsValidEventMessage(aEvent
.mMessage
))) {
604 sContentCacheLog
, LogLevel::Error
,
605 ("0x%p HandleQueryContentEvent(), FAILED due to invalid event message",
610 bool isRelativeToInsertionPoint
= aEvent
.mInput
.mRelativeToInsertionPoint
;
611 if (isRelativeToInsertionPoint
) {
612 MOZ_LOG(sContentCacheLog
, LogLevel::Debug
,
613 ("0x%p HandleQueryContentEvent(), "
614 "making offset absolute... aEvent={ mMessage=%s, mInput={ "
615 "mOffset=%" PRId64
", mLength=%" PRIu32
" } }, "
616 "mWidgetHasComposition=%s, mPendingCommitCount=%" PRIu8
617 ", mCompositionStart=%" PRIu32
", "
618 "mPendingCommitLength=%" PRIu32
", mSelection=%s",
619 this, ToChar(aEvent
.mMessage
), aEvent
.mInput
.mOffset
,
620 aEvent
.mInput
.mLength
, GetBoolName(mWidgetHasComposition
),
621 mPendingCommitCount
, mCompositionStart
.valueOr(UINT32_MAX
),
622 mPendingCommitLength
, ToString(mSelection
).c_str()));
623 if (mWidgetHasComposition
|| mPendingCommitCount
) {
624 if (NS_WARN_IF(mCompositionStart
.isNothing()) ||
625 NS_WARN_IF(!aEvent
.mInput
.MakeOffsetAbsolute(
626 mCompositionStart
.value() + mPendingCommitLength
))) {
628 sContentCacheLog
, LogLevel::Error
,
629 ("0x%p HandleQueryContentEvent(), FAILED due to "
630 "aEvent.mInput.MakeOffsetAbsolute(mCompositionStart + "
631 "mPendingCommitLength) failure, "
632 "mCompositionStart=%" PRIu32
", mPendingCommitLength=%" PRIu32
", "
633 "aEvent={ mMessage=%s, mInput={ mOffset=%" PRId64
634 ", mLength=%" PRIu32
" } }",
635 this, mCompositionStart
.valueOr(UINT32_MAX
), mPendingCommitLength
,
636 ToChar(aEvent
.mMessage
), aEvent
.mInput
.mOffset
,
637 aEvent
.mInput
.mLength
));
640 } else if (NS_WARN_IF(mSelection
.isNothing())) {
641 MOZ_LOG(sContentCacheLog
, LogLevel::Error
,
642 ("0x%p HandleQueryContentEvent(), FAILED due to mSelection is "
646 } else if (NS_WARN_IF(!aEvent
.mInput
.MakeOffsetAbsolute(
647 mSelection
->StartOffset() + mPendingCommitLength
))) {
648 MOZ_LOG(sContentCacheLog
, LogLevel::Error
,
649 ("0x%p HandleQueryContentEvent(), FAILED due to "
650 "aEvent.mInput.MakeOffsetAbsolute(mSelection.StartOffset() + "
651 "mPendingCommitLength) failure, mSelection=%s, "
652 "mPendingCommitLength=%" PRIu32
", aEvent={ mMessage=%s, "
653 "mInput={ mOffset=%" PRId64
", mLength=%" PRIu32
" } }",
654 this, ToString(mSelection
).c_str(), mPendingCommitLength
,
655 ToChar(aEvent
.mMessage
), aEvent
.mInput
.mOffset
,
656 aEvent
.mInput
.mLength
));
661 switch (aEvent
.mMessage
) {
662 case eQuerySelectedText
:
663 MOZ_LOG(sContentCacheLog
, LogLevel::Info
,
664 ("0x%p HandleQueryContentEvent(aEvent={ "
665 "mMessage=eQuerySelectedText }, aWidget=0x%p)",
667 if (NS_WARN_IF(!IsSelectionValid())) {
668 // If content cache hasn't been initialized properly, make the query
670 MOZ_LOG(sContentCacheLog
, LogLevel::Error
,
671 ("0x%p HandleQueryContentEvent(), FAILED because mSelection is "
676 if (!mSelection
->Collapsed() &&
677 NS_WARN_IF(mSelection
->EndOffset() > mText
.Length())) {
678 MOZ_LOG(sContentCacheLog
, LogLevel::Error
,
679 ("0x%p HandleQueryContentEvent(), FAILED because "
680 "mSelection->EndOffset()=%u is larger than mText.Length()=%u",
681 this, mSelection
->EndOffset(), mText
.Length()));
684 aEvent
.EmplaceReply();
685 aEvent
.mReply
->mFocusedWidget
= aWidget
;
686 aEvent
.mReply
->mOffsetAndData
.emplace(
687 mSelection
->StartOffset(),
688 Substring(mText
, mSelection
->StartOffset(), mSelection
->Length()),
689 OffsetAndDataFor::SelectedString
);
690 aEvent
.mReply
->mReversed
= mSelection
->Reversed();
691 aEvent
.mReply
->mHasSelection
= true;
692 aEvent
.mReply
->mWritingMode
= mSelection
->mWritingMode
;
693 MOZ_LOG(sContentCacheLog
, LogLevel::Info
,
694 ("0x%p HandleQueryContentEvent(), "
695 "Succeeded, aEvent={ mMessage=eQuerySelectedText, mReply=%s }",
696 this, ToString(aEvent
.mReply
).c_str()));
698 case eQueryTextContent
: {
699 MOZ_LOG(sContentCacheLog
, LogLevel::Info
,
700 ("0x%p HandleQueryContentEvent("
701 "aEvent={ mMessage=eQueryTextContent, mInput={ mOffset=%" PRId64
702 ", mLength=%u } }, aWidget=0x%p), mText.Length()=%u",
703 this, aEvent
.mInput
.mOffset
, aEvent
.mInput
.mLength
, aWidget
,
705 uint32_t inputOffset
= aEvent
.mInput
.mOffset
;
706 uint32_t inputEndOffset
=
707 std::min(aEvent
.mInput
.EndOffset(), mText
.Length());
708 if (NS_WARN_IF(inputEndOffset
< inputOffset
)) {
709 MOZ_LOG(sContentCacheLog
, LogLevel::Error
,
710 ("0x%p HandleQueryContentEvent(), FAILED because "
711 "inputOffset=%u is larger than inputEndOffset=%u",
712 this, inputOffset
, inputEndOffset
));
715 aEvent
.EmplaceReply();
716 aEvent
.mReply
->mFocusedWidget
= aWidget
;
717 aEvent
.mReply
->mOffsetAndData
.emplace(
719 Substring(mText
, inputOffset
, inputEndOffset
- inputOffset
),
720 OffsetAndDataFor::EditorString
);
721 // TODO: Support font ranges
722 MOZ_LOG(sContentCacheLog
, LogLevel::Info
,
723 ("0x%p HandleQueryContentEvent(), Succeeded, aEvent={ "
724 "mMessage=eQueryTextContent, mReply=%s }",
725 this, ToString(aEvent
.mReply
).c_str()));
728 case eQueryTextRect
: {
729 MOZ_LOG(sContentCacheLog
, LogLevel::Info
,
730 ("0x%p HandleQueryContentEvent("
731 "aEvent={ mMessage=eQueryTextRect, mInput={ mOffset=%" PRId64
732 ", mLength=%u } }, aWidget=0x%p), mText.Length()=%u",
733 this, aEvent
.mInput
.mOffset
, aEvent
.mInput
.mLength
, aWidget
,
735 if (NS_WARN_IF(!IsSelectionValid())) {
736 // If content cache hasn't been initialized properly, make the query
738 MOZ_LOG(sContentCacheLog
, LogLevel::Error
,
739 ("0x%p HandleQueryContentEvent(), FAILED because mSelection is "
744 // Note that if the query is relative to insertion point, the query was
745 // probably requested by native IME. In such case, we should return
746 // non-empty rect since returning failure causes IME showing its window
748 LayoutDeviceIntRect textRect
;
749 if (aEvent
.mInput
.mLength
) {
751 !GetUnionTextRects(aEvent
.mInput
.mOffset
, aEvent
.mInput
.mLength
,
752 isRelativeToInsertionPoint
, textRect
))) {
753 // XXX We don't have cache for this request.
754 MOZ_LOG(sContentCacheLog
, LogLevel::Error
,
755 ("0x%p HandleQueryContentEvent(), FAILED to get union rect",
760 // If the length is 0, we should return caret rect instead.
761 if (NS_WARN_IF(!GetCaretRect(aEvent
.mInput
.mOffset
,
762 isRelativeToInsertionPoint
, textRect
))) {
763 MOZ_LOG(sContentCacheLog
, LogLevel::Error
,
764 ("0x%p HandleQueryContentEvent(), FAILED to get caret rect",
769 aEvent
.EmplaceReply();
770 aEvent
.mReply
->mFocusedWidget
= aWidget
;
771 aEvent
.mReply
->mRect
= textRect
;
772 aEvent
.mReply
->mOffsetAndData
.emplace(
773 aEvent
.mInput
.mOffset
,
774 aEvent
.mInput
.mOffset
< mText
.Length()
775 ? static_cast<const nsAString
&>(
776 Substring(mText
, aEvent
.mInput
.mOffset
,
777 mText
.Length() >= aEvent
.mInput
.EndOffset()
778 ? aEvent
.mInput
.mLength
780 : static_cast<const nsAString
&>(EmptyString()),
781 OffsetAndDataFor::EditorString
);
782 // XXX This may be wrong if storing range isn't in the selection range.
783 aEvent
.mReply
->mWritingMode
= mSelection
->mWritingMode
;
784 MOZ_LOG(sContentCacheLog
, LogLevel::Info
,
785 ("0x%p HandleQueryContentEvent(), Succeeded, aEvent={ "
786 "mMessage=eQueryTextRect mReply=%s }",
787 this, ToString(aEvent
.mReply
).c_str()));
790 case eQueryCaretRect
: {
792 sContentCacheLog
, LogLevel::Info
,
793 ("0x%p HandleQueryContentEvent(aEvent={ mMessage=eQueryCaretRect, "
794 "mInput={ mOffset=%" PRId64
" } }, aWidget=0x%p), mText.Length()=%u",
795 this, aEvent
.mInput
.mOffset
, aWidget
, mText
.Length()));
796 if (NS_WARN_IF(!IsSelectionValid())) {
797 // If content cache hasn't been initialized properly, make the query
799 MOZ_LOG(sContentCacheLog
, LogLevel::Error
,
800 ("0x%p HandleQueryContentEvent(), FAILED because mSelection is "
805 // Note that if the query is relative to insertion point, the query was
806 // probably requested by native IME. In such case, we should return
807 // non-empty rect since returning failure causes IME showing its window
809 LayoutDeviceIntRect caretRect
;
810 if (NS_WARN_IF(!GetCaretRect(aEvent
.mInput
.mOffset
,
811 isRelativeToInsertionPoint
, caretRect
))) {
813 sContentCacheLog
, LogLevel::Error
,
814 ("0x%p HandleQueryContentEvent(),FAILED to get caret rect", this));
817 aEvent
.EmplaceReply();
818 aEvent
.mReply
->mFocusedWidget
= aWidget
;
819 aEvent
.mReply
->mRect
= caretRect
;
820 aEvent
.mReply
->mOffsetAndData
.emplace(aEvent
.mInput
.mOffset
,
822 OffsetAndDataFor::SelectedString
);
823 // TODO: Set mWritingMode here
824 MOZ_LOG(sContentCacheLog
, LogLevel::Info
,
825 ("0x%p HandleQueryContentEvent(), Succeeded, aEvent={ "
826 "mMessage=eQueryCaretRect, mReply=%s }",
827 this, ToString(aEvent
.mReply
).c_str()));
830 case eQueryEditorRect
:
831 MOZ_LOG(sContentCacheLog
, LogLevel::Info
,
832 ("0x%p HandleQueryContentEvent(aEvent={ "
833 "mMessage=eQueryEditorRect }, aWidget=0x%p)",
835 aEvent
.EmplaceReply();
836 aEvent
.mReply
->mFocusedWidget
= aWidget
;
837 aEvent
.mReply
->mRect
= mEditorRect
;
838 MOZ_LOG(sContentCacheLog
, LogLevel::Info
,
839 ("0x%p HandleQueryContentEvent(), Succeeded, aEvent={ "
840 "mMessage=eQueryEditorRect, mReply=%s }",
841 this, ToString(aEvent
.mReply
).c_str()));
844 aEvent
.EmplaceReply();
845 aEvent
.mReply
->mFocusedWidget
= aWidget
;
846 if (NS_WARN_IF(aEvent
.Failed())) {
848 sContentCacheLog
, LogLevel::Error
,
849 ("0x%p HandleQueryContentEvent(), FAILED due to not set enough "
850 "data, aEvent={ mMessage=%s, mReply=%s }",
851 this, ToChar(aEvent
.mMessage
), ToString(aEvent
.mReply
).c_str()));
854 MOZ_LOG(sContentCacheLog
, LogLevel::Info
,
855 ("0x%p HandleQueryContentEvent(), Succeeded, aEvent={ "
856 "mMessage=%s, mReply=%s }",
857 this, ToChar(aEvent
.mMessage
), ToString(aEvent
.mReply
).c_str()));
862 bool ContentCacheInParent::GetTextRect(uint32_t aOffset
,
863 bool aRoundToExistingOffset
,
864 LayoutDeviceIntRect
& aTextRect
) const {
866 sContentCacheLog
, LogLevel::Info
,
867 ("0x%p GetTextRect(aOffset=%u, aRoundToExistingOffset=%s), "
868 "mTextRectArray=%s, mSelection=%s, mLastCommitStringTextRectArray=%s",
869 this, aOffset
, GetBoolName(aRoundToExistingOffset
),
870 ToString(mTextRectArray
).c_str(), ToString(mSelection
).c_str(),
871 ToString(mLastCommitStringTextRectArray
).c_str()));
874 NS_WARNING_ASSERTION(!mFirstCharRect
.IsEmpty(), "empty rect");
875 aTextRect
= mFirstCharRect
;
876 return !aTextRect
.IsEmpty();
878 if (mSelection
.isSome()) {
879 if (aOffset
== mSelection
->mAnchor
) {
880 NS_WARNING_ASSERTION(
881 !mSelection
->mAnchorCharRects
[eNextCharRect
].IsEmpty(), "empty rect");
882 aTextRect
= mSelection
->mAnchorCharRects
[eNextCharRect
];
883 return !aTextRect
.IsEmpty();
885 if (mSelection
->mAnchor
&& aOffset
== mSelection
->mAnchor
- 1) {
886 NS_WARNING_ASSERTION(
887 !mSelection
->mAnchorCharRects
[ePrevCharRect
].IsEmpty(), "empty rect");
888 aTextRect
= mSelection
->mAnchorCharRects
[ePrevCharRect
];
889 return !aTextRect
.IsEmpty();
891 if (aOffset
== mSelection
->mFocus
) {
892 NS_WARNING_ASSERTION(
893 !mSelection
->mFocusCharRects
[eNextCharRect
].IsEmpty(), "empty rect");
894 aTextRect
= mSelection
->mFocusCharRects
[eNextCharRect
];
895 return !aTextRect
.IsEmpty();
897 if (mSelection
->mFocus
&& aOffset
== mSelection
->mFocus
- 1) {
898 NS_WARNING_ASSERTION(
899 !mSelection
->mFocusCharRects
[ePrevCharRect
].IsEmpty(), "empty rect");
900 aTextRect
= mSelection
->mFocusCharRects
[ePrevCharRect
];
901 return !aTextRect
.IsEmpty();
905 if (mTextRectArray
.isSome() && mTextRectArray
->IsOffsetInRange(aOffset
)) {
906 aTextRect
= mTextRectArray
->GetRect(aOffset
);
907 return !aTextRect
.IsEmpty();
910 if (mLastCommitStringTextRectArray
.isSome() &&
911 mLastCommitStringTextRectArray
->IsOffsetInRange(aOffset
)) {
912 aTextRect
= mLastCommitStringTextRectArray
->GetRect(aOffset
);
913 return !aTextRect
.IsEmpty();
916 if (!aRoundToExistingOffset
) {
917 aTextRect
.SetEmpty();
921 if (mTextRectArray
.isNothing() || !mTextRectArray
->HasRects()) {
922 // If there are no rects in mTextRectArray, we should refer the start of
923 // the selection because IME must query a char rect around it if there is
925 aTextRect
= mSelection
->StartCharRect();
926 return !aTextRect
.IsEmpty();
929 // Although we may have mLastCommitStringTextRectArray here and it must have
930 // previous character rects at selection. However, we should stop using it
931 // because it's stored really short time after commiting a composition.
932 // So, multiple query may return different rect and it may cause flickerling
934 uint32_t offset
= aOffset
;
935 if (offset
< mTextRectArray
->StartOffset()) {
936 offset
= mTextRectArray
->StartOffset();
938 offset
= mTextRectArray
->EndOffset() - 1;
940 aTextRect
= mTextRectArray
->GetRect(offset
);
941 return !aTextRect
.IsEmpty();
944 bool ContentCacheInParent::GetUnionTextRects(
945 uint32_t aOffset
, uint32_t aLength
, bool aRoundToExistingOffset
,
946 LayoutDeviceIntRect
& aUnionTextRect
) const {
947 MOZ_LOG(sContentCacheLog
, LogLevel::Info
,
948 ("0x%p GetUnionTextRects(aOffset=%u, "
949 "aLength=%u, aRoundToExistingOffset=%s), mTextRectArray=%s, "
950 "mSelection=%s, mLastCommitStringTextRectArray=%s",
951 this, aOffset
, aLength
, GetBoolName(aRoundToExistingOffset
),
952 ToString(mTextRectArray
).c_str(), ToString(mSelection
).c_str(),
953 ToString(mLastCommitStringTextRectArray
).c_str()));
955 CheckedInt
<uint32_t> endOffset
= CheckedInt
<uint32_t>(aOffset
) + aLength
;
956 if (!endOffset
.isValid()) {
960 if (mSelection
.isSome() && !mSelection
->Collapsed() &&
961 aOffset
== mSelection
->StartOffset() && aLength
== mSelection
->Length()) {
962 NS_WARNING_ASSERTION(!mSelection
->mRect
.IsEmpty(), "empty rect");
963 aUnionTextRect
= mSelection
->mRect
;
964 return !aUnionTextRect
.IsEmpty();
969 NS_WARNING_ASSERTION(!mFirstCharRect
.IsEmpty(), "empty rect");
970 aUnionTextRect
= mFirstCharRect
;
971 return !aUnionTextRect
.IsEmpty();
973 if (mSelection
.isSome()) {
974 if (aOffset
== mSelection
->mAnchor
) {
975 NS_WARNING_ASSERTION(
976 !mSelection
->mAnchorCharRects
[eNextCharRect
].IsEmpty(),
978 aUnionTextRect
= mSelection
->mAnchorCharRects
[eNextCharRect
];
979 return !aUnionTextRect
.IsEmpty();
981 if (mSelection
->mAnchor
&& aOffset
== mSelection
->mAnchor
- 1) {
982 NS_WARNING_ASSERTION(
983 !mSelection
->mAnchorCharRects
[ePrevCharRect
].IsEmpty(),
985 aUnionTextRect
= mSelection
->mAnchorCharRects
[ePrevCharRect
];
986 return !aUnionTextRect
.IsEmpty();
988 if (aOffset
== mSelection
->mFocus
) {
989 NS_WARNING_ASSERTION(
990 !mSelection
->mFocusCharRects
[eNextCharRect
].IsEmpty(),
992 aUnionTextRect
= mSelection
->mFocusCharRects
[eNextCharRect
];
993 return !aUnionTextRect
.IsEmpty();
995 if (mSelection
->mFocus
&& aOffset
== mSelection
->mFocus
- 1) {
996 NS_WARNING_ASSERTION(
997 !mSelection
->mFocusCharRects
[ePrevCharRect
].IsEmpty(),
999 aUnionTextRect
= mSelection
->mFocusCharRects
[ePrevCharRect
];
1000 return !aUnionTextRect
.IsEmpty();
1005 // Even if some text rects are not cached of the queried range,
1006 // we should return union rect when the first character's rect is cached
1007 // since the first character rect is important and the others are not so
1010 if (!aOffset
&& mSelection
.isSome() && aOffset
!= mSelection
->mAnchor
&&
1011 aOffset
!= mSelection
->mFocus
&&
1012 (mTextRectArray
.isNothing() ||
1013 !mTextRectArray
->IsOffsetInRange(aOffset
)) &&
1014 (mLastCommitStringTextRectArray
.isNothing() ||
1015 !mLastCommitStringTextRectArray
->IsOffsetInRange(aOffset
))) {
1016 // The first character rect isn't cached.
1020 // Use mLastCommitStringTextRectArray only when it overlaps with aOffset
1021 // even if aROundToExistingOffset is true for avoiding flickerling IME UI.
1022 // See the last comment in GetTextRect() for the detail.
1023 if (mLastCommitStringTextRectArray
.isSome() &&
1024 mLastCommitStringTextRectArray
->IsOverlappingWith(aOffset
, aLength
)) {
1026 mLastCommitStringTextRectArray
->GetUnionRectAsFarAsPossible(
1027 aOffset
, aLength
, aRoundToExistingOffset
);
1029 aUnionTextRect
.SetEmpty();
1032 if (mTextRectArray
.isSome() &&
1033 ((aRoundToExistingOffset
&& mTextRectArray
->HasRects()) ||
1034 mTextRectArray
->IsOverlappingWith(aOffset
, aLength
))) {
1036 aUnionTextRect
.Union(mTextRectArray
->GetUnionRectAsFarAsPossible(
1037 aOffset
, aLength
, aRoundToExistingOffset
));
1041 aUnionTextRect
= aUnionTextRect
.Union(mFirstCharRect
);
1043 if (mSelection
.isSome()) {
1044 if (aOffset
<= mSelection
->mAnchor
&&
1045 mSelection
->mAnchor
< endOffset
.value()) {
1047 aUnionTextRect
.Union(mSelection
->mAnchorCharRects
[eNextCharRect
]);
1049 if (mSelection
->mAnchor
&& aOffset
<= mSelection
->mAnchor
- 1 &&
1050 mSelection
->mAnchor
- 1 < endOffset
.value()) {
1052 aUnionTextRect
.Union(mSelection
->mAnchorCharRects
[ePrevCharRect
]);
1054 if (aOffset
<= mSelection
->mFocus
&&
1055 mSelection
->mFocus
< endOffset
.value()) {
1057 aUnionTextRect
.Union(mSelection
->mFocusCharRects
[eNextCharRect
]);
1059 if (mSelection
->mFocus
&& aOffset
<= mSelection
->mFocus
- 1 &&
1060 mSelection
->mFocus
- 1 < endOffset
.value()) {
1062 aUnionTextRect
.Union(mSelection
->mFocusCharRects
[ePrevCharRect
]);
1066 return !aUnionTextRect
.IsEmpty();
1069 bool ContentCacheInParent::GetCaretRect(uint32_t aOffset
,
1070 bool aRoundToExistingOffset
,
1071 LayoutDeviceIntRect
& aCaretRect
) const {
1072 MOZ_LOG(sContentCacheLog
, LogLevel::Info
,
1073 ("0x%p GetCaretRect(aOffset=%u, aRoundToExistingOffset=%s), "
1074 "mCaret=%s, mTextRectArray=%s, mSelection=%s, mFirstCharRect=%s",
1075 this, aOffset
, GetBoolName(aRoundToExistingOffset
),
1076 ToString(mCaret
).c_str(), ToString(mTextRectArray
).c_str(),
1077 ToString(mSelection
).c_str(), ToString(mFirstCharRect
).c_str()));
1079 if (mCaret
.isSome() && mCaret
->mOffset
== aOffset
) {
1080 aCaretRect
= mCaret
->mRect
;
1084 // Guess caret rect from the text rect if it's stored.
1085 if (!GetTextRect(aOffset
, aRoundToExistingOffset
, aCaretRect
)) {
1086 // There might be previous character rect in the cache. If so, we can
1087 // guess the caret rect with it.
1089 !GetTextRect(aOffset
- 1, aRoundToExistingOffset
, aCaretRect
)) {
1090 aCaretRect
.SetEmpty();
1094 if (mSelection
.isSome() && mSelection
->mWritingMode
.IsVertical()) {
1095 aCaretRect
.MoveToY(aCaretRect
.YMost());
1097 // XXX bidi-unaware.
1098 aCaretRect
.MoveToX(aCaretRect
.XMost());
1102 // XXX This is not bidi aware because we don't cache each character's
1103 // direction. However, this is usually used by IME, so, assuming the
1104 // character is in LRT context must not cause any problem.
1105 if (mSelection
.isSome() && mSelection
->mWritingMode
.IsVertical()) {
1106 aCaretRect
.SetHeight(mCaret
.isSome() ? mCaret
->mRect
.Height() : 1);
1108 aCaretRect
.SetWidth(mCaret
.isSome() ? mCaret
->mRect
.Width() : 1);
1113 bool ContentCacheInParent::OnCompositionEvent(
1114 const WidgetCompositionEvent
& aEvent
) {
1116 sContentCacheLog
, LogLevel::Info
,
1117 ("0x%p OnCompositionEvent(aEvent={ "
1118 "mMessage=%s, mData=\"%s\" (Length()=%u), mRanges->Length()=%zu }), "
1119 "mPendingEventsNeedingAck=%u, mWidgetHasComposition=%s, "
1120 "mPendingCompositionCount=%" PRIu8
", mPendingCommitCount=%" PRIu8
", "
1121 "mIsChildIgnoringCompositionEvents=%s, mCommitStringByRequest=0x%p",
1122 this, ToChar(aEvent
.mMessage
),
1123 PrintStringDetail(aEvent
.mData
,
1124 PrintStringDetail::kMaxLengthForCompositionString
)
1126 aEvent
.mData
.Length(), aEvent
.mRanges
? aEvent
.mRanges
->Length() : 0,
1127 mPendingEventsNeedingAck
, GetBoolName(mWidgetHasComposition
),
1128 mPendingCompositionCount
, mPendingCommitCount
,
1129 GetBoolName(mIsChildIgnoringCompositionEvents
), mCommitStringByRequest
));
1131 #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
1132 mDispatchedEventMessages
.AppendElement(aEvent
.mMessage
);
1133 #endif // #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
1135 // We must be able to simulate the selection because
1136 // we might not receive selection updates in time
1137 if (!mWidgetHasComposition
) {
1138 if (mCompositionStartInChild
.isSome()) {
1139 // If there is pending composition in the remote process, let's use
1140 // its start offset temporarily because this stores a lot of information
1141 // around it and the user must look around there, so, showing some UI
1142 // around it must make sense.
1143 mCompositionStart
= mCompositionStartInChild
;
1146 Some(mSelection
.isSome() ? mSelection
->StartOffset() : 0);
1148 MOZ_ASSERT(aEvent
.mMessage
== eCompositionStart
);
1149 MOZ_RELEASE_ASSERT(mPendingCompositionCount
< UINT8_MAX
);
1150 mPendingCompositionCount
++;
1153 mWidgetHasComposition
= !aEvent
.CausesDOMCompositionEndEvent();
1155 if (!mWidgetHasComposition
) {
1156 // mCompositionStart will be reset when commit event is completely handled
1157 // in the remote process.
1158 if (mPendingCompositionCount
== 1) {
1159 mPendingCommitLength
= aEvent
.mData
.Length();
1161 mPendingCommitCount
++;
1162 } else if (aEvent
.mMessage
!= eCompositionStart
) {
1163 mCompositionString
= aEvent
.mData
;
1166 // During REQUEST_TO_COMMIT_COMPOSITION or REQUEST_TO_CANCEL_COMPOSITION,
1167 // widget usually sends a eCompositionChange and/or eCompositionCommit event
1168 // to finalize or clear the composition, respectively. In this time,
1169 // we need to intercept all composition events here and pass the commit
1170 // string for returning to the remote process as a result of
1171 // RequestIMEToCommitComposition(). Then, eCommitComposition event will
1172 // be dispatched with the committed string in the remote process internally.
1173 if (mCommitStringByRequest
) {
1174 if (aEvent
.mMessage
== eCompositionCommitAsIs
) {
1175 *mCommitStringByRequest
= mCompositionString
;
1177 MOZ_ASSERT(aEvent
.mMessage
== eCompositionChange
||
1178 aEvent
.mMessage
== eCompositionCommit
);
1179 *mCommitStringByRequest
= aEvent
.mData
;
1181 // We need to wait eCompositionCommitRequestHandled from the remote process
1182 // in this case. Therefore, mPendingEventsNeedingAck needs to be
1183 // incremented here. Additionally, we stop sending eCompositionCommit(AsIs)
1184 // event. Therefore, we need to decrement mPendingCommitCount which has
1185 // been incremented above.
1186 if (!mWidgetHasComposition
) {
1187 mPendingEventsNeedingAck
++;
1188 MOZ_DIAGNOSTIC_ASSERT(mPendingCommitCount
);
1189 if (mPendingCommitCount
) {
1190 mPendingCommitCount
--;
1196 mPendingEventsNeedingAck
++;
1200 void ContentCacheInParent::OnSelectionEvent(
1201 const WidgetSelectionEvent
& aSelectionEvent
) {
1203 sContentCacheLog
, LogLevel::Info
,
1204 ("0x%p OnSelectionEvent(aEvent={ "
1205 "mMessage=%s, mOffset=%u, mLength=%u, mReversed=%s, "
1206 "mExpandToClusterBoundary=%s, mUseNativeLineBreak=%s }), "
1207 "mPendingEventsNeedingAck=%u, mWidgetHasComposition=%s, "
1208 "mPendingCompositionCount=%" PRIu8
", mPendingCommitCount=%" PRIu8
", "
1209 "mIsChildIgnoringCompositionEvents=%s",
1210 this, ToChar(aSelectionEvent
.mMessage
), aSelectionEvent
.mOffset
,
1211 aSelectionEvent
.mLength
, GetBoolName(aSelectionEvent
.mReversed
),
1212 GetBoolName(aSelectionEvent
.mExpandToClusterBoundary
),
1213 GetBoolName(aSelectionEvent
.mUseNativeLineBreak
),
1214 mPendingEventsNeedingAck
, GetBoolName(mWidgetHasComposition
),
1215 mPendingCompositionCount
, mPendingCommitCount
,
1216 GetBoolName(mIsChildIgnoringCompositionEvents
)));
1218 #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
1219 mDispatchedEventMessages
.AppendElement(aSelectionEvent
.mMessage
);
1220 #endif // MOZ_DIAGNOSTIC_ASSERT_ENABLED
1222 mPendingEventsNeedingAck
++;
1225 void ContentCacheInParent::OnEventNeedingAckHandled(nsIWidget
* aWidget
,
1226 EventMessage aMessage
) {
1227 // This is called when the child process receives WidgetCompositionEvent or
1228 // WidgetSelectionEvent.
1231 sContentCacheLog
, LogLevel::Info
,
1232 ("0x%p OnEventNeedingAckHandled(aWidget=0x%p, "
1233 "aMessage=%s), mPendingEventsNeedingAck=%u, "
1234 "mWidgetHasComposition=%s, mPendingCompositionCount=%" PRIu8
", "
1235 "mPendingCommitCount=%" PRIu8
", mIsChildIgnoringCompositionEvents=%s",
1236 this, aWidget
, ToChar(aMessage
), mPendingEventsNeedingAck
,
1237 GetBoolName(mWidgetHasComposition
), mPendingCompositionCount
,
1238 mPendingCommitCount
, GetBoolName(mIsChildIgnoringCompositionEvents
)));
1240 #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
1241 mReceivedEventMessages
.AppendElement(aMessage
);
1242 #endif // #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
1244 bool isCommittedInChild
=
1245 // Commit requester in the remote process has committed the composition.
1246 aMessage
== eCompositionCommitRequestHandled
||
1247 // The commit event has been handled normally in the remote process.
1248 (!mIsChildIgnoringCompositionEvents
&&
1249 WidgetCompositionEvent::IsFollowedByCompositionEnd(aMessage
));
1251 if (isCommittedInChild
) {
1252 #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
1253 if (mPendingCompositionCount
== 1) {
1254 RemoveUnnecessaryEventMessageLog();
1256 #endif // #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
1258 if (NS_WARN_IF(!mPendingCompositionCount
)) {
1259 #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
1260 nsPrintfCString
info(
1261 "\nThere is no pending composition but received %s "
1262 "message from the remote child\n\n",
1264 AppendEventMessageLog(info
);
1265 CrashReporter::AppendAppNotesToCrashReport(info
);
1266 MOZ_DIAGNOSTIC_ASSERT(
1267 false, "No pending composition but received unexpected commit event");
1268 #endif // #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
1270 // Prevent odd behavior in release channel.
1271 mPendingCompositionCount
= 1;
1274 mPendingCompositionCount
--;
1276 // Forget composition string only when the latest composition string is
1277 // handled in the remote process because if there is 2 or more pending
1278 // composition, this value shouldn't be referred.
1279 if (!mPendingCompositionCount
) {
1280 mCompositionString
.Truncate();
1283 // Forget pending commit string length if it's handled in the remote
1284 // process. Note that this doesn't care too old composition's commit
1285 // string because in such case, we cannot return proper information
1286 // to IME synchornously.
1287 mPendingCommitLength
= 0;
1290 if (WidgetCompositionEvent::IsFollowedByCompositionEnd(aMessage
)) {
1291 // After the remote process receives eCompositionCommit(AsIs) event,
1292 // it'll restart to handle composition events.
1293 mIsChildIgnoringCompositionEvents
= false;
1295 if (NS_WARN_IF(!mPendingCommitCount
)) {
1296 #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
1297 nsPrintfCString
info(
1298 "\nThere is no pending comment events but received "
1299 "%s message from the remote child\n\n",
1301 AppendEventMessageLog(info
);
1302 CrashReporter::AppendAppNotesToCrashReport(info
);
1303 MOZ_DIAGNOSTIC_ASSERT(
1305 "No pending commit events but received unexpected commit event");
1306 #endif // #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
1308 // Prevent odd behavior in release channel.
1309 mPendingCommitCount
= 1;
1312 mPendingCommitCount
--;
1313 } else if (aMessage
== eCompositionCommitRequestHandled
&&
1314 mPendingCommitCount
) {
1315 // If the remote process commits composition synchronously after
1316 // requesting commit composition and we've already sent commit composition,
1317 // it starts to ignore following composition events until receiving
1318 // eCompositionStart event.
1319 mIsChildIgnoringCompositionEvents
= true;
1322 // If neither widget (i.e., IME) nor the remote process has composition,
1323 // now, we can forget composition string informations.
1324 if (!mWidgetHasComposition
&& !mPendingCompositionCount
&&
1325 !mPendingCommitCount
) {
1326 mCompositionStart
.reset();
1329 if (NS_WARN_IF(!mPendingEventsNeedingAck
)) {
1330 #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
1331 nsPrintfCString
info(
1332 "\nThere is no pending events but received %s "
1333 "message from the remote child\n\n",
1335 AppendEventMessageLog(info
);
1336 CrashReporter::AppendAppNotesToCrashReport(info
);
1337 #endif // #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
1338 MOZ_DIAGNOSTIC_ASSERT(
1339 false, "No pending event message but received unexpected event");
1340 mPendingEventsNeedingAck
= 1;
1342 if (--mPendingEventsNeedingAck
) {
1346 FlushPendingNotifications(aWidget
);
1349 bool ContentCacheInParent::RequestIMEToCommitComposition(
1350 nsIWidget
* aWidget
, bool aCancel
, nsAString
& aCommittedString
) {
1352 sContentCacheLog
, LogLevel::Info
,
1353 ("0x%p RequestToCommitComposition(aWidget=%p, "
1354 "aCancel=%s), mPendingCompositionCount=%" PRIu8
", "
1355 "mPendingCommitCount=%" PRIu8
", mIsChildIgnoringCompositionEvents=%s, "
1356 "IMEStateManager::DoesBrowserParentHaveIMEFocus(&mBrowserParent)=%s, "
1357 "mWidgetHasComposition=%s, mCommitStringByRequest=%p",
1358 this, aWidget
, GetBoolName(aCancel
), mPendingCompositionCount
,
1359 mPendingCommitCount
, GetBoolName(mIsChildIgnoringCompositionEvents
),
1361 IMEStateManager::DoesBrowserParentHaveIMEFocus(&mBrowserParent
)),
1362 GetBoolName(mWidgetHasComposition
), mCommitStringByRequest
));
1364 MOZ_ASSERT(!mCommitStringByRequest
);
1366 // If there are 2 or more pending compositions, we already sent
1367 // eCompositionCommit(AsIs) to the remote process. So, this request is
1368 // too late for IME. The remote process should wait following
1369 // composition events for cleaning up TextComposition and handle the
1370 // request as it's handled asynchronously.
1371 if (mPendingCompositionCount
> 1) {
1372 #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
1373 mRequestIMEToCommitCompositionResults
.AppendElement(
1374 RequestIMEToCommitCompositionResult::eToOldCompositionReceived
);
1375 #endif // #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
1379 // If there is no pending composition, we may have already sent
1380 // eCompositionCommit(AsIs) event for the active composition. If so, the
1381 // remote process will receive composition events which causes cleaning up
1382 // TextComposition. So, this shouldn't do nothing and TextComposition
1383 // should handle the request as it's handled asynchronously.
1384 // XXX Perhaps, this is wrong because TextComposition in child process
1385 // may commit the composition with current composition string in the
1386 // remote process. I.e., it may be different from actual commit string
1387 // which user typed. So, perhaps, we should return true and the commit
1389 if (mPendingCommitCount
) {
1390 #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
1391 mRequestIMEToCommitCompositionResults
.AppendElement(
1392 RequestIMEToCommitCompositionResult::eToCommittedCompositionReceived
);
1393 #endif // #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
1397 // If BrowserParent which has IME focus was already changed to different one,
1398 // the request shouldn't be sent to IME because it's too late.
1399 if (!IMEStateManager::DoesBrowserParentHaveIMEFocus(&mBrowserParent
)) {
1400 // Use the latest composition string which may not be handled in the
1401 // remote process for avoiding data loss.
1402 #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
1403 mRequestIMEToCommitCompositionResults
.AppendElement(
1404 RequestIMEToCommitCompositionResult::eReceivedAfterBrowserParentBlur
);
1405 #endif // #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
1406 aCommittedString
= mCompositionString
;
1407 // After we return true from here, i.e., without actually requesting IME
1408 // to commit composition, we will receive eCompositionCommitRequestHandled
1409 // pseudo event message from the remote process. So, we need to increment
1410 // mPendingEventsNeedingAck here.
1411 mPendingEventsNeedingAck
++;
1415 RefPtr
<TextComposition
> composition
=
1416 IMEStateManager::GetTextCompositionFor(aWidget
);
1417 if (NS_WARN_IF(!composition
)) {
1418 MOZ_LOG(sContentCacheLog
, LogLevel::Warning
,
1419 (" 0x%p RequestToCommitComposition(), "
1420 "does nothing due to no composition",
1422 #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
1423 mRequestIMEToCommitCompositionResults
.AppendElement(
1424 RequestIMEToCommitCompositionResult::eReceivedButNoTextComposition
);
1425 #endif // #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
1429 mCommitStringByRequest
= &aCommittedString
;
1431 // Request commit or cancel composition with TextComposition because we may
1432 // have already requested to commit or cancel the composition or we may
1433 // have already received eCompositionCommit(AsIs) event. Those status are
1434 // managed by composition. So, if we don't request commit composition,
1435 // we should do nothing with native IME here.
1436 composition
->RequestToCommit(aWidget
, aCancel
);
1438 mCommitStringByRequest
= nullptr;
1441 sContentCacheLog
, LogLevel::Info
,
1442 (" 0x%p RequestToCommitComposition(), "
1443 "mWidgetHasComposition=%s, the composition %s committed synchronously",
1444 this, GetBoolName(mWidgetHasComposition
),
1445 composition
->Destroyed() ? "WAS" : "has NOT been"));
1447 if (!composition
->Destroyed()) {
1448 // When the composition isn't committed synchronously, the remote process's
1449 // TextComposition instance will synthesize commit events and wait to
1450 // receive delayed composition events. When TextComposition instances both
1451 // in this process and the remote process will be destroyed when delayed
1452 // composition events received. TextComposition instance in the parent
1453 // process will dispatch following composition events and be destroyed
1454 // normally. On the other hand, TextComposition instance in the remote
1455 // process won't dispatch following composition events and will be
1456 // destroyed by IMEStateManager::DispatchCompositionEvent().
1457 #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
1458 mRequestIMEToCommitCompositionResults
.AppendElement(
1459 RequestIMEToCommitCompositionResult::eHandledAsynchronously
);
1460 #endif // #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
1464 // When the composition is committed synchronously, the commit string will be
1465 // returned to the remote process. Then, PuppetWidget will dispatch
1466 // eCompositionCommit event with the returned commit string (i.e., the value
1467 // is aCommittedString of this method) and that causes destroying
1468 // TextComposition instance in the remote process (Note that TextComposition
1469 // instance in this process was already destroyed).
1470 #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
1471 mRequestIMEToCommitCompositionResults
.AppendElement(
1472 RequestIMEToCommitCompositionResult::eHandledSynchronously
);
1473 #endif // #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
1477 void ContentCacheInParent::MaybeNotifyIME(
1478 nsIWidget
* aWidget
, const IMENotification
& aNotification
) {
1479 if (!mPendingEventsNeedingAck
) {
1480 IMEStateManager::NotifyIME(aNotification
, aWidget
, &mBrowserParent
);
1484 switch (aNotification
.mMessage
) {
1485 case NOTIFY_IME_OF_SELECTION_CHANGE
:
1486 mPendingSelectionChange
.MergeWith(aNotification
);
1488 case NOTIFY_IME_OF_TEXT_CHANGE
:
1489 mPendingTextChange
.MergeWith(aNotification
);
1491 case NOTIFY_IME_OF_POSITION_CHANGE
:
1492 mPendingLayoutChange
.MergeWith(aNotification
);
1494 case NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED
:
1495 mPendingCompositionUpdate
.MergeWith(aNotification
);
1498 MOZ_CRASH("Unsupported notification");
1503 void ContentCacheInParent::FlushPendingNotifications(nsIWidget
* aWidget
) {
1504 MOZ_ASSERT(!mPendingEventsNeedingAck
);
1506 // If the BrowserParent's widget has already gone, this can do nothing since
1507 // widget is necessary to notify IME of something.
1512 // New notifications which are notified during flushing pending notifications
1513 // should be merged again.
1514 mPendingEventsNeedingAck
++;
1516 nsCOMPtr
<nsIWidget
> widget
= aWidget
;
1518 // First, text change notification should be sent because selection change
1519 // notification notifies IME of current selection range in the latest content.
1520 // So, IME may need the latest content before that.
1521 if (mPendingTextChange
.HasNotification()) {
1522 IMENotification
notification(mPendingTextChange
);
1523 if (!widget
->Destroyed()) {
1524 mPendingTextChange
.Clear();
1525 IMEStateManager::NotifyIME(notification
, widget
, &mBrowserParent
);
1529 if (mPendingSelectionChange
.HasNotification()) {
1530 IMENotification
notification(mPendingSelectionChange
);
1531 if (!widget
->Destroyed()) {
1532 mPendingSelectionChange
.Clear();
1533 IMEStateManager::NotifyIME(notification
, widget
, &mBrowserParent
);
1537 // Layout change notification should be notified after selection change
1538 // notification because IME may want to query position of new caret position.
1539 if (mPendingLayoutChange
.HasNotification()) {
1540 IMENotification
notification(mPendingLayoutChange
);
1541 if (!widget
->Destroyed()) {
1542 mPendingLayoutChange
.Clear();
1543 IMEStateManager::NotifyIME(notification
, widget
, &mBrowserParent
);
1547 // Finally, send composition update notification because it notifies IME of
1548 // finishing handling whole sending events.
1549 if (mPendingCompositionUpdate
.HasNotification()) {
1550 IMENotification
notification(mPendingCompositionUpdate
);
1551 if (!widget
->Destroyed()) {
1552 mPendingCompositionUpdate
.Clear();
1553 IMEStateManager::NotifyIME(notification
, widget
, &mBrowserParent
);
1557 if (!--mPendingEventsNeedingAck
&& !widget
->Destroyed() &&
1558 (mPendingTextChange
.HasNotification() ||
1559 mPendingSelectionChange
.HasNotification() ||
1560 mPendingLayoutChange
.HasNotification() ||
1561 mPendingCompositionUpdate
.HasNotification())) {
1562 FlushPendingNotifications(widget
);
1566 #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
1568 void ContentCacheInParent::RemoveUnnecessaryEventMessageLog() {
1569 bool foundLastCompositionStart
= false;
1570 for (size_t i
= mDispatchedEventMessages
.Length(); i
> 1; i
--) {
1571 if (mDispatchedEventMessages
[i
- 1] != eCompositionStart
) {
1574 if (!foundLastCompositionStart
) {
1575 // Find previous eCompositionStart of the latest eCompositionStart.
1576 foundLastCompositionStart
= true;
1579 // Remove the messages before the last 2 sets of composition events.
1580 mDispatchedEventMessages
.RemoveElementsAt(0, i
- 1);
1583 uint32_t numberOfCompositionCommitRequestHandled
= 0;
1584 foundLastCompositionStart
= false;
1585 for (size_t i
= mReceivedEventMessages
.Length(); i
> 1; i
--) {
1586 if (mReceivedEventMessages
[i
- 1] == eCompositionCommitRequestHandled
) {
1587 numberOfCompositionCommitRequestHandled
++;
1589 if (mReceivedEventMessages
[i
- 1] != eCompositionStart
) {
1592 if (!foundLastCompositionStart
) {
1593 // Find previous eCompositionStart of the latest eCompositionStart.
1594 foundLastCompositionStart
= true;
1597 // Remove the messages before the last 2 sets of composition events.
1598 mReceivedEventMessages
.RemoveElementsAt(0, i
- 1);
1602 if (!numberOfCompositionCommitRequestHandled
) {
1603 // If there is no eCompositionCommitRequestHandled in
1604 // mReceivedEventMessages, we don't need to store log of
1605 // RequestIMEToCommmitComposition().
1606 mRequestIMEToCommitCompositionResults
.Clear();
1608 // We need to keep all reason of eCompositionCommitRequestHandled, which
1609 // is sent when mRequestIMEToCommitComposition() returns true.
1610 // So, we can discard older log than the first
1611 // eCompositionCommitRequestHandled in mReceivedEventMessages.
1612 for (size_t i
= mRequestIMEToCommitCompositionResults
.Length(); i
> 1;
1614 if (mRequestIMEToCommitCompositionResults
[i
- 1] ==
1615 RequestIMEToCommitCompositionResult::
1616 eReceivedAfterBrowserParentBlur
||
1617 mRequestIMEToCommitCompositionResults
[i
- 1] ==
1618 RequestIMEToCommitCompositionResult::eHandledSynchronously
) {
1619 --numberOfCompositionCommitRequestHandled
;
1620 if (!numberOfCompositionCommitRequestHandled
) {
1621 mRequestIMEToCommitCompositionResults
.RemoveElementsAt(0, i
- 1);
1629 void ContentCacheInParent::AppendEventMessageLog(nsACString
& aLog
) const {
1630 aLog
.AppendLiteral("Dispatched Event Message Log:\n");
1631 for (EventMessage message
: mDispatchedEventMessages
) {
1632 aLog
.AppendLiteral(" ");
1633 aLog
.Append(ToChar(message
));
1634 aLog
.AppendLiteral("\n");
1636 aLog
.AppendLiteral("\nReceived Event Message Log:\n");
1637 for (EventMessage message
: mReceivedEventMessages
) {
1638 aLog
.AppendLiteral(" ");
1639 aLog
.Append(ToChar(message
));
1640 aLog
.AppendLiteral("\n");
1642 aLog
.AppendLiteral("\nResult of RequestIMEToCommitComposition():\n");
1643 for (RequestIMEToCommitCompositionResult result
:
1644 mRequestIMEToCommitCompositionResults
) {
1645 aLog
.AppendLiteral(" ");
1646 aLog
.Append(ToReadableText(result
));
1647 aLog
.AppendLiteral("\n");
1649 aLog
.AppendLiteral("\n");
1652 #endif // #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
1654 /*****************************************************************************
1655 * mozilla::ContentCache::TextRectArray
1656 *****************************************************************************/
1658 LayoutDeviceIntRect
ContentCache::TextRectArray::GetRect(
1659 uint32_t aOffset
) const {
1660 LayoutDeviceIntRect rect
;
1661 if (IsOffsetInRange(aOffset
)) {
1662 rect
= mRects
[aOffset
- mStart
];
1667 LayoutDeviceIntRect
ContentCache::TextRectArray::GetUnionRect(
1668 uint32_t aOffset
, uint32_t aLength
) const {
1669 LayoutDeviceIntRect rect
;
1670 if (!IsRangeCompletelyInRange(aOffset
, aLength
)) {
1673 for (uint32_t i
= 0; i
< aLength
; i
++) {
1674 rect
= rect
.Union(mRects
[aOffset
- mStart
+ i
]);
1679 LayoutDeviceIntRect
ContentCache::TextRectArray::GetUnionRectAsFarAsPossible(
1680 uint32_t aOffset
, uint32_t aLength
, bool aRoundToExistingOffset
) const {
1681 LayoutDeviceIntRect rect
;
1683 (!aRoundToExistingOffset
&& !IsOverlappingWith(aOffset
, aLength
))) {
1686 uint32_t startOffset
= std::max(aOffset
, mStart
);
1687 if (aRoundToExistingOffset
&& startOffset
>= EndOffset()) {
1688 startOffset
= EndOffset() - 1;
1690 uint32_t endOffset
= std::min(aOffset
+ aLength
, EndOffset());
1691 if (aRoundToExistingOffset
&& endOffset
< mStart
+ 1) {
1692 endOffset
= mStart
+ 1;
1694 if (NS_WARN_IF(endOffset
< startOffset
)) {
1697 for (uint32_t i
= 0; i
< endOffset
- startOffset
; i
++) {
1698 rect
= rect
.Union(mRects
[startOffset
- mStart
+ i
]);
1703 } // namespace mozilla