Bug 655877 - Part 41b: Make SVG text selectable with the mouse. r=roc,jwatt

This commit is contained in:
Cameron McCormack 2013-02-11 17:22:18 +11:00
Родитель e8ca525d3a
Коммит 56be0350f9
8 изменённых файлов: 463 добавлений и 37 удалений

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

@ -1312,15 +1312,14 @@ nsLayoutUtils::GetEventCoordinatesRelativeTo(nsIWidget* aWidget,
/* If we encountered a transform, we can't do simple arithmetic to figure
* out how to convert back to aFrame's coordinates and must use the CTM.
*/
if (transformFound) {
if (transformFound || aFrame->IsSVGText()) {
return TransformRootPointToFrame(aFrame, widgetToView);
}
/* Otherwise, all coordinate systems are translations of one another,
* so we can just subtract out the different.
* so we can just subtract out the difference.
*/
nsPoint offset = aFrame->GetOffsetToCrossDoc(rootFrame);
return widgetToView - offset;
return widgetToView - aFrame->GetOffsetToCrossDoc(rootFrame);
}
nsIFrame*
@ -1538,15 +1537,35 @@ TransformGfxRectToAncestor(nsIFrame *aFrame,
return ctm.TransformBounds(aRect);
}
nsPoint
nsLayoutUtils::TransformRootPointToFrame(nsIFrame *aFrame,
const nsPoint &aPoint)
static nsSVGTextFrame2*
GetContainingSVGTextFrame(nsIFrame* aFrame)
{
if (!aFrame->IsSVGText()) {
return nullptr;
}
return static_cast<nsSVGTextFrame2*>
(nsLayoutUtils::GetClosestFrameOfType(aFrame->GetParent(),
nsGkAtoms::svgTextFrame2));
}
nsPoint
nsLayoutUtils::TransformAncestorPointToFrame(nsIFrame* aFrame,
const nsPoint& aPoint,
nsIFrame* aAncestor)
{
nsSVGTextFrame2* text = GetContainingSVGTextFrame(aFrame);
float factor = aFrame->PresContext()->AppUnitsPerDevPixel();
gfxPoint result(NSAppUnitsToFloatPixels(aPoint.x, factor),
NSAppUnitsToFloatPixels(aPoint.y, factor));
result = TransformGfxPointFromAncestor(aFrame, result, nullptr);
if (text) {
result = TransformGfxPointFromAncestor(text, result, aAncestor);
result = text->TransformFramePointToTextChild(result, aFrame);
} else {
result = TransformGfxPointFromAncestor(aFrame, result, nullptr);
}
return nsPoint(NSFloatPixelsToAppUnits(float(result.x), factor),
NSFloatPixelsToAppUnits(float(result.y), factor));
@ -1557,13 +1576,20 @@ nsLayoutUtils::TransformAncestorRectToFrame(nsIFrame* aFrame,
const nsRect &aRect,
const nsIFrame* aAncestor)
{
nsSVGTextFrame2* text = GetContainingSVGTextFrame(aFrame);
float srcAppUnitsPerDevPixel = aAncestor->PresContext()->AppUnitsPerDevPixel();
gfxRect result(NSAppUnitsToFloatPixels(aRect.x, srcAppUnitsPerDevPixel),
NSAppUnitsToFloatPixels(aRect.y, srcAppUnitsPerDevPixel),
NSAppUnitsToFloatPixels(aRect.width, srcAppUnitsPerDevPixel),
NSAppUnitsToFloatPixels(aRect.height, srcAppUnitsPerDevPixel));
result = TransformGfxRectFromAncestor(aFrame, result, aAncestor);
if (text) {
result = TransformGfxRectFromAncestor(text, result, aAncestor);
result = text->TransformFrameRectToTextChild(result, aFrame);
} else {
result = TransformGfxRectFromAncestor(aFrame, result, aAncestor);
}
float destAppUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
return nsRect(NSFloatPixelsToAppUnits(float(result.x), destAppUnitsPerDevPixel),
@ -1577,13 +1603,21 @@ nsLayoutUtils::TransformFrameRectToAncestor(nsIFrame* aFrame,
const nsRect& aRect,
const nsIFrame* aAncestor)
{
float srcAppUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
gfxRect result(NSAppUnitsToFloatPixels(aRect.x, srcAppUnitsPerDevPixel),
NSAppUnitsToFloatPixels(aRect.y, srcAppUnitsPerDevPixel),
NSAppUnitsToFloatPixels(aRect.width, srcAppUnitsPerDevPixel),
NSAppUnitsToFloatPixels(aRect.height, srcAppUnitsPerDevPixel));
nsSVGTextFrame2* text = GetContainingSVGTextFrame(aFrame);
result = TransformGfxRectToAncestor(aFrame, result, aAncestor);
float srcAppUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
gfxRect result;
if (text) {
result = text->TransformFrameRectFromTextChild(aRect, aFrame);
result = TransformGfxRectToAncestor(text, result, aAncestor);
} else {
result = gfxRect(NSAppUnitsToFloatPixels(aRect.x, srcAppUnitsPerDevPixel),
NSAppUnitsToFloatPixels(aRect.y, srcAppUnitsPerDevPixel),
NSAppUnitsToFloatPixels(aRect.width, srcAppUnitsPerDevPixel),
NSAppUnitsToFloatPixels(aRect.height, srcAppUnitsPerDevPixel));
result = TransformGfxRectToAncestor(aFrame, result, aAncestor);
}
float destAppUnitsPerDevPixel = aAncestor->PresContext()->AppUnitsPerDevPixel();
return nsRect(NSFloatPixelsToAppUnits(float(result.x), destAppUnitsPerDevPixel),

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

@ -473,7 +473,7 @@ public:
static nsIFrame* GetPopupFrameForEventCoordinates(nsPresContext* aPresContext,
const nsEvent* aEvent);
/**
/**
* Translate from widget coordinates to the view's coordinates
* @param aPresContext the PresContext for the view
* @param aWidget the widget
@ -588,7 +588,18 @@ public:
* @return aPoint, expressed in aFrame's canonical coordinate space.
*/
static nsPoint TransformRootPointToFrame(nsIFrame* aFrame,
const nsPoint &aPt);
const nsPoint &aPoint)
{
return TransformAncestorPointToFrame(aFrame, aPoint, nullptr);
}
/**
* Transform aPoint relative to aAncestor down to the coordinate system of
* aFrame.
*/
static nsPoint TransformAncestorPointToFrame(nsIFrame* aFrame,
const nsPoint& aPoint,
nsIFrame* aAncestor);
/**
* Helper function that, given a rectangle and a matrix, returns the smallest

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

@ -335,6 +335,18 @@ nsIFrame::IsVisibleConsideringAncestors(uint32_t aFlags) const
return true;
}
void
nsIFrame::FindCloserFrameForSelection(
nsPoint aPoint,
nsIFrame::FrameWithDistance* aCurrentBestFrame)
{
if (nsLayoutUtils::PointIsCloserToRect(aPoint, mRect,
aCurrentBestFrame->mXDistance,
aCurrentBestFrame->mYDistance)) {
aCurrentBestFrame->mFrame = this;
}
}
static bool ApplyOverflowClipping(nsDisplayListBuilder* aBuilder,
const nsIFrame* aFrame,
const nsStyleDisplay* aDisp,
@ -3522,24 +3534,18 @@ static FrameTarget GetSelectionClosestFrame(nsIFrame* aFrame, nsPoint aPoint,
if (kid) {
// Go through all the child frames to find the closest one
// Large number to force the comparison to succeed
const nscoord HUGE_DISTANCE = nscoord_MAX;
nscoord closestXDistance = HUGE_DISTANCE;
nscoord closestYDistance = HUGE_DISTANCE;
nsIFrame *closestFrame = nullptr;
nsIFrame::FrameWithDistance closest = { nullptr, nscoord_MAX, nscoord_MAX };
for (; kid; kid = kid->GetNextSibling()) {
if (!SelfIsSelectable(kid, aFlags) || kid->IsEmpty())
continue;
if (nsLayoutUtils::PointIsCloserToRect(aPoint, kid->GetRect(),
closestXDistance,
closestYDistance))
closestFrame = kid;
kid->FindCloserFrameForSelection(aPoint, &closest);
}
if (closest.mFrame) {
if (closest.mFrame->IsSVGText())
return FrameTarget(closest.mFrame, false, false);
return GetSelectionClosestFrameForChild(closest.mFrame, aPoint, aFlags);
}
if (closestFrame)
return GetSelectionClosestFrameForChild(closestFrame, aPoint, aFlags);
}
return FrameTarget(aFrame, false, false);
}
@ -3616,8 +3622,8 @@ nsIFrame::ContentOffsets nsIFrame::GetContentOffsetsFromPoint(nsPoint aPoint,
// should lead to the whole frame being selected
if (adjustedFrame && adjustedFrame->GetStyleUIReset()->mUserSelect ==
NS_STYLE_USER_SELECT_ALL) {
return OffsetsForSingleFrame(adjustedFrame, aPoint +
this->GetOffsetTo(adjustedFrame));
nsPoint adjustedPoint = aPoint + this->GetOffsetTo(adjustedFrame);
return OffsetsForSingleFrame(adjustedFrame, adjustedPoint);
}
// For other cases, try to find a closest frame starting from the parent of
@ -3656,7 +3662,18 @@ nsIFrame::ContentOffsets nsIFrame::GetContentOffsetsFromPoint(nsPoint aPoint,
offsets.associateWithNext = (offsets.offset == range.start);
return offsets;
}
nsPoint pt = aPoint - closest.frame->GetOffsetTo(this);
nsPoint pt;
if (closest.frame != this) {
if (closest.frame->IsSVGText()) {
pt = nsLayoutUtils::TransformAncestorPointToFrame(closest.frame,
aPoint, this);
} else {
pt = aPoint - closest.frame->GetOffsetTo(this);
}
} else {
pt = aPoint;
}
return static_cast<nsFrame*>(closest.frame)->CalcContentOffsetsFromFramePoint(pt);
// XXX should I add some kind of offset standardization?

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

@ -2900,6 +2900,36 @@ NS_PTR_TO_INT32(frame->Properties().Get(nsIFrame::ParagraphDepthProperty()))
};
bool IsVisibleConsideringAncestors(uint32_t aFlags = 0) const;
struct FrameWithDistance
{
nsIFrame* mFrame;
nscoord mXDistance;
nscoord mYDistance;
};
/**
* Finds a frame that is closer to a specified point than a current
* distance. Distance is measured as for text selection -- a closer x
* distance beats a closer y distance.
*
* Normally, this function will only check the distance between this
* frame's rectangle and the specified point. nsSVGTextFrame2 overrides
* this so that it can manage all of its descendant frames and take
* into account any SVG text layout.
*
* If aPoint is closer to this frame's rectangle than aCurrentBestFrame
* indicates, then aCurrentBestFrame is updated with the distance between
* aPoint and this frame's rectangle, and with a pointer to this frame.
* If aPoint is not closer, then aCurrentBestFrame is left unchanged.
*
* @param aPoint The point to check for its distance to this frame.
* @param aCurrentBestFrame Pointer to a struct that will be updated with
* a pointer to this frame and its distance to aPoint, if this frame
* is indeed closer than the current distance in aCurrentBestFrame.
*/
virtual void FindCloserFrameForSelection(nsPoint aPoint,
FrameWithDistance* aCurrentBestFrame);
inline bool IsBlockInside() const;
inline bool IsBlockOutside() const;
inline bool IsInlineOutside() const;

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

@ -674,7 +674,7 @@ protected:
bool CombineSelectionUnderlineRect(nsPresContext* aPresContext,
nsRect& aRect);
ContentOffsets GetCharacterOffsetAtFramePointInternal(const nsPoint &aPoint,
ContentOffsets GetCharacterOffsetAtFramePointInternal(nsPoint aPoint,
bool aForInsertionPoint);
void ClearFrameOffsetCache();

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

@ -6207,7 +6207,7 @@ nsTextFrame::GetCharacterOffsetAtFramePoint(const nsPoint &aPoint)
}
nsIFrame::ContentOffsets
nsTextFrame::GetCharacterOffsetAtFramePointInternal(const nsPoint &aPoint,
nsTextFrame::GetCharacterOffsetAtFramePointInternal(nsPoint aPoint,
bool aForInsertionPoint)
{
ContentOffsets offsets;

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

@ -12,6 +12,7 @@
#include "gfxSkipChars.h"
#include "gfxTypes.h"
#include "LookAndFeel.h"
#include "nsAlgorithm.h"
#include "nsBlockFrame.h"
#include "nsCaret.h"
#include "nsContentUtils.h"
@ -124,6 +125,18 @@ ScaleAround(gfxRect& aRect, const gfxPoint& aPoint, double aScale)
aRect.height *= aScale;
}
/**
* Returns whether a gfxPoint lies within a gfxRect.
*/
static bool
Inside(const gfxRect& aRect, const gfxPoint& aPoint)
{
return aPoint.x >= aRect.x &&
aPoint.x < aRect.XMost() &&
aPoint.y >= aRect.y &&
aPoint.y < aRect.YMost();
}
/**
* Gets the measured ascent and descent of the text in the given nsTextFrame
* in app units.
@ -2731,8 +2744,10 @@ nsDisplaySVGText::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
// ToReferenceFrame() includes frame->GetPosition(), our user space position.
nsPoint userSpacePt = pointRelativeToReferenceFrame -
(ToReferenceFrame() - frame->GetPosition());
if (frame->GetFrameForPoint(userSpacePt)) {
aOutFrames->AppendElement(frame);
nsIFrame* target = frame->GetFrameForPoint(userSpacePt);
if (target) {
aOutFrames->AppendElement(target);
}
}
@ -2938,6 +2953,35 @@ nsSVGTextFrame2::InvalidateInternal(const nsRect& aDamageRect,
(this, nsSVGUtils::OuterSVGIsCallingReflowSVG(this), nullptr, aFlags);
}
void
nsSVGTextFrame2::FindCloserFrameForSelection(
nsPoint aPoint,
nsIFrame::FrameWithDistance* aCurrentBestFrame)
{
UpdateGlyphPositioning(true);
nsPresContext* presContext = PresContext();
// Find the frame that has the closest rendered run rect to aPoint.
TextRenderedRunIterator it(this);
for (TextRenderedRun run = it.Current(); run.mFrame; run = it.Next()) {
uint32_t flags = TextRenderedRun::eIncludeFill |
TextRenderedRun::eIncludeStroke |
TextRenderedRun::eNoHorizontalOverflow;
gfxRect userRect = run.GetUserSpaceRect(presContext, flags);
nsRect rect = nsSVGUtils::ToCanvasBounds(userRect,
GetCanvasTM(FOR_HIT_TESTING),
presContext);
if (nsLayoutUtils::PointIsCloserToRect(aPoint, rect,
aCurrentBestFrame->mXDistance,
aCurrentBestFrame->mYDistance)) {
aCurrentBestFrame->mFrame = run.mFrame;
}
}
}
//----------------------------------------------------------------------
// nsISVGChildFrame methods
@ -3138,6 +3182,52 @@ nsSVGTextFrame2::PaintSVG(nsRenderingContext* aContext,
return NS_OK;
}
NS_IMETHODIMP_(nsIFrame*)
nsSVGTextFrame2::GetFrameForPoint(const nsPoint& aPoint)
{
NS_ASSERTION(GetFirstPrincipalChild(), "must have a child frame");
AutoCanvasTMForMarker autoCanvasTMFor(this, FOR_HIT_TESTING);
if (mState & NS_STATE_SVG_NONDISPLAY_CHILD) {
// Text frames inside <clipPath> will never have had ReflowSVG called on
// them, so call UpdateGlyphPositioning to do this now. (Text frames
// inside <mask> and other non-display containers will never need to
// be hit tested.)
UpdateGlyphPositioning(true);
} else {
NS_ASSERTION(!NS_SUBTREE_DIRTY(this), "reflow should have happened");
}
nsPresContext* presContext = PresContext();
gfxPoint pointInOuterSVGUserUnits = AppUnitsToGfxUnits(aPoint, presContext);
TextRenderedRunIterator it(this);
nsIFrame* hit = nullptr;
for (TextRenderedRun run = it.Current(); run.mFrame; run = it.Next()) {
uint16_t hitTestFlags = nsSVGUtils::GetGeometryHitTestFlags(run.mFrame);
if (!(hitTestFlags & (SVG_HIT_TEST_FILL | SVG_HIT_TEST_STROKE))) {
continue;
}
gfxMatrix m = GetCanvasTM(FOR_HIT_TESTING);
m.PreMultiply(run.GetTransformFromRunUserSpaceToUserSpace(presContext));
m.Invert();
gfxPoint pointInRunUserSpace = m.Transform(pointInOuterSVGUserUnits);
gfxRect frameRect =
run.GetRunUserSpaceRect(presContext, TextRenderedRun::eIncludeFill |
TextRenderedRun::eIncludeStroke);
if (Inside(frameRect, pointInRunUserSpace) &&
nsSVGUtils::HitTestClip(this, aPoint)) {
hit = run.mFrame;
}
}
return hit;
}
NS_IMETHODIMP_(nsRect)
nsSVGTextFrame2::GetCoveredRegion()
{
@ -4589,3 +4679,213 @@ nsSVGTextFrame2::GetFontSizeScaleFactor() const
{
return mFontSizeScaleFactor;
}
/**
* Take aPoint, which is in the <text> element's user space, and convert
* it to the appropriate frame user space of aChildFrame according to
* which rendered run the point hits.
*/
gfxPoint
nsSVGTextFrame2::TransformFramePointToTextChild(const gfxPoint& aPoint,
nsIFrame* aChildFrame)
{
NS_ASSERTION(aChildFrame &&
nsLayoutUtils::GetClosestFrameOfType
(aChildFrame->GetParent(), nsGkAtoms::svgTextFrame2) == this,
"aChildFrame must be a descendant of this frame");
UpdateGlyphPositioning(true);
nsPresContext* presContext = PresContext();
// Add in the mRect offset to aPoint, as that will have been taken into
// account when transforming the point from the ancestor frame down
// to this one.
float cssPxPerDevPx = presContext->
AppUnitsToFloatCSSPixels(presContext->AppUnitsPerDevPixel());
float factor = presContext->AppUnitsPerCSSPixel();
gfxPoint framePosition(NSAppUnitsToFloatPixels(mRect.x, factor),
NSAppUnitsToFloatPixels(mRect.y, factor));
gfxPoint pointInUserSpace = aPoint * cssPxPerDevPx + framePosition;
// Find the closest rendered run for the text frames beneath aChildFrame.
TextRenderedRunIterator it(this, TextRenderedRunIterator::eAllFrames,
aChildFrame);
TextRenderedRun hit;
gfxPoint pointInRun;
nscoord dx = nscoord_MAX;
nscoord dy = nscoord_MAX;
for (TextRenderedRun run = it.Current(); run.mFrame; run = it.Next()) {
uint32_t flags = TextRenderedRun::eIncludeFill |
TextRenderedRun::eIncludeStroke |
TextRenderedRun::eNoHorizontalOverflow;
gfxRect runRect = run.GetRunUserSpaceRect(presContext, flags);
gfxPoint pointInRunUserSpace =
run.GetTransformFromRunUserSpaceToUserSpace(presContext).Invert().
Transform(pointInUserSpace);
if (Inside(runRect, pointInRunUserSpace)) {
// The point was inside the rendered run's rect, so we choose it.
dx = 0;
dy = 0;
pointInRun = pointInRunUserSpace;
hit = run;
} else if (nsLayoutUtils::PointIsCloserToRect(pointInRunUserSpace,
runRect, dx, dy)) {
// The point was closer to this rendered run's rect than any others
// we've seen so far.
pointInRun.x = clamped(pointInRunUserSpace.x,
runRect.X(), runRect.XMost());
pointInRun.y = clamped(pointInRunUserSpace.y,
runRect.Y(), runRect.YMost());
hit = run;
}
}
if (!hit.mFrame) {
// We didn't find any rendered runs for the frame.
return aPoint;
}
// Return the point in user units relative to the nsTextFrame,
// but taking into account mFontSizeScaleFactor.
gfxMatrix m = hit.GetTransformFromRunUserSpaceToFrameUserSpace(presContext);
m.Scale(mFontSizeScaleFactor, mFontSizeScaleFactor);
return m.Transform(pointInRun) / cssPxPerDevPx;
}
/**
* For each rendered run for frames beneath aChildFrame, convert aRect
* into the run's frame user space and intersect it with the run's
* frame user space rectangle. For each of these intersections,
* then translate them up into aChildFrame's coordinate space
* and union them all together.
*/
gfxRect
nsSVGTextFrame2::TransformFrameRectToTextChild(const gfxRect& aRect,
nsIFrame* aChildFrame)
{
NS_ASSERTION(aChildFrame &&
nsLayoutUtils::GetClosestFrameOfType
(aChildFrame->GetParent(), nsGkAtoms::svgTextFrame2) == this,
"aChildFrame must be a descendant of this frame");
UpdateGlyphPositioning(true);
nsPresContext* presContext = PresContext();
// Add in the mRect offset to aRect, as that will have been taken into
// account when transforming the rect from the ancestor frame down
// to this one.
float cssPxPerDevPx = presContext->
AppUnitsToFloatCSSPixels(presContext->AppUnitsPerDevPixel());
float factor = presContext->AppUnitsPerCSSPixel();
gfxPoint framePosition(NSAppUnitsToFloatPixels(mRect.x, factor),
NSAppUnitsToFloatPixels(mRect.y, factor));
gfxRect incomingRectInUserSpace(aRect.x * cssPxPerDevPx + framePosition.x,
aRect.y * cssPxPerDevPx + framePosition.y,
aRect.width * cssPxPerDevPx,
aRect.height * cssPxPerDevPx);
// Find each rendered run for text frames beneath aChildFrame.
gfxRect result;
TextRenderedRunIterator it(this, TextRenderedRunIterator::eAllFrames,
aChildFrame);
for (TextRenderedRun run = it.Current(); run.mFrame; run = it.Next()) {
// Convert the incoming rect into frame user space.
gfxMatrix m;
m.PreMultiply(run.GetTransformFromRunUserSpaceToUserSpace(presContext).Invert());
m.PreMultiply(run.GetTransformFromRunUserSpaceToFrameUserSpace(presContext));
gfxRect incomingRectInFrameUserSpace =
m.TransformBounds(incomingRectInUserSpace);
// Intersect it with this run's rectangle.
uint32_t flags = TextRenderedRun::eIncludeFill |
TextRenderedRun::eIncludeStroke;
gfxRect runRectInFrameUserSpace = run.GetFrameUserSpaceRect(presContext, flags);
gfxRect runIntersectionInFrameUserSpace =
incomingRectInFrameUserSpace.Intersect(runRectInFrameUserSpace);
if (!runIntersectionInFrameUserSpace.IsEmpty()) {
// Take the font size scale into account.
runIntersectionInFrameUserSpace.x *= mFontSizeScaleFactor;
runIntersectionInFrameUserSpace.y *= mFontSizeScaleFactor;
runIntersectionInFrameUserSpace.width *= mFontSizeScaleFactor;
runIntersectionInFrameUserSpace.height *= mFontSizeScaleFactor;
// Convert it into the coordinate space of aChildFrame.
nsPoint offset = run.mFrame->GetOffsetTo(aChildFrame);
gfxRect runIntersection =
runIntersectionInFrameUserSpace +
gfxPoint(NSAppUnitsToFloatPixels(offset.x, factor),
NSAppUnitsToFloatPixels(offset.y, factor));
// Union it into the result.
result.UnionRect(result, runIntersection);
}
}
return result;
}
/**
* For each rendered run beneath aChildFrame, translate aRect from
* aChildFrame to the run's text frame, transform it then into
* the run's frame user space, intersect it with the run's
* frame user space rect, then transform it up to user space.
* The result is the union of all of these.
*/
gfxRect
nsSVGTextFrame2::TransformFrameRectFromTextChild(const nsRect& aRect,
nsIFrame* aChildFrame)
{
NS_ASSERTION(aChildFrame &&
nsLayoutUtils::GetClosestFrameOfType
(aChildFrame->GetParent(), nsGkAtoms::svgTextFrame2) == this,
"aChildFrame must be a descendant of this frame");
UpdateGlyphPositioning(true);
nsPresContext* presContext = PresContext();
gfxRect result;
TextRenderedRunIterator it(this, TextRenderedRunIterator::eAllFrames,
aChildFrame);
for (TextRenderedRun run = it.Current(); run.mFrame; run = it.Next()) {
// First, translate aRect from aChildFrame to this run's frame.
nsRect rectInTextFrame = aRect + aChildFrame->GetOffsetTo(run.mFrame);
// Scale it into frame user space.
gfxRect rectInFrameUserSpace =
AppUnitsToFloatCSSPixels(gfxRect(rectInTextFrame.x,
rectInTextFrame.y,
rectInTextFrame.width,
rectInTextFrame.height), presContext);
// Intersect it with the run.
uint32_t flags = TextRenderedRun::eIncludeFill |
TextRenderedRun::eIncludeStroke;
rectInFrameUserSpace.IntersectRect
(rectInFrameUserSpace, run.GetFrameUserSpaceRect(presContext, flags));
if (!rectInFrameUserSpace.IsEmpty()) {
// Transform it up to user space of the <text>, also taking into
// account the font size scale.
gfxMatrix m = run.GetTransformFromRunUserSpaceToUserSpace(presContext);
m.Scale(mFontSizeScaleFactor, mFontSizeScaleFactor);
gfxRect rectInUserSpace = m.Transform(rectInFrameUserSpace);
// Union it into the result.
result.UnionRect(result, rectInUserSpace);
}
}
// Subtract the mRect offset from the result, as our user space for
// this frame is relative to the top-left of mRect.
float factor = presContext->AppUnitsPerCSSPixel();
gfxPoint framePosition(NSAppUnitsToFloatPixels(mRect.x, factor),
NSAppUnitsToFloatPixels(mRect.y, factor));
return result - framePosition;
}

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

@ -215,10 +215,18 @@ public:
nscoord aX, nscoord aY, nsIFrame* aForChild,
uint32_t aFlags);
/**
* Finds the nsTextFrame for the closest rendered run to the specified point.
*/
virtual void FindCloserFrameForSelection(nsPoint aPoint,
FrameWithDistance* aCurrentBestFrame);
// nsISVGChildFrame interface:
virtual void NotifySVGChanged(uint32_t aFlags);
NS_IMETHOD PaintSVG(nsRenderingContext* aContext,
const nsIntRect* aDirtyRect);
NS_IMETHOD_(nsIFrame*) GetFrameForPoint(const nsPoint& aPoint);
virtual void ReflowSVG();
NS_IMETHOD_(nsRect) GetCoveredRegion();
virtual SVGBBox GetBBoxContribution(const gfxMatrix& aToBBoxUserspace,
@ -258,6 +266,32 @@ public:
double GetFontSizeScaleFactor() const;
/**
* Takes a point from the <text> element's user space and
* converts it to the appropriate frame user space of aChildFrame,
* according to which rendered run the point hits.
*/
gfxPoint TransformFramePointToTextChild(const gfxPoint& aPoint,
nsIFrame* aChildFrame);
/**
* Takes a rectangle, aRect, in the <text> element's user space, and
* returns a rectangle in aChildFrame's frame user space that
* covers intersections of aRect with each rendered run for text frames
* within aChildFrame.
*/
gfxRect TransformFrameRectToTextChild(const gfxRect& aRect,
nsIFrame* aChildFrame);
/**
* Takes an app unit rectangle in the coordinate space of a given descendant
* frame of this frame, and returns a rectangle in the <text> element's user
* space that covers all parts of rendered runs that intersect with the
* rectangle.
*/
gfxRect TransformFrameRectFromTextChild(const nsRect& aRect,
nsIFrame* aChildFrame);
private:
/**
* This class exists purely because it would be too messy to pass the "for"