Bug 1342951, part 4 - Add a version of SVGTextFrame::GetSubStringLength that can be used independantly of reflow, to avoid sync reflows. r=heycam

MozReview-Commit-ID: CJqUwF3rXP4
This commit is contained in:
Jonathan Watt 2017-09-02 22:22:54 +01:00
parent f28f0d2890
commit 4130888dfd
4 changed files with 150 additions and 1 deletions

View File

@ -145,7 +145,7 @@ SVGTextContentElement::SelectSubString(uint32_t charnum, uint32_t nchars, ErrorR
float
SVGTextContentElement::GetSubStringLength(uint32_t charnum, uint32_t nchars, ErrorResult& rv)
{
SVGTextFrame* textFrame = GetSVGTextFrame();
SVGTextFrame* textFrame = GetSVGTextFrameForNonLayoutDependentQuery();
if (!textFrame)
return 0.0f;

View File

@ -27,6 +27,14 @@ function runTest()
var charWidth = text1.getSubStringLength(0, 1);
if (navigator.userAgent.indexOf("Linux") > -1 && charWidth == 241) {
// Workaround for a slight difference in 'charWidth' (i.e. the width of
// the 'a' char) on Linux build machines after bug 1342951. The issue
// doesn't reproduce locally on Ubuntu 17.04 so is particularly tricky to
// debug.
charWidth = 240;
}
var epsilon = 0.001;
function isClose(a, b, str)

View File

@ -4074,6 +4074,135 @@ SVGTextFrame::GetSubStringLength(nsIContent* aContent,
uint32_t charnum, uint32_t nchars,
float* aResult)
{
// For some content we cannot (or currently cannot) compute the length
// without reflowing. In those cases we need to fall back to using
// GetSubStringLengthSlowFallback.
//
// We fall back for textPath since we need glyph positioning in order to
// tell if any characters should be ignored due to having fallen off the
// end of the textPath.
//
// We fall back for bidi because GetTrimmedOffsets does not produce the
// correct results for bidi continuations when passed aPostReflow = false.
// XXX It may be possible to determine which continuations to trim from (and
// which sides), but currently we don't do that. It would require us to
// identify the visual (rather than logical) start and end of the line, to
// avoid trimming at line-internal frame boundaries. Maybe nsBidiPresUtils
// methods like GetFrameToRightOf and GetFrameToLeftOf would help?
//
TextFrameIterator frameIter(this);
for (nsTextFrame* frame = frameIter.Current(); frame; frame = frameIter.Next()) {
if (frameIter.TextPathFrame() ||
frame->GetNextContinuation()) {
return GetSubStringLengthSlowFallback(aContent, charnum, nchars, aResult);
}
}
// We only need our text correspondence to be up to date (no need to call
// UpdateGlyphPositioning).
TextNodeCorrespondenceRecorder::RecordCorrespondence(this);
// Convert charnum/nchars from addressable characters relative to
// aContent to global character indices.
CharIterator chit(this, CharIterator::eAddressable, aContent,
/* aPostReflow */ false);
if (!chit.AdvanceToSubtree() ||
!chit.Next(charnum) ||
chit.IsAfterSubtree()) {
return NS_ERROR_DOM_INDEX_SIZE_ERR;
}
// We do this after the NS_ERROR_DOM_INDEX_SIZE_ERR return so JS calls
// correctly throw when necessary.
if (nchars == 0) {
*aResult = 0.0f;
return NS_OK;
}
charnum = chit.TextElementCharIndex();
chit.NextWithinSubtree(nchars);
nchars = chit.TextElementCharIndex() - charnum;
// Sum of the substring advances.
nscoord textLength = 0;
TextFrameIterator frit(this); // aSubtree = nullptr
// Index of the first non-skipped char in the frame, and of a subsequent char
// that we're interested in. Both are relative to the index of the first
// non-skipped char in the ancestor <text> element.
uint32_t frameStartTextElementCharIndex = 0;
uint32_t textElementCharIndex;
for (nsTextFrame* frame = frit.Current(); frame; frame = frit.Next()) {
frameStartTextElementCharIndex += frit.UndisplayedCharacters();
textElementCharIndex = frameStartTextElementCharIndex;
// Offset into frame's nsTextNode:
const uint32_t untrimmedOffset = frame->GetContentOffset();
const uint32_t untrimmedLength = frame->GetContentEnd() - untrimmedOffset;
// Trim the offset/length to remove any leading/trailing white space.
uint32_t trimmedOffset = untrimmedOffset;
uint32_t trimmedLength = untrimmedLength;
nsTextFrame::TrimmedOffsets trimmedOffsets =
frame->GetTrimmedOffsets(frame->GetContent()->GetText(),
/* aTrimAfter */ true,
/* aPostReflow */ false);
TrimOffsets(trimmedOffset, trimmedLength, trimmedOffsets);
textElementCharIndex += trimmedOffset - untrimmedOffset;
if (textElementCharIndex >= charnum + nchars) {
break; // we're past the end of the substring
}
uint32_t offset = textElementCharIndex;
// Intersect the substring we are interested in with the range covered by
// the nsTextFrame.
IntersectInterval(offset, trimmedLength, charnum, nchars);
if (trimmedLength != 0) {
// Convert offset into an index into the frame.
offset += trimmedOffset - textElementCharIndex;
gfxSkipCharsIterator skipCharsIter =
frame->EnsureTextRun(nsTextFrame::eInflated);
gfxTextRun* textRun = frame->GetTextRun(nsTextFrame::eInflated);
Range range =
ConvertOriginalToSkipped(skipCharsIter, offset, trimmedLength);
// Accumulate the advance.
textLength += textRun->GetAdvanceWidth(range, nullptr);
}
// Advance, ready for next call:
frameStartTextElementCharIndex += untrimmedLength;
}
nsPresContext* presContext = PresContext();
float cssPxPerDevPx = presContext->
AppUnitsToFloatCSSPixels(presContext->AppUnitsPerDevPixel());
*aResult = presContext->AppUnitsToGfxUnits(textLength) *
cssPxPerDevPx / mFontSizeScaleFactor;
return NS_OK;
}
nsresult
SVGTextFrame::GetSubStringLengthSlowFallback(nsIContent* aContent,
uint32_t charnum, uint32_t nchars,
float* aResult)
{
// We need to make sure that we've been reflowed before updating the glyph
// positioning.
// XXX perf: It may be possible to limit reflow to just calling ReflowSVG,
// but we would still need to resort to full reflow for percentage
// positioning attributes. For now we just do a full reflow regardless since
// the cases that would cause us to be called are relatively uncommon.
PresContext()->PresShell()->FlushPendingNotifications(FlushType::Layout);
UpdateGlyphPositioning();
// Convert charnum/nchars from addressable characters relative to

View File

@ -418,6 +418,18 @@ private:
*/
void DoGlyphPositioning();
/**
* This fallback version of GetSubStringLength that flushes layout and takes
* into account glyph positioning. As per the SVG 2 spec, typically glyph
* positioning does not affect the results of getSubStringLength, but one
* exception is text in a textPath where we need to ignore characters that
* fall off the end of the textPath path.
*/
nsresult GetSubStringLengthSlowFallback(nsIContent* aContent,
uint32_t charnum,
uint32_t nchars,
float* aResult);
/**
* Converts the specified index into mPositions to an addressable
* character index (as can be used with the SVG DOM text methods)