зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1130937 part.1 nsGtkIMModule should cache selection r=m_kato
This commit is contained in:
Родитель
348288f979
Коммит
91ed158c1d
|
@ -75,8 +75,30 @@ GetEventType(GdkEventKey* aKeyEvent)
|
|||
}
|
||||
}
|
||||
|
||||
class GetWritingModeName : public nsAutoCString
|
||||
{
|
||||
public:
|
||||
explicit GetWritingModeName(const WritingMode& aWritingMode)
|
||||
{
|
||||
if (!aWritingMode.IsVertical()) {
|
||||
AssignLiteral("Horizontal");
|
||||
return;
|
||||
}
|
||||
if (aWritingMode.IsVerticalLR()) {
|
||||
AssignLiteral("Vertical (LTR)");
|
||||
return;
|
||||
}
|
||||
AssignLiteral("Vertical (RTL)");
|
||||
}
|
||||
virtual ~GetWritingModeName() {}
|
||||
};
|
||||
|
||||
const static bool kUseSimpleContextDefault = MOZ_WIDGET_GTK == 2;
|
||||
|
||||
/******************************************************************************
|
||||
* nsGtkIMModule
|
||||
******************************************************************************/
|
||||
|
||||
nsGtkIMModule* nsGtkIMModule::sLastFocusedModule = nullptr;
|
||||
bool nsGtkIMModule::sUseSimpleContext;
|
||||
|
||||
|
@ -418,6 +440,7 @@ nsGtkIMModule::OnFocusChangeInGecko(bool aFocus)
|
|||
|
||||
// We shouldn't carry over the removed string to another editor.
|
||||
mSelectedString.Truncate();
|
||||
mSelection.Clear();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -714,8 +737,11 @@ nsGtkIMModule::Blur()
|
|||
}
|
||||
|
||||
void
|
||||
nsGtkIMModule::OnSelectionChange(nsWindow* aCaller)
|
||||
nsGtkIMModule::OnSelectionChange(nsWindow* aCaller,
|
||||
const IMENotification& aIMENotification)
|
||||
{
|
||||
mSelection.Assign(aIMENotification);
|
||||
|
||||
if (MOZ_UNLIKELY(IsDestroyed())) {
|
||||
return;
|
||||
}
|
||||
|
@ -739,42 +765,13 @@ nsGtkIMModule::OnSelectionChange(nsWindow* aCaller)
|
|||
// event handler. So, we're dispatching NS_COMPOSITION_START,
|
||||
// we should ignore selection change notification.
|
||||
if (mCompositionState == eCompositionState_CompositionStartDispatched) {
|
||||
nsCOMPtr<nsIWidget> focusedWindow(mLastFocusedWindow);
|
||||
nsEventStatus status;
|
||||
WidgetQueryContentEvent selection(true, NS_QUERY_SELECTED_TEXT,
|
||||
focusedWindow);
|
||||
InitEvent(selection);
|
||||
mLastFocusedWindow->DispatchEvent(&selection, status);
|
||||
|
||||
bool cannotContinueComposition = false;
|
||||
if (MOZ_UNLIKELY(IsDestroyed())) {
|
||||
MOZ_LOG(gGtkIMLog, LogLevel::Info,
|
||||
(" ERROR: nsGtkIMModule instance is destroyed during "
|
||||
"querying selection offset"));
|
||||
return;
|
||||
} else if (NS_WARN_IF(!selection.mSucceeded)) {
|
||||
cannotContinueComposition = true;
|
||||
MOZ_LOG(gGtkIMLog, LogLevel::Info,
|
||||
(" ERROR: failed to retrieve new caret offset"));
|
||||
} else if (selection.mReply.mOffset == UINT32_MAX) {
|
||||
cannotContinueComposition = true;
|
||||
if (NS_WARN_IF(!mSelection.IsValid())) {
|
||||
MOZ_LOG(gGtkIMLog, LogLevel::Info,
|
||||
(" ERROR: new offset is too large, cannot keep composing"));
|
||||
} else if (!mLastFocusedWindow || focusedWindow != mLastFocusedWindow) {
|
||||
cannotContinueComposition = true;
|
||||
MOZ_LOG(gGtkIMLog, LogLevel::Info,
|
||||
(" ERROR: focus is changed during querying selection "
|
||||
"offset"));
|
||||
} else if (focusedWindow->Destroyed()) {
|
||||
cannotContinueComposition = true;
|
||||
MOZ_LOG(gGtkIMLog, LogLevel::Info,
|
||||
(" ERROR: focused window started to be being destroyed "
|
||||
"during querying selection offset"));
|
||||
}
|
||||
|
||||
if (!cannotContinueComposition) {
|
||||
} else {
|
||||
// Modify the selection start offset with new offset.
|
||||
mCompositionStart = selection.mReply.mOffset;
|
||||
mCompositionStart = mSelection.mOffset;
|
||||
// XXX We should modify mSelectedString? But how?
|
||||
MOZ_LOG(gGtkIMLog, LogLevel::Info,
|
||||
(" NOTE: mCompositionStart is updated to %u, "
|
||||
"the selection change doesn't cause resetting IM context",
|
||||
|
@ -1082,13 +1079,7 @@ nsGtkIMModule::DispatchCompositionStart(GtkIMContext* aContext)
|
|||
return false;
|
||||
}
|
||||
|
||||
nsEventStatus status;
|
||||
WidgetQueryContentEvent selection(true, NS_QUERY_SELECTED_TEXT,
|
||||
mLastFocusedWindow);
|
||||
InitEvent(selection);
|
||||
mLastFocusedWindow->DispatchEvent(&selection, status);
|
||||
|
||||
if (!selection.mSucceeded || selection.mReply.mOffset == UINT32_MAX) {
|
||||
if (NS_WARN_IF(!EnsureToCacheSelection())) {
|
||||
MOZ_LOG(gGtkIMLog, LogLevel::Info,
|
||||
(" FAILED, cannot query the selection offset"));
|
||||
return false;
|
||||
|
@ -1098,7 +1089,7 @@ nsGtkIMModule::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 = selection.mReply.mOffset;
|
||||
mCompositionStart = mSelection.mOffset;
|
||||
mDispatchedCompositionString.Truncate();
|
||||
|
||||
if (mProcessingKeyEvent && !mKeyDownEventWasSent &&
|
||||
|
@ -1126,6 +1117,7 @@ nsGtkIMModule::DispatchCompositionStart(GtkIMContext* aContext)
|
|||
mLastFocusedWindow);
|
||||
InitEvent(compEvent);
|
||||
nsCOMPtr<nsIWidget> kungFuDeathGrip = mLastFocusedWindow;
|
||||
nsEventStatus status;
|
||||
mLastFocusedWindow->DispatchEvent(&compEvent, status);
|
||||
if (static_cast<nsWindow*>(kungFuDeathGrip.get())->IsDestroyed() ||
|
||||
kungFuDeathGrip != mLastFocusedWindow) {
|
||||
|
@ -1167,15 +1159,12 @@ nsGtkIMModule::DispatchCompositionChangeEvent(
|
|||
// Store the selected string which will be removed by following
|
||||
// compositionchange event.
|
||||
if (mCompositionState == eCompositionState_CompositionStartDispatched) {
|
||||
// XXX We should assume, for now, any web applications don't change
|
||||
// selection at handling this compositionchange event.
|
||||
WidgetQueryContentEvent querySelectedTextEvent(true,
|
||||
NS_QUERY_SELECTED_TEXT,
|
||||
mLastFocusedWindow);
|
||||
mLastFocusedWindow->DispatchEvent(&querySelectedTextEvent, status);
|
||||
if (querySelectedTextEvent.mSucceeded) {
|
||||
mSelectedString = querySelectedTextEvent.mReply.mString;
|
||||
mCompositionStart = querySelectedTextEvent.mReply.mOffset;
|
||||
if (NS_WARN_IF(!EnsureToCacheSelection(&mSelectedString))) {
|
||||
// XXX How should we behave in this case??
|
||||
} else {
|
||||
// XXX We should assume, for now, any web applications don't change
|
||||
// selection at handling this compositionchange event.
|
||||
mCompositionStart = mSelection.mOffset;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1483,14 +1472,14 @@ nsGtkIMModule::GetCurrentParagraph(nsAString& aText, uint32_t& aCursorPos)
|
|||
// current selection.
|
||||
if (!EditorHasCompositionString()) {
|
||||
// Query cursor position & selection
|
||||
WidgetQueryContentEvent querySelectedTextEvent(true,
|
||||
NS_QUERY_SELECTED_TEXT,
|
||||
mLastFocusedWindow);
|
||||
mLastFocusedWindow->DispatchEvent(&querySelectedTextEvent, status);
|
||||
NS_ENSURE_TRUE(querySelectedTextEvent.mSucceeded, NS_ERROR_FAILURE);
|
||||
if (NS_WARN_IF(!EnsureToCacheSelection())) {
|
||||
MOZ_LOG(gGtkIMLog, LogLevel::Info,
|
||||
(" FAILED, due to no valid selection information"));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
selOffset = querySelectedTextEvent.mReply.mOffset;
|
||||
selLength = querySelectedTextEvent.mReply.mString.Length();
|
||||
selOffset = mSelection.mOffset;
|
||||
selLength = mSelection.mLength;
|
||||
}
|
||||
|
||||
MOZ_LOG(gGtkIMLog, LogLevel::Info,
|
||||
|
@ -1588,14 +1577,12 @@ nsGtkIMModule::DeleteText(GtkIMContext* aContext,
|
|||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
} else {
|
||||
// Query cursor position & selection
|
||||
WidgetQueryContentEvent querySelectedTextEvent(true,
|
||||
NS_QUERY_SELECTED_TEXT,
|
||||
mLastFocusedWindow);
|
||||
lastFocusedWindow->DispatchEvent(&querySelectedTextEvent, status);
|
||||
NS_ENSURE_TRUE(querySelectedTextEvent.mSucceeded, NS_ERROR_FAILURE);
|
||||
|
||||
selOffset = querySelectedTextEvent.mReply.mOffset;
|
||||
if (NS_WARN_IF(!EnsureToCacheSelection())) {
|
||||
MOZ_LOG(gGtkIMLog, LogLevel::Info,
|
||||
(" FAILED, due to no valid selection information"));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
selOffset = mSelection.mOffset;
|
||||
}
|
||||
|
||||
// Get all text contents of the focused editor
|
||||
|
@ -1718,3 +1705,77 @@ nsGtkIMModule::InitEvent(WidgetGUIEvent& aEvent)
|
|||
{
|
||||
aEvent.time = PR_Now() / 1000;
|
||||
}
|
||||
|
||||
bool
|
||||
nsGtkIMModule::EnsureToCacheSelection(nsAString* aSelectedString)
|
||||
{
|
||||
if (aSelectedString) {
|
||||
aSelectedString->Truncate();
|
||||
}
|
||||
|
||||
if (mSelection.IsValid() &&
|
||||
(!mSelection.Collapsed() || !aSelectedString)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(!mLastFocusedWindow)) {
|
||||
MOZ_LOG(gGtkIMLog, LogLevel::Error,
|
||||
("GtkIMModule(%p): EnsureToCacheSelection(), FAILED, due to "
|
||||
"no focused window", this));
|
||||
return false;
|
||||
}
|
||||
|
||||
nsEventStatus status;
|
||||
WidgetQueryContentEvent selection(true, NS_QUERY_SELECTED_TEXT,
|
||||
mLastFocusedWindow);
|
||||
InitEvent(selection);
|
||||
mLastFocusedWindow->DispatchEvent(&selection, status);
|
||||
if (NS_WARN_IF(!selection.mSucceeded)) {
|
||||
MOZ_LOG(gGtkIMLog, LogLevel::Error,
|
||||
("GtkIMModule(%p): EnsureToCacheSelection(), FAILED, due to "
|
||||
"failure of query selection event", this));
|
||||
return false;
|
||||
}
|
||||
|
||||
mSelection.Assign(selection);
|
||||
if (!mSelection.IsValid()) {
|
||||
MOZ_LOG(gGtkIMLog, LogLevel::Error,
|
||||
("GtkIMModule(%p): EnsureToCacheSelection(), FAILED, due to "
|
||||
"failure of query selection event (invalid result)", this));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mSelection.Collapsed() && aSelectedString) {
|
||||
aSelectedString->Assign(selection.mReply.mString);
|
||||
}
|
||||
|
||||
MOZ_LOG(gGtkIMLog, LogLevel::Debug,
|
||||
("GtkIMModule(%p): EnsureToCacheSelection(), Succeeded, mSelection="
|
||||
"{ mOffset=%u, mLength=%u, mWritingMode=%s }",
|
||||
this, mSelection.mOffset, mSelection.mLength,
|
||||
GetWritingModeName(mSelection.mWritingMode).get()));
|
||||
return true;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* nsGtkIMModule::Selection
|
||||
******************************************************************************/
|
||||
|
||||
void
|
||||
nsGtkIMModule::Selection::Assign(const IMENotification& aIMENotification)
|
||||
{
|
||||
MOZ_ASSERT(aIMENotification.mMessage == NOTIFY_IME_OF_SELECTION_CHANGE);
|
||||
mOffset = aIMENotification.mSelectionChangeData.mOffset;
|
||||
mLength = aIMENotification.mSelectionChangeData.mLength;
|
||||
mWritingMode = aIMENotification.mSelectionChangeData.GetWritingMode();
|
||||
}
|
||||
|
||||
void
|
||||
nsGtkIMModule::Selection::Assign(const WidgetQueryContentEvent& aEvent)
|
||||
{
|
||||
MOZ_ASSERT(aEvent.message == NS_QUERY_SELECTED_TEXT);
|
||||
MOZ_ASSERT(aEvent.mSucceeded);
|
||||
mOffset = aEvent.mReply.mOffset;
|
||||
mLength = aEvent.mReply.mString.Length();
|
||||
mWritingMode = aEvent.GetWritingMode();
|
||||
}
|
||||
|
|
|
@ -16,13 +16,16 @@
|
|||
#include "nsCOMPtr.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsIWidget.h"
|
||||
#include "mozilla/CheckedInt.h"
|
||||
#include "mozilla/EventForwards.h"
|
||||
#include "WritingModes.h"
|
||||
|
||||
class nsWindow;
|
||||
|
||||
class nsGtkIMModule
|
||||
{
|
||||
protected:
|
||||
typedef mozilla::widget::IMENotification IMENotification;
|
||||
typedef mozilla::widget::InputContext InputContext;
|
||||
typedef mozilla::widget::InputContextAction InputContextAction;
|
||||
|
||||
|
@ -48,7 +51,8 @@ public:
|
|||
void OnFocusChangeInGecko(bool aFocus);
|
||||
// OnSelectionChange is a notification that selection (caret) is changed
|
||||
// in the focused editor.
|
||||
void OnSelectionChange(nsWindow* aCaller);
|
||||
void OnSelectionChange(nsWindow* aCaller,
|
||||
const IMENotification& aIMENotification);
|
||||
|
||||
// OnKeyEvent is called when aWindow gets a native key press event or a
|
||||
// native key release event. If this returns TRUE, the key event was
|
||||
|
@ -177,6 +181,44 @@ protected:
|
|||
}
|
||||
}
|
||||
|
||||
struct Selection final
|
||||
{
|
||||
uint32_t mOffset;
|
||||
uint32_t mLength;
|
||||
WritingMode mWritingMode;
|
||||
|
||||
Selection()
|
||||
: mOffset(UINT32_MAX)
|
||||
, mLength(UINT32_MAX)
|
||||
{
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
mOffset = UINT32_MAX;
|
||||
mLength = UINT32_MAX;
|
||||
mWritingMode = WritingMode();
|
||||
}
|
||||
|
||||
void Assign(const IMENotification& aIMENotification);
|
||||
void Assign(const WidgetQueryContentEvent& aSelectedTextEvent);
|
||||
|
||||
bool IsValid() const { return mOffset != UINT32_MAX; }
|
||||
bool Collapsed() const { return !mLength; }
|
||||
uint32_t EndOffset() const
|
||||
{
|
||||
if (NS_WARN_IF(!IsValid())) {
|
||||
return UINT32_MAX;
|
||||
}
|
||||
CheckedInt<uint32_t> endOffset =
|
||||
CheckedInt<uint32_t>(mOffset) + mLength;
|
||||
if (NS_WARN_IF(!endOffset.isValid())) {
|
||||
return UINT32_MAX;
|
||||
}
|
||||
return endOffset.value();
|
||||
}
|
||||
} mSelection;
|
||||
bool EnsureToCacheSelection(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().
|
||||
|
|
|
@ -5955,7 +5955,7 @@ nsWindow::NotifyIMEInternal(const IMENotification& aIMENotification)
|
|||
mIMModule->OnUpdateComposition();
|
||||
return NS_OK;
|
||||
case NOTIFY_IME_OF_SELECTION_CHANGE:
|
||||
mIMModule->OnSelectionChange(this);
|
||||
mIMModule->OnSelectionChange(this, aIMENotification);
|
||||
return NS_OK;
|
||||
default:
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
|
|
Загрузка…
Ссылка в новой задаче