зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
f27a9abf8b
Коммит
139876e527
|
@ -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
|
|
@ -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;
|
||||
|
|
Загрузка…
Ссылка в новой задаче