diff --git a/dom/events/ContentEventHandler.cpp b/dom/events/ContentEventHandler.cpp index ad9da350c1a19..301b8c672d70d 100644 --- a/dom/events/ContentEventHandler.cpp +++ b/dom/events/ContentEventHandler.cpp @@ -1199,6 +1199,8 @@ ContentEventHandler::HandleQueryContentEvent(WidgetQueryContentEvent* aEvent) return OnQueryCaretRect(aEvent); case eQueryTextRect: return OnQueryTextRect(aEvent); + case eQueryTextRectArray: + return OnQueryTextRectArray(aEvent); case eQueryEditorRect: return OnQueryEditorRect(aEvent); case eQueryContentState: @@ -1393,6 +1395,85 @@ static nsINode* AdjustTextRectNode(nsINode* aNode, return node; } +static +nsIFrame* +GetFirstFrameInRange(nsRange* aRange) +{ + // used to iterate over all contents and their frames + nsCOMPtr iter = NS_NewContentIterator(); + iter->Init(aRange); + + // get the starting frame + int32_t nodeOffset = aRange->StartOffset(); + nsINode* node = iter->GetCurrentNode(); + if (!node) { + node = AdjustTextRectNode(aRange->GetStartParent(), nodeOffset); + } + nsIFrame* firstFrame = nullptr; + GetFrameForTextRect(node, nodeOffset, true, &firstFrame); + return firstFrame; +} + +nsresult +ContentEventHandler::OnQueryTextRectArray(WidgetQueryContentEvent* aEvent) +{ + nsresult rv = Init(aEvent); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + LineBreakType lineBreakType = GetLineBreakType(aEvent); + RefPtr range = new nsRange(mRootContent); + uint32_t offset = aEvent->mInput.mOffset; + + LayoutDeviceIntRect rect; + WritingMode writingMode; + while (aEvent->mInput.mLength > aEvent->mReply.mRectArray.Length()) { + rv = SetRangeFromFlatTextOffset(range, offset, 1, lineBreakType, true, + nullptr); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // get the starting frame + nsIFrame* firstFrame = GetFirstFrameInRange(range); + if (NS_WARN_IF(!firstFrame)) { + 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; + } + + int32_t nodeOffset = range->StartOffset(); + AutoTArray charRects; + rv = firstFrame->GetCharacterRectsInRange( + nodeOffset, + aEvent->mInput.mLength - aEvent->mReply.mRectArray.Length(), + charRects); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + for (size_t i = 0; i < charRects.Length(); i++) { + nsRect charRect = charRects[i]; + charRect.x += frameRect.x; + charRect.y += frameRect.y; + + rect = LayoutDeviceIntRect::FromUnknownRect( + charRect.ToOutsidePixels(mPresContext->AppUnitsPerDevPixel())); + + aEvent->mReply.mRectArray.AppendElement(rect); + } + offset += charRects.Length(); + } + aEvent->mSucceeded = true; + return NS_OK; +} + nsresult ContentEventHandler::OnQueryTextRect(WidgetQueryContentEvent* aEvent) { diff --git a/dom/events/ContentEventHandler.h b/dom/events/ContentEventHandler.h index d7ba1726a6a97..4e63c5270e3b3 100644 --- a/dom/events/ContentEventHandler.h +++ b/dom/events/ContentEventHandler.h @@ -53,6 +53,8 @@ class MOZ_STACK_CLASS ContentEventHandler nsresult OnQueryCaretRect(WidgetQueryContentEvent* aEvent); // eQueryTextRect event handler nsresult OnQueryTextRect(WidgetQueryContentEvent* aEvent); + // eQueryTextRectArray event handler + nsresult OnQueryTextRectArray(WidgetQueryContentEvent* aEvent); // eQueryEditorRect event handler nsresult OnQueryEditorRect(WidgetQueryContentEvent* aEvent); // eQueryContentState event handler @@ -299,6 +301,9 @@ class MOZ_STACK_CLASS ContentEventHandler FontRangeArray& aFontRanges, uint32_t& aLength, LineBreakType aLineBreakType); + nsresult QueryTextRectByRange(nsRange* aRange, + LayoutDeviceIntRect& aRect, + WritingMode& aWritingMode); }; } // namespace mozilla diff --git a/dom/events/EventStateManager.cpp b/dom/events/EventStateManager.cpp index 532b7f8a021be..4eb92863953d4 100644 --- a/dom/events/EventStateManager.cpp +++ b/dom/events/EventStateManager.cpp @@ -860,6 +860,7 @@ EventStateManager::HandleQueryContentEvent(WidgetQueryContentEvent* aEvent) case eQuerySelectionAsTransferable: case eQueryCharacterAtPoint: case eQueryDOMWidgetHittest: + case eQueryTextRectArray: break; default: return; diff --git a/widget/EventMessageList.h b/widget/EventMessageList.h index ba3014473ef12..61f6528fce64c 100644 --- a/widget/EventMessageList.h +++ b/widget/EventMessageList.h @@ -241,6 +241,9 @@ NS_EVENT_MESSAGE(eQueryCaretRect) // valid character range given offset and length. Result is relative to top // level widget coordinates NS_EVENT_MESSAGE(eQueryTextRect) +// Query for the bounding rect array of a range of characters. +// Thiis similar event of eQueryTextRect. +NS_EVENT_MESSAGE(eQueryTextRectArray) // Query for the bounding rect of the current focused frame. Result is relative // to top level widget coordinates NS_EVENT_MESSAGE(eQueryEditorRect) diff --git a/widget/TextEvents.h b/widget/TextEvents.h index e37a7d48227c1..1243cd358b48d 100644 --- a/widget/TextEvents.h +++ b/widget/TextEvents.h @@ -712,6 +712,16 @@ class WidgetQueryContentEvent : public WidgetGUIEvent mRefPoint = aPoint; } + void InitForQueryTextRectArray(uint32_t aOffset, uint32_t aLength, + const Options& aOptions = Options()) + { + NS_ASSERTION(mMessage == eQueryTextRectArray, + "wrong initializer is called"); + mInput.mOffset = aOffset; + mInput.mLength = aLength; + Init(aOptions); + } + void RequestFontRanges() { NS_ASSERTION(mMessage == eQueryTextContent, @@ -833,6 +843,8 @@ class WidgetQueryContentEvent : public WidgetGUIEvent nsCOMPtr mTransferable; // Used by eQueryTextContent with font ranges requested AutoTArray mFontRanges; + // Used by eQueryTextRectArray + nsTArray mRectArray; // true if selection is reversed (end < start) bool mReversed; // true if the selection exists