--- a/dom/events/ContentEventHandler.cpp
+++ b/dom/events/ContentEventHandler.cpp
@@ -35,16 +35,186 @@
#include <algorithm>
namespace mozilla {
using namespace dom;
using namespace widget;
/******************************************************************/
+/* ContentEventHandler::RawRange */
+/******************************************************************/
+
+void
+ContentEventHandler::RawRange::AssertStartIsBeforeOrEqualToEnd()
+{
+ MOZ_ASSERT(
+ nsContentUtils::ComparePoints(mStartContainer,
+ static_cast<int32_t>(mStartOffset),
+ mEndContainer,
+ static_cast<int32_t>(mEndOffset)) <= 0);
+}
+
+bool
+ContentEventHandler::RawRange::IsValidOffset(nsINode* aContainer,
+ uint32_t aOffset) const
+{
+ return aContainer && aOffset <= aContainer->Length();
+}
+
+nsresult
+ContentEventHandler::RawRange::SetStart(nsINode* aStartContainer,
+ uint32_t aStartOffset)
+{
+ nsINode* newRoot = nsRange::ComputeRootNode(aStartContainer);
+ if (!newRoot) {
+ return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR;
+ }
+
+ if (!IsValidOffset(aStartContainer, aStartOffset)) {
+ return NS_ERROR_DOM_INDEX_SIZE_ERR;
+ }
+
+ // Collapse if not positioned yet, or if positioned in another document.
+ if (!IsPositioned() || newRoot != mRoot) {
+ mRoot = newRoot;
+ mStartContainer = mEndContainer = aStartContainer;
+ mStartOffset = mEndOffset = aStartOffset;
+ return NS_OK;
+ }
+
+ mStartContainer = aStartContainer;
+ mStartOffset = aStartOffset;
+ AssertStartIsBeforeOrEqualToEnd();
+ return NS_OK;
+}
+
+nsresult
+ContentEventHandler::RawRange::SetEnd(nsINode* aEndContainer,
+ uint32_t aEndOffset)
+{
+ nsINode* newRoot = nsRange::ComputeRootNode(aEndContainer);
+ if (!newRoot) {
+ return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR;
+ }
+
+ if (!IsValidOffset(aEndContainer, aEndOffset)) {
+ return NS_ERROR_DOM_INDEX_SIZE_ERR;
+ }
+
+ // Collapse if not positioned yet, or if positioned in another document.
+ if (!IsPositioned() || newRoot != mRoot) {
+ mRoot = newRoot;
+ mStartContainer = mEndContainer = aEndContainer;
+ mStartOffset = mEndOffset = aEndOffset;
+ return NS_OK;
+ }
+
+ mEndContainer = aEndContainer;
+ mEndOffset = aEndOffset;
+ AssertStartIsBeforeOrEqualToEnd();
+ return NS_OK;
+}
+
+nsresult
+ContentEventHandler::RawRange::SetEndAfter(nsINode* aEndContainer)
+{
+ uint32_t offset = 0;
+ nsINode* container =
+ nsRange::GetContainerAndOffsetAfter(aEndContainer, &offset);
+ return SetEnd(container, offset);
+}
+
+void
+ContentEventHandler::RawRange::SetStartAndEnd(const nsRange* aRange)
+{
+ DebugOnly<nsresult> rv = SetStartAndEnd(aRange->GetStartContainer(),
+ aRange->StartOffset(),
+ aRange->GetEndContainer(),
+ aRange->EndOffset());
+ MOZ_ASSERT(!aRange->IsPositioned() || NS_SUCCEEDED(rv));
+}
+
+nsresult
+ContentEventHandler::RawRange::SetStartAndEnd(nsINode* aStartContainer,
+ uint32_t aStartOffset,
+ nsINode* aEndContainer,
+ uint32_t aEndOffset)
+{
+ nsINode* newStartRoot = nsRange::ComputeRootNode(aStartContainer);
+ if (!newStartRoot) {
+ return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR;
+ }
+ if (!IsValidOffset(aStartContainer, aStartOffset)) {
+ return NS_ERROR_DOM_INDEX_SIZE_ERR;
+ }
+
+ if (aStartContainer == aEndContainer) {
+ if (!IsValidOffset(aEndContainer, aEndOffset)) {
+ return NS_ERROR_DOM_INDEX_SIZE_ERR;
+ }
+ MOZ_ASSERT(aStartOffset <= aEndOffset);
+ mRoot = newStartRoot;
+ mStartContainer = mEndContainer = aStartContainer;
+ mStartOffset = aStartOffset;
+ mEndOffset = aEndOffset;
+ return NS_OK;
+ }
+
+ nsINode* newEndRoot = nsRange::ComputeRootNode(aEndContainer);
+ if (!newEndRoot) {
+ return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR;
+ }
+ if (!IsValidOffset(aEndContainer, aEndOffset)) {
+ return NS_ERROR_DOM_INDEX_SIZE_ERR;
+ }
+
+ // If they have different root, this should be collapsed at the end point.
+ if (newStartRoot != newEndRoot) {
+ mRoot = newEndRoot;
+ mStartContainer = mEndContainer = aEndContainer;
+ mStartOffset = mEndOffset = aEndOffset;
+ return NS_OK;
+ }
+
+ // Otherwise, set the range as specified.
+ mRoot = newStartRoot;
+ mStartContainer = aStartContainer;
+ mStartOffset = aStartOffset;
+ mEndContainer = aEndContainer;
+ mEndOffset = aEndOffset;
+ AssertStartIsBeforeOrEqualToEnd();
+ return NS_OK;
+}
+
+nsresult
+ContentEventHandler::RawRange::SelectNodeContents(
+ nsINode* aNodeToSelectContents)
+{
+ nsINode* newRoot = nsRange::ComputeRootNode(aNodeToSelectContents);
+ if (!newRoot) {
+ return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR;
+ }
+ mRoot = newRoot;
+ mStartContainer = mEndContainer = aNodeToSelectContents;
+ mStartOffset = 0;
+ mEndOffset = aNodeToSelectContents->Length();
+ return NS_OK;
+}
+
+already_AddRefed<nsRange>
+ContentEventHandler::RawRange::CreateRange() const
+{
+ RefPtr<nsRange> range = new nsRange(mRoot);
+ range->SetStartAndEnd(mStartContainer, mStartOffset,
+ mEndContainer, mEndOffset);
+ return range.forget();
+}
+
+/******************************************************************/
/* ContentEventHandler */
/******************************************************************/
// NOTE
//
// ContentEventHandler *creates* ranges as following rules:
// 1. Start of range:
// 1.1. Cases: [textNode or text[Node or textNode[
@@ -100,19 +270,16 @@ using namespace widget;
// it includes the end (i.e., includes its close tag except empty element).
// Although, currently, any close tags don't cause line break, this also
// includes its open tag. For example, if end position is { <br>, 0 }, the
// line break caused by the <br> should be included into the flatten text.
ContentEventHandler::ContentEventHandler(nsPresContext* aPresContext)
: mPresContext(aPresContext)
, mPresShell(aPresContext->GetPresShell())
- , mSelection(nullptr)
- , mFirstSelectedRange(nullptr)
- , mRootContent(nullptr)
{
}
nsresult
ContentEventHandler::InitBasic()
{
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
@@ -190,18 +357,18 @@ ContentEventHandler::InitRootContent(Sel
nsresult
ContentEventHandler::InitCommon(SelectionType aSelectionType)
{
if (mSelection && mSelection->Type() == aSelectionType) {
return NS_OK;
}
mSelection = nullptr;
- mFirstSelectedRange = nullptr;
mRootContent = nullptr;
+ mFirstSelectedRawRange.Clear();
nsresult rv = InitBasic();
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsISelectionController> selectionController =
mPresShell->GetSelectionControllerForFocusedContent();
if (NS_WARN_IF(!selectionController)) {
return NS_ERROR_NOT_AVAILABLE;
@@ -240,35 +407,31 @@ ContentEventHandler::InitCommon(Selectio
}
rv = InitRootContent(normalSelection);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (mSelection->RangeCount()) {
- mFirstSelectedRange = mSelection->GetRangeAt(0);
- if (NS_WARN_IF(!mFirstSelectedRange)) {
- return NS_ERROR_UNEXPECTED;
- }
+ mFirstSelectedRawRange.SetStartAndEnd(mSelection->GetRangeAt(0));
return NS_OK;
}
// Even if there are no selection ranges, it's usual case if aSelectionType
// is a special selection.
if (aSelectionType != SelectionType::eNormal) {
- MOZ_ASSERT(!mFirstSelectedRange);
+ MOZ_ASSERT(!mFirstSelectedRawRange.IsPositioned());
return NS_OK;
}
// But otherwise, we need to assume that there is a selection range at the
// beginning of the root content if aSelectionType is eNormal.
- rv = nsRange::CreateRange(mRootContent, 0, mRootContent, 0,
- getter_AddRefs(mFirstSelectedRange));
- if (NS_WARN_IF(NS_FAILED(rv)) || NS_WARN_IF(!mFirstSelectedRange)) {
+ rv = mFirstSelectedRawRange.CollapseTo(mRootContent, 0);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
return NS_ERROR_UNEXPECTED;
}
return NS_OK;
}
nsresult
ContentEventHandler::Init(WidgetQueryContentEvent* aEvent)
{
@@ -304,17 +467,18 @@ ContentEventHandler::Init(WidgetQueryCon
if (composition) {
uint32_t compositionStart = composition->NativeOffsetOfStartComposition();
if (NS_WARN_IF(!aEvent->mInput.MakeOffsetAbsolute(compositionStart))) {
return NS_ERROR_FAILURE;
}
} else {
LineBreakType lineBreakType = GetLineBreakType(aEvent);
uint32_t selectionStart = 0;
- rv = GetStartOffset(mFirstSelectedRange, &selectionStart, lineBreakType);
+ rv = GetStartOffset(mFirstSelectedRawRange, &selectionStart,
+ lineBreakType);
if (NS_WARN_IF(NS_FAILED(rv))) {
return NS_ERROR_FAILURE;
}
if (NS_WARN_IF(!aEvent->mInput.MakeOffsetAbsolute(selectionStart))) {
return NS_ERROR_FAILURE;
}
}
}
@@ -665,71 +829,71 @@ ContentEventHandler::ShouldBreakLineBefo
nsresult
ContentEventHandler::GenerateFlatTextContent(nsIContent* aContent,
nsString& aString,
LineBreakType aLineBreakType)
{
MOZ_ASSERT(aString.IsEmpty());
- RefPtr<nsRange> range = new nsRange(mRootContent);
- ErrorResult rv;
- range->SelectNodeContents(*aContent, rv);
- if (NS_WARN_IF(rv.Failed())) {
- return rv.StealNSResult();
+ RawRange rawRange;
+ nsresult rv = rawRange.SelectNodeContents(aContent);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
}
- return GenerateFlatTextContent(range, aString, aLineBreakType);
+ return GenerateFlatTextContent(rawRange, aString, aLineBreakType);
}
nsresult
-ContentEventHandler::GenerateFlatTextContent(nsRange* aRange,
+ContentEventHandler::GenerateFlatTextContent(const RawRange& aRawRange,
nsString& aString,
LineBreakType aLineBreakType)
{
MOZ_ASSERT(aString.IsEmpty());
- if (aRange->Collapsed()) {
+ if (aRawRange.Collapsed()) {
return NS_OK;
}
- nsINode* startNode = aRange->GetStartContainer();
- nsINode* endNode = aRange->GetEndContainer();
+ nsINode* startNode = aRawRange.GetStartContainer();
+ nsINode* endNode = aRawRange.GetEndContainer();
if (NS_WARN_IF(!startNode) || NS_WARN_IF(!endNode)) {
return NS_ERROR_FAILURE;
}
if (startNode == endNode && startNode->IsNodeOfType(nsINode::eTEXT)) {
nsIContent* content = startNode->AsContent();
- AppendSubString(aString, content, aRange->StartOffset(),
- aRange->EndOffset() - aRange->StartOffset());
+ AppendSubString(aString, content, aRawRange.StartOffset(),
+ aRawRange.EndOffset() - aRawRange.StartOffset());
ConvertToNativeNewlines(aString);
return NS_OK;
}
nsCOMPtr<nsIContentIterator> iter = NS_NewPreContentIterator();
- nsresult rv = iter->Init(aRange);
+ RefPtr<nsRange> range = aRawRange.CreateRange();
+ nsresult rv = iter->Init(range);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
for (; !iter->IsDone(); iter->Next()) {
nsINode* node = iter->GetCurrentNode();
if (NS_WARN_IF(!node)) {
break;
}
if (!node->IsContent()) {
continue;
}
nsIContent* content = node->AsContent();
if (content->IsNodeOfType(nsINode::eTEXT)) {
if (content == startNode) {
- AppendSubString(aString, content, aRange->StartOffset(),
- content->TextLength() - aRange->StartOffset());
+ AppendSubString(aString, content, aRawRange.StartOffset(),
+ content->TextLength() - aRawRange.StartOffset());
} else if (content == endNode) {
- AppendSubString(aString, content, 0, aRange->EndOffset());
+ AppendSubString(aString, content, 0, aRawRange.EndOffset());
} else {
AppendString(aString, content);
}
} else if (ShouldBreakLineBefore(content, mRootContent)) {
aString.Append(char16_t('\n'));
}
}
if (aLineBreakType == LINE_BREAK_TYPE_NATIVE) {
@@ -757,66 +921,69 @@ ContentEventHandler::GetTextLengthInRang
return aLineBreakType == LINE_BREAK_TYPE_NATIVE ?
GetNativeTextLength(aContent, aXPStartOffset, aXPEndOffset) :
aXPEndOffset - aXPStartOffset;
}
/* static */ void
ContentEventHandler::AppendFontRanges(FontRangeArray& aFontRanges,
nsIContent* aContent,
- int32_t aBaseOffset,
- int32_t aXPStartOffset,
- int32_t aXPEndOffset,
+ uint32_t aBaseOffset,
+ uint32_t aXPStartOffset,
+ uint32_t aXPEndOffset,
LineBreakType aLineBreakType)
{
MOZ_ASSERT(aContent->IsNodeOfType(nsINode::eTEXT));
nsIFrame* frame = aContent->GetPrimaryFrame();
if (!frame) {
// It is a non-rendered content, create an empty range for it.
AppendFontRange(aFontRanges, aBaseOffset);
return;
}
- int32_t baseOffset = aBaseOffset;
+ uint32_t baseOffset = aBaseOffset;
#ifdef DEBUG
{
nsTextFrame* text = do_QueryFrame(frame);
MOZ_ASSERT(text, "Not a text frame");
}
#endif
auto* curr = static_cast<nsTextFrame*>(frame);
while (curr) {
- int32_t frameXPStart = std::max(curr->GetContentOffset(), aXPStartOffset);
- int32_t frameXPEnd = std::min(curr->GetContentEnd(), aXPEndOffset);
+ uint32_t frameXPStart =
+ std::max(static_cast<uint32_t>(curr->GetContentOffset()), aXPStartOffset);
+ uint32_t frameXPEnd =
+ std::min(static_cast<uint32_t>(curr->GetContentEnd()), aXPEndOffset);
if (frameXPStart >= frameXPEnd) {
curr = curr->GetNextContinuation();
continue;
}
gfxSkipCharsIterator iter = curr->EnsureTextRun(nsTextFrame::eInflated);
gfxTextRun* textRun = curr->GetTextRun(nsTextFrame::eInflated);
nsTextFrame* next = nullptr;
if (frameXPEnd < aXPEndOffset) {
next = curr->GetNextContinuation();
while (next && next->GetTextRun(nsTextFrame::eInflated) == textRun) {
- frameXPEnd = std::min(next->GetContentEnd(), aXPEndOffset);
+ frameXPEnd =
+ std::min(static_cast<uint32_t>(next->GetContentEnd()), aXPEndOffset);
next = frameXPEnd < aXPEndOffset ?
next->GetNextContinuation() : nullptr;
}
}
gfxTextRun::Range skipRange(iter.ConvertOriginalToSkipped(frameXPStart),
iter.ConvertOriginalToSkipped(frameXPEnd));
gfxTextRun::GlyphRunIterator runIter(textRun, skipRange);
- int32_t lastXPEndOffset = frameXPStart;
+ uint32_t lastXPEndOffset = frameXPStart;
while (runIter.NextRun()) {
gfxFont* font = runIter.GetGlyphRun()->mFont.get();
- int32_t startXPOffset =
+ uint32_t startXPOffset =
iter.ConvertSkippedToOriginal(runIter.GetStringStart());
// It is possible that the first glyph run has exceeded the frame,
// because the whole frame is filled by skipped chars.
if (startXPOffset >= frameXPEnd) {
break;
}
if (startXPOffset > lastXPEndOffset) {
@@ -828,17 +995,17 @@ ContentEventHandler::AppendFontRanges(Fo
}
FontRange* fontRange = AppendFontRange(aFontRanges, baseOffset);
fontRange->mFontName = font->GetName();
fontRange->mFontSize = font->GetAdjustedSize();
// The converted original offset may exceed the range,
// hence we need to clamp it.
- int32_t endXPOffset =
+ uint32_t endXPOffset =
iter.ConvertSkippedToOriginal(runIter.GetStringEnd());
endXPOffset = std::min(frameXPEnd, endXPOffset);
baseOffset += GetTextLengthInRange(aContent, startXPOffset, endXPOffset,
aLineBreakType);
lastXPEndOffset = endXPOffset;
}
if (lastXPEndOffset < frameXPEnd) {
// Create range for skipped trailing chars. It also handles case
@@ -848,54 +1015,55 @@ ContentEventHandler::AppendFontRanges(Fo
aContent, lastXPEndOffset, frameXPEnd, aLineBreakType);
}
curr = next;
}
}
nsresult
-ContentEventHandler::GenerateFlatFontRanges(nsRange* aRange,
+ContentEventHandler::GenerateFlatFontRanges(const RawRange& aRawRange,
FontRangeArray& aFontRanges,
uint32_t& aLength,
LineBreakType aLineBreakType)
{
MOZ_ASSERT(aFontRanges.IsEmpty(), "aRanges must be empty array");
- if (aRange->Collapsed()) {
+ if (aRawRange.Collapsed()) {
return NS_OK;
}
- nsINode* startNode = aRange->GetStartContainer();
- nsINode* endNode = aRange->GetEndContainer();
+ nsINode* startNode = aRawRange.GetStartContainer();
+ nsINode* endNode = aRawRange.GetEndContainer();
if (NS_WARN_IF(!startNode) || NS_WARN_IF(!endNode)) {
return NS_ERROR_FAILURE;
}
// baseOffset is the flattened offset of each content node.
int32_t baseOffset = 0;
nsCOMPtr<nsIContentIterator> iter = NS_NewPreContentIterator();
- nsresult rv = iter->Init(aRange);
+ RefPtr<nsRange> range = aRawRange.CreateRange();
+ nsresult rv = iter->Init(range);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
for (; !iter->IsDone(); iter->Next()) {
nsINode* node = iter->GetCurrentNode();
if (NS_WARN_IF(!node)) {
break;
}
if (!node->IsContent()) {
continue;
}
nsIContent* content = node->AsContent();
if (content->IsNodeOfType(nsINode::eTEXT)) {
- int32_t startOffset = content != startNode ? 0 : aRange->StartOffset();
- int32_t endOffset = content != endNode ?
- content->TextLength() : aRange->EndOffset();
+ uint32_t startOffset = content != startNode ? 0 : aRawRange.StartOffset();
+ uint32_t endOffset = content != endNode ?
+ content->TextLength() : aRawRange.EndOffset();
AppendFontRanges(aFontRanges, content, baseOffset,
startOffset, endOffset, aLineBreakType);
baseOffset += GetTextLengthInRange(content, startOffset, endOffset,
aLineBreakType);
} else if (ShouldBreakLineBefore(content, mRootContent)) {
if (aFontRanges.IsEmpty()) {
MOZ_ASSERT(baseOffset == 0);
FontRange* fontRange = AppendFontRange(aFontRanges, baseOffset);
@@ -974,34 +1142,35 @@ ContentEventHandler::ExpandToClusterBoun
if (NS_IS_LOW_SURROGATE(text->CharAt(*aXPOffset)) &&
NS_IS_HIGH_SURROGATE(text->CharAt(*aXPOffset - 1))) {
*aXPOffset += aForward ? 1 : -1;
}
return NS_OK;
}
nsresult
-ContentEventHandler::SetRangeFromFlatTextOffset(nsRange* aRange,
- uint32_t aOffset,
- uint32_t aLength,
- LineBreakType aLineBreakType,
- bool aExpandToClusterBoundaries,
- uint32_t* aNewOffset,
- nsIContent** aLastTextNode)
+ContentEventHandler::SetRawRangeFromFlatTextOffset(
+ RawRange* aRawRange,
+ uint32_t aOffset,
+ uint32_t aLength,
+ LineBreakType aLineBreakType,
+ bool aExpandToClusterBoundaries,
+ uint32_t* aNewOffset,
+ nsIContent** aLastTextNode)
{
if (aNewOffset) {
*aNewOffset = aOffset;
}
if (aLastTextNode) {
*aLastTextNode = nullptr;
}
// Special case like <br contenteditable>
if (!mRootContent->HasChildren()) {
- nsresult rv = aRange->CollapseTo(mRootContent, 0);
+ nsresult rv = aRawRange->CollapseTo(mRootContent, 0);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
nsCOMPtr<nsIContentIterator> iter = NS_NewPreContentIterator();
nsresult rv = iter->Init(mRootContent);
if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -1086,24 +1255,26 @@ ContentEventHandler::SetRangeFromFlatTex
} else {
// Rule #1.4: <element>[
startNode = content;
startNodeOffset = 0;
}
NS_ASSERTION(startNode, "startNode must not be nullptr");
NS_ASSERTION(startNodeOffset >= 0,
"startNodeOffset must not be negative");
- rv = aRange->SetStart(startNode, startNodeOffset);
+ rv = aRawRange->SetStart(startNode,
+ static_cast<uint32_t>(startNodeOffset));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
startSet = true;
if (!aLength) {
- rv = aRange->SetEnd(startNode, startNodeOffset);
+ rv = aRawRange->SetEnd(startNode,
+ static_cast<uint32_t>(startNodeOffset));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
}
// When the end offset is in the content, the node is the end node of the
@@ -1132,17 +1303,17 @@ ContentEventHandler::SetRangeFromFlatTex
if (aExpandToClusterBoundaries) {
rv = ExpandToClusterBoundary(content, true, &xpOffset);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
NS_ASSERTION(xpOffset <= INT32_MAX,
"The end node offset is too large");
- rv = aRange->SetEnd(content, static_cast<int32_t>(xpOffset));
+ rv = aRawRange->SetEnd(content, xpOffset);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
if (endOffset == offset) {
// Rule #2.2: ]<element>
@@ -1152,34 +1323,34 @@ ContentEventHandler::SetRangeFromFlatTex
MOZ_ASSERT(false, "This case should've already been handled at "
"the last node which caused some text");
return NS_ERROR_FAILURE;
}
if (content->HasChildren() &&
ShouldBreakLineBefore(content, mRootContent)) {
// Rule #2.3: </element>]
- rv = aRange->SetEnd(content, 0);
+ rv = aRawRange->SetEnd(content, 0);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
// Rule #2.4: <element/>]
nsINode* endNode = content->GetParent();
if (NS_WARN_IF(!endNode)) {
return NS_ERROR_FAILURE;
}
int32_t indexInParent = endNode->IndexOf(content);
if (NS_WARN_IF(indexInParent == -1)) {
// The content is being removed from the parent!
return NS_ERROR_FAILURE;
}
- rv = aRange->SetEnd(endNode, indexInParent + 1);
+ rv = aRawRange->SetEnd(endNode, indexInParent + 1);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
offset += textLength;
}
@@ -1187,42 +1358,40 @@ ContentEventHandler::SetRangeFromFlatTex
if (!startSet) {
MOZ_ASSERT(!mRootContent->IsNodeOfType(nsINode::eTEXT));
if (!offset) {
// Rule #1.5: <root>[</root>
// When there are no nodes causing text, the start of the DOM range
// should be start of the root node since clicking on such editor (e.g.,
// <div contenteditable><span></span></div>) sets caret to the start of
// the editor (i.e., before <span> in the example).
- rv = aRange->SetStart(mRootContent, 0);
+ rv = aRawRange->SetStart(mRootContent, 0);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (!aLength) {
- rv = aRange->SetEnd(mRootContent, 0);
+ rv = aRawRange->SetEnd(mRootContent, 0);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
} else {
// Rule #1.5: [</root>
- rv = aRange->SetStart(mRootContent,
- static_cast<int32_t>(mRootContent->GetChildCount()));
+ rv = aRawRange->SetStart(mRootContent, mRootContent->GetChildCount());
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
if (aNewOffset) {
*aNewOffset = offset;
}
}
// Rule #2.5: ]</root>
- rv = aRange->SetEnd(mRootContent,
- static_cast<int32_t>(mRootContent->GetChildCount()));
+ rv = aRawRange->SetEnd(mRootContent, mRootContent->GetChildCount());
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
/* static */ LineBreakType
ContentEventHandler::GetLineBreakType(WidgetQueryContentEvent* aEvent)
@@ -1293,39 +1462,39 @@ static nsresult GetFrameForTextRect(nsIN
nsresult
ContentEventHandler::OnQuerySelectedText(WidgetQueryContentEvent* aEvent)
{
nsresult rv = Init(aEvent);
if (NS_FAILED(rv)) {
return rv;
}
- if (!mFirstSelectedRange) {
+ if (!mFirstSelectedRawRange.IsPositioned()) {
MOZ_ASSERT(aEvent->mInput.mSelectionType != SelectionType::eNormal);
MOZ_ASSERT(aEvent->mReply.mOffset == WidgetQueryContentEvent::NOT_FOUND);
MOZ_ASSERT(aEvent->mReply.mString.IsEmpty());
MOZ_ASSERT(!aEvent->mReply.mHasSelection);
aEvent->mSucceeded = true;
return NS_OK;
}
- nsINode* const startNode = mFirstSelectedRange->GetStartContainer();
- nsINode* const endNode = mFirstSelectedRange->GetEndContainer();
+ nsINode* const startNode = mFirstSelectedRawRange.GetStartContainer();
+ nsINode* const endNode = mFirstSelectedRawRange.GetEndContainer();
// Make sure the selection is within the root content range.
if (!nsContentUtils::ContentIsDescendantOf(startNode, mRootContent) ||
!nsContentUtils::ContentIsDescendantOf(endNode, mRootContent)) {
return NS_ERROR_NOT_AVAILABLE;
}
NS_ASSERTION(aEvent->mReply.mString.IsEmpty(),
"The reply string must be empty");
LineBreakType lineBreakType = GetLineBreakType(aEvent);
- rv = GetStartOffset(mFirstSelectedRange,
+ rv = GetStartOffset(mFirstSelectedRawRange,
&aEvent->mReply.mOffset, lineBreakType);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsINode> anchorNode, focusNode;
int32_t anchorOffset = 0, focusOffset = 0;
if (mSelection->RangeCount()) {
// If there is only one selection range, the anchor/focus node and offset
// are the information of the range. Therefore, we have the direction
@@ -1347,35 +1516,35 @@ ContentEventHandler::OnQuerySelectedText
aEvent->mReply.mReversed = compare > 0;
}
// However, if there are 2 or more selection ranges, we have no information
// of that.
else {
aEvent->mReply.mReversed = false;
}
- if (!mFirstSelectedRange->Collapsed()) {
- rv = GenerateFlatTextContent(mFirstSelectedRange, aEvent->mReply.mString,
- lineBreakType);
+ if (!mFirstSelectedRawRange.Collapsed()) {
+ rv = GenerateFlatTextContent(mFirstSelectedRawRange,
+ aEvent->mReply.mString, lineBreakType);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
} else {
aEvent->mReply.mString.Truncate();
}
} else {
- NS_ASSERTION(mFirstSelectedRange->Collapsed(),
- "When mSelection doesn't have selection, mFirstSelectedRange must be "
+ NS_ASSERTION(mFirstSelectedRawRange.Collapsed(),
+ "When mSelection doesn't have selection, mFirstSelectedRawRange must be "
"collapsed");
- anchorNode = focusNode = mFirstSelectedRange->GetStartContainer();
+ anchorNode = focusNode = mFirstSelectedRawRange.GetStartContainer();
if (NS_WARN_IF(!anchorNode)) {
return NS_ERROR_FAILURE;
}
anchorOffset = focusOffset =
- static_cast<int32_t>(mFirstSelectedRange->StartOffset());
+ static_cast<int32_t>(mFirstSelectedRawRange.StartOffset());
if (NS_WARN_IF(anchorOffset < 0)) {
return NS_ERROR_FAILURE;
}
aEvent->mReply.mReversed = false;
aEvent->mReply.mString.Truncate();
}
@@ -1400,28 +1569,28 @@ ContentEventHandler::OnQueryTextContent(
return rv;
}
NS_ASSERTION(aEvent->mReply.mString.IsEmpty(),
"The reply string must be empty");
LineBreakType lineBreakType = GetLineBreakType(aEvent);
- RefPtr<nsRange> range = new nsRange(mRootContent);
- rv = SetRangeFromFlatTextOffset(range, aEvent->mInput.mOffset,
- aEvent->mInput.mLength, lineBreakType, false,
- &aEvent->mReply.mOffset);
+ RawRange rawRange;
+ rv = SetRawRangeFromFlatTextOffset(&rawRange, aEvent->mInput.mOffset,
+ aEvent->mInput.mLength, lineBreakType,
+ false, &aEvent->mReply.mOffset);
NS_ENSURE_SUCCESS(rv, rv);
- rv = GenerateFlatTextContent(range, aEvent->mReply.mString, lineBreakType);
+ rv = GenerateFlatTextContent(rawRange, aEvent->mReply.mString, lineBreakType);
NS_ENSURE_SUCCESS(rv, rv);
if (aEvent->mWithFontRanges) {
uint32_t fontRangeLength;
- rv = GenerateFlatFontRanges(range, aEvent->mReply.mFontRanges,
+ rv = GenerateFlatFontRanges(rawRange, aEvent->mReply.mFontRanges,
fontRangeLength, lineBreakType);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
MOZ_ASSERT(fontRangeLength == aEvent->mReply.mString.Length(),
"Font ranges doesn't match the string");
}
@@ -1486,35 +1655,36 @@ ContentEventHandler::GetNodePositionHavi
static_cast<int32_t>(result.mNode->AsContent()->TextLength()) : 1;
}
NS_WARNING("aNodeOffset is invalid value");
return NodePosition();
}
ContentEventHandler::FrameAndNodeOffset
-ContentEventHandler::GetFirstFrameInRangeForTextRect(nsRange* aRange)
+ContentEventHandler::GetFirstFrameInRangeForTextRect(const RawRange& aRawRange)
{
NodePosition nodePosition;
nsCOMPtr<nsIContentIterator> iter = NS_NewPreContentIterator();
- for (iter->Init(aRange); !iter->IsDone(); iter->Next()) {
+ RefPtr<nsRange> range = aRawRange.CreateRange();
+ for (iter->Init(range); !iter->IsDone(); iter->Next()) {
nsINode* node = iter->GetCurrentNode();
if (NS_WARN_IF(!node)) {
break;
}
if (!node->IsContent()) {
continue;
}
if (node->IsNodeOfType(nsINode::eTEXT)) {
// If the range starts at the end of a text node, we need to find
// next node which causes text.
int32_t offsetInNode =
- node == aRange->GetStartContainer() ? aRange->StartOffset() : 0;
+ node == aRawRange.GetStartContainer() ? aRawRange.StartOffset() : 0;
if (static_cast<uint32_t>(offsetInNode) < node->Length()) {
nodePosition.mNode = node;
nodePosition.mOffset = offsetInNode;
break;
}
continue;
}
@@ -1533,47 +1703,48 @@ ContentEventHandler::GetFirstFrameInRang
nsIFrame* firstFrame = nullptr;
GetFrameForTextRect(nodePosition.mNode, nodePosition.mOffset,
true, &firstFrame);
return FrameAndNodeOffset(firstFrame, nodePosition.mOffset);
}
ContentEventHandler::FrameAndNodeOffset
-ContentEventHandler::GetLastFrameInRangeForTextRect(nsRange* aRange)
+ContentEventHandler::GetLastFrameInRangeForTextRect(const RawRange& aRawRange)
{
NodePosition nodePosition;
nsCOMPtr<nsIContentIterator> iter = NS_NewPreContentIterator();
- iter->Init(aRange);
-
- nsINode* endNode = aRange->GetEndContainer();
- uint32_t endOffset = static_cast<uint32_t>(aRange->EndOffset());
+ RefPtr<nsRange> range = aRawRange.CreateRange();
+ iter->Init(range);
+
+ nsINode* endNode = aRawRange.GetEndContainer();
+ uint32_t endOffset = aRawRange.EndOffset();
// If the end point is start of a text node or specified by its parent and
// index, the node shouldn't be included into the range. For example,
// with this case, |<p>abc[<br>]def</p>|, the range ends at 3rd children of
// <p> (see the range creation rules, "2.4. Cases: <element/>]"). This causes
// following frames:
// +----+-----+
// | abc|[<br>|
// +----+-----+
// +----+
// |]def|
// +----+
// So, if this method includes the 2nd text frame's rect to its result, the
// caller will return too tall rect which includes 2 lines in this case isn't
// expected by native IME (e.g., popup of IME will be positioned at bottom
// of "d" instead of right-bottom of "c"). Therefore, this method shouldn't
- // include the last frame when its content isn't really in aRange.
+ // include the last frame when its content isn't really in aRawRange.
nsINode* nextNodeOfRangeEnd = nullptr;
if (endNode->IsNodeOfType(nsINode::eTEXT)) {
- // Don't set nextNodeOfRangeEnd to the start node of aRange because if
+ // Don't set nextNodeOfRangeEnd to the start node of aRawRange because if
// endNode is same as start node of the range, the text node shouldn't be
// next of range end even if the offset is 0. This could occur with empty
// text node.
- if (!endOffset && aRange->GetStartContainer() != endNode) {
+ if (!endOffset && aRawRange.GetStartContainer() != endNode) {
nextNodeOfRangeEnd = endNode;
}
} else if (endOffset < endNode->GetChildCount()) {
nextNodeOfRangeEnd = endNode->GetChildAt(endOffset);
}
for (iter->Last(); !iter->IsDone(); iter->Prev()) {
nsINode* node = iter->GetCurrentNode();
@@ -1582,18 +1753,18 @@ ContentEventHandler::GetLastFrameInRange
}
if (!node->IsContent() || node == nextNodeOfRangeEnd) {
continue;
}
if (node->IsNodeOfType(nsINode::eTEXT)) {
nodePosition.mNode = node;
- if (node == aRange->GetEndContainer()) {
- nodePosition.mOffset = aRange->EndOffset();
+ if (node == aRawRange.GetEndContainer()) {
+ nodePosition.mOffset = aRawRange.EndOffset();
} else {
nodePosition.mOffset = node->Length();
}
// If the text node is empty or the last node of the range but the index
// is 0, we should store current position but continue looking for
// previous node (If there are no nodes before it, we should use current
// node position for returning its frame).
if (!nodePosition.mOffset) {
@@ -1809,44 +1980,44 @@ ContentEventHandler::OnQueryTextRectArra
nsresult rv = Init(aEvent);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
LineBreakType lineBreakType = GetLineBreakType(aEvent);
const uint32_t kBRLength = GetBRLength(lineBreakType);
- RefPtr<nsRange> range = new nsRange(mRootContent);
-
bool isVertical = false;
LayoutDeviceIntRect rect;
uint32_t offset = aEvent->mInput.mOffset;
const uint32_t kEndOffset = offset + aEvent->mInput.mLength;
bool wasLineBreaker = false;
// lastCharRect stores the last charRect value (see below for the detail of
// charRect).
nsRect lastCharRect;
// lastFrame is base frame of lastCharRect.
nsIFrame* lastFrame = nullptr;
while (offset < kEndOffset) {
nsCOMPtr<nsIContent> lastTextContent;
- rv = SetRangeFromFlatTextOffset(range, offset, 1, lineBreakType, true,
- nullptr, getter_AddRefs(lastTextContent));
+ RawRange rawRange;
+ rv = SetRawRangeFromFlatTextOffset(&rawRange, offset, 1, lineBreakType,
+ true, nullptr,
+ getter_AddRefs(lastTextContent));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// If the range is collapsed, offset has already reached the end of the
// contents.
- if (range->Collapsed()) {
+ if (rawRange.Collapsed()) {
break;
}
// Get the first frame which causes some text after the offset.
- FrameAndNodeOffset firstFrame = GetFirstFrameInRangeForTextRect(range);
+ FrameAndNodeOffset firstFrame = GetFirstFrameInRangeForTextRect(rawRange);
// If GetFirstFrameInRangeForTextRect() does not return valid frame, that
// means that there are no visible frames having text or the offset reached
// the end of contents.
if (!firstFrame.IsValid()) {
nsAutoString allText;
rv = GenerateFlatTextContent(mRootContent, allText, lineBreakType);
// If the offset doesn't reach the end of contents yet but there is no
@@ -1894,27 +2065,27 @@ ContentEventHandler::OnQueryTextRectArra
return NS_ERROR_UNEXPECTED;
}
if (kBRLength > 1 && chars[0] == '\n' &&
offset == aEvent->mInput.mOffset && offset) {
// If start of range starting from previous offset of query range is
// same as the start of query range, the query range starts from
// between a line breaker (i.e., the range starts between "\r" and
// "\n").
- RefPtr<nsRange> rangeToPrevOffset = new nsRange(mRootContent);
- rv = SetRangeFromFlatTextOffset(rangeToPrevOffset,
- aEvent->mInput.mOffset - 1, 1,
- lineBreakType, true, nullptr);
+ RawRange rawRangeToPrevOffset;
+ rv = SetRawRangeFromFlatTextOffset(&rawRangeToPrevOffset,
+ aEvent->mInput.mOffset - 1, 1,
+ lineBreakType, true, nullptr);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
startsBetweenLineBreaker =
- range->GetStartContainer() ==
- rangeToPrevOffset->GetStartContainer() &&
- range->StartOffset() == rangeToPrevOffset->StartOffset();
+ rawRange.GetStartContainer() ==
+ rawRangeToPrevOffset.GetStartContainer() &&
+ rawRange.StartOffset() == rawRangeToPrevOffset.StartOffset();
}
}
// Other contents should cause a line breaker rect before it.
// Note that moz-<br> element does not cause any text, however,
// it represents empty line at the last of current block. Therefore,
// we need to compute its rect too.
else if (ShouldBreakLineBefore(firstContent, mRootContent) ||
IsMozBR(firstContent)) {
@@ -1997,23 +2168,24 @@ ContentEventHandler::OnQueryTextRectArra
}
charRects.AppendElement(brRect);
chars.AssignLiteral("\n");
if (kBRLength > 1 && offset == aEvent->mInput.mOffset && offset) {
// If the first frame for the previous offset of the query range and
// the first frame for the start of query range are same, that means
// the start offset is between the first line breaker (i.e., the range
// starts between "\r" and "\n").
- rv = SetRangeFromFlatTextOffset(range, aEvent->mInput.mOffset - 1, 1,
- lineBreakType, true, nullptr);
+ rv = SetRawRangeFromFlatTextOffset(&rawRange,
+ aEvent->mInput.mOffset - 1, 1,
+ lineBreakType, true, nullptr);
if (NS_WARN_IF(NS_FAILED(rv))) {
return NS_ERROR_UNEXPECTED;
}
FrameAndNodeOffset frameForPrevious =
- GetFirstFrameInRangeForTextRect(range);
+ GetFirstFrameInRangeForTextRect(rawRange);
startsBetweenLineBreaker = frameForPrevious.mFrame == firstFrame.mFrame;
}
} else {
NS_WARNING("The frame is neither a text frame nor a frame whose content "
"causes a line break");
return NS_ERROR_FAILURE;
}
@@ -2132,32 +2304,33 @@ ContentEventHandler::OnQueryTextRect(Wid
// If mLength is 0 (this may be caused by bug of native IME), we should
// redirect this event to OnQueryCaretRect().
if (!aEvent->mInput.mLength) {
return OnQueryCaretRect(aEvent);
}
LineBreakType lineBreakType = GetLineBreakType(aEvent);
- RefPtr<nsRange> range = new nsRange(mRootContent);
+ RawRange rawRange;
nsCOMPtr<nsIContent> lastTextContent;
- rv = SetRangeFromFlatTextOffset(range, aEvent->mInput.mOffset,
- aEvent->mInput.mLength, lineBreakType, true,
- &aEvent->mReply.mOffset,
- getter_AddRefs(lastTextContent));
+ rv = SetRawRangeFromFlatTextOffset(&rawRange, aEvent->mInput.mOffset,
+ aEvent->mInput.mLength, lineBreakType,
+ true, &aEvent->mReply.mOffset,
+ getter_AddRefs(lastTextContent));
NS_ENSURE_SUCCESS(rv, rv);
- rv = GenerateFlatTextContent(range, aEvent->mReply.mString, lineBreakType);
+ rv = GenerateFlatTextContent(rawRange, aEvent->mReply.mString, lineBreakType);
NS_ENSURE_SUCCESS(rv, rv);
// used to iterate over all contents and their frames
nsCOMPtr<nsIContentIterator> iter = NS_NewContentIterator();
+ RefPtr<nsRange> range = rawRange.CreateRange();
iter->Init(range);
// Get the first frame which causes some text after the offset.
- FrameAndNodeOffset firstFrame = GetFirstFrameInRangeForTextRect(range);
+ FrameAndNodeOffset firstFrame = GetFirstFrameInRangeForTextRect(rawRange);
// If GetFirstFrameInRangeForTextRect() does not return valid frame, that
// means that there are no visible frames having text or the offset reached
// the end of contents.
if (!firstFrame.IsValid()) {
nsAutoString allText;
rv = GenerateFlatTextContent(mRootContent, allText, lineBreakType);
// If the offset doesn't reach the end of contents but there is no frames
@@ -2166,23 +2339,22 @@ ContentEventHandler::OnQueryTextRect(Wid
// node's last character's rect, but it's not usual cases in actual web
// services. Therefore, currently, we should make this case fail.
if (NS_WARN_IF(NS_FAILED(rv)) ||
static_cast<uint32_t>(aEvent->mInput.mOffset) < allText.Length()) {
return NS_ERROR_FAILURE;
}
// Look for the last frame which should be included text rects.
- IgnoredErrorResult erv;
- range->SelectNodeContents(*mRootContent, erv);
- if (NS_WARN_IF(erv.Failed())) {
+ rv = rawRange.SelectNodeContents(mRootContent);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
return NS_ERROR_UNEXPECTED;
}
nsRect rect;
- FrameAndNodeOffset lastFrame = GetLastFrameInRangeForTextRect(range);
+ FrameAndNodeOffset lastFrame = GetLastFrameInRangeForTextRect(rawRange);
// If there is at least one frame which can be used for computing a rect
// for a character or a line breaker, we should use it for guessing the
// caret rect at the end of the contents.
if (lastFrame) {
if (NS_WARN_IF(!lastFrame->GetContent())) {
return NS_ERROR_FAILURE;
}
FrameRelativeRect relativeRect;
@@ -2316,17 +2488,17 @@ ContentEventHandler::OnQueryTextRect(Wid
}
frameRect = rect;
}
// UnionRect() requires non-empty rect. So, let's make sure to get non-emtpy
// rect from the first frame.
EnsureNonEmptyRect(rect);
// Get the last frame which causes some text in the range.
- FrameAndNodeOffset lastFrame = GetLastFrameInRangeForTextRect(range);
+ FrameAndNodeOffset lastFrame = GetLastFrameInRangeForTextRect(rawRange);
if (NS_WARN_IF(!lastFrame.IsValid())) {
return NS_ERROR_FAILURE;
}
// iterate over all covered frames
for (nsIFrame* frame = firstFrame; frame != lastFrame;) {
frame = frame->GetNextContinuation();
if (!frame) {
@@ -2445,17 +2617,17 @@ ContentEventHandler::OnQueryCaretRect(Wi
// When the selection is collapsed and the queried offset is current caret
// position, we should return the "real" caret rect.
if (mSelection->IsCollapsed()) {
nsRect caretRect;
nsIFrame* caretFrame = nsCaret::GetGeometry(mSelection, &caretRect);
if (caretFrame) {
uint32_t offset;
- rv = GetStartOffset(mFirstSelectedRange,
+ rv = GetStartOffset(mFirstSelectedRawRange,
&offset, GetLineBreakType(aEvent));
NS_ENSURE_SUCCESS(rv, rv);
if (offset == aEvent->mInput.mOffset) {
rv = ConvertToRootRelativeOffset(caretFrame, caretRect);
NS_ENSURE_SUCCESS(rv, rv);
nscoord appUnitsPerDevPixel =
caretFrame->PresContext()->AppUnitsPerDevPixel();
aEvent->mReply.mRect = LayoutDeviceIntRect::FromUnknownRect(
@@ -2729,18 +2901,19 @@ ContentEventHandler::GetFlatTextLengthIn
endPosition.mNode->GetChildCount(),
"When the node is being removed, the end offset should be child count");
iter = NS_NewPreContentIterator();
nsresult rv = iter->Init(aStartPosition.mNode);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
} else {
- RefPtr<nsRange> prev = new nsRange(aRootContent);
- nsresult rv = aStartPosition.SetToRangeStart(prev);
+ RawRange prevRawRange;
+ nsresult rv =
+ prevRawRange.SetStart(aStartPosition.mNode, aStartPosition.mOffset);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// When the end position is immediately after non-root element's open tag,
// we need to include a line break caused by the open tag.
if (endPosition.mNode != aRootContent &&
endPosition.IsImmediatelyAfterOpenTag()) {
@@ -2763,33 +2936,35 @@ ContentEventHandler::GetFlatTextLengthIn
return NS_ERROR_FAILURE;
}
endPosition = NodePositionBefore(parentContent, indexInParent + 1);
}
}
if (endPosition.OffsetIsValid()) {
// Offset is within node's length; set end of range to that offset
- rv = endPosition.SetToRangeEnd(prev);
+ rv = prevRawRange.SetEnd(endPosition.mNode, endPosition.mOffset);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
iter = NS_NewPreContentIterator();
- rv = iter->Init(prev);
+ RefPtr<nsRange> prevRange = prevRawRange.CreateRange();
+ rv = iter->Init(prevRange);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
} else if (endPosition.mNode != aRootContent) {
// Offset is past node's length; set end of range to end of node
- rv = endPosition.SetToRangeEndAfter(prev);
+ rv = prevRawRange.SetEndAfter(endPosition.mNode);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
iter = NS_NewPreContentIterator();
- rv = iter->Init(prev);
+ RefPtr<nsRange> prevRange = prevRawRange.CreateRange();
+ rv = iter->Init(prevRange);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
} else {
// Offset is past the root node; set end of range to end of root node
iter = NS_NewPreContentIterator();
rv = iter->Init(aRootContent);
if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -2830,68 +3005,66 @@ ContentEventHandler::GetFlatTextLengthIn
}
*aLength += GetBRLength(aLineBreakType);
}
}
return NS_OK;
}
nsresult
-ContentEventHandler::GetStartOffset(nsRange* aRange,
+ContentEventHandler::GetStartOffset(const RawRange& aRawRange,
uint32_t* aOffset,
LineBreakType aLineBreakType)
{
- MOZ_ASSERT(aRange);
// To match the "no skip start" hack in nsContentIterator::Init, when range
// offset is 0 and the range node is not a container, we have to assume the
// range _includes_ the node, which means the start offset should _not_
// include the node.
//
// For example, for this content: <br>abc, and range (<br>, 0)-("abc", 1), the
// range includes the linebreak from <br>, so the start offset should _not_
// include <br>, and the start offset should be 0.
//
// However, for this content: <p/>abc, and range (<p>, 0)-("abc", 1), the
// range does _not_ include the linebreak from <p> because <p> is a container,
// so the start offset _should_ include <p>, and the start offset should be 1.
- nsINode* startNode = aRange->GetStartContainer();
+ nsINode* startNode = aRawRange.GetStartContainer();
bool startIsContainer = true;
if (startNode->IsHTMLElement()) {
if (nsIParserService* ps = nsContentUtils::GetParserService()) {
nsIAtom* name = startNode->NodeInfo()->NameAtom();
ps->IsContainer(ps->HTMLAtomTagToId(name), startIsContainer);
}
}
const NodePosition& startPos =
- startIsContainer
- ? NodePosition(startNode, aRange->StartOffset())
- : NodePositionBefore(startNode, aRange->StartOffset());
+ startIsContainer ?
+ NodePosition(startNode, aRawRange.StartOffset()) :
+ NodePositionBefore(startNode, aRawRange.StartOffset());
return GetFlatTextLengthInRange(
NodePosition(mRootContent, 0), startPos,
mRootContent, aOffset, aLineBreakType);
}
nsresult
-ContentEventHandler::AdjustCollapsedRangeMaybeIntoTextNode(nsRange* aRange)
+ContentEventHandler::AdjustCollapsedRangeMaybeIntoTextNode(RawRange& aRawRange)
{
- MOZ_ASSERT(aRange);
- MOZ_ASSERT(aRange->Collapsed());
-
- if (!aRange || !aRange->Collapsed()) {
+ MOZ_ASSERT(aRawRange.Collapsed());
+
+ if (!aRawRange.Collapsed()) {
return NS_ERROR_INVALID_ARG;
}
- nsCOMPtr<nsINode> container = aRange->GetStartContainer();
- int32_t offsetInParentNode = aRange->StartOffset();
+ nsCOMPtr<nsINode> container = aRawRange.GetStartContainer();
+ int32_t offsetInParentNode = aRawRange.StartOffset();
if (NS_WARN_IF(!container) || NS_WARN_IF(offsetInParentNode < 0)) {
return NS_ERROR_INVALID_ARG;
}
- // If the node is text node, we don't need to modify aRange.
+ // If the node is text node, we don't need to modify aRawRange.
if (container->IsNodeOfType(nsINode::eTEXT)) {
return NS_OK;
}
// If the container is not a text node but it has a text node at the offset,
// we should adjust the range into the text node.
// NOTE: This is emulating similar situation of EditorBase.
nsINode* childNode = nullptr;
@@ -2910,41 +3083,39 @@ ContentEventHandler::AdjustCollapsedRang
}
// But if the found node isn't a text node, we cannot modify the range.
if (!childNode || !childNode->IsNodeOfType(nsINode::eTEXT) ||
NS_WARN_IF(offsetInChildNode < 0)) {
return NS_OK;
}
- nsresult rv = aRange->CollapseTo(childNode, offsetInChildNode);
+ nsresult rv = aRawRange.CollapseTo(childNode, offsetInChildNode);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
nsresult
-ContentEventHandler::GetStartFrameAndOffset(const nsRange* aRange,
+ContentEventHandler::GetStartFrameAndOffset(const RawRange& aRawRange,
nsIFrame*& aFrame,
int32_t& aOffsetInFrame)
{
- MOZ_ASSERT(aRange);
-
aFrame = nullptr;
aOffsetInFrame = -1;
- nsINode* node = aRange->GetStartContainer();
+ nsINode* node = aRawRange.GetStartContainer();
if (NS_WARN_IF(!node) ||
NS_WARN_IF(!node->IsNodeOfType(nsINode::eCONTENT))) {
return NS_ERROR_FAILURE;
}
nsIContent* content = static_cast<nsIContent*>(node);
RefPtr<nsFrameSelection> fs = mPresShell->FrameSelection();
- aFrame = fs->GetFrameForNodeOffset(content, aRange->StartOffset(),
+ aFrame = fs->GetFrameForNodeOffset(content, aRawRange.StartOffset(),
fs->GetHint(), &aOffsetInFrame);
if (NS_WARN_IF(!aFrame)) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
nsresult
@@ -3028,26 +3199,27 @@ ContentEventHandler::OnSelectionEvent(Wi
if (rv != NS_ERROR_NOT_AVAILABLE) {
NS_ENSURE_SUCCESS(rv, rv);
} else {
rv = Init(aEvent);
NS_ENSURE_SUCCESS(rv, rv);
}
// Get range from offset and length
- RefPtr<nsRange> range = new nsRange(mRootContent);
- rv = SetRangeFromFlatTextOffset(range, aEvent->mOffset, aEvent->mLength,
- GetLineBreakType(aEvent),
- aEvent->mExpandToClusterBoundary);
+ RawRange rawRange;
+ rv = SetRawRangeFromFlatTextOffset(&rawRange,
+ aEvent->mOffset, aEvent->mLength,
+ GetLineBreakType(aEvent),
+ aEvent->mExpandToClusterBoundary);
NS_ENSURE_SUCCESS(rv, rv);
- nsINode* startNode = range->GetStartContainer();
- nsINode* endNode = range->GetEndContainer();
- int32_t startNodeOffset = range->StartOffset();
- int32_t endNodeOffset = range->EndOffset();
+ nsINode* startNode = rawRange.GetStartContainer();
+ nsINode* endNode = rawRange.GetEndContainer();
+ int32_t startNodeOffset = rawRange.StartOffset();
+ int32_t endNodeOffset = rawRange.EndOffset();
AdjustRangeForSelection(mRootContent, &startNode, &startNodeOffset);
AdjustRangeForSelection(mRootContent, &endNode, &endNodeOffset);
if (NS_WARN_IF(!startNode) || NS_WARN_IF(!endNode) ||
NS_WARN_IF(startNodeOffset < 0) || NS_WARN_IF(endNodeOffset < 0)) {
return NS_ERROR_UNEXPECTED;
}
mSelection->StartBatchChanges();
--- a/dom/events/ContentEventHandler.h
+++ b/dom/events/ContentEventHandler.h
@@ -8,19 +8,19 @@
#define mozilla_ContentEventHandler_h_
#include "mozilla/EventForwards.h"
#include "mozilla/dom/Selection.h"
#include "nsCOMPtr.h"
#include "nsIFrame.h"
#include "nsINode.h"
#include "nsISelectionController.h"
-#include "nsRange.h"
class nsPresContext;
+class nsRange;
struct nsRect;
namespace mozilla {
enum LineBreakType
{
LINE_BREAK_TYPE_NATIVE,
@@ -32,16 +32,79 @@ enum LineBreakType
* ContentEventHandler is a helper class for EventStateManager.
* The platforms request some content informations, e.g., the selected text,
* the offset of the selected text and the text for specified range.
* This class answers to NS_QUERY_* events from actual contents.
*/
class MOZ_STACK_CLASS ContentEventHandler
{
+private:
+ /**
+ * RawRange is a helper class of ContentEventHandler class. The caller is
+ * responsible for making sure the start/end nodes are in document order.
+ * This is enforced by assertions in DEBUG builds.
+ */
+ class MOZ_STACK_CLASS RawRange final
+ {
+ public:
+ RawRange()
+ : mStartOffset(0)
+ , mEndOffset(0)
+ {
+ }
+
+ void Clear()
+ {
+ mRoot = mStartContainer = mEndContainer = nullptr;
+ mStartOffset = mEndOffset = 0;
+ }
+
+ bool IsPositioned() const
+ {
+ return mStartContainer && mEndContainer;
+ }
+ bool Collapsed() const
+ {
+ return mStartContainer == mEndContainer &&
+ mStartOffset == mEndOffset &&
+ IsPositioned();
+ }
+ nsINode* GetStartContainer() const { return mStartContainer; }
+ nsINode* GetEndContainer() const { return mEndContainer; }
+ uint32_t StartOffset() const { return mStartOffset; }
+ uint32_t EndOffset() const { return mEndOffset; }
+
+ nsresult CollapseTo(nsINode* aContainer, uint32_t aOffset)
+ {
+ return SetStartAndEnd(aContainer, aOffset, aContainer, aOffset);
+ }
+ nsresult SetStart(nsINode* aStartContainer, uint32_t aStartOffset);
+ nsresult SetEnd(nsINode* aEndContainer, uint32_t aEndOffset);
+ nsresult SetEndAfter(nsINode* aEndContainer);
+ void SetStartAndEnd(const nsRange* aRange);
+ nsresult SetStartAndEnd(nsINode* aStartContainer, uint32_t aStartOffset,
+ nsINode* aEndContainer, uint32_t aEndOffset);
+
+ nsresult SelectNodeContents(nsINode* aNodeToSelectContents);
+
+ already_AddRefed<nsRange> CreateRange() const;
+
+ private:
+ bool IsValidOffset(nsINode* aContainer, uint32_t aOffset) const;
+ nsINode* IsValidBoundary(nsINode* aNode) const;
+ inline void AssertStartIsBeforeOrEqualToEnd();
+
+ nsCOMPtr<nsINode> mRoot;
+ nsCOMPtr<nsINode> mStartContainer;
+ nsCOMPtr<nsINode> mEndContainer;
+ uint32_t mStartOffset;
+ uint32_t mEndOffset;
+ };
+
public:
typedef dom::Selection Selection;
explicit ContentEventHandler(nsPresContext* aPresContext);
// Handle aEvent in the current process.
nsresult HandleQueryContentEvent(WidgetQueryContentEvent* aEvent);
@@ -71,21 +134,19 @@ public:
protected:
nsPresContext* mPresContext;
nsCOMPtr<nsIPresShell> mPresShell;
// mSelection is typically normal selection but if OnQuerySelectedText()
// is called, i.e., handling eQuerySelectedText, it's the specified selection
// by WidgetQueryContentEvent::mInput::mSelectionType.
RefPtr<Selection> mSelection;
- // mFirstSelectedRange is the first selected range of mSelection. If
- // mSelection is normal selection, this must not be nullptr if Init()
- // succeed. Otherwise, this may be nullptr if there are no selection
- // ranges.
- RefPtr<nsRange> mFirstSelectedRange;
+ // mFirstSelectedRawRange is initialized from the first range of mSelection,
+ // if it exists. Otherwise, it is reset by Clear().
+ RawRange mFirstSelectedRawRange;
nsCOMPtr<nsIContent> mRootContent;
nsresult Init(WidgetQueryContentEvent* aEvent);
nsresult Init(WidgetSelectionEvent* aEvent);
nsresult InitBasic();
nsresult InitCommon(SelectionType aSelectionType = SelectionType::eNormal);
/**
@@ -159,43 +220,16 @@ public:
bool IsBeforeOpenTag() const
{
return IsValid() && mNode->IsElement() && !mOffset && !mAfterOpenTag;
}
bool IsImmediatelyAfterOpenTag() const
{
return IsValid() && mNode->IsElement() && !mOffset && mAfterOpenTag;
}
- nsresult SetToRangeStart(nsRange* aRange) const
- {
- if (!IsValid()) {
- return NS_ERROR_FAILURE;
- }
- ErrorResult errorResult;
- aRange->SetStart(*mNode, mOffset, errorResult);
- return errorResult.StealNSResult();
- }
- nsresult SetToRangeEnd(nsRange* aRange) const
- {
- if (!IsValid()) {
- return NS_ERROR_FAILURE;
- }
- ErrorResult errorResult;
- aRange->SetEnd(*mNode, mOffset, errorResult);
- return errorResult.StealNSResult();
- }
- nsresult SetToRangeEndAfter(nsRange* aRange) const
- {
- if (!IsValid()) {
- return NS_ERROR_FAILURE;
- }
- ErrorResult errorResult;
- aRange->SetEndAfter(*mNode, errorResult);
- return errorResult.StealNSResult();
- }
};
// NodePositionBefore isn't good name if mNode isn't an element node nor
// mOffset is not 0, though, when mNode is an element node and mOffset is 0,
// this is treated as before the open tag of mNode.
struct NodePositionBefore final : public NodePosition
{
NodePositionBefore(nsINode* aNode, int32_t aOffset)
@@ -256,23 +290,23 @@ protected:
// text. E.g., specifying mRootContent gets whole text in it.
// Note that the result is not same as .textContent. The result is
// optimized for native IMEs. For example, <br> element and some block
// elements causes "\n" (or "\r\n"), see also ShouldBreakLineBefore().
nsresult GenerateFlatTextContent(nsIContent* aContent,
nsString& aString,
LineBreakType aLineBreakType);
// Get the contents of aRange as plain text.
- nsresult GenerateFlatTextContent(nsRange* aRange,
+ nsresult GenerateFlatTextContent(const RawRange& aRawRange,
nsString& aString,
LineBreakType aLineBreakType);
// Get offset of start of aRange. Note that the result includes the length
// of line breaker caused by the start of aContent because aRange never
// includes the line breaker caused by its start node.
- nsresult GetStartOffset(nsRange* aRange,
+ nsresult GetStartOffset(const RawRange& aRawRange,
uint32_t* aOffset,
LineBreakType aLineBreakType);
// Check if we should insert a line break before aContent.
// This should return false only when aContent is an html element which
// is typically used in a paragraph like <em>.
static bool ShouldBreakLineBefore(nsIContent* aContent,
nsINode* aRootNode);
// Get the line breaker length.
@@ -282,54 +316,54 @@ protected:
static LineBreakType GetLineBreakType(bool aUseNativeLineBreak);
// Returns focused content (including its descendant documents).
nsIContent* GetFocusedContent();
// Returns true if the content is a plugin host.
bool IsPlugin(nsIContent* aContent);
// QueryContentRect() sets the rect of aContent's frame(s) to aEvent.
nsresult QueryContentRect(nsIContent* aContent,
WidgetQueryContentEvent* aEvent);
- // Make the DOM range from the offset of FlatText and the text length.
+ // Initialize aRawRange from the offset of FlatText and the text length.
// If aExpandToClusterBoundaries is true, the start offset and the end one are
// expanded to nearest cluster boundaries.
- nsresult SetRangeFromFlatTextOffset(nsRange* aRange,
- uint32_t aOffset,
- uint32_t aLength,
- LineBreakType aLineBreakType,
- bool aExpandToClusterBoundaries,
- uint32_t* aNewOffset = nullptr,
- nsIContent** aLastTextNode = nullptr);
- // If the aRange isn't in text node but next to a text node, this method
- // modifies it in the text node. Otherwise, not modified.
- nsresult AdjustCollapsedRangeMaybeIntoTextNode(nsRange* aCollapsedRange);
+ nsresult SetRawRangeFromFlatTextOffset(RawRange* aRawRange,
+ uint32_t aOffset,
+ uint32_t aLength,
+ LineBreakType aLineBreakType,
+ bool aExpandToClusterBoundaries,
+ uint32_t* aNewOffset = nullptr,
+ nsIContent** aLastTextNode = nullptr);
+ // If the aCollapsedRawRange isn't in text node but next to a text node,
+ // this method modifies it in the text node. Otherwise, not modified.
+ nsresult AdjustCollapsedRangeMaybeIntoTextNode(RawRange& aCollapsedRawRange);
// Find the first frame for the range and get the start offset in it.
- nsresult GetStartFrameAndOffset(const nsRange* aRange,
+ nsresult GetStartFrameAndOffset(const RawRange& aRawRange,
nsIFrame*& aFrame,
int32_t& aOffsetInFrame);
// Convert the frame relative offset to be relative to the root frame of the
// root presContext (but still measured in appUnits of aFrame's presContext).
nsresult ConvertToRootRelativeOffset(nsIFrame* aFrame,
nsRect& aRect);
// Expand aXPOffset to the nearest offset in cluster boundary. aForward is
// true, it is expanded to forward.
nsresult ExpandToClusterBoundary(nsIContent* aContent, bool aForward,
uint32_t* aXPOffset);
typedef nsTArray<mozilla::FontRange> FontRangeArray;
static void AppendFontRanges(FontRangeArray& aFontRanges,
nsIContent* aContent,
- int32_t aBaseOffset,
- int32_t aXPStartOffset,
- int32_t aXPEndOffset,
+ uint32_t aBaseOffset,
+ uint32_t aXPStartOffset,
+ uint32_t aXPEndOffset,
LineBreakType aLineBreakType);
- nsresult GenerateFlatFontRanges(nsRange* aRange,
+ nsresult GenerateFlatFontRanges(const RawRange& aRawRange,
FontRangeArray& aFontRanges,
uint32_t& aLength,
LineBreakType aLineBreakType);
- nsresult QueryTextRectByRange(nsRange* aRange,
+ nsresult QueryTextRectByRange(const RawRange& aRawRange,
LayoutDeviceIntRect& aRect,
WritingMode& aWritingMode);
// Returns a node and position in the node for computing text rect.
NodePosition GetNodePositionHavingFlatText(const NodePosition& aNodePosition);
NodePosition GetNodePositionHavingFlatText(nsINode* aNode,
int32_t aNodeOffset);
@@ -361,23 +395,23 @@ protected:
operator nsIFrame*() { return mFrame; }
operator const nsIFrame*() const { return mFrame; }
bool IsValid() const { return mFrame && mOffsetInNode >= 0; }
};
// Get first frame after the start of the given range for computing text rect.
// This returns invalid FrameAndNodeOffset if there is no content which
// should affect to computing text rect in the range. mOffsetInNode is start
// offset in the frame.
- FrameAndNodeOffset GetFirstFrameInRangeForTextRect(nsRange* aRange);
+ FrameAndNodeOffset GetFirstFrameInRangeForTextRect(const RawRange& aRawRange);
// Get last frame before the end of the given range for computing text rect.
// This returns invalid FrameAndNodeOffset if there is no content which
// should affect to computing text rect in the range. mOffsetInNode is end
// offset in the frame.
- FrameAndNodeOffset GetLastFrameInRangeForTextRect(nsRange* aRange);
+ FrameAndNodeOffset GetLastFrameInRangeForTextRect(const RawRange& aRawRange);
struct MOZ_STACK_CLASS FrameRelativeRect final
{
// mRect is relative to the mBaseFrame's position.
nsRect mRect;
nsIFrame* mBaseFrame;
FrameRelativeRect()