/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- * vim: sw=2 ts=8 et :
*/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
bool ContentCache::IsValid() const { if (mText.isNothing()) { // mSelection and mCaret depend on mText. if (NS_WARN_IF(mSelection.isSome()) || NS_WARN_IF(mCaret.isSome())) { returnfalse;
}
} else { // mSelection depends on mText. if (mSelection.isSome() && NS_WARN_IF(!mSelection->IsValidIn(*mText))) { returnfalse;
}
// mCaret depends on mSelection. if (mCaret.isSome() &&
(NS_WARN_IF(mSelection.isNothing()) ||
NS_WARN_IF(!mSelection->mHasRange) ||
NS_WARN_IF(mSelection->StartOffset() != mCaret->Offset()))) { returnfalse;
}
}
// mTextRectArray stores character rects around composition string. // Note that even if we fail to collect the rects, we may keep storing // mCompositionStart. if (mTextRectArray.isSome()) { if (NS_WARN_IF(mCompositionStart.isNothing())) { returnfalse;
}
}
// This text will appear in the crash reports without any permissions. // Do not use `ToString` here to avoid to expose unexpected data with // changing the type or `operator<<()`.
nsPrintfCString info( "ContentCache={ mText=%s, mSelection=%s, mCaret=%s, mTextRectArray=%s, " "mCompositionStart=%s }\n", // Don't expose mText.ref() value for protecting the user's privacy.
mText.isNothing()
? "Nothing"
: nsPrintfCString("{ Length()=%zu }", mText->Length()).get(),
mSelection.isNothing()
? "Nothing"
: nsPrintfCString("{ mAnchor=%u, mFocus=%u }", mSelection->mAnchor,
mSelection->mFocus)
.get(),
mCaret.isNothing()
? "Nothing"
: nsPrintfCString("{ mOffset=%u }", mCaret->mOffset).get(),
mTextRectArray.isNothing()
? "Nothing"
: nsPrintfCString("{ Length()=%u }", mTextRectArray->Length()).get(),
mCompositionStart.isNothing()
? "Nothing"
: nsPrintfCString("%u", mCompositionStart.value()).get());
CrashReporter::AppendAppNotesToCrashReport(info);
MOZ_DIAGNOSTIC_CRASH("Invalid ContentCache data"); #endif// #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
}
nsEventStatus status = nsEventStatus_eIgnore;
WidgetQueryContentEvent querySelectedTextEvent(true, eQuerySelectedText,
aWidget);
aWidget->DispatchEvent(&querySelectedTextEvent, status); if (NS_WARN_IF(querySelectedTextEvent.Failed())) {
MOZ_LOG(
sContentCacheLog, LogLevel::Error,
("0x%p CacheSelection(), FAILED, couldn't retrieve the selected text", this)); // XXX Allowing selection-independent character rects makes things // complicated in the parent...
} // ContentCache should store only editable content. Therefore, if current // selection root is not editable, we don't need to store the selection, i.e., // let's treat it as there is no selection. However, if we already have // previously editable text, let's store the selection even if it becomes // uneditable because not doing so would create odd situation. E.g., IME may // fail only querying selection after succeeded querying text. elseif (NS_WARN_IF(!querySelectedTextEvent.mReply->mIsEditableContent)) {
MOZ_LOG(sContentCacheLog, LogLevel::Error,
("0x%p CacheSelection(), FAILED, editable content had already been " "blurred", this));
AssertIfInvalid(); returnfalse;
} else {
mSelection.emplace(querySelectedTextEvent);
}
nsEventStatus status = nsEventStatus_eIgnore;
WidgetQueryContentEvent queryEditorRectEvent(true, eQueryEditorRect, aWidget);
aWidget->DispatchEvent(&queryEditorRectEvent, status); if (NS_WARN_IF(queryEditorRectEvent.Failed())) {
MOZ_LOG(
sContentCacheLog, LogLevel::Error,
("0x%p CacheEditorRect(), FAILED, couldn't retrieve the editor rect", this)); returnfalse;
} // ContentCache should store only editable content. Therefore, if current // selection root is not editable, we don't need to store the editor rect, // i.e., let's treat it as there is no focused editor. if (NS_WARN_IF(!queryEditorRectEvent.mReply->mIsEditableContent)) {
MOZ_LOG(sContentCacheLog, LogLevel::Error,
("0x%p CacheText(), FAILED, editable content had already been " "blurred", this)); returnfalse;
}
mEditorRect = queryEditorRectEvent.mReply->mRect;
MOZ_LOG(sContentCacheLog, LogLevel::Info,
("0x%p CacheEditorRect(), Succeeded, mEditorRect=%s", this,
ToString(mEditorRect).c_str())); returntrue;
}
nsEventStatus status = nsEventStatus_eIgnore;
WidgetQueryContentEvent queryTextContentEvent(true, eQueryTextContent,
aWidget);
queryTextContentEvent.InitForQueryTextContent(0, UINT32_MAX);
aWidget->DispatchEvent(&queryTextContentEvent, status); if (NS_WARN_IF(queryTextContentEvent.Failed())) {
MOZ_LOG(sContentCacheLog, LogLevel::Error,
("0x%p CacheText(), FAILED, couldn't retrieve whole text", this));
mText.reset();
} // ContentCache should store only editable content. Therefore, if current // selection root is not editable, we don't need to store the text, i.e., // let's treat it as there is no editable text. elseif (NS_WARN_IF(!queryTextContentEvent.mReply->mIsEditableContent)) {
MOZ_LOG(sContentCacheLog, LogLevel::Error,
("0x%p CacheText(), FAILED, editable content had already been " "blurred", this));
mText.reset();
} else {
mText = Some(nsString(queryTextContentEvent.mReply->DataRef()));
MOZ_LOG(sContentCacheLog, LogLevel::Info,
("0x%p CacheText(), Succeeded, mText=%s", this,
PrintStringDetail(mText, PrintStringDetail::kMaxLengthForEditor)
.get()));
}
// Forget last commit range if string in the range is different from the // last commit string. if (mLastCommit.isSome() &&
(mText.isNothing() ||
nsDependentSubstring(mText.ref(), mLastCommit->StartOffset(),
mLastCommit->Length()) != mLastCommit->DataRef())) {
MOZ_LOG(sContentCacheLog, LogLevel::Debug,
("0x%p CacheText(), resetting the last composition string data " "(mLastCommit=%s, current string=\"%s\")", this, ToString(mLastCommit).c_str(),
PrintStringDetail(
nsDependentSubstring(mText.ref(), mLastCommit->StartOffset(),
mLastCommit->Length()),
PrintStringDetail::kMaxLengthForCompositionString)
.get()));
mLastCommit.reset();
}
// If we fail to get editable text content, it must mean that there is no // focused element anymore or focused element is not editable. In this case, // we should not get selection of non-editable content if (MOZ_UNLIKELY(mText.isNothing())) {
mSelection.reset();
mCaret.reset();
mTextRectArray.reset();
AssertIfInvalid(); returnfalse;
}
nsEventStatus status = nsEventStatus_eIgnore;
WidgetQueryContentEvent queryTextRectEvent(true, eQueryTextRect, aWidget);
queryTextRectEvent.InitForQueryTextRect(aOffset, 1);
aWidget->DispatchEvent(&queryTextRectEvent, status); if (NS_WARN_IF(queryTextRectEvent.Failed())) { returnfalse;
}
aCharRect = queryTextRectEvent.mReply->mRect;
// Guarantee the rect is not empty. if (NS_WARN_IF(!aCharRect.Height())) {
aCharRect.SetHeight(1);
} if (NS_WARN_IF(!aCharRect.Width())) {
aCharRect.SetWidth(1);
} returntrue;
}
if (mSelection.isSome()) {
mSelection->ClearRects();
}
// Retrieve text rects in composition string if there is.
RefPtr<TextComposition> textComposition =
IMEStateManager::GetTextCompositionFor(aWidget); if (textComposition) { // mCompositionStart may be updated by some composition event handlers. // So, let's update it with the latest information.
mCompositionStart = Some(textComposition->NativeOffsetOfStartComposition()); // Note that TextComposition::String() may not be modified here because // it's modified after all edit action listeners are performed but this // is called while some of them are performed. // FYI: For supporting IME which commits composition and restart new // composition immediately, we should cache next character of current // composition too.
uint32_t length = textComposition->LastData().Length() + 1;
mTextRectArray = Some(TextRectArray(mCompositionStart.value())); if (NS_WARN_IF(!QueryCharRectArray(aWidget, mTextRectArray->mStart, length,
mTextRectArray->mRects))) {
MOZ_LOG(sContentCacheLog, LogLevel::Error,
("0x%p CacheTextRects(), FAILED, " "couldn't retrieve text rect array of the composition string", this));
mTextRectArray.reset();
}
} else {
mCompositionStart.reset();
mTextRectArray.reset();
}
if (mSelection.isSome()) { // Set mSelection->mAnchorCharRects // If we've already have the rect in mTextRectArray, save the query cost. if (mSelection->mHasRange && mTextRectArray.isSome() &&
mTextRectArray->IsOffsetInRange(mSelection->mAnchor) &&
(!mSelection->mAnchor ||
mTextRectArray->IsOffsetInRange(mSelection->mAnchor - 1))) {
mSelection->mAnchorCharRects[eNextCharRect] =
mTextRectArray->GetRect(mSelection->mAnchor); if (mSelection->mAnchor) {
mSelection->mAnchorCharRects[ePrevCharRect] =
mTextRectArray->GetRect(mSelection->mAnchor - 1);
}
} // Otherwise, get it from content even if there is no selection ranges. else {
RectArray rects; const uint32_t startOffset = mSelection->mHasRange && mSelection->mAnchor
? mSelection->mAnchor - 1u
: 0u; const uint32_t length =
mSelection->mHasRange && mSelection->mAnchor ? 2u : 1u; if (NS_WARN_IF(
!QueryCharRectArray(aWidget, startOffset, length, rects))) {
MOZ_LOG(
sContentCacheLog, LogLevel::Error,
("0x%p CacheTextRects(), FAILED, couldn't retrieve text rect " "array around the selection anchor (%s)", this,
mSelection ? ToString(mSelection->mAnchor).c_str() : "Nothing"));
MOZ_ASSERT_IF(mSelection.isSome(),
mSelection->mAnchorCharRects[ePrevCharRect].IsEmpty());
MOZ_ASSERT_IF(mSelection.isSome(),
mSelection->mAnchorCharRects[eNextCharRect].IsEmpty());
} elseif (rects.Length()) { if (rects.Length() > 1) {
mSelection->mAnchorCharRects[ePrevCharRect] = rects[0];
mSelection->mAnchorCharRects[eNextCharRect] = rects[1];
} else {
mSelection->mAnchorCharRects[eNextCharRect] = rects[0];
MOZ_ASSERT(mSelection->mAnchorCharRects[ePrevCharRect].IsEmpty());
}
}
}
// Set mSelection->mFocusCharRects // If selection is collapsed (including no selection case), the focus char // rects are same as the anchor char rects so that we can just copy them. if (mSelection->IsCollapsed()) {
mSelection->mFocusCharRects[0] = mSelection->mAnchorCharRects[0];
mSelection->mFocusCharRects[1] = mSelection->mAnchorCharRects[1];
} // If the selection range is in mTextRectArray, save the query cost. elseif (mTextRectArray.isSome() &&
mTextRectArray->IsOffsetInRange(mSelection->mFocus) &&
(!mSelection->mFocus ||
mTextRectArray->IsOffsetInRange(mSelection->mFocus - 1))) {
MOZ_ASSERT(mSelection->mHasRange);
mSelection->mFocusCharRects[eNextCharRect] =
mTextRectArray->GetRect(mSelection->mFocus); if (mSelection->mFocus) {
mSelection->mFocusCharRects[ePrevCharRect] =
mTextRectArray->GetRect(mSelection->mFocus - 1);
}
} // Otherwise, including no selection range cases, need to query the rects. else {
MOZ_ASSERT(mSelection->mHasRange);
RectArray rects; const uint32_t startOffset =
mSelection->mFocus ? mSelection->mFocus - 1u : 0u; const uint32_t length = mSelection->mFocus ? 2u : 1u; if (NS_WARN_IF(
!QueryCharRectArray(aWidget, startOffset, length, rects))) {
MOZ_LOG(
sContentCacheLog, LogLevel::Error,
("0x%p CacheTextRects(), FAILED, couldn't retrieve text rect " "array around the selection focus (%s)", this,
mSelection ? ToString(mSelection->mFocus).c_str() : "Nothing"));
MOZ_ASSERT_IF(mSelection.isSome(),
mSelection->mFocusCharRects[ePrevCharRect].IsEmpty());
MOZ_ASSERT_IF(mSelection.isSome(),
mSelection->mFocusCharRects[eNextCharRect].IsEmpty());
} elseif (NS_WARN_IF(mSelection.isNothing())) {
MOZ_LOG(sContentCacheLog, LogLevel::Error,
("0x%p CacheTextRects(), FAILED, mSelection was reset during " "the call of QueryCharRectArray", this));
} else { if (rects.Length() > 1) {
mSelection->mFocusCharRects[ePrevCharRect] = rects[0];
mSelection->mFocusCharRects[eNextCharRect] = rects[1];
} elseif (rects.Length()) {
mSelection->mFocusCharRects[eNextCharRect] = rects[0];
MOZ_ASSERT(mSelection->mFocusCharRects[ePrevCharRect].IsEmpty());
}
}
}
}
// If there is a non-collapsed selection range, let's query the whole selected // text rect. Note that the result cannot be computed from first character // rect and last character rect of the selection because they both may be in // middle of different line. if (mSelection.isSome() && mSelection->mHasRange &&
!mSelection->IsCollapsed()) {
nsEventStatus status = nsEventStatus_eIgnore;
WidgetQueryContentEvent queryTextRectEvent(true, eQueryTextRect, aWidget);
queryTextRectEvent.InitForQueryTextRect(mSelection->StartOffset(),
mSelection->Length());
aWidget->DispatchEvent(&queryTextRectEvent, status); if (NS_WARN_IF(queryTextRectEvent.Failed())) {
MOZ_LOG(sContentCacheLog, LogLevel::Error,
("0x%p CacheTextRects(), FAILED, " "couldn't retrieve text rect of whole selected text", this));
} else {
mSelection->mRect = queryTextRectEvent.mReply->mRect;
}
}
// Even if there is no selection range, we should have the first character // rect for the last resort of suggesting position of IME UI. if (mSelection.isSome() && mSelection->mHasRange && !mSelection->mFocus) {
mFirstCharRect = mSelection->mFocusCharRects[eNextCharRect];
} elseif (mSelection.isSome() && mSelection->mHasRange &&
mSelection->mFocus == 1) {
mFirstCharRect = mSelection->mFocusCharRects[ePrevCharRect];
} elseif (mSelection.isSome() && mSelection->mHasRange &&
!mSelection->mAnchor) {
mFirstCharRect = mSelection->mAnchorCharRects[eNextCharRect];
} elseif (mSelection.isSome() && mSelection->mHasRange &&
mSelection->mAnchor == 1) {
mFirstCharRect = mSelection->mFocusCharRects[ePrevCharRect];
} elseif (mTextRectArray.isSome() && mTextRectArray->IsOffsetInRange(0u)) {
mFirstCharRect = mTextRectArray->GetRect(0u);
} else {
LayoutDeviceIntRect charRect; if (MOZ_UNLIKELY(NS_WARN_IF(!QueryCharRect(aWidget, 0, charRect)))) {
MOZ_LOG(sContentCacheLog, LogLevel::Error,
("0x%p CacheTextRects(), FAILED, " "couldn't retrieve first char rect", this));
mFirstCharRect.SetEmpty();
} else {
mFirstCharRect = charRect;
}
}
// Finally, let's cache the last commit string's character rects until // selection change or something other editing because user may reconvert // or undo the last commit. Then, IME requires the character rects for // positioning their UI. if (mLastCommit.isSome()) {
mLastCommitStringTextRectArray =
Some(TextRectArray(mLastCommit->StartOffset())); if (mLastCommit->Length() == 1 && mSelection.isSome() &&
mSelection->mHasRange &&
mSelection->mAnchor - 1 == mLastCommit->StartOffset() &&
!mSelection->mAnchorCharRects[ePrevCharRect].IsEmpty()) {
mLastCommitStringTextRectArray->mRects.AppendElement(
mSelection->mAnchorCharRects[ePrevCharRect]);
} elseif (NS_WARN_IF(!QueryCharRectArray(
aWidget, mLastCommit->StartOffset(), mLastCommit->Length(),
mLastCommitStringTextRectArray->mRects))) {
MOZ_LOG(sContentCacheLog, LogLevel::Error,
("0x%p CacheTextRects(), FAILED, " "couldn't retrieve text rect array of the last commit string", this));
mLastCommitStringTextRectArray.reset();
mLastCommit.reset();
}
MOZ_ASSERT((mLastCommitStringTextRectArray.isSome()
? mLastCommitStringTextRectArray->mRects.Length()
: 0) == (mLastCommit.isSome() ? mLastCommit->Length() : 0));
} else {
mLastCommitStringTextRectArray.reset();
}
if (mLastCommit.isSome()) { // Forget last commit string range if selection is not collapsed // at end of the last commit string. if (!mSelection->mHasRange || !mSelection->IsCollapsed() ||
mSelection->mAnchor != mLastCommit->EndOffset()) {
MOZ_LOG(
sContentCacheLog, LogLevel::Debug,
("0x%p SetSelection(), forgetting last commit composition data " "(mSelection=%s, mLastCommit=%s)", this, ToString(mSelection).c_str(), ToString(mLastCommit).c_str()));
mLastCommit.reset();
}
}
// Only when there is one composition, the TextComposition instance in this // process is managing the composition in the remote process. Therefore, // we shouldn't update composition start offset of TextComposition with // old composition which is still being handled by the child process. if (WidgetHasComposition() && mHandlingCompositions.Length() == 1 &&
mCompositionStart.isSome()) {
IMEStateManager::MaybeStartOffsetUpdatedInChild(aWidget,
mCompositionStart.value());
}
// When this instance allows to query content relative to composition string, // we should modify mCompositionStart with the latest information in the // remote process because now we have the information around the composition // string.
mCompositionStartInChild = aOther.mCompositionStart; if (WidgetHasComposition() || HasPendingCommit()) { if (mCompositionStartInChild.isSome()) { if (mCompositionStart.valueOr(UINT32_MAX) !=
mCompositionStartInChild.value()) {
mCompositionStart = mCompositionStartInChild;
mPendingCommitLength = 0;
}
} elseif (mCompositionStart.isSome() && mSelection.isSome() &&
mSelection->mHasRange &&
mCompositionStart.value() != mSelection->StartOffset()) {
mCompositionStart = Some(mSelection->StartOffset());
mPendingCommitLength = 0;
}
}
// ContentCache doesn't store offset of its start with XP linebreaks. // So, we don't support to query contents relative to composition start // offset with XP linebreaks. if (NS_WARN_IF(!aEvent.mUseNativeLineBreak)) {
MOZ_LOG(sContentCacheLog, LogLevel::Error,
("0x%p HandleQueryContentEvent(), FAILED due to query with XP " "linebreaks", this)); returnfalse;
}
if (NS_WARN_IF(!aEvent.mInput.IsValidOffset())) {
MOZ_LOG(
sContentCacheLog, LogLevel::Error,
("0x%p HandleQueryContentEvent(), FAILED due to invalid offset", this)); returnfalse;
}
if (NS_WARN_IF(!aEvent.mInput.IsValidEventMessage(aEvent.mMessage))) {
MOZ_LOG(
sContentCacheLog, LogLevel::Error,
("0x%p HandleQueryContentEvent(), FAILED due to invalid event message", this)); returnfalse;
}
if (!aRoundToExistingOffset) {
aTextRect.SetEmpty(); returnfalse;
}
if (mTextRectArray.isNothing() || !mTextRectArray->HasRects()) { // If there are no rects in mTextRectArray, we should refer the start of // the selection if there is because IME must query a char rect around it if // there is no composition. if (mSelection.isNothing()) { // Unfortunately, there is no data about text rect...
aTextRect.SetEmpty(); returnfalse;
}
aTextRect = mSelection->StartCharRect(); return !aTextRect.IsEmpty();
}
// Although we may have mLastCommitStringTextRectArray here and it must have // previous character rects at selection. However, we should stop using it // because it's stored really short time after commiting a composition. // So, multiple query may return different rect and it may cause flickerling // the IME UI.
uint32_t offset = aOffset; if (offset < mTextRectArray->StartOffset()) {
offset = mTextRectArray->StartOffset();
} else {
offset = mTextRectArray->EndOffset() - 1;
}
aTextRect = mTextRectArray->GetRect(offset); return !aTextRect.IsEmpty();
}
// Even if some text rects are not cached of the queried range, // we should return union rect when the first character's rect is cached // since the first character rect is important and the others are not so // in most cases.
if (!aOffset && mSelection.isSome() && mSelection->mHasRange &&
aOffset != mSelection->mAnchor && aOffset != mSelection->mFocus &&
(mTextRectArray.isNothing() ||
!mTextRectArray->IsOffsetInRange(aOffset)) &&
(mLastCommitStringTextRectArray.isNothing() ||
!mLastCommitStringTextRectArray->IsOffsetInRange(aOffset))) { // The first character rect isn't cached. returnfalse;
}
// Use mLastCommitStringTextRectArray only when it overlaps with aOffset // even if aROundToExistingOffset is true for avoiding flickerling IME UI. // See the last comment in GetTextRect() for the detail. if (mLastCommitStringTextRectArray.isSome() &&
mLastCommitStringTextRectArray->IsOverlappingWith(aOffset, aLength)) {
aUnionTextRect =
mLastCommitStringTextRectArray->GetUnionRectAsFarAsPossible(
aOffset, aLength, aRoundToExistingOffset);
} else {
aUnionTextRect.SetEmpty();
}
// Guess caret rect from the text rect if it's stored. if (!GetTextRect(aOffset, aRoundToExistingOffset, aCaretRect)) { // There might be previous character rect in the cache. If so, we can // guess the caret rect with it. if (!aOffset ||
!GetTextRect(aOffset - 1, aRoundToExistingOffset, aCaretRect)) {
aCaretRect.SetEmpty(); returnfalse;
}
// XXX This is not bidi aware because we don't cache each character's // direction. However, this is usually used by IME, so, assuming the // character is in LRT context must not cause any problem. if (mSelection.isSome() && mSelection->mWritingMode.IsVertical()) {
aCaretRect.SetHeight(mCaret.isSome() ? mCaret->mRect.Height() : 1);
} else {
aCaretRect.SetWidth(mCaret.isSome() ? mCaret->mRect.Width() : 1);
} returntrue;
}
// We must be able to simulate the selection because // we might not receive selection updates in time if (!WidgetHasComposition()) { if (mCompositionStartInChild.isSome()) { // If there is pending composition in the remote process, let's use // its start offset temporarily because this stores a lot of information // around it and the user must look around there, so, showing some UI // around it must make sense.
mCompositionStart = mCompositionStartInChild;
} else {
mCompositionStart = Some(mSelection.isSome() && mSelection->mHasRange
? mSelection->StartOffset()
: 0u);
}
MOZ_ASSERT(aCompositionEvent.mMessage == eCompositionStart);
mHandlingCompositions.AppendElement(
HandlingCompositionData(aCompositionEvent.mCompositionId));
}
if (!WidgetHasComposition()) { // mCompositionStart will be reset when commit event is completely handled // in the remote process. if (mHandlingCompositions.Length() == 1u) {
mPendingCommitLength = aCompositionEvent.mData.Length();
}
MOZ_ASSERT(HasPendingCommit());
} elseif (aCompositionEvent.mMessage != eCompositionStart) {
mHandlingCompositions.LastElement().mCompositionString =
aCompositionEvent.mData;
}
// During REQUEST_TO_COMMIT_COMPOSITION or REQUEST_TO_CANCEL_COMPOSITION, // widget usually sends a eCompositionChange and/or eCompositionCommit event // to finalize or clear the composition, respectively. In this time, // we need to intercept all composition events here and pass the commit // string for returning to the remote process as a result of // RequestIMEToCommitComposition(). Then, eCommitComposition event will // be dispatched with the committed string in the remote process internally. if (mCommitStringByRequest) { if (aCompositionEvent.mMessage == eCompositionCommitAsIs) {
*mCommitStringByRequest =
mHandlingCompositions.LastElement().mCompositionString;
} else {
MOZ_ASSERT(aCompositionEvent.mMessage == eCompositionChange ||
aCompositionEvent.mMessage == eCompositionCommit);
*mCommitStringByRequest = aCompositionEvent.mData;
} // We need to wait eCompositionCommitRequestHandled from the remote process // in this case. Therefore, mPendingEventsNeedingAck needs to be // incremented here. if (!WidgetHasComposition()) {
mHandlingCompositions.LastElement().mPendingEventsNeedingAck++;
} returnfalse;
}
void ContentCacheInParent::OnEventNeedingAckHandled(nsIWidget* aWidget,
EventMessage aMessage,
uint32_t aCompositionId) { // This is called when the child process receives WidgetCompositionEvent, // WidgetSelectionEvent or WidgetContentCommandEvent.
constbool isCompositionEvent = [&]() { switch (aMessage) { case eCompositionStart: case eCompositionEnd: case eCompositionChange: case eCompositionCommitAsIs: case eCompositionCommit: case eCompositionCommitRequestHandled: returntrue; case eSetSelection: case eContentCommandCut: case eContentCommandCopy: case eContentCommandPaste: case eContentCommandDelete: case eContentCommandUndo: case eContentCommandRedo: case eContentCommandInsertText: case eContentCommandReplaceText: returnfalse; default:
NS_ASSERTION( false, nsPrintfCString( "%s message is NOT expected in OnEventNeedingAckHandled",
ToChar(aMessage))
.get()); returnfalse;
}
}();
// If we receive composition event messages for older one or invalid one, // we should ignore them. if (NS_WARN_IF(isCompositionEvent && !handlingCompositionData)) { return;
}
constbool isCommittedInChild = // Commit requester in the remote process has committed the composition.
aMessage == eCompositionCommitRequestHandled || // The commit event has been handled normally in the remote process.
(!mIsChildIgnoringCompositionEvents &&
WidgetCompositionEvent::IsFollowedByCompositionEnd(aMessage)); constbool hasPendingCommit = HasPendingCommit();
if (isCommittedInChild) { #if MOZ_DIAGNOSTIC_ASSERT_ENABLED && !defined(FUZZING_SNAPSHOT) if (mHandlingCompositions.Length() == 1u) {
RemoveUnnecessaryEventMessageLog();
}
if (NS_WARN_IF(aMessage != eCompositionCommitRequestHandled &&
!handlingCompositionData->mSentCommitEvent)) {
nsPrintfCString info( "\nReceived unexpected commit event message (%s) which we've " "not sent yet\n\n",
ToChar(aMessage));
AppendEventMessageLog(info);
CrashReporter::AppendAppNotesToCrashReport(info);
MOZ_DIAGNOSTIC_ASSERT( false, "Received unexpected commit event which has not been sent yet");
} #endif// #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
// This should not occur, though. If we receive a commit notification for // not the oldest composition, we should forget all older compositions.
size_t numberOfOutdatedCompositions = 1u; for (auto& data : mHandlingCompositions) { if (&data == handlingCompositionData) { if ( // Don't put the info into the log when we've already sent commit // event because it may be just inserting a character without
--> --------------------
--> maximum size reached
--> --------------------
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.33Angebot
Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.