diff --git a/widget/ContentData.cpp b/widget/ContentData.cpp new file mode 100644 index 000000000000..c8019f8f9597 --- /dev/null +++ b/widget/ContentData.cpp @@ -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(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 diff --git a/widget/ContentData.h b/widget/ContentData.h new file mode 100644 index 000000000000..3e641a268455 --- /dev/null +++ b/widget/ContentData.h @@ -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 + +#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( + aOffset, EmptyString(), OffsetAndDataFor::SelectedString))), + mWritingMode(aWritingMode) {} + + const OffsetAndData& 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> mOffsetAndData; + WritingMode mWritingMode; +}; + +} // namespace mozilla + +#endif // #ifndef mozilla_ContentData_h diff --git a/widget/gtk/IMContextWrapper.cpp b/widget/gtk/IMContextWrapper.cpp index 4b1e1affb7a1..7659a384d973 100644 --- a/widget/gtk/IMContextWrapper.cpp +++ b/widget/gtk/IMContextWrapper.cpp @@ -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() - : mCompositionStart) + - aCommitString->Length()); + 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, - ("0x%p SetCursorPosition(aContext=0x%p), " - "mCompositionTargetRange={ mOffset=%u, mLength=%u }, mSelection=%s", - this, aContext, mCompositionTargetRange.mOffset, - mCompositionTargetRange.mLength, ToString(mSelection).c_str())); + MOZ_LOG( + gIMELog, LogLevel::Info, + ("0x%p SetCursorPosition(aContext=0x%p), " + "mCompositionTargetRange={ mOffset=%u, mLength=%u }, " + "mContentSelection=%s", + this, aContext, mCompositionTargetRange.mOffset, + 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; } diff --git a/widget/gtk/IMContextWrapper.h b/widget/gtk/IMContextWrapper.h index 676486de6427..ef6a6e4eede8 100644 --- a/widget/gtk/IMContextWrapper.h +++ b/widget/gtk/IMContextWrapper.h @@ -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(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( - aOffset, EmptyString(), OffsetAndDataFor::SelectedString))), - mWritingMode(aWritingMode) {} - - const OffsetAndData& 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> mOffsetAndData; - WritingMode mWritingMode; - }; - // If mSelection is Nothing, it means that EnsureToCacheSelection failed to - // get selection or just not caching the selection. - Maybe mSelection; + // If mContentSelection is Nothing, it means that + // EnsureToCacheContentSelection failed to get selection or just not caching + // the selection. + Maybe 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(). diff --git a/widget/moz.build b/widget/moz.build index 070cdcfcd69b..c354bc4d7aec 100644 --- a/widget/moz.build +++ b/widget/moz.build @@ -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", diff --git a/widget/windows/IMMHandler.cpp b/widget/windows/IMMHandler.cpp index 3acf5b414ecc..65ca4fc25efb 100644 --- a/widget/windows/IMMHandler.cpp +++ b/widget/windows/IMMHandler.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 = GetSelectionWithQueryIfNothing(aWindow); - if (selection.isNothing()) { + const Maybe& 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 dispatcher = GetTextEventDispatcherFor(aWindow); @@ -1259,16 +1258,18 @@ bool IMMHandler::HandleReconvert(nsWindow* aWindow, LPARAM lParam, *oResult = 0; RECONVERTSTRING* pReconv = reinterpret_cast(lParam); - const Maybe& selection = GetSelectionWithQueryIfNothing(aWindow); - if (selection.isNothing()) { + const Maybe& 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(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 = GetSelectionWithQueryIfNothing(aWindow); - if (selection.isNothing()) { + const Maybe& 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(selection->OffsetAndDataRef().StartOffset()); + if (contentSelection->HasRange()) { + targetOffset = static_cast( + contentSelection->OffsetAndDataRef().StartOffset()); targetLength = - static_cast(selection->OffsetAndDataRef().Length()); + static_cast(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 = GetSelectionWithQueryIfNothing(aWindow); - if (selection.isNothing()) { + const Maybe& 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::QuerySelection( - nsWindow* aWindow) { +Maybe IMMHandler::QueryContentSelection(nsWindow* aWindow) { WidgetQueryContentEvent querySelectedTextEvent(true, eQuerySelectedText, aWindow); LayoutDeviceIntPoint point(0, 0); @@ -2406,21 +2378,19 @@ Maybe 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 diff --git a/widget/windows/IMMHandler.h b/widget/windows/IMMHandler.h index cb9614b80c92..2d9c71812677 100644 --- a/widget/windows/IMMHandler.h +++ b/widget/windows/IMMHandler.h @@ -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 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& 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 QuerySelection(nsWindow* aWindow); - - private: - Maybe> 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 mSelection; - - const Maybe& GetSelectionWithQueryIfNothing(nsWindow* aWindow) { - // When IME has focus, mSelection is automatically updated by + const Maybe& 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 sTempSelection; - sTempSelection = Selection::QuerySelection(aWindow); - return sTempSelection; + static Maybe sTempContentSelection; + sTempContentSelection = QueryContentSelection(aWindow); + return sTempContentSelection; } + /** + * Query content selection on aWindow with WidgetQueryContent event. + */ + static Maybe QueryContentSelection(nsWindow* aWindow); + bool mIsComposing; static mozilla::WritingMode sWritingModeOfCompositionFont;