--- a/dom/events/ContentEventHandler.cpp
+++ b/dom/events/ContentEventHandler.cpp
@@ -1370,53 +1370,80 @@ ContentEventHandler::OnQueryTextContent(
"Font ranges doesn't match the string");
}
aEvent->mSucceeded = true;
return NS_OK;
}
-// Adjust to use a child node if possible
-// to make the returned rect more accurate
-static nsINode* AdjustTextRectNode(nsINode* aNode,
- int32_t& aNodeOffset)
+ContentEventHandler::NodePosition
+ContentEventHandler::GetNodePositionHavingFlatText(
+ const NodePosition& aNodePosition)
{
- int32_t childCount = int32_t(aNode->GetChildCount());
- nsINode* node = aNode;
- if (childCount) {
- if (aNodeOffset < childCount) {
- node = aNode->GetChildAt(aNodeOffset);
- aNodeOffset = 0;
- } else if (aNodeOffset == childCount) {
- node = aNode->GetChildAt(childCount - 1);
- aNodeOffset = node->IsNodeOfType(nsINode::eTEXT) ?
- static_cast<int32_t>(static_cast<nsIContent*>(node)->TextLength()) : 1;
- }
- }
- return node;
+ return GetNodePositionHavingFlatText(aNodePosition.mNode,
+ aNodePosition.mOffset);
}
-static
-nsIFrame*
-GetFirstFrameInRange(nsRange* aRange, int32_t& aNodeOffset)
+ContentEventHandler::NodePosition
+ContentEventHandler::GetNodePositionHavingFlatText(nsINode* aNode,
+ int32_t aNodeOffset)
+{
+ if (aNode->IsNodeOfType(nsINode::eTEXT)) {
+ return NodePosition(aNode, aNodeOffset);
+ }
+
+ int32_t childCount = static_cast<int32_t>(aNode->GetChildCount());
+
+ // If it's a empty element node, returns itself.
+ if (!childCount) {
+ MOZ_ASSERT(!aNodeOffset || aNodeOffset == 1);
+ return NodePosition(aNode, aNodeOffset);
+ }
+
+ // If there is a node at given position, return the start of it.
+ if (aNodeOffset < childCount) {
+ return NodePosition(aNode->GetChildAt(aNodeOffset), 0);
+ }
+
+ // If the offset represents "after" the node, we need to return the last
+ // child of it. For example, if a range is |<p>[<br>]</p>|, then, the
+ // end point is {<p>, 1}. In such case, callers need the <br> node.
+ if (aNodeOffset == childCount) {
+ NodePosition result;
+ result.mNode = aNode->GetChildAt(childCount - 1);
+ result.mOffset = result.mNode->IsNodeOfType(nsINode::eTEXT) ?
+ static_cast<int32_t>(result.mNode->AsContent()->TextLength()) : 1;
+ }
+
+ NS_WARNING("aNodeOffset is invalid value");
+ return NodePosition();
+}
+
+ContentEventHandler::FrameAndNodeOffset
+ContentEventHandler::GetFirstFrameHavingFlatTextInRange(nsRange* aRange)
{
// used to iterate over all contents and their frames
nsCOMPtr<nsIContentIterator> iter = NS_NewContentIterator();
iter->Init(aRange);
// get the starting frame
- aNodeOffset = aRange->StartOffset();
- nsINode* node = iter->GetCurrentNode();
- if (!node) {
- node = AdjustTextRectNode(aRange->GetStartParent(), aNodeOffset);
+ NodePosition nodePosition(iter->GetCurrentNode(), aRange->StartOffset());
+ if (!nodePosition.mNode) {
+ nodePosition =
+ GetNodePositionHavingFlatText(aRange->GetStartParent(),
+ nodePosition.mOffset);
+ if (NS_WARN_IF(!nodePosition.IsValid())) {
+ return FrameAndNodeOffset();
+ }
}
nsIFrame* firstFrame = nullptr;
- GetFrameForTextRect(node, aNodeOffset, true, &firstFrame);
- return firstFrame;
+ GetFrameForTextRect(nodePosition.mNode, nodePosition.mOffset,
+ true, &firstFrame);
+ return FrameAndNodeOffset(firstFrame, nodePosition.mOffset);
}
nsresult
ContentEventHandler::OnQueryTextRectArray(WidgetQueryContentEvent* aEvent)
{
nsresult rv = Init(aEvent);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
@@ -1431,32 +1458,31 @@ ContentEventHandler::OnQueryTextRectArra
while (offset < kEndOffset) {
rv = SetRangeFromFlatTextOffset(range, offset, 1, lineBreakType, true,
nullptr);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// get the starting frame
- int32_t nodeOffset = -1;
- nsIFrame* firstFrame = GetFirstFrameInRange(range, nodeOffset);
- if (NS_WARN_IF(!firstFrame)) {
+ FrameAndNodeOffset firstFrame = GetFirstFrameHavingFlatTextInRange(range);
+ if (NS_WARN_IF(!firstFrame.IsValid())) {
return NS_ERROR_FAILURE;
}
// get the starting frame rect
nsRect frameRect(nsPoint(0, 0), firstFrame->GetRect().Size());
rv = ConvertToRootRelativeOffset(firstFrame, frameRect);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
AutoTArray<nsRect, 16> charRects;
- rv = firstFrame->GetCharacterRectsInRange(nodeOffset, kEndOffset - offset,
- charRects);
+ rv = firstFrame->GetCharacterRectsInRange(firstFrame.mStartOffsetInNode,
+ kEndOffset - offset, charRects);
if (NS_WARN_IF(NS_FAILED(rv)) || NS_WARN_IF(charRects.IsEmpty())) {
return rv;
}
for (size_t i = 0; i < charRects.Length(); i++) {
nsRect charRect = charRects[i];
charRect.x += frameRect.x;
charRect.y += frameRect.y;
@@ -1493,79 +1519,88 @@ ContentEventHandler::OnQueryTextRect(Wid
rv = GenerateFlatTextContent(range, aEvent->mReply.mString, lineBreakType);
NS_ENSURE_SUCCESS(rv, rv);
// used to iterate over all contents and their frames
nsCOMPtr<nsIContentIterator> iter = NS_NewContentIterator();
iter->Init(range);
// get the starting frame
- int32_t nodeOffset = range->StartOffset();
- nsINode* node = iter->GetCurrentNode();
- if (!node) {
- node = AdjustTextRectNode(range->GetStartParent(), nodeOffset);
+ NodePosition startNodePosition(iter->GetCurrentNode(), range->StartOffset());
+ if (!startNodePosition.mNode) {
+ startNodePosition =
+ GetNodePositionHavingFlatText(range->GetStartParent(),
+ startNodePosition.mOffset);
+ if (NS_WARN_IF(!startNodePosition.IsValid())) {
+ return NS_ERROR_FAILURE;
+ }
}
nsIFrame* firstFrame = nullptr;
- rv = GetFrameForTextRect(node, nodeOffset, true, &firstFrame);
+ rv = GetFrameForTextRect(startNodePosition.mNode, startNodePosition.mOffset,
+ true, &firstFrame);
NS_ENSURE_SUCCESS(rv, rv);
// get the starting frame rect
nsRect rect(nsPoint(0, 0), firstFrame->GetRect().Size());
rv = ConvertToRootRelativeOffset(firstFrame, rect);
NS_ENSURE_SUCCESS(rv, rv);
nsRect frameRect = rect;
nsPoint ptOffset;
- firstFrame->GetPointFromOffset(nodeOffset, &ptOffset);
+ firstFrame->GetPointFromOffset(startNodePosition.mOffset, &ptOffset);
// minus 1 to avoid creating an empty rect
if (firstFrame->GetWritingMode().IsVertical()) {
rect.y += ptOffset.y - 1;
rect.height -= ptOffset.y - 1;
} else {
rect.x += ptOffset.x - 1;
rect.width -= ptOffset.x - 1;
}
// get the ending frame
- nodeOffset = range->EndOffset();
- node = AdjustTextRectNode(range->GetEndParent(), nodeOffset);
+ NodePosition endNodePosition =
+ GetNodePositionHavingFlatText(range->GetEndParent(), range->EndOffset());
+ if (NS_WARN_IF(!endNodePosition.IsValid())) {
+ return NS_ERROR_FAILURE;
+ }
nsIFrame* lastFrame = nullptr;
- rv = GetFrameForTextRect(node, nodeOffset, range->Collapsed(), &lastFrame);
+ rv = GetFrameForTextRect(endNodePosition.mNode, endNodePosition.mOffset,
+ range->Collapsed(), &lastFrame);
NS_ENSURE_SUCCESS(rv, rv);
// iterate over all covered frames
for (nsIFrame* frame = firstFrame; frame != lastFrame;) {
frame = frame->GetNextContinuation();
if (!frame) {
do {
iter->Next();
- node = iter->GetCurrentNode();
+ nsINode* node = iter->GetCurrentNode();
if (!node) {
break;
}
if (!node->IsNodeOfType(nsINode::eCONTENT)) {
continue;
}
- frame = static_cast<nsIContent*>(node)->GetPrimaryFrame();
+ frame = node->AsContent()->GetPrimaryFrame();
} while (!frame && !iter->IsDone());
if (!frame) {
// this can happen when the end offset of the range is 0.
frame = lastFrame;
}
}
frameRect.SetRect(nsPoint(0, 0), frame->GetRect().Size());
rv = ConvertToRootRelativeOffset(frame, frameRect);
NS_ENSURE_SUCCESS(rv, rv);
if (frame != lastFrame) {
// not last frame, so just add rect to previous result
rect.UnionRect(rect, frameRect);
}
}
// get the ending frame rect
- lastFrame->GetPointFromOffset(nodeOffset, &ptOffset);
+ lastFrame->GetPointFromOffset(endNodePosition.mOffset, &ptOffset);
// minus 1 to avoid creating an empty rect
if (lastFrame->GetWritingMode().IsVertical()) {
frameRect.height -= lastFrame->GetRect().height - ptOffset.y - 1;
} else {
frameRect.width -= lastFrame->GetRect().width - ptOffset.x - 1;
}
if (firstFrame == lastFrame) {
--- a/dom/events/ContentEventHandler.h
+++ b/dom/events/ContentEventHandler.h
@@ -299,13 +299,50 @@ protected:
LineBreakType aLineBreakType);
nsresult GenerateFlatFontRanges(nsRange* aRange,
FontRangeArray& aFontRanges,
uint32_t& aLength,
LineBreakType aLineBreakType);
nsresult QueryTextRectByRange(nsRange* aRange,
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);
+
+ struct MOZ_STACK_CLASS FrameAndNodeOffset final
+ {
+ // mFrame is safe since this can live in only stack class and
+ // ContentEventHandler doesn't modify layout after
+ // ContentEventHandler::Init() flushes pending layout. In other words,
+ // this struct shouldn't be used before calling
+ // ContentEventHandler::Init().
+ nsIFrame* mFrame;
+ // Start offset in the node of mFrame
+ int32_t mStartOffsetInNode;
+
+ FrameAndNodeOffset()
+ : mFrame(nullptr)
+ , mStartOffsetInNode(-1)
+ {
+ }
+
+ FrameAndNodeOffset(nsIFrame* aFrame, int32_t aStartOffsetInNode)
+ : mFrame(aFrame)
+ , mStartOffsetInNode(aStartOffsetInNode)
+ {
+ }
+
+ nsIFrame* operator->() { return mFrame; }
+ const nsIFrame* operator->() const { return mFrame; }
+ operator nsIFrame*() { return mFrame; }
+ operator const nsIFrame*() const { return mFrame; }
+ bool IsValid() const { return mFrame && mStartOffsetInNode >= 0; }
+ };
+ // Get first frame in the given range for computing text rect.
+ FrameAndNodeOffset GetFirstFrameHavingFlatTextInRange(nsRange* aRange);
};
} // namespace mozilla
#endif // mozilla_ContentEventHandler_h_