Bug 1746104 - part 4: Make `IMContextWrapper` and `IMMHandler` use same class to store content selection r=m_kato

Now, `IMContextWrapper::Selection` and `IMMHandler::Selection` have same
structure.  Therefore, we can merge them into one place.  This will help to
fix bug 1259690 in the future.

Differential Revision: https://phabricator.services.mozilla.com/D137421
This commit is contained in:
Masayuki Nakano 2022-02-07 22:33:37 +00:00
Родитель f27a9abf8b
Коммит 139876e527
7 изменённых файлов: 255 добавлений и 233 удалений

27
widget/ContentData.cpp Normal file
Просмотреть файл

@ -0,0 +1,27 @@
/* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
#include "ContentData.h"
#include "TextEvents.h"
namespace mozilla {
/******************************************************************************
* ContentSelection
******************************************************************************/
ContentSelection::ContentSelection(
const WidgetQueryContentEvent& aSelectedTextEvent)
: mOffsetAndData(
Some(OffsetAndData<uint32_t>(aSelectedTextEvent.mReply->StartOffset(),
aSelectedTextEvent.mReply->DataRef(),
OffsetAndDataFor::SelectedString))),
mWritingMode(aSelectedTextEvent.mReply->WritingModeRef()) {
MOZ_ASSERT(aSelectedTextEvent.mMessage == eQuerySelectedText);
MOZ_ASSERT(aSelectedTextEvent.mReply->mOffsetAndData.isSome());
}
} // namespace mozilla

84
widget/ContentData.h Normal file
Просмотреть файл

@ -0,0 +1,84 @@
/* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
#ifndef mozilla_ContentData_h
#define mozilla_ContentData_h
#include <sstream>
#include "mozilla/Attributes.h"
#include "mozilla/Debug.h"
#include "mozilla/EventForwards.h"
#include "mozilla/Maybe.h"
#include "mozilla/WritingModes.h"
#include "mozilla/widget/IMEData.h"
/**
* This file is intended for declaring classes which store DOM content data for
* widget classes. Those data should be retrived by `WidgetQueryContentEvent`,
* notified with `IMENotification`, or set assumed data as result of dispatching
* widget events such as `WidgetKeyboardEvent`, `WidgetCompositionEvent`,
* `WidgetSelectionEvent` etc.
*/
namespace mozilla {
/**
* ContentSelection stores DOM selection in flattend text by
* ContentEventHandler. It should be retrieved with `eQuerySelectedText` event,
* notified with `NOTIFY_IME_OF_SELECTION_CHANGE` or set by widget itself.
*/
class ContentSelection {
public:
using SelectionChangeDataBase =
widget::IMENotification::SelectionChangeDataBase;
ContentSelection() = default;
explicit ContentSelection(const SelectionChangeDataBase& aSelectionChangeData)
: mOffsetAndData(Some(aSelectionChangeData.ToUint32OffsetAndData())),
mWritingMode(aSelectionChangeData.GetWritingMode()) {}
explicit ContentSelection(const WidgetQueryContentEvent& aSelectedTextEvent);
ContentSelection(uint32_t aOffset, const WritingMode& aWritingMode)
: mOffsetAndData(Some(OffsetAndData<uint32_t>(
aOffset, EmptyString(), OffsetAndDataFor::SelectedString))),
mWritingMode(aWritingMode) {}
const OffsetAndData<uint32_t>& OffsetAndDataRef() const {
return mOffsetAndData.ref();
}
void Collapse(uint32_t aOffset) {
if (mOffsetAndData.isSome()) {
mOffsetAndData->Collapse(aOffset);
} else {
mOffsetAndData.emplace(aOffset, EmptyString(),
OffsetAndDataFor::SelectedString);
}
}
void Clear() {
mOffsetAndData.reset();
mWritingMode = WritingMode();
}
bool HasRange() const { return mOffsetAndData.isSome(); }
const WritingMode& WritingModeRef() const { return mWritingMode; }
friend std::ostream& operator<<(std::ostream& aStream,
const ContentSelection& aContentSelection) {
if (aContentSelection.HasRange()) {
return aStream << "{ HasRange()=false }";
}
aStream << "{ mOffsetAndData=" << aContentSelection.mOffsetAndData
<< ", mWritingMode=" << aContentSelection.mWritingMode << " }";
return aStream;
}
private:
Maybe<OffsetAndData<uint32_t>> mOffsetAndData;
WritingMode mWritingMode;
};
} // namespace mozilla
#endif // #ifndef mozilla_ContentData_h

Просмотреть файл

@ -1100,12 +1100,12 @@ void IMContextWrapper::OnFocusChangeInGecko(bool aFocus) {
// We shouldn't carry over the removed string to another editor.
mSelectedStringRemovedByComposition.Truncate();
mSelection.reset();
mContentSelection.reset();
// When the focus changes, we need to inform IM about the new cursor
// position. Chinese input methods generally rely on this because they
// usually don't start composition until a character is picked.
if (aFocus && EnsureToCacheSelection()) {
if (aFocus && EnsureToCacheContentSelection()) {
SetCursorPosition(GetActiveContext());
}
}
@ -1216,8 +1216,8 @@ void IMContextWrapper::OnUpdateComposition() {
if (!IsComposing()) {
// Composition has been committed. So we need update selection for
// caret later
mSelection.reset();
EnsureToCacheSelection();
mContentSelection.reset();
EnsureToCacheContentSelection();
mSetCursorPositionOnKeyEvent = true;
}
@ -1445,12 +1445,13 @@ void IMContextWrapper::Blur() {
void IMContextWrapper::OnSelectionChange(
nsWindow* aCaller, const IMENotification& aIMENotification) {
const bool isSelectionRangeChanged =
mSelection.isNothing() ||
mSelection->OffsetAndDataRef().StartOffset() !=
mContentSelection.isNothing() ||
mContentSelection->OffsetAndDataRef().StartOffset() !=
aIMENotification.mSelectionChangeData.mOffset ||
mSelection->OffsetAndDataRef().DataRef() !=
mContentSelection->OffsetAndDataRef().DataRef() !=
*aIMENotification.mSelectionChangeData.mString;
mSelection = Some(Selection(aIMENotification.mSelectionChangeData));
mContentSelection =
Some(ContentSelection(aIMENotification.mSelectionChangeData));
const bool retrievedSurroundingSignalReceived =
mRetrieveSurroundingSignalReceived;
mRetrieveSurroundingSignalReceived = false;
@ -1492,14 +1493,14 @@ void IMContextWrapper::OnSelectionChange(
// event handler. So, we're dispatching eCompositionStart,
// we should ignore selection change notification.
if (mCompositionState == eCompositionState_CompositionStartDispatched) {
if (NS_WARN_IF(mSelection.isNothing())) {
if (NS_WARN_IF(mContentSelection.isNothing())) {
MOZ_LOG(gIMELog, LogLevel::Error,
("0x%p OnSelectionChange(), FAILED, "
"new offset is too large, cannot keep composing",
this));
} else if (mSelection->HasRange()) {
} else if (mContentSelection->HasRange()) {
// Modify the selection start offset with new offset.
mCompositionStart = mSelection->OffsetAndDataRef().StartOffset();
mCompositionStart = mContentSelection->OffsetAndDataRef().StartOffset();
// XXX We should modify mSelectedStringRemovedByComposition?
// But how?
MOZ_LOG(gIMELog, LogLevel::Debug,
@ -2128,7 +2129,7 @@ bool IMContextWrapper::DispatchCompositionStart(GtkIMContext* aContext) {
return false;
}
if (NS_WARN_IF(!EnsureToCacheSelection())) {
if (NS_WARN_IF(!EnsureToCacheContentSelection())) {
MOZ_LOG(gIMELog, LogLevel::Error,
("0x%p DispatchCompositionStart(), FAILED, "
"cannot query the selection offset",
@ -2136,7 +2137,7 @@ bool IMContextWrapper::DispatchCompositionStart(GtkIMContext* aContext) {
return false;
}
if (NS_WARN_IF(!mSelection->HasRange())) {
if (NS_WARN_IF(!mContentSelection->HasRange())) {
MOZ_LOG(gIMELog, LogLevel::Error,
("0x%p DispatchCompositionStart(), FAILED, "
"due to no selection",
@ -2154,7 +2155,7 @@ bool IMContextWrapper::DispatchCompositionStart(GtkIMContext* aContext) {
// even though we strongly hope it doesn't happen.
// Every composition event should have the start offset for the result
// because it may high cost if we query the offset every time.
mCompositionStart = mSelection->OffsetAndDataRef().StartOffset();
mCompositionStart = mContentSelection->OffsetAndDataRef().StartOffset();
mDispatchedCompositionString.Truncate();
// If this composition is started by a key press, we need to dispatch
@ -2262,13 +2263,13 @@ bool IMContextWrapper::DispatchCompositionChangeEvent(
// Store the selected string which will be removed by following
// compositionchange event.
if (mCompositionState == eCompositionState_CompositionStartDispatched) {
if (NS_WARN_IF(
!EnsureToCacheSelection(&mSelectedStringRemovedByComposition))) {
if (NS_WARN_IF(!EnsureToCacheContentSelection(
&mSelectedStringRemovedByComposition))) {
// XXX How should we behave in this case??
} else if (mSelection->HasRange()) {
} else if (mContentSelection->HasRange()) {
// XXX We should assume, for now, any web applications don't change
// selection at handling this compositionchange event.
mCompositionStart = mSelection->OffsetAndDataRef().StartOffset();
mCompositionStart = mContentSelection->OffsetAndDataRef().StartOffset();
} else {
// If there is no selection range, we should keep previously storing
// mCompositionStart.
@ -2356,7 +2357,7 @@ bool IMContextWrapper::DispatchCompositionCommitEvent(
this));
return true;
}
if (MOZ_UNLIKELY(!EnsureToCacheSelection())) {
if (MOZ_UNLIKELY(!EnsureToCacheContentSelection())) {
MOZ_LOG(gIMELog, LogLevel::Warning,
("0x%p DispatchCompositionCommitEvent(), Warning, "
"Failed to cache selection before dispatching "
@ -2375,14 +2376,15 @@ bool IMContextWrapper::DispatchCompositionCommitEvent(
// apps, i.e., selection range is same as what selection expects, we
// shouldn't reset IME because the trigger of causing this commit may be an
// input for next composition and we shouldn't cancel it.
if (mSelection.isSome()) {
mSelection->Collapse((mSelection->HasRange()
? mSelection->OffsetAndDataRef().StartOffset()
if (mContentSelection.isSome()) {
mContentSelection->Collapse(
(mContentSelection->HasRange()
? mContentSelection->OffsetAndDataRef().StartOffset()
: mCompositionStart) +
aCommitString->Length());
MOZ_LOG(gIMELog, LogLevel::Info,
("0x%p DispatchCompositionCommitEvent(), mSelection=%s", this,
ToString(mSelection).c_str()));
("0x%p DispatchCompositionCommitEvent(), mContentSelection=%s",
this, ToString(mContentSelection).c_str()));
}
MOZ_ASSERT(!dispatcher);
} else {
@ -2430,12 +2432,12 @@ bool IMContextWrapper::DispatchCompositionCommitEvent(
mCompositionStart + (aCommitString
? aCommitString->Length()
: mDispatchedCompositionString.Length());
if (mSelection.isSome()) {
mSelection->Collapse(offsetToPutCaret);
if (mContentSelection.isSome()) {
mContentSelection->Collapse(offsetToPutCaret);
} else {
// TODO: We should guarantee that there should be at least fake selection
// for IME at here. Then, we can keep the last writing mode.
mSelection.emplace(offsetToPutCaret, WritingMode());
mContentSelection.emplace(offsetToPutCaret, WritingMode());
}
}
@ -2827,22 +2829,24 @@ bool IMContextWrapper::SetTextRange(PangoAttrIterator* aPangoAttrIter,
}
void IMContextWrapper::SetCursorPosition(GtkIMContext* aContext) {
MOZ_LOG(gIMELog, LogLevel::Info,
MOZ_LOG(
gIMELog, LogLevel::Info,
("0x%p SetCursorPosition(aContext=0x%p), "
"mCompositionTargetRange={ mOffset=%u, mLength=%u }, mSelection=%s",
"mCompositionTargetRange={ mOffset=%u, mLength=%u }, "
"mContentSelection=%s",
this, aContext, mCompositionTargetRange.mOffset,
mCompositionTargetRange.mLength, ToString(mSelection).c_str()));
mCompositionTargetRange.mLength, ToString(mContentSelection).c_str()));
bool useCaret = false;
if (!mCompositionTargetRange.IsValid()) {
if (mSelection.isNothing()) {
if (mContentSelection.isNothing()) {
MOZ_LOG(gIMELog, LogLevel::Error,
("0x%p SetCursorPosition(), FAILED, "
"mCompositionTargetRange and mSelection are invalid",
"mCompositionTargetRange and mContentSelection are invalid",
this));
return;
}
if (!mSelection->HasRange()) {
if (!mContentSelection->HasRange()) {
MOZ_LOG(gIMELog, LogLevel::Warning,
("0x%p SetCursorPosition(), FAILED, "
"mCompositionTargetRange is invalid and there is no selection",
@ -2870,9 +2874,9 @@ void IMContextWrapper::SetCursorPosition(GtkIMContext* aContext) {
true, useCaret ? eQueryCaretRect : eQueryTextRect, mLastFocusedWindow);
if (useCaret) {
queryCaretOrTextRectEvent.InitForQueryCaretRect(
mSelection->OffsetAndDataRef().StartOffset());
mContentSelection->OffsetAndDataRef().StartOffset());
} else {
if (mSelection->WritingModeRef().IsVertical()) {
if (mContentSelection->WritingModeRef().IsVertical()) {
// For preventing the candidate window to overlap the target
// clause, we should set fake (typically, very tall) caret rect.
uint32_t length =
@ -2935,7 +2939,7 @@ nsresult IMContextWrapper::GetCurrentParagraph(nsAString& aText,
// current selection.
if (!EditorHasCompositionString()) {
// Query cursor position & selection
if (NS_WARN_IF(!EnsureToCacheSelection())) {
if (NS_WARN_IF(!EnsureToCacheContentSelection())) {
MOZ_LOG(gIMELog, LogLevel::Error,
("0x%p GetCurrentParagraph(), FAILED, due to no "
"valid selection information",
@ -2943,9 +2947,9 @@ nsresult IMContextWrapper::GetCurrentParagraph(nsAString& aText,
return NS_ERROR_FAILURE;
}
if (mSelection.isSome() && mSelection->HasRange()) {
selOffset = mSelection->OffsetAndDataRef().StartOffset();
selLength = mSelection->OffsetAndDataRef().Length();
if (mContentSelection.isSome() && mContentSelection->HasRange()) {
selOffset = mContentSelection->OffsetAndDataRef().StartOffset();
selLength = mContentSelection->OffsetAndDataRef().Length();
} else {
// If there is no range, let's get all text instead...
selOffset = 0u;
@ -3055,20 +3059,20 @@ nsresult IMContextWrapper::DeleteText(GtkIMContext* aContext, int32_t aOffset,
return NS_ERROR_FAILURE;
}
} else {
if (NS_WARN_IF(!EnsureToCacheSelection())) {
if (NS_WARN_IF(!EnsureToCacheContentSelection())) {
MOZ_LOG(gIMELog, LogLevel::Error,
("0x%p DeleteText(), FAILED, due to no valid selection "
"information",
this));
return NS_ERROR_FAILURE;
}
if (!mSelection->HasRange()) {
if (!mContentSelection->HasRange()) {
MOZ_LOG(gIMELog, LogLevel::Debug,
("0x%p DeleteText(), does nothing, due to no selection range",
this));
return NS_OK;
}
selOffset = mSelection->OffsetAndDataRef().StartOffset();
selOffset = mContentSelection->OffsetAndDataRef().StartOffset();
}
// Get all text contents of the focused editor
@ -3197,21 +3201,22 @@ void IMContextWrapper::InitEvent(WidgetGUIEvent& aEvent) {
aEvent.mTime = PR_Now() / 1000;
}
bool IMContextWrapper::EnsureToCacheSelection(nsAString* aSelectedString) {
bool IMContextWrapper::EnsureToCacheContentSelection(
nsAString* aSelectedString) {
if (aSelectedString) {
aSelectedString->Truncate();
}
if (mSelection.isSome()) {
if (mSelection->HasRange() && aSelectedString) {
aSelectedString->Assign(mSelection->OffsetAndDataRef().DataRef());
if (mContentSelection.isSome()) {
if (mContentSelection->HasRange() && aSelectedString) {
aSelectedString->Assign(mContentSelection->OffsetAndDataRef().DataRef());
}
return true;
}
if (NS_WARN_IF(!mLastFocusedWindow)) {
MOZ_LOG(gIMELog, LogLevel::Error,
("0x%p EnsureToCacheSelection(), FAILED, due to "
("0x%p EnsureToCacheContentSelection(), FAILED, due to "
"no focused window",
this));
return false;
@ -3224,22 +3229,24 @@ bool IMContextWrapper::EnsureToCacheSelection(nsAString* aSelectedString) {
mLastFocusedWindow->DispatchEvent(&querySelectedTextEvent, status);
if (NS_WARN_IF(querySelectedTextEvent.Failed())) {
MOZ_LOG(gIMELog, LogLevel::Error,
("0x%p EnsureToCacheSelection(), FAILED, due to "
("0x%p EnsureToCacheContentSelection(), FAILED, due to "
"failure of query selection event",
this));
return false;
}
mSelection = Some(Selection(querySelectedTextEvent));
if (mSelection->HasRange()) {
if (!mSelection->OffsetAndDataRef().IsDataEmpty() && aSelectedString) {
mContentSelection = Some(ContentSelection(querySelectedTextEvent));
if (mContentSelection->HasRange()) {
if (!mContentSelection->OffsetAndDataRef().IsDataEmpty() &&
aSelectedString) {
aSelectedString->Assign(querySelectedTextEvent.mReply->DataRef());
}
}
MOZ_LOG(gIMELog, LogLevel::Debug,
("0x%p EnsureToCacheSelection(), Succeeded, mSelection=%s", this,
ToString(mSelection).c_str()));
MOZ_LOG(
gIMELog, LogLevel::Debug,
("0x%p EnsureToCacheContentSelection(), Succeeded, mContentSelection=%s",
this, ToString(mContentSelection).c_str()));
return true;
}

Просмотреть файл

@ -16,9 +16,9 @@
#include "nsTArray.h"
#include "nsIWidget.h"
#include "mozilla/CheckedInt.h"
#include "mozilla/ContentData.h"
#include "mozilla/EventForwards.h"
#include "mozilla/Maybe.h"
#include "mozilla/TextEvents.h" // TODO: Stop this in the following patch
#include "mozilla/TextEventDispatcherListener.h"
#include "mozilla/WritingModes.h"
#include "mozilla/widget/IMEData.h"
@ -384,69 +384,15 @@ class IMContextWrapper final : public TextEventDispatcherListener {
// IM which user selected.
IMContextID mIMContextID;
class Selection final {
public:
Selection() = default;
explicit Selection(
const IMENotification::SelectionChangeDataBase& aSelectionChangeData)
: mOffsetAndData(Some(aSelectionChangeData.ToUint32OffsetAndData())),
mWritingMode(aSelectionChangeData.GetWritingMode()) {}
explicit Selection(const WidgetQueryContentEvent& aSelectedTextEvent)
: mOffsetAndData(Some(
OffsetAndData<uint32_t>(aSelectedTextEvent.mReply->StartOffset(),
aSelectedTextEvent.mReply->DataRef(),
OffsetAndDataFor::SelectedString))),
mWritingMode(aSelectedTextEvent.mReply->WritingModeRef()) {
MOZ_ASSERT(aSelectedTextEvent.mMessage == eQuerySelectedText);
MOZ_ASSERT(aSelectedTextEvent.mReply->mOffsetAndData.isSome());
}
Selection(uint32_t aOffset, const WritingMode& aWritingMode)
: mOffsetAndData(Some(OffsetAndData<uint32_t>(
aOffset, EmptyString(), OffsetAndDataFor::SelectedString))),
mWritingMode(aWritingMode) {}
const OffsetAndData<uint32_t>& OffsetAndDataRef() const {
return mOffsetAndData.ref();
}
void Collapse(uint32_t aOffset) {
if (mOffsetAndData.isSome()) {
mOffsetAndData->Collapse(aOffset);
} else {
mOffsetAndData.emplace(aOffset, EmptyString(),
OffsetAndDataFor::SelectedString);
}
}
void Clear() {
mOffsetAndData.reset();
mWritingMode = WritingMode();
}
bool HasRange() const { return mOffsetAndData.isSome(); }
const WritingMode& WritingModeRef() const { return mWritingMode; }
friend std::ostream& operator<<(std::ostream& aStream,
const Selection& aSelection) {
if (aSelection.HasRange()) {
return aStream << "{ HasRange()=false }";
}
aStream << "{ mOffsetAndData=" << aSelection.mOffsetAndData
<< ", mWritingMode=" << aSelection.mWritingMode << " }";
return aStream;
}
private:
Maybe<OffsetAndData<uint32_t>> mOffsetAndData;
WritingMode mWritingMode;
};
// If mSelection is Nothing, it means that EnsureToCacheSelection failed to
// get selection or just not caching the selection.
Maybe<Selection> mSelection;
// If mContentSelection is Nothing, it means that
// EnsureToCacheContentSelection failed to get selection or just not caching
// the selection.
Maybe<ContentSelection> mContentSelection;
/**
* Return true if mSelection is set to some. Otherwise, false.
* Return true if mContentSelection is set to some. Otherwise, false.
*/
bool EnsureToCacheSelection(nsAString* aSelectedString = nullptr);
bool EnsureToCacheContentSelection(nsAString* aSelectedString = nullptr);
// mIsIMFocused is set to TRUE when we call gtk_im_context_focus_in(). And
// it's set to FALSE when we call gtk_im_context_focus_out().

Просмотреть файл

@ -25,12 +25,18 @@ with Files("reftests/*fallback*"):
with Files("*CompositorWidget*"):
BUG_COMPONENT = ("Core", "Graphics")
with Files("*Gfx*"):
BUG_COMPONENT = ("Core", "Graphics")
with Files("*ContentData*"):
BUG_COMPONENT = ("Core", "DOM: UI Events & Focus Handling")
with Files("*FontRange*"):
BUG_COMPONENT = ("Core", "Widget: Cocoa")
with Files("*Gfx*"):
BUG_COMPONENT = ("Core", "Graphics")
with Files("*IMEData*"):
BUG_COMPONENT = ("Core", "DOM: UI Events & Focus Handling")
toolkit = CONFIG["MOZ_WIDGET_TOOLKIT"]
if toolkit in ("android", "cocoa", "gtk", "uikit", "windows"):
@ -144,6 +150,7 @@ EXPORTS.mozilla += [
"ColorScheme.h",
"CommandList.h",
"ContentCache.h",
"ContentData.h",
"ContentEvents.h",
"EventClassList.h",
"EventForwards.h",
@ -183,6 +190,7 @@ EXPORTS.mozilla.widget += [
UNIFIED_SOURCES += [
"CompositorWidget.cpp",
"ContentCache.cpp",
"ContentData.cpp",
"GfxDriverInfo.cpp",
"GfxInfoBase.cpp",
"GfxInfoCollector.cpp",

Просмотреть файл

@ -468,7 +468,7 @@ void IMMHandler::OnFocusChange(bool aFocus, nsWindow* aWindow) {
}
}
if (gIMMHandler) {
gIMMHandler->mSelection.reset();
gIMMHandler->mContentSelection.reset();
}
sHasFocus = aFocus;
}
@ -495,10 +495,8 @@ void IMMHandler::OnSelectionChange(nsWindow* aWindow,
// MaybeAdjustCompositionFont() may create gIMMHandler. So, check it
// after a call of MaybeAdjustCompositionFont().
if (gIMMHandler) {
if (gIMMHandler->mSelection.isNothing()) {
gIMMHandler->mSelection.emplace();
}
gIMMHandler->mSelection->Update(aIMENotification.mSelectionChangeData);
gIMMHandler->mContentSelection =
Some(ContentSelection(aIMENotification.mSelectionChangeData));
}
}
@ -936,23 +934,24 @@ void IMMHandler::HandleStartComposition(nsWindow* aWindow,
MOZ_ASSERT(!mIsComposing,
"HandleStartComposition is called but mIsComposing is TRUE");
const Maybe<Selection>& selection = GetSelectionWithQueryIfNothing(aWindow);
if (selection.isNothing()) {
const Maybe<ContentSelection>& contentSelection =
GetContentSelectionWithQueryIfNothing(aWindow);
if (contentSelection.isNothing()) {
MOZ_LOG(gIMELog, LogLevel::Error,
(" IMMHandler::HandleStartComposition, FAILED, due to "
"Selection::GetSelectionWithQueryIfNothing() failure"));
"Selection::GetContentSelectionWithQueryIfNothing() failure"));
return;
}
if (!selection->HasRange()) {
if (!contentSelection->HasRange()) {
MOZ_LOG(gIMELog, LogLevel::Error,
(" IMMHandler::HandleStartComposition, FAILED, due to "
"there is no selection"));
return;
}
AdjustCompositionFont(aWindow, aContext, selection->WritingModeRef());
AdjustCompositionFont(aWindow, aContext, contentSelection->WritingModeRef());
mCompositionStart = selection->OffsetAndDataRef().StartOffset();
mCompositionStart = contentSelection->OffsetAndDataRef().StartOffset();
mCursorPosition = NO_IME_CARET;
RefPtr<TextEventDispatcher> dispatcher = GetTextEventDispatcherFor(aWindow);
@ -1259,16 +1258,18 @@ bool IMMHandler::HandleReconvert(nsWindow* aWindow, LPARAM lParam,
*oResult = 0;
RECONVERTSTRING* pReconv = reinterpret_cast<RECONVERTSTRING*>(lParam);
const Maybe<Selection>& selection = GetSelectionWithQueryIfNothing(aWindow);
if (selection.isNothing()) {
const Maybe<ContentSelection>& contentSelection =
GetContentSelectionWithQueryIfNothing(aWindow);
if (contentSelection.isNothing()) {
MOZ_LOG(gIMELog, LogLevel::Error,
("IMMHandler::HandleReconvert, FAILED, due to "
"Selection::GetSelectionWithQueryIfNothing() failure"));
"Selection::GetContentSelectionWithQueryIfNothing() failure"));
return false;
}
const uint32_t len =
selection->HasRange() ? selection->OffsetAndDataRef().Length() : 0u;
const uint32_t len = contentSelection->HasRange()
? contentSelection->OffsetAndDataRef().Length()
: 0u;
uint32_t needSize = sizeof(RECONVERTSTRING) + len * sizeof(WCHAR);
if (!pReconv) {
@ -1305,7 +1306,7 @@ bool IMMHandler::HandleReconvert(nsWindow* aWindow, LPARAM lParam,
if (len) {
::CopyMemory(reinterpret_cast<LPVOID>(lParam + sizeof(RECONVERTSTRING)),
selection->OffsetAndDataRef().DataRef().get(),
contentSelection->OffsetAndDataRef().DataRef().get(),
len * sizeof(WCHAR));
}
@ -1414,18 +1415,19 @@ bool IMMHandler::HandleDocumentFeed(nsWindow* aWindow, LPARAM lParam,
int32_t targetOffset, targetLength;
if (!hasCompositionString) {
const Maybe<Selection>& selection = GetSelectionWithQueryIfNothing(aWindow);
if (selection.isNothing()) {
const Maybe<ContentSelection>& contentSelection =
GetContentSelectionWithQueryIfNothing(aWindow);
if (contentSelection.isNothing()) {
MOZ_LOG(gIMELog, LogLevel::Error,
("IMMHandler::HandleDocumentFeed, FAILED, due to "
"Selection::GetSelectionWithQueryIfNothing() failure"));
"Selection::GetContentSelectionWithQueryIfNothing() failure"));
return false;
}
if (selection->HasRange()) {
targetOffset =
static_cast<int32_t>(selection->OffsetAndDataRef().StartOffset());
if (contentSelection->HasRange()) {
targetOffset = static_cast<int32_t>(
contentSelection->OffsetAndDataRef().StartOffset());
targetLength =
static_cast<int32_t>(selection->OffsetAndDataRef().Length());
static_cast<int32_t>(contentSelection->OffsetAndDataRef().Length());
} else {
// If there is no selection range, let's return all text in the editor.
targetOffset = 0;
@ -1821,17 +1823,18 @@ bool IMMHandler::GetCharacterRectOfSelectedTextAt(
WritingMode* aWritingMode) {
LayoutDeviceIntPoint point(0, 0);
const Maybe<Selection>& selection = GetSelectionWithQueryIfNothing(aWindow);
if (selection.isNothing()) {
const Maybe<ContentSelection>& contentSelection =
GetContentSelectionWithQueryIfNothing(aWindow);
if (contentSelection.isNothing()) {
MOZ_LOG(gIMELog, LogLevel::Error,
("IMMHandler::GetCharacterRectOfSelectedTextAt, FAILED, due to "
"Selection::GetSelectionWithQueryIfNothing() failure"));
"Selection::GetContentSelectionWithQueryIfNothing() failure"));
return false;
}
// If there is neither a selection range nor composition string, cannot return
// character rect, of course.
if (!selection->HasRange() && !mIsComposing) {
if (!contentSelection->HasRange() && !mIsComposing) {
MOZ_LOG(gIMELog, LogLevel::Warning,
("IMMHandler::GetCharacterRectOfSelectedTextAt, FAILED, due to "
"there is neither a selection range nor composition string"));
@ -1842,9 +1845,9 @@ bool IMMHandler::GetCharacterRectOfSelectedTextAt(
// string, we should return false since such case must be a bug of the caller
// or the active IME. If it's an IME's bug, we need to set targetLength to
// aOffset.
const uint32_t targetLength = mIsComposing
? mCompositionString.Length()
: selection->OffsetAndDataRef().Length();
const uint32_t targetLength =
mIsComposing ? mCompositionString.Length()
: contentSelection->OffsetAndDataRef().Length();
if (NS_WARN_IF(aOffset > targetLength)) {
MOZ_LOG(
gIMELog, LogLevel::Error,
@ -1857,7 +1860,8 @@ bool IMMHandler::GetCharacterRectOfSelectedTextAt(
// If there is caret, we might be able to use caret rect.
uint32_t caretOffset = UINT32_MAX;
// There is a caret only when the normal selection is collapsed.
if (selection.isNothing() || selection->OffsetAndDataRef().IsDataEmpty()) {
if (contentSelection.isNothing() ||
contentSelection->OffsetAndDataRef().IsDataEmpty()) {
if (mIsComposing) {
// If it's composing, mCursorPosition is the offset to caret in
// the composition string.
@ -2353,39 +2357,7 @@ bool IMMHandler::OnKeyDownEvent(nsWindow* aWindow, WPARAM wParam, LPARAM lParam,
}
}
/******************************************************************************
* IMMHandler::Selection
******************************************************************************/
void IMMHandler::Selection::Update(
const IMENotification::SelectionChangeDataBase& aSelectionChangeData) {
if (!aSelectionChangeData.IsValid()) {
ClearRange();
}
mOffsetAndData = Some(aSelectionChangeData.ToUint32OffsetAndData());
if (mOffsetAndData.isSome()) {
// Don't reset WritingMode if there is no selection because users must not
// want to update UI of IME temporarily since no selection range cause is
// created only by web apps, and they would restore selection later at the
// last point.
mWritingMode = aSelectionChangeData.GetWritingMode();
}
MOZ_LOG(gIMELog, LogLevel::Info,
("IMMHandler::Selection::Update, aIMENotification={ "
"mSelectionChangeData={ "
"mOffsetAndData=%s, mWritingMode=%s } }",
ToString(mOffsetAndData).c_str(), ToString(mWritingMode).c_str()));
if (!mOffsetAndData->IsValid()) {
MOZ_LOG(gIMELog, LogLevel::Error,
(" IMMHandler::Selection::Update, FAILED, due to invalid range"));
ClearRange();
}
}
Maybe<IMMHandler::Selection> IMMHandler::Selection::QuerySelection(
nsWindow* aWindow) {
Maybe<ContentSelection> IMMHandler::QueryContentSelection(nsWindow* aWindow) {
WidgetQueryContentEvent querySelectedTextEvent(true, eQuerySelectedText,
aWindow);
LayoutDeviceIntPoint point(0, 0);
@ -2406,21 +2378,19 @@ Maybe<IMMHandler::Selection> IMMHandler::Selection::QuerySelection(
return Nothing();
}
Selection selection;
selection.mOffsetAndData = querySelectedTextEvent.mReply->mOffsetAndData;
selection.mWritingMode = querySelectedTextEvent.mReply->WritingModeRef();
ContentSelection contentSelection(querySelectedTextEvent);
MOZ_LOG(gIMELog, LogLevel::Info,
("IMMHandler::Selection::Init, querySelectedTextEvent={ mReply=%s }",
ToString(querySelectedTextEvent.mReply).c_str()));
if (selection.mOffsetAndData.isSome() &&
!selection.mOffsetAndData->IsValid()) {
if (contentSelection.HasRange() &&
!contentSelection.OffsetAndDataRef().IsValid()) {
MOZ_LOG(gIMELog, LogLevel::Error,
(" IMMHandler::Selection::Init, FAILED, due to invalid range"));
return Nothing();
}
return Some(selection);
return Some(contentSelection);
}
} // namespace widget

Просмотреть файл

@ -6,6 +6,7 @@
#ifndef IMMHandler_h_
#define IMMHandler_h_
#include "mozilla/ContentData.h"
#include "mozilla/EventForwards.h"
#include "mozilla/TextEventDispatcher.h"
#include "mozilla/WritingModes.h"
@ -377,57 +378,36 @@ class IMMHandler final {
int32_t mCursorPosition;
uint32_t mCompositionStart;
class Selection {
public:
Selection() = default;
// mContentSelection stores the latest selection data only when sHasFocus is
// true. Don't access mContentSelection directly. You should use
// GetContentSelectionWithQueryIfNothing() for getting proper state.
Maybe<ContentSelection> mContentSelection;
void ClearRange() {
mOffsetAndData.reset();
// Don't reset WritingMode because users must not want to update UI of IME
// temporarily since no selection range cause is created only by web apps,
// and they would restore selection later at the last point.
}
const OffsetAndData<uint32_t>& OffsetAndDataRef() const {
return mOffsetAndData.ref();
}
const WritingMode& WritingModeRef() const { return mWritingMode; }
bool HasRange() const { return mOffsetAndData.isSome(); }
void Update(
const IMENotification::SelectionChangeDataBase& aSelectionChangeData);
static Maybe<Selection> QuerySelection(nsWindow* aWindow);
private:
Maybe<OffsetAndData<uint32_t>> mOffsetAndData;
WritingMode mWritingMode;
};
// mSelection stores the latest selection data only when sHasFocus is true.
// Don't access mSelection directly. You should use
// GetSelectionWithQueryIfNothing() for getting proper state.
Maybe<Selection> mSelection;
const Maybe<Selection>& GetSelectionWithQueryIfNothing(nsWindow* aWindow) {
// When IME has focus, mSelection is automatically updated by
const Maybe<ContentSelection>& GetContentSelectionWithQueryIfNothing(
nsWindow* aWindow) {
// When IME has focus, mContentSelection is automatically updated by
// NOTIFY_IME_OF_SELECTION_CHANGE.
if (sHasFocus) {
if (mSelection.isNothing()) {
// But if this is the first access of mSelection, we need to query
// selection now.
mSelection = Selection::QuerySelection(aWindow);
if (mContentSelection.isNothing()) {
// But if this is the first access of mContentSelection, we need to
// query selection now.
mContentSelection = QueryContentSelection(aWindow);
}
return mSelection;
return mContentSelection;
}
// Otherwise, i.e., While IME doesn't have focus, we cannot observe
// selection changes. So, in such case, we need to query selection
// when it's necessary.
static Maybe<Selection> sTempSelection;
sTempSelection = Selection::QuerySelection(aWindow);
return sTempSelection;
static Maybe<ContentSelection> sTempContentSelection;
sTempContentSelection = QueryContentSelection(aWindow);
return sTempContentSelection;
}
/**
* Query content selection on aWindow with WidgetQueryContent event.
*/
static Maybe<ContentSelection> QueryContentSelection(nsWindow* aWindow);
bool mIsComposing;
static mozilla::WritingMode sWritingModeOfCompositionFont;