Bug 1130937 part.1 nsGtkIMModule should cache selection r=m_kato

This commit is contained in:
Masayuki Nakano 2015-06-11 19:50:15 +09:00
Родитель 348288f979
Коммит 91ed158c1d
3 изменённых файлов: 171 добавлений и 68 удалений

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

@ -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;