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:
27
widget/ContentData.cpp
Normal file
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
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.
|
// We shouldn't carry over the removed string to another editor.
|
||||||
mSelectedStringRemovedByComposition.Truncate();
|
mSelectedStringRemovedByComposition.Truncate();
|
||||||
mSelection.reset();
|
mContentSelection.reset();
|
||||||
|
|
||||||
// When the focus changes, we need to inform IM about the new cursor
|
// When the focus changes, we need to inform IM about the new cursor
|
||||||
// position. Chinese input methods generally rely on this because they
|
// position. Chinese input methods generally rely on this because they
|
||||||
// usually don't start composition until a character is picked.
|
// usually don't start composition until a character is picked.
|
||||||
if (aFocus && EnsureToCacheSelection()) {
|
if (aFocus && EnsureToCacheContentSelection()) {
|
||||||
SetCursorPosition(GetActiveContext());
|
SetCursorPosition(GetActiveContext());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1216,8 +1216,8 @@ void IMContextWrapper::OnUpdateComposition() {
|
|||||||
if (!IsComposing()) {
|
if (!IsComposing()) {
|
||||||
// Composition has been committed. So we need update selection for
|
// Composition has been committed. So we need update selection for
|
||||||
// caret later
|
// caret later
|
||||||
mSelection.reset();
|
mContentSelection.reset();
|
||||||
EnsureToCacheSelection();
|
EnsureToCacheContentSelection();
|
||||||
mSetCursorPositionOnKeyEvent = true;
|
mSetCursorPositionOnKeyEvent = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1445,12 +1445,13 @@ void IMContextWrapper::Blur() {
|
|||||||
void IMContextWrapper::OnSelectionChange(
|
void IMContextWrapper::OnSelectionChange(
|
||||||
nsWindow* aCaller, const IMENotification& aIMENotification) {
|
nsWindow* aCaller, const IMENotification& aIMENotification) {
|
||||||
const bool isSelectionRangeChanged =
|
const bool isSelectionRangeChanged =
|
||||||
mSelection.isNothing() ||
|
mContentSelection.isNothing() ||
|
||||||
mSelection->OffsetAndDataRef().StartOffset() !=
|
mContentSelection->OffsetAndDataRef().StartOffset() !=
|
||||||
aIMENotification.mSelectionChangeData.mOffset ||
|
aIMENotification.mSelectionChangeData.mOffset ||
|
||||||
mSelection->OffsetAndDataRef().DataRef() !=
|
mContentSelection->OffsetAndDataRef().DataRef() !=
|
||||||
*aIMENotification.mSelectionChangeData.mString;
|
*aIMENotification.mSelectionChangeData.mString;
|
||||||
mSelection = Some(Selection(aIMENotification.mSelectionChangeData));
|
mContentSelection =
|
||||||
|
Some(ContentSelection(aIMENotification.mSelectionChangeData));
|
||||||
const bool retrievedSurroundingSignalReceived =
|
const bool retrievedSurroundingSignalReceived =
|
||||||
mRetrieveSurroundingSignalReceived;
|
mRetrieveSurroundingSignalReceived;
|
||||||
mRetrieveSurroundingSignalReceived = false;
|
mRetrieveSurroundingSignalReceived = false;
|
||||||
@@ -1492,14 +1493,14 @@ void IMContextWrapper::OnSelectionChange(
|
|||||||
// event handler. So, we're dispatching eCompositionStart,
|
// event handler. So, we're dispatching eCompositionStart,
|
||||||
// we should ignore selection change notification.
|
// we should ignore selection change notification.
|
||||||
if (mCompositionState == eCompositionState_CompositionStartDispatched) {
|
if (mCompositionState == eCompositionState_CompositionStartDispatched) {
|
||||||
if (NS_WARN_IF(mSelection.isNothing())) {
|
if (NS_WARN_IF(mContentSelection.isNothing())) {
|
||||||
MOZ_LOG(gIMELog, LogLevel::Error,
|
MOZ_LOG(gIMELog, LogLevel::Error,
|
||||||
("0x%p OnSelectionChange(), FAILED, "
|
("0x%p OnSelectionChange(), FAILED, "
|
||||||
"new offset is too large, cannot keep composing",
|
"new offset is too large, cannot keep composing",
|
||||||
this));
|
this));
|
||||||
} else if (mSelection->HasRange()) {
|
} else if (mContentSelection->HasRange()) {
|
||||||
// Modify the selection start offset with new offset.
|
// Modify the selection start offset with new offset.
|
||||||
mCompositionStart = mSelection->OffsetAndDataRef().StartOffset();
|
mCompositionStart = mContentSelection->OffsetAndDataRef().StartOffset();
|
||||||
// XXX We should modify mSelectedStringRemovedByComposition?
|
// XXX We should modify mSelectedStringRemovedByComposition?
|
||||||
// But how?
|
// But how?
|
||||||
MOZ_LOG(gIMELog, LogLevel::Debug,
|
MOZ_LOG(gIMELog, LogLevel::Debug,
|
||||||
@@ -2128,7 +2129,7 @@ bool IMContextWrapper::DispatchCompositionStart(GtkIMContext* aContext) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NS_WARN_IF(!EnsureToCacheSelection())) {
|
if (NS_WARN_IF(!EnsureToCacheContentSelection())) {
|
||||||
MOZ_LOG(gIMELog, LogLevel::Error,
|
MOZ_LOG(gIMELog, LogLevel::Error,
|
||||||
("0x%p DispatchCompositionStart(), FAILED, "
|
("0x%p DispatchCompositionStart(), FAILED, "
|
||||||
"cannot query the selection offset",
|
"cannot query the selection offset",
|
||||||
@@ -2136,7 +2137,7 @@ bool IMContextWrapper::DispatchCompositionStart(GtkIMContext* aContext) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NS_WARN_IF(!mSelection->HasRange())) {
|
if (NS_WARN_IF(!mContentSelection->HasRange())) {
|
||||||
MOZ_LOG(gIMELog, LogLevel::Error,
|
MOZ_LOG(gIMELog, LogLevel::Error,
|
||||||
("0x%p DispatchCompositionStart(), FAILED, "
|
("0x%p DispatchCompositionStart(), FAILED, "
|
||||||
"due to no selection",
|
"due to no selection",
|
||||||
@@ -2154,7 +2155,7 @@ bool IMContextWrapper::DispatchCompositionStart(GtkIMContext* aContext) {
|
|||||||
// even though we strongly hope it doesn't happen.
|
// even though we strongly hope it doesn't happen.
|
||||||
// Every composition event should have the start offset for the result
|
// Every composition event should have the start offset for the result
|
||||||
// because it may high cost if we query the offset every time.
|
// because it may high cost if we query the offset every time.
|
||||||
mCompositionStart = mSelection->OffsetAndDataRef().StartOffset();
|
mCompositionStart = mContentSelection->OffsetAndDataRef().StartOffset();
|
||||||
mDispatchedCompositionString.Truncate();
|
mDispatchedCompositionString.Truncate();
|
||||||
|
|
||||||
// If this composition is started by a key press, we need to dispatch
|
// 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
|
// Store the selected string which will be removed by following
|
||||||
// compositionchange event.
|
// compositionchange event.
|
||||||
if (mCompositionState == eCompositionState_CompositionStartDispatched) {
|
if (mCompositionState == eCompositionState_CompositionStartDispatched) {
|
||||||
if (NS_WARN_IF(
|
if (NS_WARN_IF(!EnsureToCacheContentSelection(
|
||||||
!EnsureToCacheSelection(&mSelectedStringRemovedByComposition))) {
|
&mSelectedStringRemovedByComposition))) {
|
||||||
// XXX How should we behave in this case??
|
// 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
|
// XXX We should assume, for now, any web applications don't change
|
||||||
// selection at handling this compositionchange event.
|
// selection at handling this compositionchange event.
|
||||||
mCompositionStart = mSelection->OffsetAndDataRef().StartOffset();
|
mCompositionStart = mContentSelection->OffsetAndDataRef().StartOffset();
|
||||||
} else {
|
} else {
|
||||||
// If there is no selection range, we should keep previously storing
|
// If there is no selection range, we should keep previously storing
|
||||||
// mCompositionStart.
|
// mCompositionStart.
|
||||||
@@ -2356,7 +2357,7 @@ bool IMContextWrapper::DispatchCompositionCommitEvent(
|
|||||||
this));
|
this));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (MOZ_UNLIKELY(!EnsureToCacheSelection())) {
|
if (MOZ_UNLIKELY(!EnsureToCacheContentSelection())) {
|
||||||
MOZ_LOG(gIMELog, LogLevel::Warning,
|
MOZ_LOG(gIMELog, LogLevel::Warning,
|
||||||
("0x%p DispatchCompositionCommitEvent(), Warning, "
|
("0x%p DispatchCompositionCommitEvent(), Warning, "
|
||||||
"Failed to cache selection before dispatching "
|
"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
|
// 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
|
// shouldn't reset IME because the trigger of causing this commit may be an
|
||||||
// input for next composition and we shouldn't cancel it.
|
// input for next composition and we shouldn't cancel it.
|
||||||
if (mSelection.isSome()) {
|
if (mContentSelection.isSome()) {
|
||||||
mSelection->Collapse((mSelection->HasRange()
|
mContentSelection->Collapse(
|
||||||
? mSelection->OffsetAndDataRef().StartOffset()
|
(mContentSelection->HasRange()
|
||||||
: mCompositionStart) +
|
? mContentSelection->OffsetAndDataRef().StartOffset()
|
||||||
aCommitString->Length());
|
: mCompositionStart) +
|
||||||
|
aCommitString->Length());
|
||||||
MOZ_LOG(gIMELog, LogLevel::Info,
|
MOZ_LOG(gIMELog, LogLevel::Info,
|
||||||
("0x%p DispatchCompositionCommitEvent(), mSelection=%s", this,
|
("0x%p DispatchCompositionCommitEvent(), mContentSelection=%s",
|
||||||
ToString(mSelection).c_str()));
|
this, ToString(mContentSelection).c_str()));
|
||||||
}
|
}
|
||||||
MOZ_ASSERT(!dispatcher);
|
MOZ_ASSERT(!dispatcher);
|
||||||
} else {
|
} else {
|
||||||
@@ -2430,12 +2432,12 @@ bool IMContextWrapper::DispatchCompositionCommitEvent(
|
|||||||
mCompositionStart + (aCommitString
|
mCompositionStart + (aCommitString
|
||||||
? aCommitString->Length()
|
? aCommitString->Length()
|
||||||
: mDispatchedCompositionString.Length());
|
: mDispatchedCompositionString.Length());
|
||||||
if (mSelection.isSome()) {
|
if (mContentSelection.isSome()) {
|
||||||
mSelection->Collapse(offsetToPutCaret);
|
mContentSelection->Collapse(offsetToPutCaret);
|
||||||
} else {
|
} else {
|
||||||
// TODO: We should guarantee that there should be at least fake selection
|
// TODO: We should guarantee that there should be at least fake selection
|
||||||
// for IME at here. Then, we can keep the last writing mode.
|
// 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) {
|
void IMContextWrapper::SetCursorPosition(GtkIMContext* aContext) {
|
||||||
MOZ_LOG(gIMELog, LogLevel::Info,
|
MOZ_LOG(
|
||||||
("0x%p SetCursorPosition(aContext=0x%p), "
|
gIMELog, LogLevel::Info,
|
||||||
"mCompositionTargetRange={ mOffset=%u, mLength=%u }, mSelection=%s",
|
("0x%p SetCursorPosition(aContext=0x%p), "
|
||||||
this, aContext, mCompositionTargetRange.mOffset,
|
"mCompositionTargetRange={ mOffset=%u, mLength=%u }, "
|
||||||
mCompositionTargetRange.mLength, ToString(mSelection).c_str()));
|
"mContentSelection=%s",
|
||||||
|
this, aContext, mCompositionTargetRange.mOffset,
|
||||||
|
mCompositionTargetRange.mLength, ToString(mContentSelection).c_str()));
|
||||||
|
|
||||||
bool useCaret = false;
|
bool useCaret = false;
|
||||||
if (!mCompositionTargetRange.IsValid()) {
|
if (!mCompositionTargetRange.IsValid()) {
|
||||||
if (mSelection.isNothing()) {
|
if (mContentSelection.isNothing()) {
|
||||||
MOZ_LOG(gIMELog, LogLevel::Error,
|
MOZ_LOG(gIMELog, LogLevel::Error,
|
||||||
("0x%p SetCursorPosition(), FAILED, "
|
("0x%p SetCursorPosition(), FAILED, "
|
||||||
"mCompositionTargetRange and mSelection are invalid",
|
"mCompositionTargetRange and mContentSelection are invalid",
|
||||||
this));
|
this));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!mSelection->HasRange()) {
|
if (!mContentSelection->HasRange()) {
|
||||||
MOZ_LOG(gIMELog, LogLevel::Warning,
|
MOZ_LOG(gIMELog, LogLevel::Warning,
|
||||||
("0x%p SetCursorPosition(), FAILED, "
|
("0x%p SetCursorPosition(), FAILED, "
|
||||||
"mCompositionTargetRange is invalid and there is no selection",
|
"mCompositionTargetRange is invalid and there is no selection",
|
||||||
@@ -2870,9 +2874,9 @@ void IMContextWrapper::SetCursorPosition(GtkIMContext* aContext) {
|
|||||||
true, useCaret ? eQueryCaretRect : eQueryTextRect, mLastFocusedWindow);
|
true, useCaret ? eQueryCaretRect : eQueryTextRect, mLastFocusedWindow);
|
||||||
if (useCaret) {
|
if (useCaret) {
|
||||||
queryCaretOrTextRectEvent.InitForQueryCaretRect(
|
queryCaretOrTextRectEvent.InitForQueryCaretRect(
|
||||||
mSelection->OffsetAndDataRef().StartOffset());
|
mContentSelection->OffsetAndDataRef().StartOffset());
|
||||||
} else {
|
} else {
|
||||||
if (mSelection->WritingModeRef().IsVertical()) {
|
if (mContentSelection->WritingModeRef().IsVertical()) {
|
||||||
// For preventing the candidate window to overlap the target
|
// For preventing the candidate window to overlap the target
|
||||||
// clause, we should set fake (typically, very tall) caret rect.
|
// clause, we should set fake (typically, very tall) caret rect.
|
||||||
uint32_t length =
|
uint32_t length =
|
||||||
@@ -2935,7 +2939,7 @@ nsresult IMContextWrapper::GetCurrentParagraph(nsAString& aText,
|
|||||||
// current selection.
|
// current selection.
|
||||||
if (!EditorHasCompositionString()) {
|
if (!EditorHasCompositionString()) {
|
||||||
// Query cursor position & selection
|
// Query cursor position & selection
|
||||||
if (NS_WARN_IF(!EnsureToCacheSelection())) {
|
if (NS_WARN_IF(!EnsureToCacheContentSelection())) {
|
||||||
MOZ_LOG(gIMELog, LogLevel::Error,
|
MOZ_LOG(gIMELog, LogLevel::Error,
|
||||||
("0x%p GetCurrentParagraph(), FAILED, due to no "
|
("0x%p GetCurrentParagraph(), FAILED, due to no "
|
||||||
"valid selection information",
|
"valid selection information",
|
||||||
@@ -2943,9 +2947,9 @@ nsresult IMContextWrapper::GetCurrentParagraph(nsAString& aText,
|
|||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mSelection.isSome() && mSelection->HasRange()) {
|
if (mContentSelection.isSome() && mContentSelection->HasRange()) {
|
||||||
selOffset = mSelection->OffsetAndDataRef().StartOffset();
|
selOffset = mContentSelection->OffsetAndDataRef().StartOffset();
|
||||||
selLength = mSelection->OffsetAndDataRef().Length();
|
selLength = mContentSelection->OffsetAndDataRef().Length();
|
||||||
} else {
|
} else {
|
||||||
// If there is no range, let's get all text instead...
|
// If there is no range, let's get all text instead...
|
||||||
selOffset = 0u;
|
selOffset = 0u;
|
||||||
@@ -3055,20 +3059,20 @@ nsresult IMContextWrapper::DeleteText(GtkIMContext* aContext, int32_t aOffset,
|
|||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (NS_WARN_IF(!EnsureToCacheSelection())) {
|
if (NS_WARN_IF(!EnsureToCacheContentSelection())) {
|
||||||
MOZ_LOG(gIMELog, LogLevel::Error,
|
MOZ_LOG(gIMELog, LogLevel::Error,
|
||||||
("0x%p DeleteText(), FAILED, due to no valid selection "
|
("0x%p DeleteText(), FAILED, due to no valid selection "
|
||||||
"information",
|
"information",
|
||||||
this));
|
this));
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
if (!mSelection->HasRange()) {
|
if (!mContentSelection->HasRange()) {
|
||||||
MOZ_LOG(gIMELog, LogLevel::Debug,
|
MOZ_LOG(gIMELog, LogLevel::Debug,
|
||||||
("0x%p DeleteText(), does nothing, due to no selection range",
|
("0x%p DeleteText(), does nothing, due to no selection range",
|
||||||
this));
|
this));
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
selOffset = mSelection->OffsetAndDataRef().StartOffset();
|
selOffset = mContentSelection->OffsetAndDataRef().StartOffset();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get all text contents of the focused editor
|
// Get all text contents of the focused editor
|
||||||
@@ -3197,21 +3201,22 @@ void IMContextWrapper::InitEvent(WidgetGUIEvent& aEvent) {
|
|||||||
aEvent.mTime = PR_Now() / 1000;
|
aEvent.mTime = PR_Now() / 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IMContextWrapper::EnsureToCacheSelection(nsAString* aSelectedString) {
|
bool IMContextWrapper::EnsureToCacheContentSelection(
|
||||||
|
nsAString* aSelectedString) {
|
||||||
if (aSelectedString) {
|
if (aSelectedString) {
|
||||||
aSelectedString->Truncate();
|
aSelectedString->Truncate();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mSelection.isSome()) {
|
if (mContentSelection.isSome()) {
|
||||||
if (mSelection->HasRange() && aSelectedString) {
|
if (mContentSelection->HasRange() && aSelectedString) {
|
||||||
aSelectedString->Assign(mSelection->OffsetAndDataRef().DataRef());
|
aSelectedString->Assign(mContentSelection->OffsetAndDataRef().DataRef());
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NS_WARN_IF(!mLastFocusedWindow)) {
|
if (NS_WARN_IF(!mLastFocusedWindow)) {
|
||||||
MOZ_LOG(gIMELog, LogLevel::Error,
|
MOZ_LOG(gIMELog, LogLevel::Error,
|
||||||
("0x%p EnsureToCacheSelection(), FAILED, due to "
|
("0x%p EnsureToCacheContentSelection(), FAILED, due to "
|
||||||
"no focused window",
|
"no focused window",
|
||||||
this));
|
this));
|
||||||
return false;
|
return false;
|
||||||
@@ -3224,22 +3229,24 @@ bool IMContextWrapper::EnsureToCacheSelection(nsAString* aSelectedString) {
|
|||||||
mLastFocusedWindow->DispatchEvent(&querySelectedTextEvent, status);
|
mLastFocusedWindow->DispatchEvent(&querySelectedTextEvent, status);
|
||||||
if (NS_WARN_IF(querySelectedTextEvent.Failed())) {
|
if (NS_WARN_IF(querySelectedTextEvent.Failed())) {
|
||||||
MOZ_LOG(gIMELog, LogLevel::Error,
|
MOZ_LOG(gIMELog, LogLevel::Error,
|
||||||
("0x%p EnsureToCacheSelection(), FAILED, due to "
|
("0x%p EnsureToCacheContentSelection(), FAILED, due to "
|
||||||
"failure of query selection event",
|
"failure of query selection event",
|
||||||
this));
|
this));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
mSelection = Some(Selection(querySelectedTextEvent));
|
mContentSelection = Some(ContentSelection(querySelectedTextEvent));
|
||||||
if (mSelection->HasRange()) {
|
if (mContentSelection->HasRange()) {
|
||||||
if (!mSelection->OffsetAndDataRef().IsDataEmpty() && aSelectedString) {
|
if (!mContentSelection->OffsetAndDataRef().IsDataEmpty() &&
|
||||||
|
aSelectedString) {
|
||||||
aSelectedString->Assign(querySelectedTextEvent.mReply->DataRef());
|
aSelectedString->Assign(querySelectedTextEvent.mReply->DataRef());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MOZ_LOG(gIMELog, LogLevel::Debug,
|
MOZ_LOG(
|
||||||
("0x%p EnsureToCacheSelection(), Succeeded, mSelection=%s", this,
|
gIMELog, LogLevel::Debug,
|
||||||
ToString(mSelection).c_str()));
|
("0x%p EnsureToCacheContentSelection(), Succeeded, mContentSelection=%s",
|
||||||
|
this, ToString(mContentSelection).c_str()));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,9 +16,9 @@
|
|||||||
#include "nsTArray.h"
|
#include "nsTArray.h"
|
||||||
#include "nsIWidget.h"
|
#include "nsIWidget.h"
|
||||||
#include "mozilla/CheckedInt.h"
|
#include "mozilla/CheckedInt.h"
|
||||||
|
#include "mozilla/ContentData.h"
|
||||||
#include "mozilla/EventForwards.h"
|
#include "mozilla/EventForwards.h"
|
||||||
#include "mozilla/Maybe.h"
|
#include "mozilla/Maybe.h"
|
||||||
#include "mozilla/TextEvents.h" // TODO: Stop this in the following patch
|
|
||||||
#include "mozilla/TextEventDispatcherListener.h"
|
#include "mozilla/TextEventDispatcherListener.h"
|
||||||
#include "mozilla/WritingModes.h"
|
#include "mozilla/WritingModes.h"
|
||||||
#include "mozilla/widget/IMEData.h"
|
#include "mozilla/widget/IMEData.h"
|
||||||
@@ -384,69 +384,15 @@ class IMContextWrapper final : public TextEventDispatcherListener {
|
|||||||
// IM which user selected.
|
// IM which user selected.
|
||||||
IMContextID mIMContextID;
|
IMContextID mIMContextID;
|
||||||
|
|
||||||
class Selection final {
|
// If mContentSelection is Nothing, it means that
|
||||||
public:
|
// EnsureToCacheContentSelection failed to get selection or just not caching
|
||||||
Selection() = default;
|
// the selection.
|
||||||
explicit Selection(
|
Maybe<ContentSelection> mContentSelection;
|
||||||
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;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
// 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().
|
// it's set to FALSE when we call gtk_im_context_focus_out().
|
||||||
|
|||||||
@@ -25,12 +25,18 @@ with Files("reftests/*fallback*"):
|
|||||||
with Files("*CompositorWidget*"):
|
with Files("*CompositorWidget*"):
|
||||||
BUG_COMPONENT = ("Core", "Graphics")
|
BUG_COMPONENT = ("Core", "Graphics")
|
||||||
|
|
||||||
with Files("*Gfx*"):
|
with Files("*ContentData*"):
|
||||||
BUG_COMPONENT = ("Core", "Graphics")
|
BUG_COMPONENT = ("Core", "DOM: UI Events & Focus Handling")
|
||||||
|
|
||||||
with Files("*FontRange*"):
|
with Files("*FontRange*"):
|
||||||
BUG_COMPONENT = ("Core", "Widget: Cocoa")
|
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"]
|
toolkit = CONFIG["MOZ_WIDGET_TOOLKIT"]
|
||||||
|
|
||||||
if toolkit in ("android", "cocoa", "gtk", "uikit", "windows"):
|
if toolkit in ("android", "cocoa", "gtk", "uikit", "windows"):
|
||||||
@@ -144,6 +150,7 @@ EXPORTS.mozilla += [
|
|||||||
"ColorScheme.h",
|
"ColorScheme.h",
|
||||||
"CommandList.h",
|
"CommandList.h",
|
||||||
"ContentCache.h",
|
"ContentCache.h",
|
||||||
|
"ContentData.h",
|
||||||
"ContentEvents.h",
|
"ContentEvents.h",
|
||||||
"EventClassList.h",
|
"EventClassList.h",
|
||||||
"EventForwards.h",
|
"EventForwards.h",
|
||||||
@@ -183,6 +190,7 @@ EXPORTS.mozilla.widget += [
|
|||||||
UNIFIED_SOURCES += [
|
UNIFIED_SOURCES += [
|
||||||
"CompositorWidget.cpp",
|
"CompositorWidget.cpp",
|
||||||
"ContentCache.cpp",
|
"ContentCache.cpp",
|
||||||
|
"ContentData.cpp",
|
||||||
"GfxDriverInfo.cpp",
|
"GfxDriverInfo.cpp",
|
||||||
"GfxInfoBase.cpp",
|
"GfxInfoBase.cpp",
|
||||||
"GfxInfoCollector.cpp",
|
"GfxInfoCollector.cpp",
|
||||||
|
|||||||
@@ -468,7 +468,7 @@ void IMMHandler::OnFocusChange(bool aFocus, nsWindow* aWindow) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (gIMMHandler) {
|
if (gIMMHandler) {
|
||||||
gIMMHandler->mSelection.reset();
|
gIMMHandler->mContentSelection.reset();
|
||||||
}
|
}
|
||||||
sHasFocus = aFocus;
|
sHasFocus = aFocus;
|
||||||
}
|
}
|
||||||
@@ -495,10 +495,8 @@ void IMMHandler::OnSelectionChange(nsWindow* aWindow,
|
|||||||
// MaybeAdjustCompositionFont() may create gIMMHandler. So, check it
|
// MaybeAdjustCompositionFont() may create gIMMHandler. So, check it
|
||||||
// after a call of MaybeAdjustCompositionFont().
|
// after a call of MaybeAdjustCompositionFont().
|
||||||
if (gIMMHandler) {
|
if (gIMMHandler) {
|
||||||
if (gIMMHandler->mSelection.isNothing()) {
|
gIMMHandler->mContentSelection =
|
||||||
gIMMHandler->mSelection.emplace();
|
Some(ContentSelection(aIMENotification.mSelectionChangeData));
|
||||||
}
|
|
||||||
gIMMHandler->mSelection->Update(aIMENotification.mSelectionChangeData);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -936,23 +934,24 @@ void IMMHandler::HandleStartComposition(nsWindow* aWindow,
|
|||||||
MOZ_ASSERT(!mIsComposing,
|
MOZ_ASSERT(!mIsComposing,
|
||||||
"HandleStartComposition is called but mIsComposing is TRUE");
|
"HandleStartComposition is called but mIsComposing is TRUE");
|
||||||
|
|
||||||
const Maybe<Selection>& selection = GetSelectionWithQueryIfNothing(aWindow);
|
const Maybe<ContentSelection>& contentSelection =
|
||||||
if (selection.isNothing()) {
|
GetContentSelectionWithQueryIfNothing(aWindow);
|
||||||
|
if (contentSelection.isNothing()) {
|
||||||
MOZ_LOG(gIMELog, LogLevel::Error,
|
MOZ_LOG(gIMELog, LogLevel::Error,
|
||||||
(" IMMHandler::HandleStartComposition, FAILED, due to "
|
(" IMMHandler::HandleStartComposition, FAILED, due to "
|
||||||
"Selection::GetSelectionWithQueryIfNothing() failure"));
|
"Selection::GetContentSelectionWithQueryIfNothing() failure"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!selection->HasRange()) {
|
if (!contentSelection->HasRange()) {
|
||||||
MOZ_LOG(gIMELog, LogLevel::Error,
|
MOZ_LOG(gIMELog, LogLevel::Error,
|
||||||
(" IMMHandler::HandleStartComposition, FAILED, due to "
|
(" IMMHandler::HandleStartComposition, FAILED, due to "
|
||||||
"there is no selection"));
|
"there is no selection"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
AdjustCompositionFont(aWindow, aContext, selection->WritingModeRef());
|
AdjustCompositionFont(aWindow, aContext, contentSelection->WritingModeRef());
|
||||||
|
|
||||||
mCompositionStart = selection->OffsetAndDataRef().StartOffset();
|
mCompositionStart = contentSelection->OffsetAndDataRef().StartOffset();
|
||||||
mCursorPosition = NO_IME_CARET;
|
mCursorPosition = NO_IME_CARET;
|
||||||
|
|
||||||
RefPtr<TextEventDispatcher> dispatcher = GetTextEventDispatcherFor(aWindow);
|
RefPtr<TextEventDispatcher> dispatcher = GetTextEventDispatcherFor(aWindow);
|
||||||
@@ -1259,16 +1258,18 @@ bool IMMHandler::HandleReconvert(nsWindow* aWindow, LPARAM lParam,
|
|||||||
*oResult = 0;
|
*oResult = 0;
|
||||||
RECONVERTSTRING* pReconv = reinterpret_cast<RECONVERTSTRING*>(lParam);
|
RECONVERTSTRING* pReconv = reinterpret_cast<RECONVERTSTRING*>(lParam);
|
||||||
|
|
||||||
const Maybe<Selection>& selection = GetSelectionWithQueryIfNothing(aWindow);
|
const Maybe<ContentSelection>& contentSelection =
|
||||||
if (selection.isNothing()) {
|
GetContentSelectionWithQueryIfNothing(aWindow);
|
||||||
|
if (contentSelection.isNothing()) {
|
||||||
MOZ_LOG(gIMELog, LogLevel::Error,
|
MOZ_LOG(gIMELog, LogLevel::Error,
|
||||||
("IMMHandler::HandleReconvert, FAILED, due to "
|
("IMMHandler::HandleReconvert, FAILED, due to "
|
||||||
"Selection::GetSelectionWithQueryIfNothing() failure"));
|
"Selection::GetContentSelectionWithQueryIfNothing() failure"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const uint32_t len =
|
const uint32_t len = contentSelection->HasRange()
|
||||||
selection->HasRange() ? selection->OffsetAndDataRef().Length() : 0u;
|
? contentSelection->OffsetAndDataRef().Length()
|
||||||
|
: 0u;
|
||||||
uint32_t needSize = sizeof(RECONVERTSTRING) + len * sizeof(WCHAR);
|
uint32_t needSize = sizeof(RECONVERTSTRING) + len * sizeof(WCHAR);
|
||||||
|
|
||||||
if (!pReconv) {
|
if (!pReconv) {
|
||||||
@@ -1305,7 +1306,7 @@ bool IMMHandler::HandleReconvert(nsWindow* aWindow, LPARAM lParam,
|
|||||||
|
|
||||||
if (len) {
|
if (len) {
|
||||||
::CopyMemory(reinterpret_cast<LPVOID>(lParam + sizeof(RECONVERTSTRING)),
|
::CopyMemory(reinterpret_cast<LPVOID>(lParam + sizeof(RECONVERTSTRING)),
|
||||||
selection->OffsetAndDataRef().DataRef().get(),
|
contentSelection->OffsetAndDataRef().DataRef().get(),
|
||||||
len * sizeof(WCHAR));
|
len * sizeof(WCHAR));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1414,18 +1415,19 @@ bool IMMHandler::HandleDocumentFeed(nsWindow* aWindow, LPARAM lParam,
|
|||||||
|
|
||||||
int32_t targetOffset, targetLength;
|
int32_t targetOffset, targetLength;
|
||||||
if (!hasCompositionString) {
|
if (!hasCompositionString) {
|
||||||
const Maybe<Selection>& selection = GetSelectionWithQueryIfNothing(aWindow);
|
const Maybe<ContentSelection>& contentSelection =
|
||||||
if (selection.isNothing()) {
|
GetContentSelectionWithQueryIfNothing(aWindow);
|
||||||
|
if (contentSelection.isNothing()) {
|
||||||
MOZ_LOG(gIMELog, LogLevel::Error,
|
MOZ_LOG(gIMELog, LogLevel::Error,
|
||||||
("IMMHandler::HandleDocumentFeed, FAILED, due to "
|
("IMMHandler::HandleDocumentFeed, FAILED, due to "
|
||||||
"Selection::GetSelectionWithQueryIfNothing() failure"));
|
"Selection::GetContentSelectionWithQueryIfNothing() failure"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (selection->HasRange()) {
|
if (contentSelection->HasRange()) {
|
||||||
targetOffset =
|
targetOffset = static_cast<int32_t>(
|
||||||
static_cast<int32_t>(selection->OffsetAndDataRef().StartOffset());
|
contentSelection->OffsetAndDataRef().StartOffset());
|
||||||
targetLength =
|
targetLength =
|
||||||
static_cast<int32_t>(selection->OffsetAndDataRef().Length());
|
static_cast<int32_t>(contentSelection->OffsetAndDataRef().Length());
|
||||||
} else {
|
} else {
|
||||||
// If there is no selection range, let's return all text in the editor.
|
// If there is no selection range, let's return all text in the editor.
|
||||||
targetOffset = 0;
|
targetOffset = 0;
|
||||||
@@ -1821,17 +1823,18 @@ bool IMMHandler::GetCharacterRectOfSelectedTextAt(
|
|||||||
WritingMode* aWritingMode) {
|
WritingMode* aWritingMode) {
|
||||||
LayoutDeviceIntPoint point(0, 0);
|
LayoutDeviceIntPoint point(0, 0);
|
||||||
|
|
||||||
const Maybe<Selection>& selection = GetSelectionWithQueryIfNothing(aWindow);
|
const Maybe<ContentSelection>& contentSelection =
|
||||||
if (selection.isNothing()) {
|
GetContentSelectionWithQueryIfNothing(aWindow);
|
||||||
|
if (contentSelection.isNothing()) {
|
||||||
MOZ_LOG(gIMELog, LogLevel::Error,
|
MOZ_LOG(gIMELog, LogLevel::Error,
|
||||||
("IMMHandler::GetCharacterRectOfSelectedTextAt, FAILED, due to "
|
("IMMHandler::GetCharacterRectOfSelectedTextAt, FAILED, due to "
|
||||||
"Selection::GetSelectionWithQueryIfNothing() failure"));
|
"Selection::GetContentSelectionWithQueryIfNothing() failure"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there is neither a selection range nor composition string, cannot return
|
// If there is neither a selection range nor composition string, cannot return
|
||||||
// character rect, of course.
|
// character rect, of course.
|
||||||
if (!selection->HasRange() && !mIsComposing) {
|
if (!contentSelection->HasRange() && !mIsComposing) {
|
||||||
MOZ_LOG(gIMELog, LogLevel::Warning,
|
MOZ_LOG(gIMELog, LogLevel::Warning,
|
||||||
("IMMHandler::GetCharacterRectOfSelectedTextAt, FAILED, due to "
|
("IMMHandler::GetCharacterRectOfSelectedTextAt, FAILED, due to "
|
||||||
"there is neither a selection range nor composition string"));
|
"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
|
// 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
|
// or the active IME. If it's an IME's bug, we need to set targetLength to
|
||||||
// aOffset.
|
// aOffset.
|
||||||
const uint32_t targetLength = mIsComposing
|
const uint32_t targetLength =
|
||||||
? mCompositionString.Length()
|
mIsComposing ? mCompositionString.Length()
|
||||||
: selection->OffsetAndDataRef().Length();
|
: contentSelection->OffsetAndDataRef().Length();
|
||||||
if (NS_WARN_IF(aOffset > targetLength)) {
|
if (NS_WARN_IF(aOffset > targetLength)) {
|
||||||
MOZ_LOG(
|
MOZ_LOG(
|
||||||
gIMELog, LogLevel::Error,
|
gIMELog, LogLevel::Error,
|
||||||
@@ -1857,7 +1860,8 @@ bool IMMHandler::GetCharacterRectOfSelectedTextAt(
|
|||||||
// If there is caret, we might be able to use caret rect.
|
// If there is caret, we might be able to use caret rect.
|
||||||
uint32_t caretOffset = UINT32_MAX;
|
uint32_t caretOffset = UINT32_MAX;
|
||||||
// There is a caret only when the normal selection is collapsed.
|
// 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 (mIsComposing) {
|
||||||
// If it's composing, mCursorPosition is the offset to caret in
|
// If it's composing, mCursorPosition is the offset to caret in
|
||||||
// the composition string.
|
// the composition string.
|
||||||
@@ -2353,39 +2357,7 @@ bool IMMHandler::OnKeyDownEvent(nsWindow* aWindow, WPARAM wParam, LPARAM lParam,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************************************************************************
|
Maybe<ContentSelection> IMMHandler::QueryContentSelection(nsWindow* aWindow) {
|
||||||
* 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) {
|
|
||||||
WidgetQueryContentEvent querySelectedTextEvent(true, eQuerySelectedText,
|
WidgetQueryContentEvent querySelectedTextEvent(true, eQuerySelectedText,
|
||||||
aWindow);
|
aWindow);
|
||||||
LayoutDeviceIntPoint point(0, 0);
|
LayoutDeviceIntPoint point(0, 0);
|
||||||
@@ -2406,21 +2378,19 @@ Maybe<IMMHandler::Selection> IMMHandler::Selection::QuerySelection(
|
|||||||
return Nothing();
|
return Nothing();
|
||||||
}
|
}
|
||||||
|
|
||||||
Selection selection;
|
ContentSelection contentSelection(querySelectedTextEvent);
|
||||||
selection.mOffsetAndData = querySelectedTextEvent.mReply->mOffsetAndData;
|
|
||||||
selection.mWritingMode = querySelectedTextEvent.mReply->WritingModeRef();
|
|
||||||
|
|
||||||
MOZ_LOG(gIMELog, LogLevel::Info,
|
MOZ_LOG(gIMELog, LogLevel::Info,
|
||||||
("IMMHandler::Selection::Init, querySelectedTextEvent={ mReply=%s }",
|
("IMMHandler::Selection::Init, querySelectedTextEvent={ mReply=%s }",
|
||||||
ToString(querySelectedTextEvent.mReply).c_str()));
|
ToString(querySelectedTextEvent.mReply).c_str()));
|
||||||
|
|
||||||
if (selection.mOffsetAndData.isSome() &&
|
if (contentSelection.HasRange() &&
|
||||||
!selection.mOffsetAndData->IsValid()) {
|
!contentSelection.OffsetAndDataRef().IsValid()) {
|
||||||
MOZ_LOG(gIMELog, LogLevel::Error,
|
MOZ_LOG(gIMELog, LogLevel::Error,
|
||||||
(" IMMHandler::Selection::Init, FAILED, due to invalid range"));
|
(" IMMHandler::Selection::Init, FAILED, due to invalid range"));
|
||||||
return Nothing();
|
return Nothing();
|
||||||
}
|
}
|
||||||
return Some(selection);
|
return Some(contentSelection);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace widget
|
} // namespace widget
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#ifndef IMMHandler_h_
|
#ifndef IMMHandler_h_
|
||||||
#define IMMHandler_h_
|
#define IMMHandler_h_
|
||||||
|
|
||||||
|
#include "mozilla/ContentData.h"
|
||||||
#include "mozilla/EventForwards.h"
|
#include "mozilla/EventForwards.h"
|
||||||
#include "mozilla/TextEventDispatcher.h"
|
#include "mozilla/TextEventDispatcher.h"
|
||||||
#include "mozilla/WritingModes.h"
|
#include "mozilla/WritingModes.h"
|
||||||
@@ -377,57 +378,36 @@ class IMMHandler final {
|
|||||||
int32_t mCursorPosition;
|
int32_t mCursorPosition;
|
||||||
uint32_t mCompositionStart;
|
uint32_t mCompositionStart;
|
||||||
|
|
||||||
class Selection {
|
// mContentSelection stores the latest selection data only when sHasFocus is
|
||||||
public:
|
// true. Don't access mContentSelection directly. You should use
|
||||||
Selection() = default;
|
// GetContentSelectionWithQueryIfNothing() for getting proper state.
|
||||||
|
Maybe<ContentSelection> mContentSelection;
|
||||||
|
|
||||||
void ClearRange() {
|
const Maybe<ContentSelection>& GetContentSelectionWithQueryIfNothing(
|
||||||
mOffsetAndData.reset();
|
nsWindow* aWindow) {
|
||||||
// Don't reset WritingMode because users must not want to update UI of IME
|
// When IME has focus, mContentSelection is automatically updated by
|
||||||
// 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
|
|
||||||
// NOTIFY_IME_OF_SELECTION_CHANGE.
|
// NOTIFY_IME_OF_SELECTION_CHANGE.
|
||||||
if (sHasFocus) {
|
if (sHasFocus) {
|
||||||
if (mSelection.isNothing()) {
|
if (mContentSelection.isNothing()) {
|
||||||
// But if this is the first access of mSelection, we need to query
|
// But if this is the first access of mContentSelection, we need to
|
||||||
// selection now.
|
// query selection now.
|
||||||
mSelection = Selection::QuerySelection(aWindow);
|
mContentSelection = QueryContentSelection(aWindow);
|
||||||
}
|
}
|
||||||
return mSelection;
|
return mContentSelection;
|
||||||
}
|
}
|
||||||
// Otherwise, i.e., While IME doesn't have focus, we cannot observe
|
// Otherwise, i.e., While IME doesn't have focus, we cannot observe
|
||||||
// selection changes. So, in such case, we need to query selection
|
// selection changes. So, in such case, we need to query selection
|
||||||
// when it's necessary.
|
// when it's necessary.
|
||||||
static Maybe<Selection> sTempSelection;
|
static Maybe<ContentSelection> sTempContentSelection;
|
||||||
sTempSelection = Selection::QuerySelection(aWindow);
|
sTempContentSelection = QueryContentSelection(aWindow);
|
||||||
return sTempSelection;
|
return sTempContentSelection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query content selection on aWindow with WidgetQueryContent event.
|
||||||
|
*/
|
||||||
|
static Maybe<ContentSelection> QueryContentSelection(nsWindow* aWindow);
|
||||||
|
|
||||||
bool mIsComposing;
|
bool mIsComposing;
|
||||||
|
|
||||||
static mozilla::WritingMode sWritingModeOfCompositionFont;
|
static mozilla::WritingMode sWritingModeOfCompositionFont;
|
||||||
|
|||||||
Reference in New Issue
Block a user