Bug 1375502 - part1: ContentEventHandler shouldn't use nsRange for temporary use r=mats

Allocating and initializing nsRange is too expensive especially for temporary
use.  However, ContentEventHandler uses nsRange only for representing two DOM
points.  So, it should use simpler helper class, RawRange, for reducing some
unnecessary runtime cost.

Note that this still uses nsRange for initializing nsIContentIterator.  This
will be fixed by the following patch.

MozReview-Commit-ID: 5TUy6yJf7HA

--HG--
extra : rebase_source : c4eb58e8f37c408c75479e6961ba9225f8bcee77
This commit is contained in:
Masayuki Nakano 2017-06-23 13:21:47 +09:00
Родитель a74cac68c6
Коммит acde25fb18
4 изменённых файлов: 439 добавлений и 214 удалений

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

@ -1216,8 +1216,9 @@ nsRange::IsValidOffset(nsINode* aNode, uint32_t aOffset)
static_cast<size_t>(aOffset) <= aNode->Length();
}
/* static */
nsINode*
nsRange::IsValidBoundary(nsINode* aNode)
nsRange::ComputeRootNode(nsINode* aNode, bool aMaySpanAnonymousSubtrees)
{
if (!aNode) {
return nullptr;
@ -1230,7 +1231,7 @@ nsRange::IsValidBoundary(nsINode* aNode)
nsIContent* content = static_cast<nsIContent*>(aNode);
if (!mMaySpanAnonymousSubtrees) {
if (!aMaySpanAnonymousSubtrees) {
// If the node is in a shadow tree then the ShadowRoot is the root.
ShadowRoot* containingShadow = content->GetContainingShadow();
if (containingShadow) {

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

@ -316,6 +316,18 @@ private:
nsINode** aFarthestAncestor);
public:
/**
* Compute the root node of aNode for initializing range classes.
* When aNode is in an anonymous subtree, this returns the shadow root or
* binding parent. Otherwise, the root node of the document or document
* fragment. If this returns nullptr, that means aNode can be neither the
* start container nor end container of any range.
*/
static nsINode* ComputeRootNode(nsINode* aNode)
{
return ComputeRootNode(aNode, false);
}
/******************************************************************************
* Utility routine to detect if a content node starts before a range and/or
* ends after a range. If neither it is contained inside the range.
@ -368,7 +380,10 @@ public:
protected:
void RegisterCommonAncestor(nsINode* aNode);
void UnregisterCommonAncestor(nsINode* aNode);
nsINode* IsValidBoundary(nsINode* aNode);
nsINode* IsValidBoundary(nsINode* aNode) const
{
return ComputeRootNode(aNode, mMaySpanAnonymousSubtrees);
}
/**
* XXX nsRange should accept 0 - UINT32_MAX as offset. However, users of
@ -382,6 +397,9 @@ protected:
}
static bool IsValidOffset(nsINode* aNode, uint32_t aOffset);
static nsINode* ComputeRootNode(nsINode* aNode,
bool aMaySpanAnonymousSubtrees);
// CharacterDataChanged set aNotInsertedYet to true to disable an assertion
// and suppress re-registering a range common ancestor node since
// the new text node of a splitText hasn't been inserted yet.

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

@ -39,6 +39,176 @@ 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 */
/******************************************************************/
@ -105,9 +275,6 @@ using namespace widget;
ContentEventHandler::ContentEventHandler(nsPresContext* aPresContext)
: mPresContext(aPresContext)
, mPresShell(aPresContext->GetPresShell())
, mSelection(nullptr)
, mFirstSelectedRange(nullptr)
, mRootContent(nullptr)
{
}
@ -195,8 +362,8 @@ ContentEventHandler::InitCommon(SelectionType aSelectionType)
}
mSelection = nullptr;
mFirstSelectedRange = nullptr;
mRootContent = nullptr;
mFirstSelectedRawRange.Clear();
nsresult rv = InitBasic();
NS_ENSURE_SUCCESS(rv, rv);
@ -245,25 +412,21 @@ ContentEventHandler::InitCommon(SelectionType aSelectionType)
}
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;
@ -309,7 +472,8 @@ ContentEventHandler::Init(WidgetQueryContentEvent* aEvent)
} 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;
}
@ -670,42 +834,42 @@ ContentEventHandler::GenerateFlatTextContent(nsIContent* aContent,
{
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;
}
@ -721,10 +885,10 @@ ContentEventHandler::GenerateFlatTextContent(nsRange* aRange,
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);
}
@ -762,9 +926,9 @@ ContentEventHandler::GetTextLengthInRange(nsIContent* aContent,
/* 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));
@ -776,7 +940,7 @@ ContentEventHandler::AppendFontRanges(FontRangeArray& aFontRanges,
return;
}
int32_t baseOffset = aBaseOffset;
uint32_t baseOffset = aBaseOffset;
#ifdef DEBUG
{
nsTextFrame* text = do_QueryFrame(frame);
@ -785,8 +949,10 @@ ContentEventHandler::AppendFontRanges(FontRangeArray& aFontRanges,
#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;
@ -799,7 +965,8 @@ ContentEventHandler::AppendFontRanges(FontRangeArray& aFontRanges,
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;
}
@ -808,10 +975,10 @@ ContentEventHandler::AppendFontRanges(FontRangeArray& aFontRanges,
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.
@ -833,7 +1000,7 @@ ContentEventHandler::AppendFontRanges(FontRangeArray& aFontRanges,
// 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,
@ -853,19 +1020,19 @@ ContentEventHandler::AppendFontRanges(FontRangeArray& aFontRanges,
}
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;
}
@ -873,7 +1040,8 @@ ContentEventHandler::GenerateFlatFontRanges(nsRange* aRange,
// 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;
}
@ -888,9 +1056,9 @@ ContentEventHandler::GenerateFlatFontRanges(nsRange* aRange,
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,
@ -979,13 +1147,14 @@ ContentEventHandler::ExpandToClusterBoundary(nsIContent* aContent,
}
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;
@ -996,7 +1165,7 @@ ContentEventHandler::SetRangeFromFlatTextOffset(nsRange* aRange,
// 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;
}
@ -1091,14 +1260,16 @@ ContentEventHandler::SetRangeFromFlatTextOffset(nsRange* aRange,
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;
}
@ -1137,7 +1308,7 @@ ContentEventHandler::SetRangeFromFlatTextOffset(nsRange* aRange,
}
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;
}
@ -1157,7 +1328,7 @@ ContentEventHandler::SetRangeFromFlatTextOffset(nsRange* aRange,
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;
}
@ -1174,7 +1345,7 @@ ContentEventHandler::SetRangeFromFlatTextOffset(nsRange* aRange,
// 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;
}
@ -1192,12 +1363,12 @@ ContentEventHandler::SetRangeFromFlatTextOffset(nsRange* aRange,
// 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;
}
@ -1205,8 +1376,7 @@ ContentEventHandler::SetRangeFromFlatTextOffset(nsRange* aRange,
}
} 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;
}
@ -1216,8 +1386,7 @@ ContentEventHandler::SetRangeFromFlatTextOffset(nsRange* aRange,
}
}
// 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;
}
@ -1298,7 +1467,7 @@ ContentEventHandler::OnQuerySelectedText(WidgetQueryContentEvent* aEvent)
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());
@ -1307,8 +1476,8 @@ ContentEventHandler::OnQuerySelectedText(WidgetQueryContentEvent* aEvent)
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) ||
@ -1320,7 +1489,7 @@ ContentEventHandler::OnQuerySelectedText(WidgetQueryContentEvent* aEvent)
"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);
@ -1352,9 +1521,9 @@ ContentEventHandler::OnQuerySelectedText(WidgetQueryContentEvent* aEvent)
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;
}
@ -1362,15 +1531,15 @@ ContentEventHandler::OnQuerySelectedText(WidgetQueryContentEvent* aEvent)
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;
}
@ -1405,18 +1574,18 @@ ContentEventHandler::OnQueryTextContent(WidgetQueryContentEvent* aEvent)
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;
@ -1491,11 +1660,12 @@ ContentEventHandler::GetNodePositionHavingFlatText(nsINode* aNode,
}
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;
@ -1509,7 +1679,7 @@ ContentEventHandler::GetFirstFrameInRangeForTextRect(nsRange* aRange)
// 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;
@ -1538,14 +1708,15 @@ ContentEventHandler::GetFirstFrameInRangeForTextRect(nsRange* aRange)
}
ContentEventHandler::FrameAndNodeOffset
ContentEventHandler::GetLastFrameInRangeForTextRect(nsRange* aRange)
ContentEventHandler::GetLastFrameInRangeForTextRect(const RawRange& aRawRange)
{
NodePosition nodePosition;
nsCOMPtr<nsIContentIterator> iter = NS_NewPreContentIterator();
iter->Init(aRange);
RefPtr<nsRange> range = aRawRange.CreateRange();
iter->Init(range);
nsINode* endNode = aRange->GetEndContainer();
uint32_t endOffset = static_cast<uint32_t>(aRange->EndOffset());
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
@ -1561,14 +1732,14 @@ ContentEventHandler::GetLastFrameInRangeForTextRect(nsRange* aRange)
// 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()) {
@ -1587,8 +1758,8 @@ ContentEventHandler::GetLastFrameInRangeForTextRect(nsRange* aRange)
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();
}
@ -1814,8 +1985,6 @@ ContentEventHandler::OnQueryTextRectArray(WidgetQueryContentEvent* aEvent)
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;
@ -1828,20 +1997,22 @@ ContentEventHandler::OnQueryTextRectArray(WidgetQueryContentEvent* aEvent)
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
@ -1899,17 +2070,17 @@ ContentEventHandler::OnQueryTextRectArray(WidgetQueryContentEvent* aEvent)
// 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.
@ -2002,13 +2173,14 @@ ContentEventHandler::OnQueryTextRectArray(WidgetQueryContentEvent* aEvent)
// 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 {
@ -2137,22 +2309,23 @@ ContentEventHandler::OnQueryTextRect(WidgetQueryContentEvent* 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
@ -2171,13 +2344,12 @@ ContentEventHandler::OnQueryTextRect(WidgetQueryContentEvent* aEvent)
}
// 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.
@ -2321,7 +2493,7 @@ ContentEventHandler::OnQueryTextRect(WidgetQueryContentEvent* aEvent)
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;
}
@ -2450,7 +2622,7 @@ ContentEventHandler::OnQueryCaretRect(WidgetQueryContentEvent* aEvent)
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) {
@ -2734,8 +2906,9 @@ ContentEventHandler::GetFlatTextLengthInRange(
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;
}
@ -2768,23 +2941,25 @@ ContentEventHandler::GetFlatTextLengthInRange(
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;
}
@ -2835,11 +3010,10 @@ ContentEventHandler::GetFlatTextLengthInRange(
}
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_
@ -2853,7 +3027,7 @@ ContentEventHandler::GetStartOffset(nsRange* aRange,
// 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()) {
@ -2862,31 +3036,30 @@ ContentEventHandler::GetStartOffset(nsRange* aRange,
}
}
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());
MOZ_ASSERT(aRawRange.Collapsed());
if (!aRange || !aRange->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;
}
@ -2915,7 +3088,7 @@ ContentEventHandler::AdjustCollapsedRangeMaybeIntoTextNode(nsRange* aRange)
return NS_OK;
}
nsresult rv = aRange->CollapseTo(childNode, offsetInChildNode);
nsresult rv = aRawRange.CollapseTo(childNode, offsetInChildNode);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -2923,23 +3096,21 @@ ContentEventHandler::AdjustCollapsedRangeMaybeIntoTextNode(nsRange* aRange)
}
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;
@ -3033,16 +3204,17 @@ ContentEventHandler::OnSelectionEvent(WidgetSelectionEvent* aEvent)
}
// 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) ||

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

@ -13,9 +13,9 @@
#include "nsIFrame.h"
#include "nsINode.h"
#include "nsISelectionController.h"
#include "nsRange.h"
class nsPresContext;
class nsRange;
struct nsRect;
@ -37,6 +37,69 @@ enum LineBreakType
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;
@ -76,11 +139,9 @@ protected:
// 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);
@ -164,33 +225,6 @@ public:
{
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
@ -261,13 +295,13 @@ protected:
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.
@ -287,21 +321,21 @@ protected:
// 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
@ -316,15 +350,15 @@ protected:
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);
@ -366,13 +400,13 @@ protected:
// 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
{