2015-06-05 12:28:18 +03:00
|
|
|
/* -*- 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/. */
|
|
|
|
|
2022-02-08 01:33:40 +03:00
|
|
|
#include "ContentCache.h"
|
2017-07-05 18:47:40 +03:00
|
|
|
|
Bug 1609996 - Reorder some includes affected by the previous patches. r=froydnj
This was done by:
This was done by applying:
```
diff --git a/python/mozbuild/mozbuild/code-analysis/mach_commands.py b/python/mozbuild/mozbuild/code-analysis/mach_commands.py
index 789affde7bbf..fe33c4c7d4d1 100644
--- a/python/mozbuild/mozbuild/code-analysis/mach_commands.py
+++ b/python/mozbuild/mozbuild/code-analysis/mach_commands.py
@@ -2007,7 +2007,7 @@ class StaticAnalysis(MachCommandBase):
from subprocess import Popen, PIPE, check_output, CalledProcessError
diff_process = Popen(self._get_clang_format_diff_command(commit), stdout=PIPE)
- args = [sys.executable, clang_format_diff, "-p1", "-binary=%s" % clang_format]
+ args = [sys.executable, clang_format_diff, "-p1", "-binary=%s" % clang_format, '-sort-includes']
if not output_file:
args.append("-i")
```
Then running `./mach clang-format -c <commit-hash>`
Then undoing that patch.
Then running check_spidermonkey_style.py --fixup
Then running `./mach clang-format`
I had to fix four things:
* I needed to move <utility> back down in GuardObjects.h because I was hitting
obscure problems with our system include wrappers like this:
0:03.94 /usr/include/stdlib.h:550:14: error: exception specification in declaration does not match previous declaration
0:03.94 extern void *realloc (void *__ptr, size_t __size)
0:03.94 ^
0:03.94 /home/emilio/src/moz/gecko-2/obj-debug/dist/include/malloc_decls.h:53:1: note: previous declaration is here
0:03.94 MALLOC_DECL(realloc, void*, void*, size_t)
0:03.94 ^
0:03.94 /home/emilio/src/moz/gecko-2/obj-debug/dist/include/mozilla/mozalloc.h:22:32: note: expanded from macro 'MALLOC_DECL'
0:03.94 MOZ_MEMORY_API return_type name##_impl(__VA_ARGS__);
0:03.94 ^
0:03.94 <scratch space>:178:1: note: expanded from here
0:03.94 realloc_impl
0:03.94 ^
0:03.94 /home/emilio/src/moz/gecko-2/obj-debug/dist/include/mozmemory_wrap.h:142:41: note: expanded from macro 'realloc_impl'
0:03.94 #define realloc_impl mozmem_malloc_impl(realloc)
Which I really didn't feel like digging into.
* I had to restore the order of TrustOverrideUtils.h and related files in nss
because the .inc files depend on TrustOverrideUtils.h being included earlier.
* I had to add a missing include to RollingNumber.h
* Also had to partially restore include order in JsepSessionImpl.cpp to avoid
some -WError issues due to some static inline functions being defined in a
header but not used in the rest of the compilation unit.
Differential Revision: https://phabricator.services.mozilla.com/D60327
--HG--
extra : moz-landing-system : lando
2020-01-20 19:19:48 +03:00
|
|
|
#include <utility>
|
|
|
|
|
2022-02-08 01:33:40 +03:00
|
|
|
#include "IMEData.h"
|
|
|
|
#include "TextEvents.h"
|
|
|
|
|
2023-05-26 04:24:35 +03:00
|
|
|
#include "mozilla/Assertions.h"
|
2015-06-05 12:28:20 +03:00
|
|
|
#include "mozilla/IMEStateManager.h"
|
2017-07-05 18:47:40 +03:00
|
|
|
#include "mozilla/IntegerPrintfMacros.h"
|
2015-06-05 12:28:20 +03:00
|
|
|
#include "mozilla/Logging.h"
|
2017-07-05 18:47:40 +03:00
|
|
|
#include "mozilla/RefPtr.h"
|
2015-06-05 12:28:20 +03:00
|
|
|
#include "mozilla/TextComposition.h"
|
2019-04-10 00:38:15 +03:00
|
|
|
#include "mozilla/dom/BrowserParent.h"
|
2017-10-12 20:50:47 +03:00
|
|
|
#include "nsExceptionHandler.h"
|
2015-06-05 12:28:19 +03:00
|
|
|
#include "nsIWidget.h"
|
Bug 1690827 - part 5: Remove the assertion checking the number of pending composition event messages when receiving a commit request r=m_kato
E.g., when a character is inserted with a composition, i.e., without composing
state, like the Emoji picker of Windows, `BrowserParent` dispatches
`eCompositionStart`, `eCompositionChange` and `eCompositionCommit` message
within a short time. Then, if focus is moved at `compositionstart` event in
the remote message, `eCompositionChange` and `eCompositionCommit` are not yet
handled by the remote process when `BrowserParent::OnEventNeedingAckHandled()`
received a commit request handled message. Therefore, it's not invalid case
that a commit is handled without acknowledges of multiple pending composition
messages. Therefore, the assertion should be removed, and instead, for making
it easier to understand the log, let's put the information of that. Then, it's
obvious that ignored log of following `OnEventNeedingAckHandled()` calls is
valid thing.
Differential Revision: https://phabricator.services.mozilla.com/D181217
2023-06-20 04:53:39 +03:00
|
|
|
#include "nsPrintfCString.h"
|
2015-06-05 12:28:18 +03:00
|
|
|
|
|
|
|
namespace mozilla {
|
|
|
|
|
2017-07-05 18:47:40 +03:00
|
|
|
using namespace dom;
|
2015-06-05 12:28:19 +03:00
|
|
|
using namespace widget;
|
|
|
|
|
2015-06-05 12:28:20 +03:00
|
|
|
static const char* GetBoolName(bool aBool) { return aBool ? "true" : "false"; }
|
|
|
|
|
|
|
|
static const char* GetNotificationName(const IMENotification* aNotification) {
|
|
|
|
if (!aNotification) {
|
|
|
|
return "Not notification";
|
|
|
|
}
|
2016-05-31 05:39:15 +03:00
|
|
|
return ToChar(aNotification->mMessage);
|
2015-06-05 12:28:20 +03:00
|
|
|
}
|
|
|
|
|
2015-06-05 12:28:19 +03:00
|
|
|
/*****************************************************************************
|
|
|
|
* mozilla::ContentCache
|
|
|
|
*****************************************************************************/
|
|
|
|
|
2016-06-13 12:17:58 +03:00
|
|
|
LazyLogModule sContentCacheLog("ContentCacheWidgets");
|
2015-06-05 12:28:20 +03:00
|
|
|
|
2023-05-26 04:24:35 +03:00
|
|
|
bool ContentCache::IsValid() const {
|
|
|
|
if (mText.isNothing()) {
|
|
|
|
// mSelection and mCaret depend on mText.
|
|
|
|
if (NS_WARN_IF(mSelection.isSome()) || NS_WARN_IF(mCaret.isSome())) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// mSelection depends on mText.
|
2023-05-26 04:24:36 +03:00
|
|
|
if (mSelection.isSome() && NS_WARN_IF(!mSelection->IsValidIn(*mText))) {
|
2023-05-26 04:24:35 +03:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// mCaret depends on mSelection.
|
|
|
|
if (mCaret.isSome() &&
|
|
|
|
(NS_WARN_IF(mSelection.isNothing()) ||
|
|
|
|
NS_WARN_IF(!mSelection->mHasRange) ||
|
|
|
|
NS_WARN_IF(mSelection->StartOffset() != mCaret->Offset()))) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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())) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-07-08 02:13:20 +03:00
|
|
|
void ContentCache::AssertIfInvalid() const {
|
|
|
|
#if MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
|
|
|
if (IsValid()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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_ASSERT(false, "Invalid ContentCache data");
|
|
|
|
#endif // #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
|
|
|
}
|
|
|
|
|
2015-06-26 02:21:13 +03:00
|
|
|
/*****************************************************************************
|
|
|
|
* mozilla::ContentCacheInChild
|
|
|
|
*****************************************************************************/
|
2015-06-05 12:28:20 +03:00
|
|
|
|
2015-06-26 02:21:13 +03:00
|
|
|
void ContentCacheInChild::Clear() {
|
2015-06-05 12:28:20 +03:00
|
|
|
MOZ_LOG(sContentCacheLog, LogLevel::Info, ("0x%p Clear()", this));
|
2015-06-05 12:28:21 +03:00
|
|
|
|
2020-11-23 12:29:34 +03:00
|
|
|
mCompositionStart.reset();
|
2020-11-27 17:47:30 +03:00
|
|
|
mLastCommit.reset();
|
2022-02-08 01:33:40 +03:00
|
|
|
mText.reset();
|
2020-11-27 17:47:37 +03:00
|
|
|
mSelection.reset();
|
2015-06-17 04:03:58 +03:00
|
|
|
mFirstCharRect.SetEmpty();
|
2020-11-27 17:47:50 +03:00
|
|
|
mCaret.reset();
|
2020-11-27 17:48:03 +03:00
|
|
|
mTextRectArray.reset();
|
|
|
|
mLastCommitStringTextRectArray.reset();
|
2015-06-05 12:28:20 +03:00
|
|
|
mEditorRect.SetEmpty();
|
2015-06-05 12:28:18 +03:00
|
|
|
}
|
|
|
|
|
2020-11-24 04:31:46 +03:00
|
|
|
void ContentCacheInChild::OnCompositionEvent(
|
|
|
|
const WidgetCompositionEvent& aCompositionEvent) {
|
|
|
|
if (aCompositionEvent.CausesDOMCompositionEndEvent()) {
|
|
|
|
RefPtr<TextComposition> composition =
|
|
|
|
IMEStateManager::GetTextCompositionFor(aCompositionEvent.mWidget);
|
|
|
|
if (composition) {
|
2020-11-27 17:47:30 +03:00
|
|
|
nsAutoString lastCommitString;
|
2020-11-24 04:31:46 +03:00
|
|
|
if (aCompositionEvent.mMessage == eCompositionCommitAsIs) {
|
2020-11-27 17:47:30 +03:00
|
|
|
lastCommitString = composition->CommitStringIfCommittedAsIs();
|
2020-11-24 04:31:46 +03:00
|
|
|
} else {
|
2020-11-27 17:47:30 +03:00
|
|
|
lastCommitString = aCompositionEvent.mData;
|
2020-11-24 04:31:46 +03:00
|
|
|
}
|
|
|
|
// We don't need to store canceling information because this is required
|
|
|
|
// by undoing of last commit (Kakutei-Undo of Japanese IME).
|
2020-11-27 17:47:30 +03:00
|
|
|
if (!lastCommitString.IsEmpty()) {
|
|
|
|
mLastCommit = Some(OffsetAndData<uint32_t>(
|
|
|
|
composition->NativeOffsetOfStartComposition(), lastCommitString));
|
2020-11-24 04:31:46 +03:00
|
|
|
MOZ_LOG(
|
|
|
|
sContentCacheLog, LogLevel::Debug,
|
|
|
|
("0x%p OnCompositionEvent(), stored last composition string data "
|
2020-11-27 17:47:30 +03:00
|
|
|
"(aCompositionEvent={ mMessage=%s, mData=\"%s\"}, mLastCommit=%s)",
|
2020-11-24 04:31:46 +03:00
|
|
|
this, ToChar(aCompositionEvent.mMessage),
|
2020-11-27 17:48:03 +03:00
|
|
|
PrintStringDetail(
|
|
|
|
aCompositionEvent.mData,
|
|
|
|
PrintStringDetail::kMaxLengthForCompositionString)
|
|
|
|
.get(),
|
2020-11-27 17:47:30 +03:00
|
|
|
ToString(mLastCommit).c_str()));
|
2020-11-24 04:31:46 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-11-27 17:47:30 +03:00
|
|
|
if (mLastCommit.isSome()) {
|
2020-11-27 17:48:03 +03:00
|
|
|
MOZ_LOG(
|
|
|
|
sContentCacheLog, LogLevel::Debug,
|
|
|
|
("0x%p OnCompositionEvent(), resetting the last composition string "
|
|
|
|
"data (aCompositionEvent={ mMessage=%s, mData=\"%s\"}, "
|
|
|
|
"mLastCommit=%s)",
|
|
|
|
this, ToChar(aCompositionEvent.mMessage),
|
|
|
|
PrintStringDetail(aCompositionEvent.mData,
|
|
|
|
PrintStringDetail::kMaxLengthForCompositionString)
|
|
|
|
.get(),
|
|
|
|
ToString(mLastCommit).c_str()));
|
2020-11-27 17:47:30 +03:00
|
|
|
mLastCommit.reset();
|
2020-11-24 04:31:46 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-26 02:21:13 +03:00
|
|
|
bool ContentCacheInChild::CacheAll(nsIWidget* aWidget,
|
|
|
|
const IMENotification* aNotification) {
|
2015-06-05 12:28:20 +03:00
|
|
|
MOZ_LOG(sContentCacheLog, LogLevel::Info,
|
2016-07-05 12:56:18 +03:00
|
|
|
("0x%p CacheAll(aWidget=0x%p, aNotification=%s)", this, aWidget,
|
2015-06-26 02:21:13 +03:00
|
|
|
GetNotificationName(aNotification)));
|
2015-06-05 12:28:20 +03:00
|
|
|
|
2022-02-08 01:33:41 +03:00
|
|
|
const bool textCached = CacheText(aWidget, aNotification);
|
|
|
|
const bool editorRectCached = CacheEditorRect(aWidget, aNotification);
|
2023-07-08 02:13:20 +03:00
|
|
|
AssertIfInvalid();
|
2023-07-07 06:25:52 +03:00
|
|
|
return (textCached || editorRectCached) && IsValid();
|
2015-06-05 12:28:20 +03:00
|
|
|
}
|
|
|
|
|
2015-06-26 02:21:13 +03:00
|
|
|
bool ContentCacheInChild::CacheSelection(nsIWidget* aWidget,
|
|
|
|
const IMENotification* aNotification) {
|
2023-05-26 04:24:35 +03:00
|
|
|
MOZ_LOG(
|
|
|
|
sContentCacheLog, LogLevel::Info,
|
|
|
|
("0x%p CacheSelection(aWidget=0x%p, aNotification=%s), mText=%s", this,
|
|
|
|
aWidget, GetNotificationName(aNotification),
|
|
|
|
PrintStringDetail(mText, PrintStringDetail::kMaxLengthForEditor).get()));
|
2015-06-05 12:28:20 +03:00
|
|
|
|
2020-11-27 17:47:37 +03:00
|
|
|
mSelection.reset();
|
2023-05-26 04:24:36 +03:00
|
|
|
mCaret.reset();
|
2015-06-05 12:28:20 +03:00
|
|
|
|
2023-05-26 04:24:35 +03:00
|
|
|
if (mText.isNothing()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-06-05 12:28:20 +03:00
|
|
|
nsEventStatus status = nsEventStatus_eIgnore;
|
2020-12-02 08:32:19 +03:00
|
|
|
WidgetQueryContentEvent querySelectedTextEvent(true, eQuerySelectedText,
|
|
|
|
aWidget);
|
|
|
|
aWidget->DispatchEvent(&querySelectedTextEvent, status);
|
2022-02-08 01:33:41 +03:00
|
|
|
if (NS_WARN_IF(querySelectedTextEvent.Failed())) {
|
2020-12-02 08:32:19 +03:00
|
|
|
MOZ_LOG(
|
|
|
|
sContentCacheLog, LogLevel::Error,
|
|
|
|
("0x%p CacheSelection(), FAILED, couldn't retrieve the selected text",
|
|
|
|
this));
|
2023-05-26 04:24:35 +03:00
|
|
|
// XXX Allowing selection-independent character rects makes things
|
|
|
|
// complicated in the parent...
|
2022-04-07 03:58:49 +03:00
|
|
|
}
|
|
|
|
// 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.
|
2023-05-26 04:24:35 +03:00
|
|
|
else if (NS_WARN_IF(!querySelectedTextEvent.mReply->mIsEditableContent)) {
|
2022-04-07 03:58:49 +03:00
|
|
|
MOZ_LOG(sContentCacheLog, LogLevel::Error,
|
|
|
|
("0x%p CacheSelection(), FAILED, editable content had already been "
|
|
|
|
"blurred",
|
|
|
|
this));
|
2023-07-08 02:13:20 +03:00
|
|
|
AssertIfInvalid();
|
2022-04-07 03:58:49 +03:00
|
|
|
return false;
|
2022-02-08 01:33:41 +03:00
|
|
|
} else {
|
|
|
|
mSelection.emplace(querySelectedTextEvent);
|
2015-06-05 12:28:20 +03:00
|
|
|
}
|
|
|
|
|
2023-07-07 06:25:52 +03:00
|
|
|
return (CacheCaretAndTextRects(aWidget, aNotification) ||
|
|
|
|
querySelectedTextEvent.Succeeded()) &&
|
|
|
|
IsValid();
|
2015-06-05 12:28:20 +03:00
|
|
|
}
|
|
|
|
|
2015-06-26 02:21:13 +03:00
|
|
|
bool ContentCacheInChild::CacheCaret(nsIWidget* aWidget,
|
|
|
|
const IMENotification* aNotification) {
|
2020-11-27 17:47:50 +03:00
|
|
|
mCaret.reset();
|
2015-06-05 12:28:20 +03:00
|
|
|
|
2023-05-26 04:24:36 +03:00
|
|
|
if (mSelection.isNothing()) {
|
2015-06-05 12:28:21 +03:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-02-08 01:33:41 +03:00
|
|
|
MOZ_LOG(sContentCacheLog, LogLevel::Info,
|
|
|
|
("0x%p CacheCaret(aWidget=0x%p, aNotification=%s)", this, aWidget,
|
|
|
|
GetNotificationName(aNotification)));
|
|
|
|
|
2022-02-08 01:33:40 +03:00
|
|
|
if (mSelection->mHasRange) {
|
|
|
|
// XXX Should be mSelection.mFocus?
|
|
|
|
const uint32_t offset = mSelection->StartOffset();
|
2015-06-05 12:28:20 +03:00
|
|
|
|
2022-02-08 01:33:40 +03:00
|
|
|
nsEventStatus status = nsEventStatus_eIgnore;
|
2023-06-14 04:57:34 +03:00
|
|
|
WidgetQueryContentEvent queryCaretRectEvent(true, eQueryCaretRect, aWidget);
|
|
|
|
queryCaretRectEvent.InitForQueryCaretRect(offset);
|
|
|
|
aWidget->DispatchEvent(&queryCaretRectEvent, status);
|
|
|
|
if (NS_WARN_IF(queryCaretRectEvent.Failed())) {
|
2022-02-08 01:33:40 +03:00
|
|
|
MOZ_LOG(sContentCacheLog, LogLevel::Error,
|
|
|
|
("0x%p CacheCaret(), FAILED, couldn't retrieve the caret rect "
|
|
|
|
"at offset=%u",
|
|
|
|
this, offset));
|
|
|
|
return false;
|
|
|
|
}
|
2023-06-14 04:57:34 +03:00
|
|
|
mCaret.emplace(offset, queryCaretRectEvent.mReply->mRect);
|
2015-06-05 12:28:20 +03:00
|
|
|
}
|
2015-06-05 12:28:20 +03:00
|
|
|
MOZ_LOG(sContentCacheLog, LogLevel::Info,
|
2022-02-08 01:33:39 +03:00
|
|
|
("0x%p CacheCaret(), Succeeded, mSelection=%s, mCaret=%s", this,
|
|
|
|
ToString(mSelection).c_str(), ToString(mCaret).c_str()));
|
2023-07-08 02:13:20 +03:00
|
|
|
AssertIfInvalid();
|
2023-07-07 06:25:52 +03:00
|
|
|
return IsValid();
|
2015-06-05 12:28:20 +03:00
|
|
|
}
|
|
|
|
|
2015-06-26 02:21:13 +03:00
|
|
|
bool ContentCacheInChild::CacheEditorRect(
|
|
|
|
nsIWidget* aWidget, const IMENotification* aNotification) {
|
2015-06-05 12:28:20 +03:00
|
|
|
MOZ_LOG(sContentCacheLog, LogLevel::Info,
|
2016-07-05 12:56:18 +03:00
|
|
|
("0x%p CacheEditorRect(aWidget=0x%p, aNotification=%s)", this,
|
2015-06-26 02:21:13 +03:00
|
|
|
aWidget, GetNotificationName(aNotification)));
|
2015-06-05 12:28:20 +03:00
|
|
|
|
2015-06-05 12:28:20 +03:00
|
|
|
nsEventStatus status = nsEventStatus_eIgnore;
|
2020-12-02 08:32:19 +03:00
|
|
|
WidgetQueryContentEvent queryEditorRectEvent(true, eQueryEditorRect, aWidget);
|
|
|
|
aWidget->DispatchEvent(&queryEditorRectEvent, status);
|
|
|
|
if (NS_WARN_IF(queryEditorRectEvent.Failed())) {
|
2022-02-08 01:33:39 +03:00
|
|
|
MOZ_LOG(
|
|
|
|
sContentCacheLog, LogLevel::Error,
|
|
|
|
("0x%p CacheEditorRect(), FAILED, couldn't retrieve the editor rect",
|
|
|
|
this));
|
2015-06-05 12:28:20 +03:00
|
|
|
return false;
|
|
|
|
}
|
2022-04-07 03:58:49 +03:00
|
|
|
// 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));
|
|
|
|
return false;
|
|
|
|
}
|
2020-12-02 08:32:19 +03:00
|
|
|
mEditorRect = queryEditorRectEvent.mReply->mRect;
|
2015-06-05 12:28:20 +03:00
|
|
|
MOZ_LOG(sContentCacheLog, LogLevel::Info,
|
2022-02-08 01:33:39 +03:00
|
|
|
("0x%p CacheEditorRect(), Succeeded, mEditorRect=%s", this,
|
|
|
|
ToString(mEditorRect).c_str()));
|
2015-06-05 12:28:20 +03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
Bug 1825693 - Make `PuppetWidget` stop trying to cache `Selection` directly r=m_kato
Currently, `PuppetWidget` calls `ContentCacheInChild::CacheSelection` directly.
However, `mText` can be `Nothing` or `mText` can be outdated, but `mSelection`
becomes the latest one. Therefore, we may notify the parent process with
invalid data combination.
The callers in `PuppetWidget` are:
1. `NotifyIMEOfCompositionUpdate`
2. `NotifyIMEOfPositionChange`
I think that the former does not need to cache anything here because
`IMEContentObserver` should've updated the text/selection changes. However,
stopping caching everything at this point is risky. In the most cases, outdated
data appears as odd IME UI position. Therefore, let's keep updating only caret
and text rectangles.
The latter is reported with new crash reports which is crashed by a
`MOZ_DIAGNOSTIC_ASSERT` failure added by the previous patch. In the case,
if `mText` and `mSelection` has not been cached, we don't need to notify
the parent process with them because they will be sent later. And also even
if they are not available, it may be useful that the character rectangles not
related to `Selection` (e.g., the first character rect). Therefore, let's
keep caching same things as the former case.
Therefore, this patch makes `CacheSelection` a private method and add
`CacheCaretAndTextRects` for them.
Differential Revision: https://phabricator.services.mozilla.com/D178145
2023-05-26 04:24:36 +03:00
|
|
|
bool ContentCacheInChild::CacheCaretAndTextRects(
|
|
|
|
nsIWidget* aWidget, const IMENotification* aNotification) {
|
|
|
|
MOZ_LOG(sContentCacheLog, LogLevel::Info,
|
|
|
|
("0x%p CacheCaretAndTextRects(aWidget=0x%p, aNotification=%s)", this,
|
|
|
|
aWidget, GetNotificationName(aNotification)));
|
|
|
|
|
2023-05-26 04:24:36 +03:00
|
|
|
const bool caretCached = CacheCaret(aWidget, aNotification);
|
Bug 1825693 - Make `PuppetWidget` stop trying to cache `Selection` directly r=m_kato
Currently, `PuppetWidget` calls `ContentCacheInChild::CacheSelection` directly.
However, `mText` can be `Nothing` or `mText` can be outdated, but `mSelection`
becomes the latest one. Therefore, we may notify the parent process with
invalid data combination.
The callers in `PuppetWidget` are:
1. `NotifyIMEOfCompositionUpdate`
2. `NotifyIMEOfPositionChange`
I think that the former does not need to cache anything here because
`IMEContentObserver` should've updated the text/selection changes. However,
stopping caching everything at this point is risky. In the most cases, outdated
data appears as odd IME UI position. Therefore, let's keep updating only caret
and text rectangles.
The latter is reported with new crash reports which is crashed by a
`MOZ_DIAGNOSTIC_ASSERT` failure added by the previous patch. In the case,
if `mText` and `mSelection` has not been cached, we don't need to notify
the parent process with them because they will be sent later. And also even
if they are not available, it may be useful that the character rectangles not
related to `Selection` (e.g., the first character rect). Therefore, let's
keep caching same things as the former case.
Therefore, this patch makes `CacheSelection` a private method and add
`CacheCaretAndTextRects` for them.
Differential Revision: https://phabricator.services.mozilla.com/D178145
2023-05-26 04:24:36 +03:00
|
|
|
const bool textRectsCached = CacheTextRects(aWidget, aNotification);
|
2023-07-08 02:13:20 +03:00
|
|
|
AssertIfInvalid();
|
2023-07-07 06:25:52 +03:00
|
|
|
return (caretCached || textRectsCached) && IsValid();
|
Bug 1825693 - Make `PuppetWidget` stop trying to cache `Selection` directly r=m_kato
Currently, `PuppetWidget` calls `ContentCacheInChild::CacheSelection` directly.
However, `mText` can be `Nothing` or `mText` can be outdated, but `mSelection`
becomes the latest one. Therefore, we may notify the parent process with
invalid data combination.
The callers in `PuppetWidget` are:
1. `NotifyIMEOfCompositionUpdate`
2. `NotifyIMEOfPositionChange`
I think that the former does not need to cache anything here because
`IMEContentObserver` should've updated the text/selection changes. However,
stopping caching everything at this point is risky. In the most cases, outdated
data appears as odd IME UI position. Therefore, let's keep updating only caret
and text rectangles.
The latter is reported with new crash reports which is crashed by a
`MOZ_DIAGNOSTIC_ASSERT` failure added by the previous patch. In the case,
if `mText` and `mSelection` has not been cached, we don't need to notify
the parent process with them because they will be sent later. And also even
if they are not available, it may be useful that the character rectangles not
related to `Selection` (e.g., the first character rect). Therefore, let's
keep caching same things as the former case.
Therefore, this patch makes `CacheSelection` a private method and add
`CacheCaretAndTextRects` for them.
Differential Revision: https://phabricator.services.mozilla.com/D178145
2023-05-26 04:24:36 +03:00
|
|
|
}
|
|
|
|
|
2015-06-26 02:21:13 +03:00
|
|
|
bool ContentCacheInChild::CacheText(nsIWidget* aWidget,
|
|
|
|
const IMENotification* aNotification) {
|
2015-06-05 12:28:20 +03:00
|
|
|
MOZ_LOG(sContentCacheLog, LogLevel::Info,
|
2016-07-05 12:56:18 +03:00
|
|
|
("0x%p CacheText(aWidget=0x%p, aNotification=%s)", this, aWidget,
|
2015-06-26 02:21:13 +03:00
|
|
|
GetNotificationName(aNotification)));
|
2015-06-05 12:28:20 +03:00
|
|
|
|
2015-06-05 12:28:20 +03:00
|
|
|
nsEventStatus status = nsEventStatus_eIgnore;
|
2020-12-02 08:32:19 +03:00
|
|
|
WidgetQueryContentEvent queryTextContentEvent(true, eQueryTextContent,
|
|
|
|
aWidget);
|
|
|
|
queryTextContentEvent.InitForQueryTextContent(0, UINT32_MAX);
|
|
|
|
aWidget->DispatchEvent(&queryTextContentEvent, status);
|
|
|
|
if (NS_WARN_IF(queryTextContentEvent.Failed())) {
|
2015-06-05 12:28:20 +03:00
|
|
|
MOZ_LOG(sContentCacheLog, LogLevel::Error,
|
2022-02-08 01:33:39 +03:00
|
|
|
("0x%p CacheText(), FAILED, couldn't retrieve whole text", this));
|
2022-02-08 01:33:40 +03:00
|
|
|
mText.reset();
|
2022-04-07 03:58:49 +03:00
|
|
|
}
|
|
|
|
// 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.
|
|
|
|
else if (NS_WARN_IF(!queryTextContentEvent.mReply->mIsEditableContent)) {
|
|
|
|
MOZ_LOG(sContentCacheLog, LogLevel::Error,
|
|
|
|
("0x%p CacheText(), FAILED, editable content had already been "
|
|
|
|
"blurred",
|
|
|
|
this));
|
|
|
|
mText.reset();
|
2022-02-08 01:33:41 +03:00
|
|
|
} else {
|
|
|
|
mText = Some(nsString(queryTextContentEvent.mReply->DataRef()));
|
|
|
|
MOZ_LOG(sContentCacheLog, LogLevel::Info,
|
|
|
|
("0x%p CacheText(), Succeeded, mText=%s", this,
|
|
|
|
PrintStringDetail(mText, PrintStringDetail::kMaxLengthForEditor)
|
|
|
|
.get()));
|
2015-06-05 12:28:20 +03:00
|
|
|
}
|
2015-06-05 12:28:20 +03:00
|
|
|
|
2020-11-24 04:31:46 +03:00
|
|
|
// Forget last commit range if string in the range is different from the
|
|
|
|
// last commit string.
|
2020-11-27 17:47:30 +03:00
|
|
|
if (mLastCommit.isSome() &&
|
2022-02-08 01:33:41 +03:00
|
|
|
(mText.isNothing() ||
|
|
|
|
nsDependentSubstring(mText.ref(), mLastCommit->StartOffset(),
|
|
|
|
mLastCommit->Length()) != mLastCommit->DataRef())) {
|
2020-11-24 04:31:46 +03:00
|
|
|
MOZ_LOG(sContentCacheLog, LogLevel::Debug,
|
2022-02-08 01:33:39 +03:00
|
|
|
("0x%p CacheText(), resetting the last composition string data "
|
2020-11-27 17:47:30 +03:00
|
|
|
"(mLastCommit=%s, current string=\"%s\")",
|
|
|
|
this, ToString(mLastCommit).c_str(),
|
|
|
|
PrintStringDetail(
|
2022-02-08 01:33:40 +03:00
|
|
|
nsDependentSubstring(mText.ref(), mLastCommit->StartOffset(),
|
2020-11-27 17:47:30 +03:00
|
|
|
mLastCommit->Length()),
|
|
|
|
PrintStringDetail::kMaxLengthForCompositionString)
|
2020-11-24 04:31:46 +03:00
|
|
|
.get()));
|
2020-11-27 17:47:30 +03:00
|
|
|
mLastCommit.reset();
|
2020-11-24 04:31:46 +03:00
|
|
|
}
|
|
|
|
|
2022-04-07 03:58:49 +03:00
|
|
|
// 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
|
2023-05-26 04:24:35 +03:00
|
|
|
if (MOZ_UNLIKELY(mText.isNothing())) {
|
2022-04-07 03:58:49 +03:00
|
|
|
mSelection.reset();
|
|
|
|
mCaret.reset();
|
|
|
|
mTextRectArray.reset();
|
2023-07-08 02:13:20 +03:00
|
|
|
AssertIfInvalid();
|
2022-04-07 03:58:49 +03:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return CacheSelection(aWidget, aNotification);
|
2015-06-05 12:28:20 +03:00
|
|
|
}
|
|
|
|
|
2015-06-26 02:21:13 +03:00
|
|
|
bool ContentCacheInChild::QueryCharRect(nsIWidget* aWidget, uint32_t aOffset,
|
|
|
|
LayoutDeviceIntRect& aCharRect) const {
|
2015-06-05 12:28:20 +03:00
|
|
|
aCharRect.SetEmpty();
|
|
|
|
|
|
|
|
nsEventStatus status = nsEventStatus_eIgnore;
|
2020-12-02 08:32:19 +03:00
|
|
|
WidgetQueryContentEvent queryTextRectEvent(true, eQueryTextRect, aWidget);
|
|
|
|
queryTextRectEvent.InitForQueryTextRect(aOffset, 1);
|
|
|
|
aWidget->DispatchEvent(&queryTextRectEvent, status);
|
|
|
|
if (NS_WARN_IF(queryTextRectEvent.Failed())) {
|
2015-06-05 12:28:20 +03:00
|
|
|
return false;
|
|
|
|
}
|
2020-12-02 08:32:19 +03:00
|
|
|
aCharRect = queryTextRectEvent.mReply->mRect;
|
2015-06-05 12:28:20 +03:00
|
|
|
|
|
|
|
// Guarantee the rect is not empty.
|
2018-01-10 19:14:16 +03:00
|
|
|
if (NS_WARN_IF(!aCharRect.Height())) {
|
|
|
|
aCharRect.SetHeight(1);
|
2015-06-05 12:28:20 +03:00
|
|
|
}
|
2018-01-10 19:14:16 +03:00
|
|
|
if (NS_WARN_IF(!aCharRect.Width())) {
|
|
|
|
aCharRect.SetWidth(1);
|
2015-06-05 12:28:20 +03:00
|
|
|
}
|
2015-06-05 12:28:20 +03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-07-22 14:47:51 +03:00
|
|
|
bool ContentCacheInChild::QueryCharRectArray(nsIWidget* aWidget,
|
|
|
|
uint32_t aOffset, uint32_t aLength,
|
|
|
|
RectArray& aCharRectArray) const {
|
|
|
|
nsEventStatus status = nsEventStatus_eIgnore;
|
2020-12-02 08:32:19 +03:00
|
|
|
WidgetQueryContentEvent queryTextRectsEvent(true, eQueryTextRectArray,
|
|
|
|
aWidget);
|
|
|
|
queryTextRectsEvent.InitForQueryTextRectArray(aOffset, aLength);
|
|
|
|
aWidget->DispatchEvent(&queryTextRectsEvent, status);
|
|
|
|
if (NS_WARN_IF(queryTextRectsEvent.Failed())) {
|
2016-07-22 14:47:51 +03:00
|
|
|
aCharRectArray.Clear();
|
|
|
|
return false;
|
|
|
|
}
|
2020-12-02 08:32:19 +03:00
|
|
|
aCharRectArray = std::move(queryTextRectsEvent.mReply->mRectArray);
|
2016-07-22 14:47:51 +03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-06-26 02:21:13 +03:00
|
|
|
bool ContentCacheInChild::CacheTextRects(nsIWidget* aWidget,
|
|
|
|
const IMENotification* aNotification) {
|
2020-11-27 17:47:50 +03:00
|
|
|
MOZ_LOG(
|
|
|
|
sContentCacheLog, LogLevel::Info,
|
|
|
|
("0x%p CacheTextRects(aWidget=0x%p, aNotification=%s), mCaret=%s", this,
|
|
|
|
aWidget, GetNotificationName(aNotification), ToString(mCaret).c_str()));
|
2015-06-05 12:28:20 +03:00
|
|
|
|
2022-02-08 01:33:41 +03:00
|
|
|
if (mSelection.isSome()) {
|
|
|
|
mSelection->ClearRects();
|
2015-06-05 12:28:21 +03:00
|
|
|
}
|
|
|
|
|
2015-06-05 12:28:20 +03:00
|
|
|
// Retrieve text rects in composition string if there is.
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<TextComposition> textComposition =
|
2015-06-05 12:28:20 +03:00
|
|
|
IMEStateManager::GetTextCompositionFor(aWidget);
|
2015-06-05 12:28:20 +03:00
|
|
|
if (textComposition) {
|
2016-06-13 12:17:58 +03:00
|
|
|
// mCompositionStart may be updated by some composition event handlers.
|
|
|
|
// So, let's update it with the latest information.
|
2020-11-23 12:29:34 +03:00
|
|
|
mCompositionStart = Some(textComposition->NativeOffsetOfStartComposition());
|
2015-06-05 12:28:20 +03:00
|
|
|
// 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.
|
2017-06-29 12:31:09 +03:00
|
|
|
// 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;
|
2022-02-08 01:33:41 +03:00
|
|
|
mTextRectArray = Some(TextRectArray(mCompositionStart.value()));
|
2020-11-27 17:48:03 +03:00
|
|
|
if (NS_WARN_IF(!QueryCharRectArray(aWidget, mTextRectArray->mStart, length,
|
|
|
|
mTextRectArray->mRects))) {
|
2016-07-22 14:47:51 +03:00
|
|
|
MOZ_LOG(sContentCacheLog, LogLevel::Error,
|
2022-02-08 01:33:39 +03:00
|
|
|
("0x%p CacheTextRects(), FAILED, "
|
2016-07-22 14:47:51 +03:00
|
|
|
"couldn't retrieve text rect array of the composition string",
|
|
|
|
this));
|
2020-11-27 17:48:03 +03:00
|
|
|
mTextRectArray.reset();
|
2016-07-22 14:47:51 +03:00
|
|
|
}
|
2022-02-08 01:33:41 +03:00
|
|
|
} else {
|
|
|
|
mCompositionStart.reset();
|
|
|
|
mTextRectArray.reset();
|
2015-06-05 12:28:20 +03:00
|
|
|
}
|
|
|
|
|
2023-05-26 04:24:36 +03:00
|
|
|
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);
|
2022-02-08 01:33:41 +03:00
|
|
|
}
|
2023-05-26 04:24:36 +03:00
|
|
|
}
|
|
|
|
// 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());
|
|
|
|
} else if (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());
|
|
|
|
}
|
2016-07-22 14:47:51 +03:00
|
|
|
}
|
2015-06-05 12:28:20 +03:00
|
|
|
}
|
2015-06-05 12:28:20 +03:00
|
|
|
|
2022-02-08 01:33:41 +03:00
|
|
|
// 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];
|
2016-07-22 14:47:51 +03:00
|
|
|
}
|
2022-02-08 01:33:41 +03:00
|
|
|
// If the selection range is in mTextRectArray, save the query cost.
|
|
|
|
else if (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))) {
|
2023-01-18 04:32:55 +03:00
|
|
|
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());
|
|
|
|
} else if (NS_WARN_IF(mSelection.isNothing())) {
|
2022-02-08 01:33:41 +03:00
|
|
|
MOZ_LOG(sContentCacheLog, LogLevel::Error,
|
2023-01-18 04:32:55 +03:00
|
|
|
("0x%p CacheTextRects(), FAILED, mSelection was reset during "
|
|
|
|
"the call of QueryCharRectArray",
|
|
|
|
this));
|
2022-02-08 01:33:41 +03:00
|
|
|
} else {
|
|
|
|
if (rects.Length() > 1) {
|
|
|
|
mSelection->mFocusCharRects[ePrevCharRect] = rects[0];
|
|
|
|
mSelection->mFocusCharRects[eNextCharRect] = rects[1];
|
|
|
|
} else if (rects.Length()) {
|
|
|
|
mSelection->mFocusCharRects[eNextCharRect] = rects[0];
|
|
|
|
MOZ_ASSERT(mSelection->mFocusCharRects[ePrevCharRect].IsEmpty());
|
|
|
|
}
|
2016-07-22 14:47:51 +03:00
|
|
|
}
|
2015-06-05 12:28:20 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-08 01:33:40 +03:00
|
|
|
// 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.
|
2022-02-08 01:33:41 +03:00
|
|
|
if (mSelection.isSome() && mSelection->mHasRange &&
|
|
|
|
!mSelection->IsCollapsed()) {
|
2015-06-08 05:46:17 +03:00
|
|
|
nsEventStatus status = nsEventStatus_eIgnore;
|
2020-12-02 08:32:19 +03:00
|
|
|
WidgetQueryContentEvent queryTextRectEvent(true, eQueryTextRect, aWidget);
|
|
|
|
queryTextRectEvent.InitForQueryTextRect(mSelection->StartOffset(),
|
|
|
|
mSelection->Length());
|
|
|
|
aWidget->DispatchEvent(&queryTextRectEvent, status);
|
|
|
|
if (NS_WARN_IF(queryTextRectEvent.Failed())) {
|
2015-06-08 05:46:17 +03:00
|
|
|
MOZ_LOG(sContentCacheLog, LogLevel::Error,
|
2022-02-08 01:33:40 +03:00
|
|
|
("0x%p CacheTextRects(), FAILED, "
|
2015-06-26 02:21:13 +03:00
|
|
|
"couldn't retrieve text rect of whole selected text",
|
|
|
|
this));
|
2015-06-08 05:46:17 +03:00
|
|
|
} else {
|
2020-12-02 08:32:19 +03:00
|
|
|
mSelection->mRect = queryTextRectEvent.mReply->mRect;
|
2015-06-08 05:46:17 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-08 01:33:40 +03:00
|
|
|
// Even if there is no selection range, we should have the first character
|
|
|
|
// rect for the last resort of suggesting position of IME UI.
|
2022-02-08 01:33:41 +03:00
|
|
|
if (mSelection.isSome() && mSelection->mHasRange && !mSelection->mFocus) {
|
2020-11-27 17:47:37 +03:00
|
|
|
mFirstCharRect = mSelection->mFocusCharRects[eNextCharRect];
|
2022-02-08 01:33:41 +03:00
|
|
|
} else if (mSelection.isSome() && mSelection->mHasRange &&
|
|
|
|
mSelection->mFocus == 1) {
|
2020-11-27 17:47:37 +03:00
|
|
|
mFirstCharRect = mSelection->mFocusCharRects[ePrevCharRect];
|
2022-02-08 01:33:41 +03:00
|
|
|
} else if (mSelection.isSome() && mSelection->mHasRange &&
|
|
|
|
!mSelection->mAnchor) {
|
2020-11-27 17:47:37 +03:00
|
|
|
mFirstCharRect = mSelection->mAnchorCharRects[eNextCharRect];
|
2022-02-08 01:33:41 +03:00
|
|
|
} else if (mSelection.isSome() && mSelection->mHasRange &&
|
|
|
|
mSelection->mAnchor == 1) {
|
2020-11-27 17:47:37 +03:00
|
|
|
mFirstCharRect = mSelection->mFocusCharRects[ePrevCharRect];
|
2022-02-08 01:33:40 +03:00
|
|
|
} else if (mTextRectArray.isSome() && mTextRectArray->IsOffsetInRange(0u)) {
|
|
|
|
mFirstCharRect = mTextRectArray->GetRect(0u);
|
2015-06-17 04:03:58 +03:00
|
|
|
} else {
|
|
|
|
LayoutDeviceIntRect charRect;
|
2022-02-08 01:33:40 +03:00
|
|
|
if (MOZ_UNLIKELY(NS_WARN_IF(!QueryCharRect(aWidget, 0, charRect)))) {
|
2015-06-17 04:03:58 +03:00
|
|
|
MOZ_LOG(sContentCacheLog, LogLevel::Error,
|
2022-02-08 01:33:39 +03:00
|
|
|
("0x%p CacheTextRects(), FAILED, "
|
2015-06-26 02:21:13 +03:00
|
|
|
"couldn't retrieve first char rect",
|
|
|
|
this));
|
2022-02-08 01:33:41 +03:00
|
|
|
mFirstCharRect.SetEmpty();
|
2015-06-17 04:03:58 +03:00
|
|
|
} else {
|
|
|
|
mFirstCharRect = charRect;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-08 01:33:40 +03:00
|
|
|
// 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.
|
2020-11-27 17:47:30 +03:00
|
|
|
if (mLastCommit.isSome()) {
|
2022-02-08 01:33:41 +03:00
|
|
|
mLastCommitStringTextRectArray =
|
|
|
|
Some(TextRectArray(mLastCommit->StartOffset()));
|
|
|
|
if (mLastCommit->Length() == 1 && mSelection.isSome() &&
|
|
|
|
mSelection->mHasRange &&
|
2022-02-08 01:33:40 +03:00
|
|
|
mSelection->mAnchor - 1 == mLastCommit->StartOffset() &&
|
|
|
|
!mSelection->mAnchorCharRects[ePrevCharRect].IsEmpty()) {
|
2020-11-27 17:48:03 +03:00
|
|
|
mLastCommitStringTextRectArray->mRects.AppendElement(
|
2020-11-27 17:47:37 +03:00
|
|
|
mSelection->mAnchorCharRects[ePrevCharRect]);
|
2020-11-24 04:31:46 +03:00
|
|
|
} else if (NS_WARN_IF(!QueryCharRectArray(
|
2020-11-27 17:47:30 +03:00
|
|
|
aWidget, mLastCommit->StartOffset(), mLastCommit->Length(),
|
2020-11-27 17:48:03 +03:00
|
|
|
mLastCommitStringTextRectArray->mRects))) {
|
2020-11-24 04:31:46 +03:00
|
|
|
MOZ_LOG(sContentCacheLog, LogLevel::Error,
|
2022-02-08 01:33:39 +03:00
|
|
|
("0x%p CacheTextRects(), FAILED, "
|
2020-11-24 04:31:46 +03:00
|
|
|
"couldn't retrieve text rect array of the last commit string",
|
|
|
|
this));
|
2020-11-27 17:48:03 +03:00
|
|
|
mLastCommitStringTextRectArray.reset();
|
2020-11-27 17:47:30 +03:00
|
|
|
mLastCommit.reset();
|
2020-11-24 04:31:46 +03:00
|
|
|
}
|
2020-11-27 17:48:03 +03:00
|
|
|
MOZ_ASSERT((mLastCommitStringTextRectArray.isSome()
|
|
|
|
? mLastCommitStringTextRectArray->mRects.Length()
|
|
|
|
: 0) == (mLastCommit.isSome() ? mLastCommit->Length() : 0));
|
2022-02-08 01:33:41 +03:00
|
|
|
} else {
|
|
|
|
mLastCommitStringTextRectArray.reset();
|
2020-11-24 04:31:46 +03:00
|
|
|
}
|
|
|
|
|
2022-02-08 01:33:39 +03:00
|
|
|
MOZ_LOG(
|
|
|
|
sContentCacheLog, LogLevel::Info,
|
|
|
|
("0x%p CacheTextRects(), Succeeded, "
|
|
|
|
"mText=%s, mTextRectArray=%s, mSelection=%s, "
|
|
|
|
"mFirstCharRect=%s, mLastCommitStringTextRectArray=%s",
|
|
|
|
this,
|
|
|
|
PrintStringDetail(mText, PrintStringDetail::kMaxLengthForEditor).get(),
|
|
|
|
ToString(mTextRectArray).c_str(), ToString(mSelection).c_str(),
|
|
|
|
ToString(mFirstCharRect).c_str(),
|
|
|
|
ToString(mLastCommitStringTextRectArray).c_str()));
|
2023-07-08 02:13:20 +03:00
|
|
|
AssertIfInvalid();
|
2023-07-07 06:25:52 +03:00
|
|
|
return IsValid();
|
2015-06-05 12:28:20 +03:00
|
|
|
}
|
|
|
|
|
2023-05-26 04:24:35 +03:00
|
|
|
bool ContentCacheInChild::SetSelection(
|
2022-02-08 01:33:40 +03:00
|
|
|
nsIWidget* aWidget,
|
|
|
|
const IMENotification::SelectionChangeDataBase& aSelectionChangeData) {
|
2022-02-08 01:33:39 +03:00
|
|
|
MOZ_LOG(
|
|
|
|
sContentCacheLog, LogLevel::Info,
|
2022-02-08 01:33:40 +03:00
|
|
|
("0x%p SetSelection(aSelectionChangeData=%s), mText=%s", this,
|
|
|
|
ToString(aSelectionChangeData).c_str(),
|
2022-02-08 01:33:39 +03:00
|
|
|
PrintStringDetail(mText, PrintStringDetail::kMaxLengthForEditor).get()));
|
2015-06-05 12:28:20 +03:00
|
|
|
|
2023-05-26 04:24:35 +03:00
|
|
|
if (MOZ_UNLIKELY(mText.isNothing())) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-02-08 01:33:40 +03:00
|
|
|
mSelection = Some(Selection(aSelectionChangeData));
|
2015-06-05 12:28:20 +03:00
|
|
|
|
2020-11-27 17:47:30 +03:00
|
|
|
if (mLastCommit.isSome()) {
|
2020-11-24 04:31:46 +03:00
|
|
|
// Forget last commit string range if selection is not collapsed
|
|
|
|
// at end of the last commit string.
|
2022-02-08 01:33:40 +03:00
|
|
|
if (!mSelection->mHasRange || !mSelection->IsCollapsed() ||
|
2020-11-27 17:47:37 +03:00
|
|
|
mSelection->mAnchor != mLastCommit->EndOffset()) {
|
2020-11-27 17:47:30 +03:00
|
|
|
MOZ_LOG(
|
|
|
|
sContentCacheLog, LogLevel::Debug,
|
2022-02-08 01:33:39 +03:00
|
|
|
("0x%p SetSelection(), forgetting last commit composition data "
|
2020-11-27 17:47:37 +03:00
|
|
|
"(mSelection=%s, mLastCommit=%s)",
|
|
|
|
this, ToString(mSelection).c_str(), ToString(mLastCommit).c_str()));
|
2020-11-27 17:47:30 +03:00
|
|
|
mLastCommit.reset();
|
2020-11-24 04:31:46 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-08 01:33:41 +03:00
|
|
|
CacheCaret(aWidget);
|
|
|
|
CacheTextRects(aWidget);
|
2023-05-26 04:24:35 +03:00
|
|
|
|
2023-07-07 06:25:52 +03:00
|
|
|
return mSelection.isSome() && IsValid();
|
2015-06-05 12:28:19 +03:00
|
|
|
}
|
|
|
|
|
2015-06-26 02:21:13 +03:00
|
|
|
/*****************************************************************************
|
|
|
|
* mozilla::ContentCacheInParent
|
|
|
|
*****************************************************************************/
|
|
|
|
|
2019-04-10 00:38:15 +03:00
|
|
|
ContentCacheInParent::ContentCacheInParent(BrowserParent& aBrowserParent)
|
2015-06-26 02:21:13 +03:00
|
|
|
: ContentCache(),
|
2019-04-10 00:38:15 +03:00
|
|
|
mBrowserParent(aBrowserParent),
|
2015-12-11 09:15:58 +03:00
|
|
|
mCommitStringByRequest(nullptr),
|
2017-11-27 12:51:01 +03:00
|
|
|
mPendingCommitLength(0),
|
2017-12-06 09:07:41 +03:00
|
|
|
mIsChildIgnoringCompositionEvents(false) {}
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2015-06-26 02:21:13 +03:00
|
|
|
void ContentCacheInParent::AssignContent(const ContentCache& aOther,
|
2016-06-14 15:06:34 +03:00
|
|
|
nsIWidget* aWidget,
|
2015-06-26 02:21:13 +03:00
|
|
|
const IMENotification* aNotification) {
|
2023-05-26 04:24:35 +03:00
|
|
|
MOZ_DIAGNOSTIC_ASSERT(aOther.IsValid());
|
|
|
|
|
2015-06-26 02:21:13 +03:00
|
|
|
mText = aOther.mText;
|
|
|
|
mSelection = aOther.mSelection;
|
|
|
|
mFirstCharRect = aOther.mFirstCharRect;
|
|
|
|
mCaret = aOther.mCaret;
|
|
|
|
mTextRectArray = aOther.mTextRectArray;
|
2020-11-24 04:31:46 +03:00
|
|
|
mLastCommitStringTextRectArray = aOther.mLastCommitStringTextRectArray;
|
2015-06-26 02:21:13 +03:00
|
|
|
mEditorRect = aOther.mEditorRect;
|
|
|
|
|
2016-10-12 16:03:16 +03:00
|
|
|
// 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.
|
2023-06-14 04:57:33 +03:00
|
|
|
if (WidgetHasComposition() && mHandlingCompositions.Length() == 1 &&
|
2020-11-23 12:29:34 +03:00
|
|
|
mCompositionStart.isSome()) {
|
|
|
|
IMEStateManager::MaybeStartOffsetUpdatedInChild(aWidget,
|
|
|
|
mCompositionStart.value());
|
2016-06-14 15:06:34 +03:00
|
|
|
}
|
|
|
|
|
2018-01-12 09:05:24 +03:00
|
|
|
// 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.
|
2016-10-12 15:52:01 +03:00
|
|
|
mCompositionStartInChild = aOther.mCompositionStart;
|
2023-06-14 04:57:34 +03:00
|
|
|
if (WidgetHasComposition() || HasPendingCommit()) {
|
2020-11-23 12:29:34 +03:00
|
|
|
if (mCompositionStartInChild.isSome()) {
|
|
|
|
if (mCompositionStart.valueOr(UINT32_MAX) !=
|
|
|
|
mCompositionStartInChild.value()) {
|
|
|
|
mCompositionStart = mCompositionStartInChild;
|
2017-06-29 12:31:09 +03:00
|
|
|
mPendingCommitLength = 0;
|
|
|
|
}
|
2020-11-27 17:47:37 +03:00
|
|
|
} else if (mCompositionStart.isSome() && mSelection.isSome() &&
|
2022-02-08 01:33:40 +03:00
|
|
|
mSelection->mHasRange &&
|
2020-11-27 17:47:37 +03:00
|
|
|
mCompositionStart.value() != mSelection->StartOffset()) {
|
|
|
|
mCompositionStart = Some(mSelection->StartOffset());
|
2017-06-29 12:31:09 +03:00
|
|
|
mPendingCommitLength = 0;
|
Bug 1304620 part.4 ContentCacheInParent::mCompositionStart should be set to better value for mWidgetHasComposition state r=m_kato
ContentCacheInParent::mCompositionStart was set to ContentCacheInChild::mCompositionStart without any check. However, that's clearly wrong approach. For example, when the remote process handles some composition events after eCompositionCommit(AsIs) in the parent process, mCompositionStart is valid offset even after mWidgetHasComposition is set to false. Similarly, even after parent process sends eCompositionStart, the remote process may send invalid offset for mCompositionStart due to no composition in it.
For solving this issue, ContentCacheInParent should check mWidgetHasComposition.
If it's true and coming offset is valid, let's use it (even if mPendingCompositionCount is 2 or bigger since widget shouldn't use WidgetQueryContentEvent when there are some pending events).
If the coming offset is invalid but mWidgetHasComposition is false, let's use selection start instead because HandleQueryContentEvent() can work around selection start offset only.
Otherwise, i.e., mWidgetHasComposition is false, we should always set mCompositionStart to invalid offset.
MozReview-Commit-ID: IONU0Cbhpil
--HG--
extra : rebase_source : 05ae9eb0e7a0bb63b65be7a54103eb798274c8ff
2016-10-12 16:05:09 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-08 01:33:39 +03:00
|
|
|
MOZ_LOG(
|
|
|
|
sContentCacheLog, LogLevel::Info,
|
|
|
|
("0x%p AssignContent(aNotification=%s), "
|
|
|
|
"Succeeded, mText=%s, mSelection=%s, mFirstCharRect=%s, "
|
2023-06-14 04:57:33 +03:00
|
|
|
"mCaret=%s, mTextRectArray=%s, WidgetHasComposition()=%s, "
|
|
|
|
"mHandlingCompositions.Length()=%zu, mCompositionStart=%s, "
|
2022-02-08 01:33:39 +03:00
|
|
|
"mPendingCommitLength=%u, mEditorRect=%s, "
|
|
|
|
"mLastCommitStringTextRectArray=%s",
|
|
|
|
this, GetNotificationName(aNotification),
|
|
|
|
PrintStringDetail(mText, PrintStringDetail::kMaxLengthForEditor).get(),
|
|
|
|
ToString(mSelection).c_str(), ToString(mFirstCharRect).c_str(),
|
|
|
|
ToString(mCaret).c_str(), ToString(mTextRectArray).c_str(),
|
2023-06-14 04:57:33 +03:00
|
|
|
GetBoolName(WidgetHasComposition()), mHandlingCompositions.Length(),
|
2022-02-08 01:33:39 +03:00
|
|
|
ToString(mCompositionStart).c_str(), mPendingCommitLength,
|
|
|
|
ToString(mEditorRect).c_str(),
|
|
|
|
ToString(mLastCommitStringTextRectArray).c_str()));
|
2015-06-26 02:21:13 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
bool ContentCacheInParent::HandleQueryContentEvent(
|
|
|
|
WidgetQueryContentEvent& aEvent, nsIWidget* aWidget) const {
|
|
|
|
MOZ_ASSERT(aWidget);
|
|
|
|
|
2016-06-14 14:54:54 +03:00
|
|
|
// 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)) {
|
2016-07-12 11:44:44 +03:00
|
|
|
MOZ_LOG(sContentCacheLog, LogLevel::Error,
|
|
|
|
("0x%p HandleQueryContentEvent(), FAILED due to query with XP "
|
|
|
|
"linebreaks",
|
|
|
|
this));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (NS_WARN_IF(!aEvent.mInput.IsValidOffset())) {
|
|
|
|
MOZ_LOG(
|
|
|
|
sContentCacheLog, LogLevel::Error,
|
|
|
|
("0x%p HandleQueryContentEvent(), FAILED due to invalid offset", this));
|
2016-06-14 14:54:54 +03:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-07-12 11:44:44 +03:00
|
|
|
if (NS_WARN_IF(!aEvent.mInput.IsValidEventMessage(aEvent.mMessage))) {
|
|
|
|
MOZ_LOG(
|
|
|
|
sContentCacheLog, LogLevel::Error,
|
|
|
|
("0x%p HandleQueryContentEvent(), FAILED due to invalid event message",
|
|
|
|
this));
|
2016-06-14 14:54:54 +03:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-06-22 08:16:59 +03:00
|
|
|
bool isRelativeToInsertionPoint = aEvent.mInput.mRelativeToInsertionPoint;
|
|
|
|
if (isRelativeToInsertionPoint) {
|
2023-06-14 04:57:34 +03:00
|
|
|
MOZ_LOG(
|
|
|
|
sContentCacheLog, LogLevel::Debug,
|
|
|
|
("0x%p HandleQueryContentEvent(), "
|
|
|
|
"making offset absolute... aEvent={ mMessage=%s, mInput={ "
|
|
|
|
"mOffset=%" PRId64 ", mLength=%" PRIu32 " } }, "
|
|
|
|
"WidgetHasComposition()=%s, HasPendingCommit()=%s, "
|
|
|
|
"mCompositionStart=%" PRIu32 ", "
|
|
|
|
"mPendingCommitLength=%" PRIu32 ", mSelection=%s",
|
|
|
|
this, ToChar(aEvent.mMessage), aEvent.mInput.mOffset,
|
|
|
|
aEvent.mInput.mLength, GetBoolName(WidgetHasComposition()),
|
|
|
|
GetBoolName(HasPendingCommit()), mCompositionStart.valueOr(UINT32_MAX),
|
|
|
|
mPendingCommitLength, ToString(mSelection).c_str()));
|
|
|
|
if (WidgetHasComposition() || HasPendingCommit()) {
|
2020-11-23 12:29:34 +03:00
|
|
|
if (NS_WARN_IF(mCompositionStart.isNothing()) ||
|
|
|
|
NS_WARN_IF(!aEvent.mInput.MakeOffsetAbsolute(
|
|
|
|
mCompositionStart.value() + mPendingCommitLength))) {
|
2016-07-12 11:44:44 +03:00
|
|
|
MOZ_LOG(
|
|
|
|
sContentCacheLog, LogLevel::Error,
|
|
|
|
("0x%p HandleQueryContentEvent(), FAILED due to "
|
2017-06-29 12:31:09 +03:00
|
|
|
"aEvent.mInput.MakeOffsetAbsolute(mCompositionStart + "
|
|
|
|
"mPendingCommitLength) failure, "
|
|
|
|
"mCompositionStart=%" PRIu32 ", mPendingCommitLength=%" PRIu32 ", "
|
|
|
|
"aEvent={ mMessage=%s, mInput={ mOffset=%" PRId64
|
|
|
|
", mLength=%" PRIu32 " } }",
|
2020-11-23 12:29:34 +03:00
|
|
|
this, mCompositionStart.valueOr(UINT32_MAX), mPendingCommitLength,
|
2017-06-29 12:31:09 +03:00
|
|
|
ToChar(aEvent.mMessage), aEvent.mInput.mOffset,
|
|
|
|
aEvent.mInput.mLength));
|
2016-06-14 14:54:54 +03:00
|
|
|
return false;
|
|
|
|
}
|
2020-11-27 17:47:37 +03:00
|
|
|
} else if (NS_WARN_IF(mSelection.isNothing())) {
|
2016-07-12 11:44:44 +03:00
|
|
|
MOZ_LOG(sContentCacheLog, LogLevel::Error,
|
|
|
|
("0x%p HandleQueryContentEvent(), FAILED due to mSelection is "
|
2022-02-08 01:33:40 +03:00
|
|
|
"Nothing",
|
|
|
|
this));
|
|
|
|
return false;
|
|
|
|
} else if (NS_WARN_IF(mSelection->mHasRange)) {
|
|
|
|
MOZ_LOG(sContentCacheLog, LogLevel::Error,
|
|
|
|
("0x%p HandleQueryContentEvent(), FAILED due to there is no "
|
|
|
|
"selection range, but the query requested with relative offset "
|
|
|
|
"from selection",
|
2016-07-12 11:44:44 +03:00
|
|
|
this));
|
|
|
|
return false;
|
|
|
|
} else if (NS_WARN_IF(!aEvent.mInput.MakeOffsetAbsolute(
|
2020-11-27 17:47:37 +03:00
|
|
|
mSelection->StartOffset() + mPendingCommitLength))) {
|
2016-07-12 11:44:44 +03:00
|
|
|
MOZ_LOG(sContentCacheLog, LogLevel::Error,
|
|
|
|
("0x%p HandleQueryContentEvent(), FAILED due to "
|
2022-02-08 01:33:40 +03:00
|
|
|
"aEvent.mInput.MakeOffsetAbsolute(mSelection->StartOffset() + "
|
2020-11-27 17:47:37 +03:00
|
|
|
"mPendingCommitLength) failure, mSelection=%s, "
|
2017-06-29 12:31:09 +03:00
|
|
|
"mPendingCommitLength=%" PRIu32 ", aEvent={ mMessage=%s, "
|
|
|
|
"mInput={ mOffset=%" PRId64 ", mLength=%" PRIu32 " } }",
|
2020-11-27 17:47:37 +03:00
|
|
|
this, ToString(mSelection).c_str(), mPendingCommitLength,
|
|
|
|
ToChar(aEvent.mMessage), aEvent.mInput.mOffset,
|
|
|
|
aEvent.mInput.mLength));
|
2016-06-14 14:54:54 +03:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-22 04:34:51 +03:00
|
|
|
switch (aEvent.mMessage) {
|
2015-09-10 04:40:05 +03:00
|
|
|
case eQuerySelectedText:
|
2015-06-26 02:21:13 +03:00
|
|
|
MOZ_LOG(sContentCacheLog, LogLevel::Info,
|
2020-12-02 08:32:19 +03:00
|
|
|
("0x%p HandleQueryContentEvent(aEvent={ "
|
|
|
|
"mMessage=eQuerySelectedText }, aWidget=0x%p)",
|
2015-06-26 02:21:13 +03:00
|
|
|
this, aWidget));
|
2022-02-08 01:33:40 +03:00
|
|
|
if (MOZ_UNLIKELY(NS_WARN_IF(mSelection.isNothing()))) {
|
2015-06-26 02:21:13 +03:00
|
|
|
// If content cache hasn't been initialized properly, make the query
|
|
|
|
// failed.
|
|
|
|
MOZ_LOG(sContentCacheLog, LogLevel::Error,
|
2022-02-08 01:33:39 +03:00
|
|
|
("0x%p HandleQueryContentEvent(), FAILED because mSelection "
|
2022-02-08 01:33:40 +03:00
|
|
|
"is Nothing",
|
2015-06-26 02:21:13 +03:00
|
|
|
this));
|
2020-12-02 08:32:19 +03:00
|
|
|
return false;
|
2015-06-26 02:21:13 +03:00
|
|
|
}
|
2023-05-26 04:24:35 +03:00
|
|
|
MOZ_DIAGNOSTIC_ASSERT(mText.isSome());
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(mSelection->IsValidIn(*mText));
|
2020-12-02 08:32:19 +03:00
|
|
|
aEvent.EmplaceReply();
|
|
|
|
aEvent.mReply->mFocusedWidget = aWidget;
|
2022-02-08 01:33:40 +03:00
|
|
|
if (mSelection->mHasRange) {
|
|
|
|
if (MOZ_LIKELY(mText.isSome())) {
|
|
|
|
aEvent.mReply->mOffsetAndData.emplace(
|
|
|
|
mSelection->StartOffset(),
|
|
|
|
Substring(mText.ref(), mSelection->StartOffset(),
|
|
|
|
mSelection->Length()),
|
|
|
|
OffsetAndDataFor::SelectedString);
|
|
|
|
} else {
|
|
|
|
// TODO: Investigate this case. I find this during
|
|
|
|
// test_mousecapture.xhtml on Linux.
|
|
|
|
aEvent.mReply->mOffsetAndData.emplace(
|
|
|
|
0u, EmptyString(), OffsetAndDataFor::SelectedString);
|
|
|
|
}
|
2022-02-08 01:33:40 +03:00
|
|
|
}
|
2020-12-02 08:32:19 +03:00
|
|
|
aEvent.mReply->mWritingMode = mSelection->mWritingMode;
|
2015-06-26 02:21:13 +03:00
|
|
|
MOZ_LOG(sContentCacheLog, LogLevel::Info,
|
2022-02-08 01:33:39 +03:00
|
|
|
("0x%p HandleQueryContentEvent(), Succeeded, aEvent={ "
|
|
|
|
"mMessage=eQuerySelectedText, mReply=%s }",
|
2020-12-02 08:32:19 +03:00
|
|
|
this, ToString(aEvent.mReply).c_str()));
|
|
|
|
return true;
|
2015-09-10 04:40:05 +03:00
|
|
|
case eQueryTextContent: {
|
2015-06-26 02:21:13 +03:00
|
|
|
MOZ_LOG(sContentCacheLog, LogLevel::Info,
|
2022-02-08 01:33:39 +03:00
|
|
|
("0x%p HandleQueryContentEvent(aEvent={ "
|
|
|
|
"mMessage=eQueryTextContent, mInput={ mOffset=%" PRId64
|
2022-02-08 01:33:40 +03:00
|
|
|
", mLength=%u } }, aWidget=0x%p), mText->Length()=%zu",
|
2015-06-26 02:21:13 +03:00
|
|
|
this, aEvent.mInput.mOffset, aEvent.mInput.mLength, aWidget,
|
2022-02-08 01:33:40 +03:00
|
|
|
mText.isSome() ? mText->Length() : 0u));
|
|
|
|
if (MOZ_UNLIKELY(NS_WARN_IF(mText.isNothing()))) {
|
|
|
|
MOZ_LOG(sContentCacheLog, LogLevel::Error,
|
|
|
|
("0x%p HandleQueryContentEvent(), FAILED because "
|
|
|
|
"there is no text data",
|
|
|
|
this));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
const uint32_t inputOffset = aEvent.mInput.mOffset;
|
2022-02-08 01:33:40 +03:00
|
|
|
const uint32_t inputEndOffset = std::min<uint32_t>(
|
|
|
|
aEvent.mInput.EndOffset(), mText.isSome() ? mText->Length() : 0u);
|
2022-02-08 01:33:40 +03:00
|
|
|
if (MOZ_UNLIKELY(NS_WARN_IF(inputEndOffset < inputOffset))) {
|
2020-12-02 08:32:19 +03:00
|
|
|
MOZ_LOG(sContentCacheLog, LogLevel::Error,
|
2022-02-08 01:33:39 +03:00
|
|
|
("0x%p HandleQueryContentEvent(), FAILED because "
|
2020-12-02 08:32:19 +03:00
|
|
|
"inputOffset=%u is larger than inputEndOffset=%u",
|
|
|
|
this, inputOffset, inputEndOffset));
|
2015-06-26 02:21:13 +03:00
|
|
|
return false;
|
|
|
|
}
|
2020-12-02 08:32:19 +03:00
|
|
|
aEvent.EmplaceReply();
|
|
|
|
aEvent.mReply->mFocusedWidget = aWidget;
|
2022-02-08 01:33:40 +03:00
|
|
|
const nsAString& textInQueriedRange =
|
|
|
|
inputEndOffset > inputOffset
|
|
|
|
? static_cast<const nsAString&>(Substring(
|
|
|
|
mText.ref(), inputOffset, inputEndOffset - inputOffset))
|
|
|
|
: static_cast<const nsAString&>(EmptyString());
|
|
|
|
aEvent.mReply->mOffsetAndData.emplace(inputOffset, textInQueriedRange,
|
|
|
|
OffsetAndDataFor::EditorString);
|
2020-12-02 08:32:19 +03:00
|
|
|
// TODO: Support font ranges
|
|
|
|
MOZ_LOG(sContentCacheLog, LogLevel::Info,
|
2022-02-08 01:33:39 +03:00
|
|
|
("0x%p HandleQueryContentEvent(), Succeeded, aEvent={ "
|
2020-12-02 08:32:19 +03:00
|
|
|
"mMessage=eQueryTextContent, mReply=%s }",
|
|
|
|
this, ToString(aEvent.mReply).c_str()));
|
|
|
|
return true;
|
2015-06-26 02:21:13 +03:00
|
|
|
}
|
2020-12-02 08:32:19 +03:00
|
|
|
case eQueryTextRect: {
|
2015-06-26 02:21:13 +03:00
|
|
|
MOZ_LOG(sContentCacheLog, LogLevel::Info,
|
2016-07-05 12:56:18 +03:00
|
|
|
("0x%p HandleQueryContentEvent("
|
2016-12-16 06:16:31 +03:00
|
|
|
"aEvent={ mMessage=eQueryTextRect, mInput={ mOffset=%" PRId64
|
2022-02-08 01:33:40 +03:00
|
|
|
", mLength=%u } }, aWidget=0x%p), mText->Length()=%zu",
|
2015-06-26 02:21:13 +03:00
|
|
|
this, aEvent.mInput.mOffset, aEvent.mInput.mLength, aWidget,
|
2022-02-08 01:33:40 +03:00
|
|
|
mText.isSome() ? mText->Length() : 0u));
|
2016-06-22 08:16:59 +03:00
|
|
|
// Note that if the query is relative to insertion point, the query was
|
|
|
|
// probably requested by native IME. In such case, we should return
|
|
|
|
// non-empty rect since returning failure causes IME showing its window
|
|
|
|
// at odd position.
|
2020-12-02 08:32:19 +03:00
|
|
|
LayoutDeviceIntRect textRect;
|
2015-06-26 02:21:13 +03:00
|
|
|
if (aEvent.mInput.mLength) {
|
2022-02-08 01:33:40 +03:00
|
|
|
if (MOZ_UNLIKELY(NS_WARN_IF(
|
2020-12-02 08:32:19 +03:00
|
|
|
!GetUnionTextRects(aEvent.mInput.mOffset, aEvent.mInput.mLength,
|
2022-02-08 01:33:40 +03:00
|
|
|
isRelativeToInsertionPoint, textRect)))) {
|
2015-06-26 02:21:13 +03:00
|
|
|
// XXX We don't have cache for this request.
|
|
|
|
MOZ_LOG(sContentCacheLog, LogLevel::Error,
|
2022-02-08 01:33:39 +03:00
|
|
|
("0x%p HandleQueryContentEvent(), FAILED to get union rect",
|
2015-06-26 02:21:13 +03:00
|
|
|
this));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// If the length is 0, we should return caret rect instead.
|
|
|
|
if (NS_WARN_IF(!GetCaretRect(aEvent.mInput.mOffset,
|
2020-12-02 08:32:19 +03:00
|
|
|
isRelativeToInsertionPoint, textRect))) {
|
2015-06-26 02:21:13 +03:00
|
|
|
MOZ_LOG(sContentCacheLog, LogLevel::Error,
|
2022-02-08 01:33:39 +03:00
|
|
|
("0x%p HandleQueryContentEvent(), FAILED to get caret rect",
|
2015-06-26 02:21:13 +03:00
|
|
|
this));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2020-12-02 08:32:19 +03:00
|
|
|
aEvent.EmplaceReply();
|
|
|
|
aEvent.mReply->mFocusedWidget = aWidget;
|
|
|
|
aEvent.mReply->mRect = textRect;
|
2022-02-08 01:33:40 +03:00
|
|
|
const nsAString& textInQueriedRange =
|
|
|
|
mText.isSome() && aEvent.mInput.mOffset <
|
|
|
|
static_cast<int64_t>(
|
|
|
|
mText.isSome() ? mText->Length() : 0u)
|
2020-12-02 08:32:19 +03:00
|
|
|
? static_cast<const nsAString&>(
|
2022-02-08 01:33:40 +03:00
|
|
|
Substring(mText.ref(), aEvent.mInput.mOffset,
|
|
|
|
mText->Length() >= aEvent.mInput.EndOffset()
|
2020-12-02 08:32:19 +03:00
|
|
|
? aEvent.mInput.mLength
|
|
|
|
: UINT32_MAX))
|
2022-02-08 01:33:40 +03:00
|
|
|
: static_cast<const nsAString&>(EmptyString());
|
|
|
|
aEvent.mReply->mOffsetAndData.emplace(aEvent.mInput.mOffset,
|
|
|
|
textInQueriedRange,
|
|
|
|
OffsetAndDataFor::EditorString);
|
2015-06-26 02:21:13 +03:00
|
|
|
// XXX This may be wrong if storing range isn't in the selection range.
|
Bug 1825693 - Make `PuppetWidget` stop trying to cache `Selection` directly r=m_kato
Currently, `PuppetWidget` calls `ContentCacheInChild::CacheSelection` directly.
However, `mText` can be `Nothing` or `mText` can be outdated, but `mSelection`
becomes the latest one. Therefore, we may notify the parent process with
invalid data combination.
The callers in `PuppetWidget` are:
1. `NotifyIMEOfCompositionUpdate`
2. `NotifyIMEOfPositionChange`
I think that the former does not need to cache anything here because
`IMEContentObserver` should've updated the text/selection changes. However,
stopping caching everything at this point is risky. In the most cases, outdated
data appears as odd IME UI position. Therefore, let's keep updating only caret
and text rectangles.
The latter is reported with new crash reports which is crashed by a
`MOZ_DIAGNOSTIC_ASSERT` failure added by the previous patch. In the case,
if `mText` and `mSelection` has not been cached, we don't need to notify
the parent process with them because they will be sent later. And also even
if they are not available, it may be useful that the character rectangles not
related to `Selection` (e.g., the first character rect). Therefore, let's
keep caching same things as the former case.
Therefore, this patch makes `CacheSelection` a private method and add
`CacheCaretAndTextRects` for them.
Differential Revision: https://phabricator.services.mozilla.com/D178145
2023-05-26 04:24:36 +03:00
|
|
|
aEvent.mReply->mWritingMode =
|
|
|
|
mSelection.isSome() ? mSelection->mWritingMode : WritingMode();
|
2015-06-26 02:21:13 +03:00
|
|
|
MOZ_LOG(sContentCacheLog, LogLevel::Info,
|
2022-02-08 01:33:39 +03:00
|
|
|
("0x%p HandleQueryContentEvent(), Succeeded, aEvent={ "
|
2020-12-02 08:32:19 +03:00
|
|
|
"mMessage=eQueryTextRect mReply=%s }",
|
|
|
|
this, ToString(aEvent.mReply).c_str()));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
case eQueryCaretRect: {
|
|
|
|
MOZ_LOG(
|
|
|
|
sContentCacheLog, LogLevel::Info,
|
|
|
|
("0x%p HandleQueryContentEvent(aEvent={ mMessage=eQueryCaretRect, "
|
2021-12-14 00:47:56 +03:00
|
|
|
"mInput={ mOffset=%" PRId64
|
2022-02-08 01:33:40 +03:00
|
|
|
" } }, aWidget=0x%p), mText->Length()=%zu",
|
|
|
|
this, aEvent.mInput.mOffset, aWidget,
|
|
|
|
mText.isSome() ? mText->Length() : 0u));
|
2016-06-22 08:16:59 +03:00
|
|
|
// Note that if the query is relative to insertion point, the query was
|
|
|
|
// probably requested by native IME. In such case, we should return
|
|
|
|
// non-empty rect since returning failure causes IME showing its window
|
|
|
|
// at odd position.
|
2020-12-02 08:32:19 +03:00
|
|
|
LayoutDeviceIntRect caretRect;
|
2015-06-26 02:21:13 +03:00
|
|
|
if (NS_WARN_IF(!GetCaretRect(aEvent.mInput.mOffset,
|
2020-12-02 08:32:19 +03:00
|
|
|
isRelativeToInsertionPoint, caretRect))) {
|
2022-02-08 01:33:39 +03:00
|
|
|
MOZ_LOG(sContentCacheLog, LogLevel::Error,
|
|
|
|
("0x%p HandleQueryContentEvent(),FAILED to get caret rect",
|
|
|
|
this));
|
2015-06-26 02:21:13 +03:00
|
|
|
return false;
|
|
|
|
}
|
2020-12-02 08:32:19 +03:00
|
|
|
aEvent.EmplaceReply();
|
|
|
|
aEvent.mReply->mFocusedWidget = aWidget;
|
|
|
|
aEvent.mReply->mRect = caretRect;
|
|
|
|
aEvent.mReply->mOffsetAndData.emplace(aEvent.mInput.mOffset,
|
|
|
|
EmptyString(),
|
|
|
|
OffsetAndDataFor::SelectedString);
|
|
|
|
// TODO: Set mWritingMode here
|
|
|
|
MOZ_LOG(sContentCacheLog, LogLevel::Info,
|
2022-02-08 01:33:39 +03:00
|
|
|
("0x%p HandleQueryContentEvent(), Succeeded, aEvent={ "
|
2020-12-02 08:32:19 +03:00
|
|
|
"mMessage=eQueryCaretRect, mReply=%s }",
|
|
|
|
this, ToString(aEvent.mReply).c_str()));
|
|
|
|
return true;
|
|
|
|
}
|
2015-09-10 04:40:06 +03:00
|
|
|
case eQueryEditorRect:
|
2015-06-26 02:21:13 +03:00
|
|
|
MOZ_LOG(sContentCacheLog, LogLevel::Info,
|
2020-12-02 08:32:19 +03:00
|
|
|
("0x%p HandleQueryContentEvent(aEvent={ "
|
|
|
|
"mMessage=eQueryEditorRect }, aWidget=0x%p)",
|
2015-06-26 02:21:13 +03:00
|
|
|
this, aWidget));
|
2022-02-17 11:53:35 +03:00
|
|
|
// XXX This query should fail if no editable elmenet has focus. Or,
|
|
|
|
// perhaps, should return rect of the window instead.
|
2020-12-02 08:32:19 +03:00
|
|
|
aEvent.EmplaceReply();
|
|
|
|
aEvent.mReply->mFocusedWidget = aWidget;
|
|
|
|
aEvent.mReply->mRect = mEditorRect;
|
2015-06-26 02:21:13 +03:00
|
|
|
MOZ_LOG(sContentCacheLog, LogLevel::Info,
|
2022-02-08 01:33:39 +03:00
|
|
|
("0x%p HandleQueryContentEvent(), Succeeded, aEvent={ "
|
2020-12-02 08:32:19 +03:00
|
|
|
"mMessage=eQueryEditorRect, mReply=%s }",
|
|
|
|
this, ToString(aEvent.mReply).c_str()));
|
|
|
|
return true;
|
2015-08-26 15:56:59 +03:00
|
|
|
default:
|
2020-12-02 08:32:19 +03:00
|
|
|
aEvent.EmplaceReply();
|
|
|
|
aEvent.mReply->mFocusedWidget = aWidget;
|
|
|
|
if (NS_WARN_IF(aEvent.Failed())) {
|
|
|
|
MOZ_LOG(
|
|
|
|
sContentCacheLog, LogLevel::Error,
|
|
|
|
("0x%p HandleQueryContentEvent(), FAILED due to not set enough "
|
|
|
|
"data, aEvent={ mMessage=%s, mReply=%s }",
|
|
|
|
this, ToChar(aEvent.mMessage), ToString(aEvent.mReply).c_str()));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
MOZ_LOG(sContentCacheLog, LogLevel::Info,
|
|
|
|
("0x%p HandleQueryContentEvent(), Succeeded, aEvent={ "
|
|
|
|
"mMessage=%s, mReply=%s }",
|
|
|
|
this, ToChar(aEvent.mMessage), ToString(aEvent.mReply).c_str()));
|
|
|
|
return true;
|
2015-06-26 02:21:13 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ContentCacheInParent::GetTextRect(uint32_t aOffset,
|
2016-06-22 08:16:59 +03:00
|
|
|
bool aRoundToExistingOffset,
|
2015-06-26 02:21:13 +03:00
|
|
|
LayoutDeviceIntRect& aTextRect) const {
|
2020-11-27 17:48:03 +03:00
|
|
|
MOZ_LOG(
|
|
|
|
sContentCacheLog, LogLevel::Info,
|
|
|
|
("0x%p GetTextRect(aOffset=%u, aRoundToExistingOffset=%s), "
|
|
|
|
"mTextRectArray=%s, mSelection=%s, mLastCommitStringTextRectArray=%s",
|
|
|
|
this, aOffset, GetBoolName(aRoundToExistingOffset),
|
|
|
|
ToString(mTextRectArray).c_str(), ToString(mSelection).c_str(),
|
|
|
|
ToString(mLastCommitStringTextRectArray).c_str()));
|
2015-06-05 12:28:20 +03:00
|
|
|
|
2015-06-17 04:03:58 +03:00
|
|
|
if (!aOffset) {
|
2016-09-02 10:12:24 +03:00
|
|
|
NS_WARNING_ASSERTION(!mFirstCharRect.IsEmpty(), "empty rect");
|
2015-06-17 04:03:58 +03:00
|
|
|
aTextRect = mFirstCharRect;
|
|
|
|
return !aTextRect.IsEmpty();
|
|
|
|
}
|
2022-02-08 01:33:40 +03:00
|
|
|
if (mSelection.isSome() && mSelection->mHasRange) {
|
2020-11-27 17:47:37 +03:00
|
|
|
if (aOffset == mSelection->mAnchor) {
|
|
|
|
NS_WARNING_ASSERTION(
|
|
|
|
!mSelection->mAnchorCharRects[eNextCharRect].IsEmpty(), "empty rect");
|
|
|
|
aTextRect = mSelection->mAnchorCharRects[eNextCharRect];
|
|
|
|
return !aTextRect.IsEmpty();
|
|
|
|
}
|
|
|
|
if (mSelection->mAnchor && aOffset == mSelection->mAnchor - 1) {
|
|
|
|
NS_WARNING_ASSERTION(
|
|
|
|
!mSelection->mAnchorCharRects[ePrevCharRect].IsEmpty(), "empty rect");
|
|
|
|
aTextRect = mSelection->mAnchorCharRects[ePrevCharRect];
|
|
|
|
return !aTextRect.IsEmpty();
|
|
|
|
}
|
|
|
|
if (aOffset == mSelection->mFocus) {
|
|
|
|
NS_WARNING_ASSERTION(
|
|
|
|
!mSelection->mFocusCharRects[eNextCharRect].IsEmpty(), "empty rect");
|
|
|
|
aTextRect = mSelection->mFocusCharRects[eNextCharRect];
|
|
|
|
return !aTextRect.IsEmpty();
|
|
|
|
}
|
|
|
|
if (mSelection->mFocus && aOffset == mSelection->mFocus - 1) {
|
|
|
|
NS_WARNING_ASSERTION(
|
|
|
|
!mSelection->mFocusCharRects[ePrevCharRect].IsEmpty(), "empty rect");
|
|
|
|
aTextRect = mSelection->mFocusCharRects[ePrevCharRect];
|
|
|
|
return !aTextRect.IsEmpty();
|
|
|
|
}
|
2015-06-05 12:28:20 +03:00
|
|
|
}
|
2015-06-05 12:28:20 +03:00
|
|
|
|
2020-11-27 17:48:03 +03:00
|
|
|
if (mTextRectArray.isSome() && mTextRectArray->IsOffsetInRange(aOffset)) {
|
|
|
|
aTextRect = mTextRectArray->GetRect(aOffset);
|
2020-11-24 04:31:46 +03:00
|
|
|
return !aTextRect.IsEmpty();
|
|
|
|
}
|
|
|
|
|
2020-11-27 17:48:03 +03:00
|
|
|
if (mLastCommitStringTextRectArray.isSome() &&
|
|
|
|
mLastCommitStringTextRectArray->IsOffsetInRange(aOffset)) {
|
|
|
|
aTextRect = mLastCommitStringTextRectArray->GetRect(aOffset);
|
2020-11-24 04:31:46 +03:00
|
|
|
return !aTextRect.IsEmpty();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!aRoundToExistingOffset) {
|
|
|
|
aTextRect.SetEmpty();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-11-27 17:48:03 +03:00
|
|
|
if (mTextRectArray.isNothing() || !mTextRectArray->HasRects()) {
|
2020-11-24 04:31:46 +03:00
|
|
|
// If there are no rects in mTextRectArray, we should refer the start of
|
2022-02-17 11:53:35 +03:00
|
|
|
// 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();
|
|
|
|
return false;
|
|
|
|
}
|
2020-11-27 17:47:37 +03:00
|
|
|
aTextRect = mSelection->StartCharRect();
|
2020-11-24 04:31:46 +03:00
|
|
|
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.
|
2016-06-22 08:16:59 +03:00
|
|
|
uint32_t offset = aOffset;
|
2020-11-27 17:48:03 +03:00
|
|
|
if (offset < mTextRectArray->StartOffset()) {
|
|
|
|
offset = mTextRectArray->StartOffset();
|
2020-11-24 04:31:46 +03:00
|
|
|
} else {
|
2020-11-27 17:48:03 +03:00
|
|
|
offset = mTextRectArray->EndOffset() - 1;
|
2015-06-05 12:28:19 +03:00
|
|
|
}
|
2020-11-27 17:48:03 +03:00
|
|
|
aTextRect = mTextRectArray->GetRect(offset);
|
2016-06-22 08:16:59 +03:00
|
|
|
return !aTextRect.IsEmpty();
|
2015-06-05 12:28:19 +03:00
|
|
|
}
|
|
|
|
|
2015-06-26 02:21:13 +03:00
|
|
|
bool ContentCacheInParent::GetUnionTextRects(
|
2016-06-22 08:16:59 +03:00
|
|
|
uint32_t aOffset, uint32_t aLength, bool aRoundToExistingOffset,
|
2015-06-26 02:21:13 +03:00
|
|
|
LayoutDeviceIntRect& aUnionTextRect) const {
|
2015-06-05 12:28:20 +03:00
|
|
|
MOZ_LOG(sContentCacheLog, LogLevel::Info,
|
2016-07-05 12:56:18 +03:00
|
|
|
("0x%p GetUnionTextRects(aOffset=%u, "
|
2020-11-27 17:48:03 +03:00
|
|
|
"aLength=%u, aRoundToExistingOffset=%s), mTextRectArray=%s, "
|
|
|
|
"mSelection=%s, mLastCommitStringTextRectArray=%s",
|
2016-06-22 08:16:59 +03:00
|
|
|
this, aOffset, aLength, GetBoolName(aRoundToExistingOffset),
|
2020-11-27 17:48:03 +03:00
|
|
|
ToString(mTextRectArray).c_str(), ToString(mSelection).c_str(),
|
|
|
|
ToString(mLastCommitStringTextRectArray).c_str()));
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2015-06-17 04:03:58 +03:00
|
|
|
CheckedInt<uint32_t> endOffset = CheckedInt<uint32_t>(aOffset) + aLength;
|
|
|
|
if (!endOffset.isValid()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-02-08 01:33:40 +03:00
|
|
|
if (mSelection.isSome() && !mSelection->IsCollapsed() &&
|
2020-11-27 17:47:37 +03:00
|
|
|
aOffset == mSelection->StartOffset() && aLength == mSelection->Length()) {
|
|
|
|
NS_WARNING_ASSERTION(!mSelection->mRect.IsEmpty(), "empty rect");
|
|
|
|
aUnionTextRect = mSelection->mRect;
|
2015-06-08 05:46:17 +03:00
|
|
|
return !aUnionTextRect.IsEmpty();
|
|
|
|
}
|
|
|
|
|
2015-06-05 12:28:20 +03:00
|
|
|
if (aLength == 1) {
|
2015-06-17 04:03:58 +03:00
|
|
|
if (!aOffset) {
|
2016-09-02 10:12:24 +03:00
|
|
|
NS_WARNING_ASSERTION(!mFirstCharRect.IsEmpty(), "empty rect");
|
2015-06-17 04:03:58 +03:00
|
|
|
aUnionTextRect = mFirstCharRect;
|
|
|
|
return !aUnionTextRect.IsEmpty();
|
|
|
|
}
|
2022-02-08 01:33:40 +03:00
|
|
|
if (mSelection.isSome() && mSelection->mHasRange) {
|
2020-11-27 17:47:37 +03:00
|
|
|
if (aOffset == mSelection->mAnchor) {
|
|
|
|
NS_WARNING_ASSERTION(
|
|
|
|
!mSelection->mAnchorCharRects[eNextCharRect].IsEmpty(),
|
|
|
|
"empty rect");
|
|
|
|
aUnionTextRect = mSelection->mAnchorCharRects[eNextCharRect];
|
|
|
|
return !aUnionTextRect.IsEmpty();
|
|
|
|
}
|
|
|
|
if (mSelection->mAnchor && aOffset == mSelection->mAnchor - 1) {
|
|
|
|
NS_WARNING_ASSERTION(
|
|
|
|
!mSelection->mAnchorCharRects[ePrevCharRect].IsEmpty(),
|
|
|
|
"empty rect");
|
|
|
|
aUnionTextRect = mSelection->mAnchorCharRects[ePrevCharRect];
|
|
|
|
return !aUnionTextRect.IsEmpty();
|
|
|
|
}
|
|
|
|
if (aOffset == mSelection->mFocus) {
|
|
|
|
NS_WARNING_ASSERTION(
|
|
|
|
!mSelection->mFocusCharRects[eNextCharRect].IsEmpty(),
|
|
|
|
"empty rect");
|
|
|
|
aUnionTextRect = mSelection->mFocusCharRects[eNextCharRect];
|
|
|
|
return !aUnionTextRect.IsEmpty();
|
|
|
|
}
|
|
|
|
if (mSelection->mFocus && aOffset == mSelection->mFocus - 1) {
|
|
|
|
NS_WARNING_ASSERTION(
|
|
|
|
!mSelection->mFocusCharRects[ePrevCharRect].IsEmpty(),
|
|
|
|
"empty rect");
|
|
|
|
aUnionTextRect = mSelection->mFocusCharRects[ePrevCharRect];
|
|
|
|
return !aUnionTextRect.IsEmpty();
|
|
|
|
}
|
2015-06-05 12:28:20 +03:00
|
|
|
}
|
|
|
|
}
|
2015-06-05 12:28:20 +03:00
|
|
|
|
2015-06-17 04:03:58 +03:00
|
|
|
// 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.
|
|
|
|
|
2022-02-08 01:33:40 +03:00
|
|
|
if (!aOffset && mSelection.isSome() && mSelection->mHasRange &&
|
|
|
|
aOffset != mSelection->mAnchor && aOffset != mSelection->mFocus &&
|
2020-11-27 17:48:03 +03:00
|
|
|
(mTextRectArray.isNothing() ||
|
|
|
|
!mTextRectArray->IsOffsetInRange(aOffset)) &&
|
|
|
|
(mLastCommitStringTextRectArray.isNothing() ||
|
|
|
|
!mLastCommitStringTextRectArray->IsOffsetInRange(aOffset))) {
|
2015-06-17 04:03:58 +03:00
|
|
|
// The first character rect isn't cached.
|
2015-06-05 12:28:19 +03:00
|
|
|
return false;
|
|
|
|
}
|
2015-06-17 04:03:58 +03:00
|
|
|
|
2020-11-24 04:31:46 +03:00
|
|
|
// 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.
|
2020-11-27 17:48:03 +03:00
|
|
|
if (mLastCommitStringTextRectArray.isSome() &&
|
|
|
|
mLastCommitStringTextRectArray->IsOverlappingWith(aOffset, aLength)) {
|
|
|
|
aUnionTextRect =
|
|
|
|
mLastCommitStringTextRectArray->GetUnionRectAsFarAsPossible(
|
|
|
|
aOffset, aLength, aRoundToExistingOffset);
|
2015-06-17 04:03:58 +03:00
|
|
|
} else {
|
|
|
|
aUnionTextRect.SetEmpty();
|
|
|
|
}
|
|
|
|
|
2020-11-27 17:48:03 +03:00
|
|
|
if (mTextRectArray.isSome() &&
|
|
|
|
((aRoundToExistingOffset && mTextRectArray->HasRects()) ||
|
|
|
|
mTextRectArray->IsOverlappingWith(aOffset, aLength))) {
|
2020-11-24 04:31:46 +03:00
|
|
|
aUnionTextRect =
|
2020-11-27 17:48:03 +03:00
|
|
|
aUnionTextRect.Union(mTextRectArray->GetUnionRectAsFarAsPossible(
|
2020-11-24 04:31:46 +03:00
|
|
|
aOffset, aLength, aRoundToExistingOffset));
|
|
|
|
}
|
|
|
|
|
2015-06-17 04:03:58 +03:00
|
|
|
if (!aOffset) {
|
|
|
|
aUnionTextRect = aUnionTextRect.Union(mFirstCharRect);
|
|
|
|
}
|
2022-02-08 01:33:40 +03:00
|
|
|
if (mSelection.isSome() && mSelection->mHasRange) {
|
2020-11-27 17:47:37 +03:00
|
|
|
if (aOffset <= mSelection->mAnchor &&
|
|
|
|
mSelection->mAnchor < endOffset.value()) {
|
|
|
|
aUnionTextRect =
|
|
|
|
aUnionTextRect.Union(mSelection->mAnchorCharRects[eNextCharRect]);
|
|
|
|
}
|
|
|
|
if (mSelection->mAnchor && aOffset <= mSelection->mAnchor - 1 &&
|
|
|
|
mSelection->mAnchor - 1 < endOffset.value()) {
|
|
|
|
aUnionTextRect =
|
|
|
|
aUnionTextRect.Union(mSelection->mAnchorCharRects[ePrevCharRect]);
|
|
|
|
}
|
|
|
|
if (aOffset <= mSelection->mFocus &&
|
|
|
|
mSelection->mFocus < endOffset.value()) {
|
|
|
|
aUnionTextRect =
|
|
|
|
aUnionTextRect.Union(mSelection->mFocusCharRects[eNextCharRect]);
|
|
|
|
}
|
|
|
|
if (mSelection->mFocus && aOffset <= mSelection->mFocus - 1 &&
|
|
|
|
mSelection->mFocus - 1 < endOffset.value()) {
|
|
|
|
aUnionTextRect =
|
|
|
|
aUnionTextRect.Union(mSelection->mFocusCharRects[ePrevCharRect]);
|
|
|
|
}
|
2015-06-17 04:03:58 +03:00
|
|
|
}
|
2016-07-22 14:47:51 +03:00
|
|
|
|
2015-06-17 04:03:58 +03:00
|
|
|
return !aUnionTextRect.IsEmpty();
|
2015-06-05 12:28:19 +03:00
|
|
|
}
|
|
|
|
|
2015-06-26 02:21:13 +03:00
|
|
|
bool ContentCacheInParent::GetCaretRect(uint32_t aOffset,
|
2016-06-22 08:16:59 +03:00
|
|
|
bool aRoundToExistingOffset,
|
2015-06-26 02:21:13 +03:00
|
|
|
LayoutDeviceIntRect& aCaretRect) const {
|
2020-11-27 17:47:37 +03:00
|
|
|
MOZ_LOG(sContentCacheLog, LogLevel::Info,
|
2020-11-27 17:47:50 +03:00
|
|
|
("0x%p GetCaretRect(aOffset=%u, aRoundToExistingOffset=%s), "
|
2020-11-27 17:48:03 +03:00
|
|
|
"mCaret=%s, mTextRectArray=%s, mSelection=%s, mFirstCharRect=%s",
|
2020-11-27 17:47:50 +03:00
|
|
|
this, aOffset, GetBoolName(aRoundToExistingOffset),
|
2020-11-27 17:48:03 +03:00
|
|
|
ToString(mCaret).c_str(), ToString(mTextRectArray).c_str(),
|
|
|
|
ToString(mSelection).c_str(), ToString(mFirstCharRect).c_str()));
|
2015-06-05 12:28:20 +03:00
|
|
|
|
2020-11-27 17:47:50 +03:00
|
|
|
if (mCaret.isSome() && mCaret->mOffset == aOffset) {
|
|
|
|
aCaretRect = mCaret->mRect;
|
2015-06-05 12:28:20 +03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Guess caret rect from the text rect if it's stored.
|
2016-06-22 08:16:59 +03:00
|
|
|
if (!GetTextRect(aOffset, aRoundToExistingOffset, aCaretRect)) {
|
2015-06-05 12:28:21 +03:00
|
|
|
// There might be previous character rect in the cache. If so, we can
|
|
|
|
// guess the caret rect with it.
|
2016-06-22 08:16:59 +03:00
|
|
|
if (!aOffset ||
|
|
|
|
!GetTextRect(aOffset - 1, aRoundToExistingOffset, aCaretRect)) {
|
2015-06-05 12:28:21 +03:00
|
|
|
aCaretRect.SetEmpty();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-11-27 17:47:37 +03:00
|
|
|
if (mSelection.isSome() && mSelection->mWritingMode.IsVertical()) {
|
2018-01-10 19:14:16 +03:00
|
|
|
aCaretRect.MoveToY(aCaretRect.YMost());
|
2015-06-05 12:28:21 +03:00
|
|
|
} else {
|
|
|
|
// XXX bidi-unaware.
|
2018-01-10 19:14:16 +03:00
|
|
|
aCaretRect.MoveToX(aCaretRect.XMost());
|
2015-06-05 12:28:21 +03:00
|
|
|
}
|
2015-06-05 12:28:19 +03:00
|
|
|
}
|
2015-06-05 12:28:20 +03:00
|
|
|
|
|
|
|
// 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.
|
2020-11-27 17:47:37 +03:00
|
|
|
if (mSelection.isSome() && mSelection->mWritingMode.IsVertical()) {
|
2020-11-27 17:47:50 +03:00
|
|
|
aCaretRect.SetHeight(mCaret.isSome() ? mCaret->mRect.Height() : 1);
|
2015-06-05 12:28:20 +03:00
|
|
|
} else {
|
2020-11-27 17:47:50 +03:00
|
|
|
aCaretRect.SetWidth(mCaret.isSome() ? mCaret->mRect.Width() : 1);
|
2015-06-05 12:28:20 +03:00
|
|
|
}
|
2015-06-05 12:28:19 +03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-06-26 02:21:13 +03:00
|
|
|
bool ContentCacheInParent::OnCompositionEvent(
|
2023-06-14 04:57:34 +03:00
|
|
|
const WidgetCompositionEvent& aCompositionEvent) {
|
2015-06-05 12:28:20 +03:00
|
|
|
MOZ_LOG(
|
|
|
|
sContentCacheLog, LogLevel::Info,
|
2023-06-14 04:57:34 +03:00
|
|
|
("0x%p OnCompositionEvent(aCompositionEvent={ "
|
2022-02-08 01:33:39 +03:00
|
|
|
"mMessage=%s, mData=\"%s\", mRanges->Length()=%zu }), "
|
2023-06-14 04:57:34 +03:00
|
|
|
"PendingEventsNeedingAck()=%u, WidgetHasComposition()=%s, "
|
2023-06-14 04:57:34 +03:00
|
|
|
"mHandlingCompositions.Length()=%zu, HasPendingCommit()=%s, "
|
2017-12-06 09:07:41 +03:00
|
|
|
"mIsChildIgnoringCompositionEvents=%s, mCommitStringByRequest=0x%p",
|
2023-06-14 04:57:34 +03:00
|
|
|
this, ToChar(aCompositionEvent.mMessage),
|
|
|
|
PrintStringDetail(aCompositionEvent.mData,
|
2020-11-27 17:48:03 +03:00
|
|
|
PrintStringDetail::kMaxLengthForCompositionString)
|
|
|
|
.get(),
|
2023-06-14 04:57:34 +03:00
|
|
|
aCompositionEvent.mRanges ? aCompositionEvent.mRanges->Length() : 0,
|
|
|
|
PendingEventsNeedingAck(), GetBoolName(WidgetHasComposition()),
|
|
|
|
mHandlingCompositions.Length(), GetBoolName(HasPendingCommit()),
|
2023-06-14 04:57:34 +03:00
|
|
|
GetBoolName(mIsChildIgnoringCompositionEvents), mCommitStringByRequest));
|
2015-06-05 12:28:20 +03:00
|
|
|
|
2017-11-23 12:59:04 +03:00
|
|
|
#if MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
2023-06-14 04:57:34 +03:00
|
|
|
mDispatchedEventMessages.AppendElement(aCompositionEvent.mMessage);
|
2017-11-20 17:30:18 +03:00
|
|
|
#endif // #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
2017-10-12 20:50:47 +03:00
|
|
|
|
2015-12-11 09:15:58 +03:00
|
|
|
// We must be able to simulate the selection because
|
|
|
|
// we might not receive selection updates in time
|
2023-06-14 04:57:33 +03:00
|
|
|
if (!WidgetHasComposition()) {
|
2020-12-21 08:59:57 +03:00
|
|
|
if (mCompositionStartInChild.isSome()) {
|
2016-10-12 15:52:01 +03:00
|
|
|
// 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;
|
2015-12-29 16:57:38 +03:00
|
|
|
} else {
|
2022-02-08 01:33:40 +03:00
|
|
|
mCompositionStart = Some(mSelection.isSome() && mSelection->mHasRange
|
|
|
|
? mSelection->StartOffset()
|
|
|
|
: 0u);
|
2015-12-29 16:57:38 +03:00
|
|
|
}
|
2023-06-14 04:57:34 +03:00
|
|
|
MOZ_ASSERT(aCompositionEvent.mMessage == eCompositionStart);
|
2023-06-14 04:57:34 +03:00
|
|
|
mHandlingCompositions.AppendElement(
|
2023-06-14 04:57:34 +03:00
|
|
|
HandlingCompositionData(aCompositionEvent.mCompositionId));
|
2015-06-05 12:28:19 +03:00
|
|
|
}
|
|
|
|
|
2023-06-14 04:57:33 +03:00
|
|
|
mHandlingCompositions.LastElement().mSentCommitEvent =
|
2023-06-14 04:57:34 +03:00
|
|
|
aCompositionEvent.CausesDOMCompositionEndEvent();
|
2023-06-14 04:57:34 +03:00
|
|
|
MOZ_ASSERT(mHandlingCompositions.LastElement().mCompositionId ==
|
2023-06-14 04:57:34 +03:00
|
|
|
aCompositionEvent.mCompositionId);
|
2015-06-05 12:28:19 +03:00
|
|
|
|
2023-06-14 04:57:33 +03:00
|
|
|
if (!WidgetHasComposition()) {
|
2018-01-12 09:05:24 +03:00
|
|
|
// mCompositionStart will be reset when commit event is completely handled
|
|
|
|
// in the remote process.
|
2023-06-14 04:57:33 +03:00
|
|
|
if (mHandlingCompositions.Length() == 1u) {
|
2023-06-14 04:57:34 +03:00
|
|
|
mPendingCommitLength = aCompositionEvent.mData.Length();
|
2017-06-29 12:31:09 +03:00
|
|
|
}
|
2023-06-14 04:57:34 +03:00
|
|
|
MOZ_ASSERT(HasPendingCommit());
|
2023-06-14 04:57:34 +03:00
|
|
|
} else if (aCompositionEvent.mMessage != eCompositionStart) {
|
|
|
|
mHandlingCompositions.LastElement().mCompositionString =
|
|
|
|
aCompositionEvent.mData;
|
2016-06-13 12:17:58 +03:00
|
|
|
}
|
|
|
|
|
2015-06-05 12:28:19 +03:00
|
|
|
// During REQUEST_TO_COMMIT_COMPOSITION or REQUEST_TO_CANCEL_COMPOSITION,
|
2015-12-11 09:15:58 +03:00
|
|
|
// 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) {
|
2023-06-14 04:57:34 +03:00
|
|
|
if (aCompositionEvent.mMessage == eCompositionCommitAsIs) {
|
2023-06-14 04:57:34 +03:00
|
|
|
*mCommitStringByRequest =
|
|
|
|
mHandlingCompositions.LastElement().mCompositionString;
|
2019-12-02 18:27:29 +03:00
|
|
|
} else {
|
2023-06-14 04:57:34 +03:00
|
|
|
MOZ_ASSERT(aCompositionEvent.mMessage == eCompositionChange ||
|
|
|
|
aCompositionEvent.mMessage == eCompositionCommit);
|
|
|
|
*mCommitStringByRequest = aCompositionEvent.mData;
|
2019-12-02 18:27:29 +03:00
|
|
|
}
|
2017-06-27 17:41:12 +03:00
|
|
|
// We need to wait eCompositionCommitRequestHandled from the remote process
|
|
|
|
// in this case. Therefore, mPendingEventsNeedingAck needs to be
|
2023-06-14 04:57:34 +03:00
|
|
|
// incremented here.
|
2023-06-14 04:57:33 +03:00
|
|
|
if (!WidgetHasComposition()) {
|
2023-06-14 04:57:34 +03:00
|
|
|
mHandlingCompositions.LastElement().mPendingEventsNeedingAck++;
|
2017-06-27 17:41:12 +03:00
|
|
|
}
|
2015-06-05 12:28:19 +03:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-06-14 04:57:34 +03:00
|
|
|
mHandlingCompositions.LastElement().mPendingEventsNeedingAck++;
|
2015-06-05 12:28:19 +03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-07-11 04:53:55 +03:00
|
|
|
void ContentCacheInParent::OnSelectionEvent(
|
|
|
|
const WidgetSelectionEvent& aSelectionEvent) {
|
2023-06-14 04:57:34 +03:00
|
|
|
MOZ_LOG(sContentCacheLog, LogLevel::Info,
|
|
|
|
("0x%p OnSelectionEvent(aEvent={ "
|
|
|
|
"mMessage=%s, mOffset=%u, mLength=%u, mReversed=%s, "
|
|
|
|
"mExpandToClusterBoundary=%s, mUseNativeLineBreak=%s }), "
|
2023-06-14 04:57:34 +03:00
|
|
|
"PendingEventsNeedingAck()=%u, WidgetHasComposition()=%s, "
|
2023-06-14 04:57:34 +03:00
|
|
|
"mHandlingCompositions.Length()=%zu, HasPendingCommit()=%s, "
|
|
|
|
"mIsChildIgnoringCompositionEvents=%s",
|
|
|
|
this, ToChar(aSelectionEvent.mMessage), aSelectionEvent.mOffset,
|
|
|
|
aSelectionEvent.mLength, GetBoolName(aSelectionEvent.mReversed),
|
|
|
|
GetBoolName(aSelectionEvent.mExpandToClusterBoundary),
|
|
|
|
GetBoolName(aSelectionEvent.mUseNativeLineBreak),
|
2023-06-14 04:57:34 +03:00
|
|
|
PendingEventsNeedingAck(), GetBoolName(WidgetHasComposition()),
|
2023-06-14 04:57:34 +03:00
|
|
|
mHandlingCompositions.Length(), GetBoolName(HasPendingCommit()),
|
|
|
|
GetBoolName(mIsChildIgnoringCompositionEvents)));
|
2015-07-11 04:53:55 +03:00
|
|
|
|
2022-07-26 17:06:24 +03:00
|
|
|
#if MOZ_DIAGNOSTIC_ASSERT_ENABLED && !defined(FUZZING_SNAPSHOT)
|
2017-10-12 20:50:47 +03:00
|
|
|
mDispatchedEventMessages.AppendElement(aSelectionEvent.mMessage);
|
2017-11-23 12:59:04 +03:00
|
|
|
#endif // MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
2017-10-12 20:50:47 +03:00
|
|
|
|
2023-06-14 04:57:34 +03:00
|
|
|
mPendingSetSelectionEventNeedingAck++;
|
2015-07-11 04:53:55 +03:00
|
|
|
}
|
|
|
|
|
2015-07-22 08:51:36 +03:00
|
|
|
void ContentCacheInParent::OnEventNeedingAckHandled(nsIWidget* aWidget,
|
2023-06-14 04:57:33 +03:00
|
|
|
EventMessage aMessage,
|
|
|
|
uint32_t aCompositionId) {
|
2015-07-11 04:53:55 +03:00
|
|
|
// This is called when the child process receives WidgetCompositionEvent or
|
|
|
|
// WidgetSelectionEvent.
|
|
|
|
|
2023-06-14 04:57:34 +03:00
|
|
|
HandlingCompositionData* handlingCompositionData =
|
|
|
|
aMessage != eSetSelection ? GetHandlingCompositionData(aCompositionId)
|
|
|
|
: nullptr;
|
|
|
|
|
|
|
|
MOZ_LOG(sContentCacheLog, LogLevel::Info,
|
|
|
|
("0x%p OnEventNeedingAckHandled(aWidget=0x%p, aMessage=%s, "
|
|
|
|
"aCompositionId=%" PRIu32
|
|
|
|
"), PendingEventsNeedingAck()=%u, WidgetHasComposition()=%s, "
|
|
|
|
"mHandlingCompositions.Length()=%zu, HasPendingCommit()=%s, "
|
|
|
|
"mIsChildIgnoringCompositionEvents=%s, handlingCompositionData=0x%p",
|
|
|
|
this, aWidget, ToChar(aMessage), aCompositionId,
|
|
|
|
PendingEventsNeedingAck(), GetBoolName(WidgetHasComposition()),
|
|
|
|
mHandlingCompositions.Length(), GetBoolName(HasPendingCommit()),
|
|
|
|
GetBoolName(mIsChildIgnoringCompositionEvents),
|
|
|
|
handlingCompositionData));
|
|
|
|
|
|
|
|
// If we receive composition event messages for older one or invalid one,
|
|
|
|
// we should ignore them.
|
|
|
|
if (NS_WARN_IF(aMessage != eSetSelection && !handlingCompositionData)) {
|
|
|
|
return;
|
|
|
|
}
|
2017-06-27 16:02:07 +03:00
|
|
|
|
2022-07-26 17:06:24 +03:00
|
|
|
#if MOZ_DIAGNOSTIC_ASSERT_ENABLED && !defined(FUZZING_SNAPSHOT)
|
2017-10-12 20:50:47 +03:00
|
|
|
mReceivedEventMessages.AppendElement(aMessage);
|
2017-12-06 09:07:41 +03:00
|
|
|
#endif // #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
2017-10-12 20:50:47 +03:00
|
|
|
|
2023-06-14 04:57:34 +03:00
|
|
|
const bool isCommittedInChild =
|
2017-12-06 09:07:41 +03:00
|
|
|
// 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));
|
2023-06-14 04:57:34 +03:00
|
|
|
const bool hasPendingCommit = HasPendingCommit();
|
2017-10-12 20:50:47 +03:00
|
|
|
|
2017-12-06 09:07:41 +03:00
|
|
|
if (isCommittedInChild) {
|
2022-07-26 17:06:24 +03:00
|
|
|
#if MOZ_DIAGNOSTIC_ASSERT_ENABLED && !defined(FUZZING_SNAPSHOT)
|
2023-06-14 04:57:33 +03:00
|
|
|
if (mHandlingCompositions.Length() == 1u) {
|
2017-10-12 20:50:47 +03:00
|
|
|
RemoveUnnecessaryEventMessageLog();
|
|
|
|
}
|
|
|
|
|
2023-06-14 04:57:34 +03:00
|
|
|
if (NS_WARN_IF(aMessage != eCompositionCommitRequestHandled &&
|
|
|
|
!handlingCompositionData->mSentCommitEvent)) {
|
2017-10-18 18:13:42 +03:00
|
|
|
nsPrintfCString info(
|
2023-06-14 04:57:34 +03:00
|
|
|
"\nReceived unexpected commit event message (%s) which we've "
|
|
|
|
"not sent yet\n\n",
|
2017-10-12 20:50:47 +03:00
|
|
|
ToChar(aMessage));
|
|
|
|
AppendEventMessageLog(info);
|
|
|
|
CrashReporter::AppendAppNotesToCrashReport(info);
|
2017-11-20 17:30:18 +03:00
|
|
|
MOZ_DIAGNOSTIC_ASSERT(
|
2023-06-14 04:57:34 +03:00
|
|
|
false,
|
|
|
|
"Received unexpected commit event which has not been sent yet");
|
2023-06-13 09:31:03 +03:00
|
|
|
}
|
2023-06-14 04:57:34 +03:00
|
|
|
#endif // #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
2023-06-13 09:31:03 +03:00
|
|
|
|
2023-06-14 04:57:34 +03:00
|
|
|
// 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) {
|
Bug 1690827 - part 5: Remove the assertion checking the number of pending composition event messages when receiving a commit request r=m_kato
E.g., when a character is inserted with a composition, i.e., without composing
state, like the Emoji picker of Windows, `BrowserParent` dispatches
`eCompositionStart`, `eCompositionChange` and `eCompositionCommit` message
within a short time. Then, if focus is moved at `compositionstart` event in
the remote message, `eCompositionChange` and `eCompositionCommit` are not yet
handled by the remote process when `BrowserParent::OnEventNeedingAckHandled()`
received a commit request handled message. Therefore, it's not invalid case
that a commit is handled without acknowledges of multiple pending composition
messages. Therefore, the assertion should be removed, and instead, for making
it easier to understand the log, let's put the information of that. Then, it's
obvious that ignored log of following `OnEventNeedingAckHandled()` calls is
valid thing.
Differential Revision: https://phabricator.services.mozilla.com/D181217
2023-06-20 04:53:39 +03:00
|
|
|
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
|
|
|
|
// composing state, but the remote process may move focus at
|
|
|
|
// eCompositionStart. This may happen with UI of IME to put only
|
|
|
|
// one character, e.g., the default Emoji picker of Windows.
|
|
|
|
!data.mSentCommitEvent &&
|
|
|
|
// In the normal case, only one message should remain, however,
|
|
|
|
// remaining 2 or more messages is also valid, for example, the
|
|
|
|
// remote process may have a composition update listener which
|
|
|
|
// takes a while. Then, we can have multiple pending messages.
|
|
|
|
data.mPendingEventsNeedingAck >= 1u) {
|
|
|
|
MOZ_LOG(
|
|
|
|
sContentCacheLog, LogLevel::Debug,
|
|
|
|
(" NOTE: BrowserParent has %" PRIu32
|
|
|
|
" pending composition messages for the handling composition, "
|
|
|
|
"but before they are handled in the remote process, the active "
|
|
|
|
"composition is commited by a request. "
|
|
|
|
"OnEventNeedingAckHandled() calls for them will be ignored",
|
|
|
|
data.mPendingEventsNeedingAck));
|
2023-06-14 04:57:34 +03:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
Bug 1690827 - part 5: Remove the assertion checking the number of pending composition event messages when receiving a commit request r=m_kato
E.g., when a character is inserted with a composition, i.e., without composing
state, like the Emoji picker of Windows, `BrowserParent` dispatches
`eCompositionStart`, `eCompositionChange` and `eCompositionCommit` message
within a short time. Then, if focus is moved at `compositionstart` event in
the remote message, `eCompositionChange` and `eCompositionCommit` are not yet
handled by the remote process when `BrowserParent::OnEventNeedingAckHandled()`
received a commit request handled message. Therefore, it's not invalid case
that a commit is handled without acknowledges of multiple pending composition
messages. Therefore, the assertion should be removed, and instead, for making
it easier to understand the log, let's put the information of that. Then, it's
obvious that ignored log of following `OnEventNeedingAckHandled()` calls is
valid thing.
Differential Revision: https://phabricator.services.mozilla.com/D181217
2023-06-20 04:53:39 +03:00
|
|
|
if (MOZ_UNLIKELY(data.mPendingEventsNeedingAck)) {
|
|
|
|
MOZ_LOG(sContentCacheLog, LogLevel::Warning,
|
|
|
|
(" BrowserParent has %" PRIu32
|
|
|
|
" pending composition messages for an older composition than "
|
|
|
|
"the handling composition, but it'll be removed because newer "
|
|
|
|
"composition gets comitted in the remote process",
|
|
|
|
data.mPendingEventsNeedingAck));
|
|
|
|
}
|
2023-06-14 04:57:34 +03:00
|
|
|
numberOfOutdatedCompositions++;
|
2017-07-05 13:55:18 +03:00
|
|
|
}
|
2023-06-14 04:57:34 +03:00
|
|
|
mHandlingCompositions.RemoveElementsAt(0u, numberOfOutdatedCompositions);
|
|
|
|
handlingCompositionData = nullptr;
|
2017-12-06 09:07:41 +03:00
|
|
|
|
2017-06-29 12:31:09 +03:00
|
|
|
// Forget pending commit string length if it's handled in the remote
|
|
|
|
// process. Note that this doesn't care too old composition's commit
|
|
|
|
// string because in such case, we cannot return proper information
|
2023-06-14 04:57:33 +03:00
|
|
|
// to IME synchronously.
|
2017-06-29 12:31:09 +03:00
|
|
|
mPendingCommitLength = 0;
|
2017-06-27 16:02:07 +03:00
|
|
|
}
|
2016-10-12 11:09:02 +03:00
|
|
|
|
2017-12-06 09:07:41 +03:00
|
|
|
if (WidgetCompositionEvent::IsFollowedByCompositionEnd(aMessage)) {
|
|
|
|
// After the remote process receives eCompositionCommit(AsIs) event,
|
|
|
|
// it'll restart to handle composition events.
|
|
|
|
mIsChildIgnoringCompositionEvents = false;
|
|
|
|
|
2022-07-26 17:06:24 +03:00
|
|
|
#if MOZ_DIAGNOSTIC_ASSERT_ENABLED && !defined(FUZZING_SNAPSHOT)
|
2023-06-14 04:57:34 +03:00
|
|
|
if (NS_WARN_IF(!hasPendingCommit)) {
|
2017-12-06 09:07:41 +03:00
|
|
|
nsPrintfCString info(
|
|
|
|
"\nThere is no pending comment events but received "
|
|
|
|
"%s message from the remote child\n\n",
|
|
|
|
ToChar(aMessage));
|
|
|
|
AppendEventMessageLog(info);
|
|
|
|
CrashReporter::AppendAppNotesToCrashReport(info);
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(
|
|
|
|
false,
|
|
|
|
"No pending commit events but received unexpected commit event");
|
|
|
|
}
|
2023-06-14 04:57:34 +03:00
|
|
|
#endif // #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
2023-06-14 04:57:34 +03:00
|
|
|
} else if (aMessage == eCompositionCommitRequestHandled && hasPendingCommit) {
|
2017-12-06 09:07:41 +03:00
|
|
|
// If the remote process commits composition synchronously after
|
|
|
|
// requesting commit composition and we've already sent commit composition,
|
|
|
|
// it starts to ignore following composition events until receiving
|
|
|
|
// eCompositionStart event.
|
|
|
|
mIsChildIgnoringCompositionEvents = true;
|
|
|
|
}
|
|
|
|
|
2018-01-12 09:05:24 +03:00
|
|
|
// If neither widget (i.e., IME) nor the remote process has composition,
|
|
|
|
// now, we can forget composition string informations.
|
2023-06-14 04:57:34 +03:00
|
|
|
if (mHandlingCompositions.IsEmpty()) {
|
2020-11-23 12:29:34 +03:00
|
|
|
mCompositionStart.reset();
|
2018-01-12 09:05:24 +03:00
|
|
|
}
|
|
|
|
|
2023-06-14 04:57:34 +03:00
|
|
|
if (handlingCompositionData) {
|
|
|
|
if (NS_WARN_IF(!handlingCompositionData->mPendingEventsNeedingAck)) {
|
|
|
|
#if MOZ_DIAGNOSTIC_ASSERT_ENABLED && !defined(FUZZING_SNAPSHOT)
|
|
|
|
nsPrintfCString info(
|
|
|
|
"\nThere is no pending events but received %s "
|
|
|
|
"message from the remote child\n\n",
|
|
|
|
ToChar(aMessage));
|
|
|
|
AppendEventMessageLog(info);
|
|
|
|
CrashReporter::AppendAppNotesToCrashReport(info);
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(
|
|
|
|
false, "No pending event message but received unexpected event");
|
|
|
|
#endif // #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
|
|
|
} else {
|
|
|
|
handlingCompositionData->mPendingEventsNeedingAck--;
|
|
|
|
}
|
|
|
|
} else if (aMessage == eSetSelection) {
|
|
|
|
if (NS_WARN_IF(!mPendingSetSelectionEventNeedingAck)) {
|
2023-06-13 07:07:45 +03:00
|
|
|
#if MOZ_DIAGNOSTIC_ASSERT_ENABLED && !defined(FUZZING_SNAPSHOT)
|
2023-06-14 04:57:34 +03:00
|
|
|
nsAutoCString info(
|
|
|
|
"\nThere is no pending set selection events but received from the "
|
|
|
|
"remote child\n\n");
|
|
|
|
AppendEventMessageLog(info);
|
|
|
|
CrashReporter::AppendAppNotesToCrashReport(info);
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(
|
|
|
|
false, "No pending event message but received unexpected event");
|
2022-07-26 17:06:24 +03:00
|
|
|
#endif // #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
2023-06-14 04:57:34 +03:00
|
|
|
} else {
|
|
|
|
mPendingSetSelectionEventNeedingAck--;
|
|
|
|
}
|
2023-06-13 07:07:45 +03:00
|
|
|
}
|
2023-06-13 09:31:03 +03:00
|
|
|
|
2023-06-14 04:57:34 +03:00
|
|
|
if (!PendingEventsNeedingAck()) {
|
|
|
|
FlushPendingNotifications(aWidget);
|
|
|
|
}
|
2015-07-11 04:53:55 +03:00
|
|
|
}
|
|
|
|
|
2015-12-11 09:15:58 +03:00
|
|
|
bool ContentCacheInParent::RequestIMEToCommitComposition(
|
2023-06-14 04:57:33 +03:00
|
|
|
nsIWidget* aWidget, bool aCancel, uint32_t aCompositionId,
|
|
|
|
nsAString& aCommittedString) {
|
2023-06-14 04:57:34 +03:00
|
|
|
HandlingCompositionData* const handlingCompositionData =
|
|
|
|
GetHandlingCompositionData(aCompositionId);
|
|
|
|
|
2015-06-05 12:28:20 +03:00
|
|
|
MOZ_LOG(
|
|
|
|
sContentCacheLog, LogLevel::Info,
|
2016-07-05 12:56:18 +03:00
|
|
|
("0x%p RequestToCommitComposition(aWidget=%p, "
|
2023-06-14 04:57:33 +03:00
|
|
|
"aCancel=%s, aCompositionId=%" PRIu32
|
2023-06-14 04:57:34 +03:00
|
|
|
"), mHandlingCompositions.Length()=%zu, HasPendingCommit()=%s, "
|
|
|
|
"mIsChildIgnoringCompositionEvents=%s, "
|
2019-04-10 00:38:15 +03:00
|
|
|
"IMEStateManager::DoesBrowserParentHaveIMEFocus(&mBrowserParent)=%s, "
|
2023-06-14 04:57:34 +03:00
|
|
|
"WidgetHasComposition()=%s, mCommitStringByRequest=%p, "
|
|
|
|
"handlingCompositionData=0x%p",
|
2023-06-14 04:57:33 +03:00
|
|
|
this, aWidget, GetBoolName(aCancel), aCompositionId,
|
2023-06-14 04:57:34 +03:00
|
|
|
mHandlingCompositions.Length(), GetBoolName(HasPendingCommit()),
|
2023-06-14 04:57:33 +03:00
|
|
|
GetBoolName(mIsChildIgnoringCompositionEvents),
|
2019-04-10 00:38:15 +03:00
|
|
|
GetBoolName(
|
|
|
|
IMEStateManager::DoesBrowserParentHaveIMEFocus(&mBrowserParent)),
|
2023-06-14 04:57:34 +03:00
|
|
|
GetBoolName(WidgetHasComposition()), mCommitStringByRequest,
|
|
|
|
handlingCompositionData));
|
2015-12-11 09:15:58 +03:00
|
|
|
|
|
|
|
MOZ_ASSERT(!mCommitStringByRequest);
|
|
|
|
|
2023-06-14 04:57:34 +03:00
|
|
|
// If we don't know the composition ID, it must have already been committed
|
|
|
|
// in this process. In the case, we should do nothing here.
|
|
|
|
if (NS_WARN_IF(!handlingCompositionData)) {
|
|
|
|
#if MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
|
|
|
mRequestIMEToCommitCompositionResults.AppendElement(
|
|
|
|
RequestIMEToCommitCompositionResult::eToUnknownCompositionReceived);
|
|
|
|
#endif // #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we receive a commit result for not latest composition, this request is
|
|
|
|
// too late for IME. The remote process should wait following composition
|
|
|
|
// events for cleaning up TextComposition and handle the request as it's
|
|
|
|
// handled asynchronously.
|
|
|
|
if (handlingCompositionData != &mHandlingCompositions.LastElement()) {
|
2017-11-23 12:59:04 +03:00
|
|
|
#if MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
2017-10-18 18:13:42 +03:00
|
|
|
mRequestIMEToCommitCompositionResults.AppendElement(
|
|
|
|
RequestIMEToCommitCompositionResult::eToOldCompositionReceived);
|
2017-11-23 12:59:04 +03:00
|
|
|
#endif // #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
2017-07-05 13:55:18 +03:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-06-14 04:57:34 +03:00
|
|
|
// If the composition has already been commit, th remote process will receive
|
|
|
|
// composition events and clean up TextComposition. So, this should do
|
|
|
|
// nothing and TextComposition should handle the request as it's handled
|
|
|
|
// asynchronously.
|
2017-12-06 09:07:41 +03:00
|
|
|
// XXX Perhaps, this is wrong because TextComposition in child process
|
|
|
|
// may commit the composition with current composition string in the
|
|
|
|
// remote process. I.e., it may be different from actual commit string
|
|
|
|
// which user typed. So, perhaps, we should return true and the commit
|
|
|
|
// string.
|
2023-06-14 04:57:34 +03:00
|
|
|
if (handlingCompositionData->mSentCommitEvent) {
|
2017-11-23 12:59:04 +03:00
|
|
|
#if MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
2017-10-18 18:13:42 +03:00
|
|
|
mRequestIMEToCommitCompositionResults.AppendElement(
|
|
|
|
RequestIMEToCommitCompositionResult::eToCommittedCompositionReceived);
|
2017-11-23 12:59:04 +03:00
|
|
|
#endif // #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
2017-07-21 15:22:23 +03:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-04-10 00:38:15 +03:00
|
|
|
// If BrowserParent which has IME focus was already changed to different one,
|
|
|
|
// the request shouldn't be sent to IME because it's too late.
|
|
|
|
if (!IMEStateManager::DoesBrowserParentHaveIMEFocus(&mBrowserParent)) {
|
2017-07-05 13:55:18 +03:00
|
|
|
// Use the latest composition string which may not be handled in the
|
|
|
|
// remote process for avoiding data loss.
|
2017-11-23 12:59:04 +03:00
|
|
|
#if MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
2017-10-18 18:13:42 +03:00
|
|
|
mRequestIMEToCommitCompositionResults.AppendElement(
|
2019-04-10 00:38:15 +03:00
|
|
|
RequestIMEToCommitCompositionResult::eReceivedAfterBrowserParentBlur);
|
2017-11-23 12:59:04 +03:00
|
|
|
#endif // #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
2023-06-14 04:57:34 +03:00
|
|
|
aCommittedString = handlingCompositionData->mCompositionString;
|
2017-11-20 16:20:02 +03:00
|
|
|
// After we return true from here, i.e., without actually requesting IME
|
|
|
|
// to commit composition, we will receive eCompositionCommitRequestHandled
|
|
|
|
// pseudo event message from the remote process. So, we need to increment
|
|
|
|
// mPendingEventsNeedingAck here.
|
2023-06-14 04:57:34 +03:00
|
|
|
handlingCompositionData->mPendingEventsNeedingAck++;
|
2017-07-05 13:55:18 +03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-12-11 09:15:58 +03:00
|
|
|
RefPtr<TextComposition> composition =
|
|
|
|
IMEStateManager::GetTextCompositionFor(aWidget);
|
|
|
|
if (NS_WARN_IF(!composition)) {
|
|
|
|
MOZ_LOG(sContentCacheLog, LogLevel::Warning,
|
2016-07-05 12:56:18 +03:00
|
|
|
(" 0x%p RequestToCommitComposition(), "
|
2015-12-11 09:15:58 +03:00
|
|
|
"does nothing due to no composition",
|
|
|
|
this));
|
2017-11-23 12:59:04 +03:00
|
|
|
#if MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
2017-10-18 18:13:42 +03:00
|
|
|
mRequestIMEToCommitCompositionResults.AppendElement(
|
|
|
|
RequestIMEToCommitCompositionResult::eReceivedButNoTextComposition);
|
2017-11-23 12:59:04 +03:00
|
|
|
#endif // #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
2015-12-11 09:15:58 +03:00
|
|
|
return false;
|
|
|
|
}
|
2015-06-05 12:28:20 +03:00
|
|
|
|
2023-06-14 04:57:34 +03:00
|
|
|
// If we receive a request for different composition, we must have already
|
|
|
|
// sent a commit event. So the remote process should handle it.
|
|
|
|
// XXX I think that this should never happen because we already checked
|
|
|
|
// whether handlingCompositionData is the latest composition or not.
|
|
|
|
// However, we don't want to commit different composition for the users.
|
|
|
|
// Therefore, let's handle the odd case here.
|
|
|
|
if (NS_WARN_IF(composition->Id() != aCompositionId)) {
|
|
|
|
#if MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
|
|
|
mRequestIMEToCommitCompositionResults.AppendElement(
|
|
|
|
RequestIMEToCommitCompositionResult::
|
|
|
|
eReceivedButForDifferentTextComposition);
|
|
|
|
#endif // #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-12-11 09:15:58 +03:00
|
|
|
mCommitStringByRequest = &aCommittedString;
|
2015-06-05 12:28:19 +03:00
|
|
|
|
2017-11-20 17:08:37 +03:00
|
|
|
// Request commit or cancel composition with TextComposition because we may
|
|
|
|
// have already requested to commit or cancel the composition or we may
|
|
|
|
// have already received eCompositionCommit(AsIs) event. Those status are
|
|
|
|
// managed by composition. So, if we don't request commit composition,
|
|
|
|
// we should do nothing with native IME here.
|
|
|
|
composition->RequestToCommit(aWidget, aCancel);
|
2015-06-05 12:28:19 +03:00
|
|
|
|
2015-12-11 09:15:58 +03:00
|
|
|
mCommitStringByRequest = nullptr;
|
|
|
|
|
|
|
|
MOZ_LOG(
|
|
|
|
sContentCacheLog, LogLevel::Info,
|
2016-07-05 12:56:18 +03:00
|
|
|
(" 0x%p RequestToCommitComposition(), "
|
2023-06-14 04:57:33 +03:00
|
|
|
"WidgetHasComposition()=%s, the composition %s committed synchronously",
|
|
|
|
this, GetBoolName(WidgetHasComposition()),
|
2015-12-11 09:15:58 +03:00
|
|
|
composition->Destroyed() ? "WAS" : "has NOT been"));
|
|
|
|
|
|
|
|
if (!composition->Destroyed()) {
|
|
|
|
// When the composition isn't committed synchronously, the remote process's
|
|
|
|
// TextComposition instance will synthesize commit events and wait to
|
|
|
|
// receive delayed composition events. When TextComposition instances both
|
|
|
|
// in this process and the remote process will be destroyed when delayed
|
|
|
|
// composition events received. TextComposition instance in the parent
|
|
|
|
// process will dispatch following composition events and be destroyed
|
|
|
|
// normally. On the other hand, TextComposition instance in the remote
|
|
|
|
// process won't dispatch following composition events and will be
|
|
|
|
// destroyed by IMEStateManager::DispatchCompositionEvent().
|
2017-11-23 12:59:04 +03:00
|
|
|
#if MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
2017-10-18 18:13:42 +03:00
|
|
|
mRequestIMEToCommitCompositionResults.AppendElement(
|
|
|
|
RequestIMEToCommitCompositionResult::eHandledAsynchronously);
|
2017-11-23 12:59:04 +03:00
|
|
|
#endif // #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
2015-12-11 09:15:58 +03:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// When the composition is committed synchronously, the commit string will be
|
2017-11-27 14:34:01 +03:00
|
|
|
// returned to the remote process. Then, PuppetWidget will dispatch
|
2015-12-11 09:15:58 +03:00
|
|
|
// eCompositionCommit event with the returned commit string (i.e., the value
|
2017-11-27 14:34:01 +03:00
|
|
|
// is aCommittedString of this method) and that causes destroying
|
|
|
|
// TextComposition instance in the remote process (Note that TextComposition
|
|
|
|
// instance in this process was already destroyed).
|
2017-11-23 12:59:04 +03:00
|
|
|
#if MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
2017-10-18 18:13:42 +03:00
|
|
|
mRequestIMEToCommitCompositionResults.AppendElement(
|
|
|
|
RequestIMEToCommitCompositionResult::eHandledSynchronously);
|
2017-11-23 12:59:04 +03:00
|
|
|
#endif // #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
2015-12-11 09:15:58 +03:00
|
|
|
return true;
|
2015-06-05 12:28:19 +03:00
|
|
|
}
|
|
|
|
|
2015-07-11 04:53:55 +03:00
|
|
|
void ContentCacheInParent::MaybeNotifyIME(
|
2015-07-17 07:30:01 +03:00
|
|
|
nsIWidget* aWidget, const IMENotification& aNotification) {
|
2023-06-14 04:57:34 +03:00
|
|
|
if (!PendingEventsNeedingAck()) {
|
2019-04-10 00:38:15 +03:00
|
|
|
IMEStateManager::NotifyIME(aNotification, aWidget, &mBrowserParent);
|
2015-06-05 12:28:20 +03:00
|
|
|
return;
|
|
|
|
}
|
2015-07-11 04:53:55 +03:00
|
|
|
|
|
|
|
switch (aNotification.mMessage) {
|
|
|
|
case NOTIFY_IME_OF_SELECTION_CHANGE:
|
|
|
|
mPendingSelectionChange.MergeWith(aNotification);
|
|
|
|
break;
|
|
|
|
case NOTIFY_IME_OF_TEXT_CHANGE:
|
|
|
|
mPendingTextChange.MergeWith(aNotification);
|
|
|
|
break;
|
2015-07-22 08:15:06 +03:00
|
|
|
case NOTIFY_IME_OF_POSITION_CHANGE:
|
|
|
|
mPendingLayoutChange.MergeWith(aNotification);
|
|
|
|
break;
|
2016-05-31 05:39:15 +03:00
|
|
|
case NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED:
|
2015-07-11 04:53:55 +03:00
|
|
|
mPendingCompositionUpdate.MergeWith(aNotification);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
MOZ_CRASH("Unsupported notification");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ContentCacheInParent::FlushPendingNotifications(nsIWidget* aWidget) {
|
2023-06-14 04:57:34 +03:00
|
|
|
MOZ_ASSERT(!PendingEventsNeedingAck());
|
2015-07-11 04:53:55 +03:00
|
|
|
|
2019-04-10 00:38:15 +03:00
|
|
|
// If the BrowserParent's widget has already gone, this can do nothing since
|
2017-07-10 11:33:26 +03:00
|
|
|
// widget is necessary to notify IME of something.
|
|
|
|
if (!aWidget) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-07-11 04:53:55 +03:00
|
|
|
// New notifications which are notified during flushing pending notifications
|
|
|
|
// should be merged again.
|
2023-06-14 04:57:34 +03:00
|
|
|
const bool pendingEventNeedingAckIncremented =
|
|
|
|
!mHandlingCompositions.IsEmpty();
|
|
|
|
if (pendingEventNeedingAckIncremented) {
|
|
|
|
mHandlingCompositions.LastElement().mPendingEventsNeedingAck++;
|
|
|
|
}
|
2015-07-11 04:53:55 +03:00
|
|
|
|
2017-07-10 11:33:26 +03:00
|
|
|
nsCOMPtr<nsIWidget> widget = aWidget;
|
2015-07-11 04:53:55 +03:00
|
|
|
|
|
|
|
// First, text change notification should be sent because selection change
|
|
|
|
// notification notifies IME of current selection range in the latest content.
|
|
|
|
// So, IME may need the latest content before that.
|
|
|
|
if (mPendingTextChange.HasNotification()) {
|
|
|
|
IMENotification notification(mPendingTextChange);
|
2017-07-10 11:33:26 +03:00
|
|
|
if (!widget->Destroyed()) {
|
2015-07-11 04:53:55 +03:00
|
|
|
mPendingTextChange.Clear();
|
2019-04-10 00:38:15 +03:00
|
|
|
IMEStateManager::NotifyIME(notification, widget, &mBrowserParent);
|
2015-07-11 04:53:55 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mPendingSelectionChange.HasNotification()) {
|
|
|
|
IMENotification notification(mPendingSelectionChange);
|
2017-07-10 11:33:26 +03:00
|
|
|
if (!widget->Destroyed()) {
|
2015-07-11 04:53:55 +03:00
|
|
|
mPendingSelectionChange.Clear();
|
2019-04-10 00:38:15 +03:00
|
|
|
IMEStateManager::NotifyIME(notification, widget, &mBrowserParent);
|
2015-07-11 04:53:55 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-22 08:15:06 +03:00
|
|
|
// Layout change notification should be notified after selection change
|
|
|
|
// notification because IME may want to query position of new caret position.
|
|
|
|
if (mPendingLayoutChange.HasNotification()) {
|
|
|
|
IMENotification notification(mPendingLayoutChange);
|
2017-07-10 11:33:26 +03:00
|
|
|
if (!widget->Destroyed()) {
|
2015-07-22 08:15:06 +03:00
|
|
|
mPendingLayoutChange.Clear();
|
2019-04-10 00:38:15 +03:00
|
|
|
IMEStateManager::NotifyIME(notification, widget, &mBrowserParent);
|
2015-07-22 08:15:06 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-11 04:53:55 +03:00
|
|
|
// Finally, send composition update notification because it notifies IME of
|
|
|
|
// finishing handling whole sending events.
|
|
|
|
if (mPendingCompositionUpdate.HasNotification()) {
|
|
|
|
IMENotification notification(mPendingCompositionUpdate);
|
2017-07-10 11:33:26 +03:00
|
|
|
if (!widget->Destroyed()) {
|
2015-07-11 04:53:55 +03:00
|
|
|
mPendingCompositionUpdate.Clear();
|
2019-04-10 00:38:15 +03:00
|
|
|
IMEStateManager::NotifyIME(notification, widget, &mBrowserParent);
|
2015-07-11 04:53:55 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-14 04:57:34 +03:00
|
|
|
// Decrement it which was incremented above.
|
|
|
|
if (!mHandlingCompositions.IsEmpty() && pendingEventNeedingAckIncremented &&
|
|
|
|
mHandlingCompositions.LastElement().mPendingEventsNeedingAck) {
|
|
|
|
mHandlingCompositions.LastElement().mPendingEventsNeedingAck--;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!PendingEventsNeedingAck() && !widget->Destroyed() &&
|
2015-07-11 04:53:55 +03:00
|
|
|
(mPendingTextChange.HasNotification() ||
|
|
|
|
mPendingSelectionChange.HasNotification() ||
|
2015-07-22 08:15:06 +03:00
|
|
|
mPendingLayoutChange.HasNotification() ||
|
2015-07-11 04:53:55 +03:00
|
|
|
mPendingCompositionUpdate.HasNotification())) {
|
2017-07-10 11:33:26 +03:00
|
|
|
FlushPendingNotifications(widget);
|
2015-07-11 04:53:55 +03:00
|
|
|
}
|
2015-06-05 12:28:20 +03:00
|
|
|
}
|
|
|
|
|
2017-11-23 12:59:04 +03:00
|
|
|
#if MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
2017-10-12 20:50:47 +03:00
|
|
|
|
|
|
|
void ContentCacheInParent::RemoveUnnecessaryEventMessageLog() {
|
|
|
|
bool foundLastCompositionStart = false;
|
|
|
|
for (size_t i = mDispatchedEventMessages.Length(); i > 1; i--) {
|
|
|
|
if (mDispatchedEventMessages[i - 1] != eCompositionStart) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!foundLastCompositionStart) {
|
|
|
|
// Find previous eCompositionStart of the latest eCompositionStart.
|
|
|
|
foundLastCompositionStart = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// Remove the messages before the last 2 sets of composition events.
|
|
|
|
mDispatchedEventMessages.RemoveElementsAt(0, i - 1);
|
|
|
|
break;
|
|
|
|
}
|
2017-10-18 18:13:42 +03:00
|
|
|
uint32_t numberOfCompositionCommitRequestHandled = 0;
|
2017-10-12 20:50:47 +03:00
|
|
|
foundLastCompositionStart = false;
|
|
|
|
for (size_t i = mReceivedEventMessages.Length(); i > 1; i--) {
|
2017-10-18 18:13:42 +03:00
|
|
|
if (mReceivedEventMessages[i - 1] == eCompositionCommitRequestHandled) {
|
|
|
|
numberOfCompositionCommitRequestHandled++;
|
|
|
|
}
|
2017-10-12 20:50:47 +03:00
|
|
|
if (mReceivedEventMessages[i - 1] != eCompositionStart) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!foundLastCompositionStart) {
|
|
|
|
// Find previous eCompositionStart of the latest eCompositionStart.
|
|
|
|
foundLastCompositionStart = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// Remove the messages before the last 2 sets of composition events.
|
|
|
|
mReceivedEventMessages.RemoveElementsAt(0, i - 1);
|
|
|
|
break;
|
|
|
|
}
|
2017-10-18 18:13:42 +03:00
|
|
|
|
|
|
|
if (!numberOfCompositionCommitRequestHandled) {
|
|
|
|
// If there is no eCompositionCommitRequestHandled in
|
|
|
|
// mReceivedEventMessages, we don't need to store log of
|
|
|
|
// RequestIMEToCommmitComposition().
|
|
|
|
mRequestIMEToCommitCompositionResults.Clear();
|
|
|
|
} else {
|
|
|
|
// We need to keep all reason of eCompositionCommitRequestHandled, which
|
|
|
|
// is sent when mRequestIMEToCommitComposition() returns true.
|
|
|
|
// So, we can discard older log than the first
|
|
|
|
// eCompositionCommitRequestHandled in mReceivedEventMessages.
|
|
|
|
for (size_t i = mRequestIMEToCommitCompositionResults.Length(); i > 1;
|
|
|
|
i--) {
|
|
|
|
if (mRequestIMEToCommitCompositionResults[i - 1] ==
|
|
|
|
RequestIMEToCommitCompositionResult::
|
2019-04-10 00:38:15 +03:00
|
|
|
eReceivedAfterBrowserParentBlur ||
|
2017-10-18 18:13:42 +03:00
|
|
|
mRequestIMEToCommitCompositionResults[i - 1] ==
|
|
|
|
RequestIMEToCommitCompositionResult::eHandledSynchronously) {
|
|
|
|
--numberOfCompositionCommitRequestHandled;
|
|
|
|
if (!numberOfCompositionCommitRequestHandled) {
|
|
|
|
mRequestIMEToCommitCompositionResults.RemoveElementsAt(0, i - 1);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-10-12 20:50:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void ContentCacheInParent::AppendEventMessageLog(nsACString& aLog) const {
|
|
|
|
aLog.AppendLiteral("Dispatched Event Message Log:\n");
|
|
|
|
for (EventMessage message : mDispatchedEventMessages) {
|
|
|
|
aLog.AppendLiteral(" ");
|
|
|
|
aLog.Append(ToChar(message));
|
|
|
|
aLog.AppendLiteral("\n");
|
|
|
|
}
|
|
|
|
aLog.AppendLiteral("\nReceived Event Message Log:\n");
|
|
|
|
for (EventMessage message : mReceivedEventMessages) {
|
|
|
|
aLog.AppendLiteral(" ");
|
|
|
|
aLog.Append(ToChar(message));
|
|
|
|
aLog.AppendLiteral("\n");
|
|
|
|
}
|
2017-10-18 18:13:42 +03:00
|
|
|
aLog.AppendLiteral("\nResult of RequestIMEToCommitComposition():\n");
|
|
|
|
for (RequestIMEToCommitCompositionResult result :
|
|
|
|
mRequestIMEToCommitCompositionResults) {
|
|
|
|
aLog.AppendLiteral(" ");
|
|
|
|
aLog.Append(ToReadableText(result));
|
|
|
|
aLog.AppendLiteral("\n");
|
|
|
|
}
|
2017-10-12 20:50:47 +03:00
|
|
|
aLog.AppendLiteral("\n");
|
|
|
|
}
|
|
|
|
|
2017-11-23 12:59:04 +03:00
|
|
|
#endif // #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
2017-10-12 20:50:47 +03:00
|
|
|
|
2022-02-08 01:33:40 +03:00
|
|
|
/*****************************************************************************
|
|
|
|
* mozilla::ContentCache::Selection
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
ContentCache::Selection::Selection(
|
|
|
|
const WidgetQueryContentEvent& aQuerySelectedTextEvent)
|
|
|
|
: mAnchor(UINT32_MAX),
|
|
|
|
mFocus(UINT32_MAX),
|
|
|
|
mWritingMode(aQuerySelectedTextEvent.mReply->WritingModeRef()),
|
|
|
|
mHasRange(aQuerySelectedTextEvent.mReply->mOffsetAndData.isSome()) {
|
|
|
|
MOZ_ASSERT(aQuerySelectedTextEvent.mMessage == eQuerySelectedText);
|
|
|
|
MOZ_ASSERT(aQuerySelectedTextEvent.Succeeded());
|
|
|
|
if (mHasRange) {
|
|
|
|
mAnchor = aQuerySelectedTextEvent.mReply->AnchorOffset();
|
|
|
|
mFocus = aQuerySelectedTextEvent.mReply->FocusOffset();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-05 12:28:19 +03:00
|
|
|
/*****************************************************************************
|
|
|
|
* mozilla::ContentCache::TextRectArray
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
LayoutDeviceIntRect ContentCache::TextRectArray::GetRect(
|
|
|
|
uint32_t aOffset) const {
|
|
|
|
LayoutDeviceIntRect rect;
|
2020-11-27 17:48:03 +03:00
|
|
|
if (IsOffsetInRange(aOffset)) {
|
2015-06-05 12:28:19 +03:00
|
|
|
rect = mRects[aOffset - mStart];
|
|
|
|
}
|
|
|
|
return rect;
|
|
|
|
}
|
|
|
|
|
|
|
|
LayoutDeviceIntRect ContentCache::TextRectArray::GetUnionRect(
|
|
|
|
uint32_t aOffset, uint32_t aLength) const {
|
|
|
|
LayoutDeviceIntRect rect;
|
2020-11-27 17:48:03 +03:00
|
|
|
if (!IsRangeCompletelyInRange(aOffset, aLength)) {
|
2015-06-05 12:28:19 +03:00
|
|
|
return rect;
|
|
|
|
}
|
|
|
|
for (uint32_t i = 0; i < aLength; i++) {
|
|
|
|
rect = rect.Union(mRects[aOffset - mStart + i]);
|
|
|
|
}
|
|
|
|
return rect;
|
|
|
|
}
|
|
|
|
|
2015-06-17 04:03:58 +03:00
|
|
|
LayoutDeviceIntRect ContentCache::TextRectArray::GetUnionRectAsFarAsPossible(
|
2016-06-22 08:16:59 +03:00
|
|
|
uint32_t aOffset, uint32_t aLength, bool aRoundToExistingOffset) const {
|
2015-06-17 04:03:58 +03:00
|
|
|
LayoutDeviceIntRect rect;
|
2016-08-16 18:15:44 +03:00
|
|
|
if (!HasRects() ||
|
|
|
|
(!aRoundToExistingOffset && !IsOverlappingWith(aOffset, aLength))) {
|
2015-06-17 04:03:58 +03:00
|
|
|
return rect;
|
|
|
|
}
|
|
|
|
uint32_t startOffset = std::max(aOffset, mStart);
|
2016-06-22 08:16:59 +03:00
|
|
|
if (aRoundToExistingOffset && startOffset >= EndOffset()) {
|
|
|
|
startOffset = EndOffset() - 1;
|
|
|
|
}
|
2015-06-17 04:03:58 +03:00
|
|
|
uint32_t endOffset = std::min(aOffset + aLength, EndOffset());
|
2016-06-22 08:16:59 +03:00
|
|
|
if (aRoundToExistingOffset && endOffset < mStart + 1) {
|
|
|
|
endOffset = mStart + 1;
|
|
|
|
}
|
2016-08-16 18:15:44 +03:00
|
|
|
if (NS_WARN_IF(endOffset < startOffset)) {
|
|
|
|
return rect;
|
|
|
|
}
|
2015-06-17 04:03:58 +03:00
|
|
|
for (uint32_t i = 0; i < endOffset - startOffset; i++) {
|
|
|
|
rect = rect.Union(mRects[startOffset - mStart + i]);
|
|
|
|
}
|
|
|
|
return rect;
|
|
|
|
}
|
|
|
|
|
2015-06-05 12:28:18 +03:00
|
|
|
} // namespace mozilla
|