Bug 790516 part.5 Store selection even if there is no composition r=jimm
This commit is contained in:
@@ -567,6 +567,8 @@ nsTextStore::Destroy(void)
|
||||
PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
|
||||
("TSF: 0x%p nsTextStore::Destroy()", this));
|
||||
|
||||
mSelection.MarkDirty();
|
||||
|
||||
if (mWidget) {
|
||||
// When blurred, Tablet Input Panel posts "blur" messages
|
||||
// and try to insert text when the message is retrieved later.
|
||||
@@ -1108,66 +1110,48 @@ nsTextStore::GetSelection(ULONG ulIndex,
|
||||
return TS_E_NOSELECTION;
|
||||
}
|
||||
|
||||
if (!GetSelectionInternal(*pSelection)) {
|
||||
Selection& currentSel = CurrentSelection();
|
||||
if (currentSel.IsDirty()) {
|
||||
PR_LOG(sTextStoreLog, PR_LOG_ERROR,
|
||||
("TSF: 0x%p nsTextStore::GetSelection() FAILED to get selection",
|
||||
this));
|
||||
("TSF: 0x%p nsTextStore::GetSelection() FAILED due to "
|
||||
"CurrentSelection() failure", this));
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
*pSelection = currentSel.ACP();
|
||||
*pcFetched = 1;
|
||||
PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
|
||||
("TSF: 0x%p nsTextStore::GetSelection() succeeded", this));
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
nsTextStore::GetSelectionInternal(TS_SELECTION_ACP &aSelectionACP)
|
||||
nsTextStore::Selection&
|
||||
nsTextStore::CurrentSelection()
|
||||
{
|
||||
if (mComposition.IsComposing()) {
|
||||
PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
|
||||
("TSF: 0x%p nsTextStore::GetSelectionInternal(), "
|
||||
"there is no composition view", this));
|
||||
|
||||
// Emulate selection during compositions
|
||||
aSelectionACP = mComposition.mSelection;
|
||||
} else {
|
||||
PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
|
||||
("TSF: 0x%p nsTextStore::GetSelectionInternal(), "
|
||||
"try to get normal selection...", this));
|
||||
|
||||
// Construct and initialize an event to get selection info
|
||||
nsQueryContentEvent event(true, NS_QUERY_SELECTED_TEXT, mWidget);
|
||||
mWidget->InitEvent(event);
|
||||
mWidget->DispatchWindowEvent(&event);
|
||||
if (!event.mSucceeded) {
|
||||
PR_LOG(sTextStoreLog, PR_LOG_ERROR,
|
||||
("TSF: 0x%p nsTextStore::GetSelectionInternal() FAILED to "
|
||||
"query selected text", this));
|
||||
return false;
|
||||
if (mSelection.IsDirty()) {
|
||||
// If the window has never been available, we should crash since working
|
||||
// with broken values may make TIP confused.
|
||||
if (!mWidget || mWidget->Destroyed()) {
|
||||
MOZ_CRASH();
|
||||
}
|
||||
// Usually the selection anchor (beginning) position corresponds to the
|
||||
// TSF start and the selection focus (ending) position corresponds to
|
||||
// the TSF end, but if selection is reversed the focus now corresponds
|
||||
// to the TSF start and the anchor now corresponds to the TSF end
|
||||
aSelectionACP.acpStart = event.mReply.mOffset;
|
||||
aSelectionACP.acpEnd =
|
||||
aSelectionACP.acpStart + event.mReply.mString.Length();
|
||||
aSelectionACP.style.ase =
|
||||
event.mReply.mString.Length() && event.mReply.mReversed ? TS_AE_START :
|
||||
TS_AE_END;
|
||||
// No support for interim character
|
||||
aSelectionACP.style.fInterimChar = FALSE;
|
||||
|
||||
nsQueryContentEvent querySelection(true, NS_QUERY_SELECTED_TEXT, mWidget);
|
||||
mWidget->InitEvent(querySelection);
|
||||
mWidget->DispatchWindowEvent(&querySelection);
|
||||
NS_ENSURE_TRUE(querySelection.mSucceeded, mSelection);
|
||||
|
||||
mSelection.SetSelection(querySelection.mReply.mOffset,
|
||||
querySelection.mReply.mString.Length(),
|
||||
querySelection.mReply.mReversed);
|
||||
}
|
||||
|
||||
PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
|
||||
("TSF: 0x%p nsTextStore::GetSelectionInternal() succeeded: "
|
||||
"acpStart=%lu, acpEnd=%lu, style.ase=%s, style.fInterimChar=%s",
|
||||
this, aSelectionACP.acpStart, aSelectionACP.acpEnd,
|
||||
GetActiveSelEndName(aSelectionACP.style.ase),
|
||||
GetBoolName(aSelectionACP.style.fInterimChar)));
|
||||
PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
|
||||
("TSF: 0x%p nsTextStore::CurrentSelection(): "
|
||||
"acpStart=%d, acpEnd=%d (length=%d), reverted=%s",
|
||||
this, mSelection.StartOffset(), mSelection.EndOffset(),
|
||||
mSelection.Length(),
|
||||
GetBoolName(mSelection.IsReversed())));
|
||||
|
||||
return true;
|
||||
return mSelection;
|
||||
}
|
||||
|
||||
static HRESULT
|
||||
@@ -1437,6 +1421,15 @@ nsTextStore::RecordCompositionUpdateAction()
|
||||
return FAILED(hr) ? hr : E_FAIL;
|
||||
}
|
||||
|
||||
// First, put the log of content and selection here.
|
||||
Selection& currentSel = CurrentSelection();
|
||||
if (currentSel.IsDirty()) {
|
||||
PR_LOG(sTextStoreLog, PR_LOG_ERROR,
|
||||
("TSF: 0x%p nsTextStore::RecordCompositionUpdateAction() FAILED "
|
||||
"due to CurrentSelection() failure", this));
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
PendingAction* action = GetPendingCompositionUpdate();
|
||||
action->mData = mComposition.mString;
|
||||
nsTArray<nsTextRange>& textRanges = action->mRanges;
|
||||
@@ -1505,15 +1498,15 @@ nsTextStore::RecordCompositionUpdateAction()
|
||||
// We need to hack for Korean Input System which is Korean standard TIP.
|
||||
// It sets no change style to IME selection (the selection is always only
|
||||
// one). So, the composition string looks like normal (or committed) string.
|
||||
// At this time, mComposition.mSelection range is same as the
|
||||
// composition string range. Other applications set a wide caret which covers
|
||||
// the composition string, however, Gecko doesn't support the wide caret
|
||||
// drawing now (Gecko doesn't support XOR drawing), unfortunately. For now,
|
||||
// we should change the range style to undefined.
|
||||
if (!mComposition.IsSelectionCollapsed() && textRanges.Length() == 1) {
|
||||
// At this time, current selection range is same as the composition string
|
||||
// range. Other applications set a wide caret which covers the composition
|
||||
// string, however, Gecko doesn't support the wide caret drawing now (Gecko
|
||||
// doesn't support XOR drawing), unfortunately. For now, we should change
|
||||
// the range style to undefined.
|
||||
if (!currentSel.IsCollapsed() && textRanges.Length() == 1) {
|
||||
nsTextRange& range = textRanges[0];
|
||||
LONG start = mComposition.MinSelectionOffset();
|
||||
LONG end = mComposition.MaxSelectionOffset();
|
||||
LONG start = currentSel.MinOffset();
|
||||
LONG end = currentSel.MaxOffset();
|
||||
if ((LONG)range.mStartOffset == start - mComposition.mStart &&
|
||||
(LONG)range.mEndOffset == end - mComposition.mStart &&
|
||||
range.mRangeStyle.IsNoChangeStyle()) {
|
||||
@@ -1524,7 +1517,7 @@ nsTextStore::RecordCompositionUpdateAction()
|
||||
}
|
||||
|
||||
// The caret position has to be collapsed.
|
||||
LONG caretPosition = mComposition.MaxSelectionOffset();
|
||||
LONG caretPosition = currentSel.MaxOffset();
|
||||
caretPosition -= mComposition.mStart;
|
||||
nsTextRange caretRange;
|
||||
caretRange.mStartOffset = caretRange.mEndOffset = uint32_t(caretPosition);
|
||||
@@ -1551,6 +1544,14 @@ nsTextStore::SetSelectionInternal(const TS_SELECTION_ACP* pSelection,
|
||||
|
||||
MOZ_ASSERT(IsReadWriteLocked());
|
||||
|
||||
Selection& currentSel = CurrentSelection();
|
||||
if (currentSel.IsDirty()) {
|
||||
PR_LOG(sTextStoreLog, PR_LOG_ERROR,
|
||||
("TSF: 0x%p nsTextStore::SetSelectionInternal() FAILED due to "
|
||||
"CurrentSelection() failure", this));
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
if (mComposition.IsComposing()) {
|
||||
if (aDispatchTextEvent) {
|
||||
HRESULT hr = UpdateCompositionExtent(nullptr);
|
||||
@@ -1569,7 +1570,7 @@ nsTextStore::SetSelectionInternal(const TS_SELECTION_ACP* pSelection,
|
||||
return TS_E_INVALIDPOS;
|
||||
}
|
||||
// Emulate selection during compositions
|
||||
mComposition.mSelection = *pSelection;
|
||||
currentSel.SetSelection(*pSelection);
|
||||
if (aDispatchTextEvent) {
|
||||
HRESULT hr = RecordCompositionUpdateAction();
|
||||
if (FAILED(hr)) {
|
||||
@@ -1588,7 +1589,7 @@ nsTextStore::SetSelectionInternal(const TS_SELECTION_ACP* pSelection,
|
||||
action->mSelectionLength = pSelection->acpEnd - pSelection->acpStart;
|
||||
action->mSelectionReversed = (pSelection->style.ase == TS_AE_START);
|
||||
|
||||
// TODO: Need to store the selection for next action.
|
||||
currentSel.SetSelection(*pSelection);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
@@ -2437,21 +2438,21 @@ nsTextStore::InsertTextAtSelection(DWORD dwFlags,
|
||||
}
|
||||
|
||||
// Get selection first
|
||||
TS_SELECTION_ACP sel;
|
||||
if (!GetSelectionInternal(sel)) {
|
||||
Selection& currentSel = CurrentSelection();
|
||||
if (currentSel.IsDirty()) {
|
||||
PR_LOG(sTextStoreLog, PR_LOG_ERROR,
|
||||
("TSF: 0x%p nsTextStore::InsertTextAtSelection() FAILED due to "
|
||||
"GetSelectionInternal() failure", this));
|
||||
"CurrentSelection() failure", this));
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
// Simulate text insertion
|
||||
*pacpStart = sel.acpStart;
|
||||
*pacpEnd = sel.acpEnd;
|
||||
*pacpStart = currentSel.StartOffset();
|
||||
*pacpEnd = currentSel.EndOffset();
|
||||
if (pChange) {
|
||||
pChange->acpStart = sel.acpStart;
|
||||
pChange->acpOldEnd = sel.acpEnd;
|
||||
pChange->acpNewEnd = sel.acpStart + cch;
|
||||
pChange->acpStart = currentSel.StartOffset();
|
||||
pChange->acpOldEnd = currentSel.EndOffset();
|
||||
pChange->acpNewEnd = currentSel.StartOffset() + static_cast<LONG>(cch);
|
||||
}
|
||||
} else {
|
||||
if (!IsReadWriteLocked()) {
|
||||
@@ -2508,12 +2509,16 @@ nsTextStore::InsertTextAtSelectionInternal(const nsAString &aInsertStr,
|
||||
this, NS_ConvertUTF16toUTF8(aInsertStr).get(), aTextChange,
|
||||
GetBoolName(mComposition.IsComposing())));
|
||||
|
||||
TS_SELECTION_ACP oldSelection;
|
||||
oldSelection.acpStart = 0;
|
||||
oldSelection.acpEnd = 0;
|
||||
Selection& currentSel = CurrentSelection();
|
||||
if (currentSel.IsDirty()) {
|
||||
PR_LOG(sTextStoreLog, PR_LOG_ERROR,
|
||||
("TSF: 0x%p nsTextStore::InsertTextAtSelectionInternal() failed "
|
||||
"due to CurrentSelection() failure()", this));
|
||||
return false;
|
||||
}
|
||||
|
||||
TS_SELECTION_ACP oldSelection = currentSel.ACP();
|
||||
if (mComposition.IsComposing()) {
|
||||
oldSelection = mComposition.mSelection;
|
||||
// Emulate text insertion during compositions, because during a
|
||||
// composition, editor expects the whole composition string to
|
||||
// be sent in NS_TEXT_TEXT, not just the inserted part.
|
||||
@@ -2523,26 +2528,7 @@ nsTextStore::InsertTextAtSelectionInternal(const nsAString &aInsertStr,
|
||||
static_cast<uint32_t>(oldSelection.acpStart) - mComposition.mStart,
|
||||
static_cast<uint32_t>(oldSelection.acpEnd - oldSelection.acpStart),
|
||||
aInsertStr);
|
||||
|
||||
mComposition.mSelection.acpStart += aInsertStr.Length();
|
||||
mComposition.mSelection.acpEnd =
|
||||
mComposition.mSelection.acpStart;
|
||||
mComposition.mSelection.style.ase = TS_AE_END;
|
||||
PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
|
||||
("TSF: 0x%p nsTextStore::InsertTextAtSelectionInternal() replaced "
|
||||
"a part of (%lu-%lu) the composition string, waiting "
|
||||
"SetSelection() or OnUpdateComposition()...", this,
|
||||
oldSelection.acpStart - mComposition.mStart,
|
||||
oldSelection.acpEnd - mComposition.mStart));
|
||||
} else {
|
||||
// Use actual selection if it's not composing.
|
||||
if (aTextChange && !GetSelectionInternal(oldSelection)) {
|
||||
PR_LOG(sTextStoreLog, PR_LOG_ERROR,
|
||||
("TSF: 0x%p nsTextStore::InsertTextAtSelectionInternal() FAILED "
|
||||
"due to GetSelectionInternal() failure", this));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Use a temporary composition to contain the text
|
||||
PendingAction* compositionStart = mPendingActions.AppendElement();
|
||||
compositionStart->mType = PendingAction::COMPOSITION_START;
|
||||
@@ -2555,10 +2541,13 @@ nsTextStore::InsertTextAtSelectionInternal(const nsAString &aInsertStr,
|
||||
compositionEnd->mData = aInsertStr;
|
||||
}
|
||||
|
||||
currentSel.CollapseAt(
|
||||
static_cast<uint32_t>(oldSelection.acpStart) + aInsertStr.Length());
|
||||
|
||||
if (aTextChange) {
|
||||
aTextChange->acpStart = oldSelection.acpStart;
|
||||
aTextChange->acpOldEnd = oldSelection.acpEnd;
|
||||
aTextChange->acpNewEnd = oldSelection.acpStart + aInsertStr.Length();
|
||||
aTextChange->acpNewEnd = currentSel.EndOffset();
|
||||
}
|
||||
|
||||
PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
|
||||
@@ -2609,6 +2598,14 @@ nsTextStore::OnStartCompositionInternal(ITfCompositionView* pComposition,
|
||||
return hr;
|
||||
}
|
||||
|
||||
Selection& currentSel = CurrentSelection();
|
||||
if (currentSel.IsDirty()) {
|
||||
PR_LOG(sTextStoreLog, PR_LOG_ERROR,
|
||||
("TSF: 0x%p nsTextStore::OnStartCompositionInternal() FAILED due "
|
||||
"to CurrentSelection() failure", this));
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
|
||||
("TSF: 0x%p nsTextStore::OnStartCompositionInternal(), "
|
||||
"mComposition={ mStart=%ld, mLength=%ld }",
|
||||
@@ -2633,22 +2630,18 @@ nsTextStore::OnStartCompositionInternal(ITfCompositionView* pComposition,
|
||||
action->mSelectionLength = mComposition.mLength;
|
||||
|
||||
if (!aPreserveSelection) {
|
||||
mComposition.mSelection.acpStart = mComposition.mStart;
|
||||
mComposition.mSelection.acpEnd = mComposition.EndOffset();
|
||||
mComposition.mSelection.style.ase = TS_AE_END;
|
||||
mComposition.mSelection.style.fInterimChar = FALSE;
|
||||
currentSel.SetSelection(mComposition.mStart, mComposition.mLength, false);
|
||||
}
|
||||
|
||||
PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
|
||||
("TSF: 0x%p nsTextStore::OnStartCompositionInternal() succeeded: "
|
||||
"mComposition={ mStart=%ld, mLength=%ld, "
|
||||
"mSelection={ acpStart=%ld, acpEnd=%ld, style.ase=%s, "
|
||||
"style.iInterimChar=%s } }",
|
||||
"style.fInterimChar=%s } }",
|
||||
this, mComposition.mStart, mComposition.mLength,
|
||||
mComposition.mSelection.acpStart,
|
||||
mComposition.mSelection.acpEnd,
|
||||
GetActiveSelEndName(mComposition.mSelection.style.ase),
|
||||
GetBoolName(mComposition.mSelection.style.fInterimChar)));
|
||||
currentSel.StartOffset(), currentSel.EndOffset(),
|
||||
GetActiveSelEndName(currentSel.ActiveSelEnd()),
|
||||
GetBoolName(currentSel.IsInterimChar())));
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
@@ -2749,17 +2742,25 @@ nsTextStore::OnUpdateComposition(ITfCompositionView* pComposition,
|
||||
return hr;
|
||||
}
|
||||
|
||||
PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
|
||||
("TSF: 0x%p nsTextStore::OnUpdateComposition() succeeded: "
|
||||
"mComposition={ mStart=%ld, mLength=%ld, "
|
||||
"mSelection={ acpStart=%ld, acpEnd=%ld, style.ase=%s, "
|
||||
"style.iInterimChar=%s }, mString=\"%s\" }",
|
||||
this, mComposition.mStart, mComposition.mLength,
|
||||
mComposition.mSelection.acpStart,
|
||||
mComposition.mSelection.acpEnd,
|
||||
GetActiveSelEndName(mComposition.mSelection.style.ase),
|
||||
GetBoolName(mComposition.mSelection.style.fInterimChar),
|
||||
NS_ConvertUTF16toUTF8(mComposition.mString).get()));
|
||||
#ifdef PR_LOGGING
|
||||
if (PR_LOG_TEST(sTextStoreLog, PR_LOG_ALWAYS)) {
|
||||
Selection& currentSel = CurrentSelection();
|
||||
if (currentSel.IsDirty()) {
|
||||
PR_LOG(sTextStoreLog, PR_LOG_ERROR,
|
||||
("TSF: 0x%p nsTextStore::OnUpdateComposition() FAILED due to "
|
||||
"CurrentSelection() failure", this));
|
||||
return E_FAIL;
|
||||
}
|
||||
PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
|
||||
("TSF: 0x%p nsTextStore::OnUpdateComposition() succeeded: "
|
||||
"mComposition={ mStart=%ld, mLength=%ld, mString=\"%s\" }, "
|
||||
"CurrentSelection()={ acpStart=%ld, acpEnd=%ld, style.ase=%s }",
|
||||
this, mComposition.mStart, mComposition.mLength,
|
||||
NS_ConvertUTF16toUTF8(mComposition.mString).get(),
|
||||
currentSel.StartOffset(), currentSel.EndOffset(),
|
||||
GetActiveSelEndName(currentSel.ActiveSelEnd())));
|
||||
}
|
||||
#endif // #ifdef PR_LOGGING
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
@@ -2853,7 +2854,14 @@ nsTextStore::OnTextChangeInternal(uint32_t aStart,
|
||||
GetSinkMaskNameStr(mSinkMask).get(), mTextChange.acpStart,
|
||||
mTextChange.acpOldEnd, mTextChange.acpNewEnd));
|
||||
|
||||
if (!mLock && mSink && 0 != (mSinkMask & TS_AS_TEXT_CHANGE)) {
|
||||
if (IsReadLocked()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_ASSERTION(!mComposition.IsComposing(), "text changed during composition");
|
||||
mSelection.MarkDirty();
|
||||
|
||||
if (mSink && 0 != (mSinkMask & TS_AS_TEXT_CHANGE)) {
|
||||
mTextChange.acpStart = std::min(mTextChange.acpStart, LONG(aStart));
|
||||
mTextChange.acpOldEnd = std::max(mTextChange.acpOldEnd, LONG(aOldEnd));
|
||||
mTextChange.acpNewEnd = std::max(mTextChange.acpNewEnd, LONG(aNewEnd));
|
||||
@@ -2896,7 +2904,15 @@ nsTextStore::OnSelectionChangeInternal(void)
|
||||
this, mSink.get(), GetSinkMaskNameStr(mSinkMask).get(),
|
||||
GetBoolName(mIsRecordingActionsWithoutLock)));
|
||||
|
||||
if (!mLock && mSink && 0 != (mSinkMask & TS_AS_SEL_CHANGE)) {
|
||||
if (IsReadLocked()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_ASSERTION(!mComposition.IsComposing(),
|
||||
"selection changed during composition");
|
||||
mSelection.MarkDirty();
|
||||
|
||||
if (mSink && 0 != (mSinkMask & TS_AS_SEL_CHANGE)) {
|
||||
PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
|
||||
("TSF: 0x%p nsTextStore::OnSelectionChangeInternal(), calling "
|
||||
"mSink->OnSelectionChange()...", this));
|
||||
|
||||
Reference in New Issue
Block a user