Actually provide an implementation for GetRenderedtext()

This commit is contained in:
Pale Moon
2017-02-23 18:31:00 +01:00
committed by roytam1
parent 69e5e7e7c5
commit 29b3acea0a
6 changed files with 157 additions and 83 deletions
+122 -58
View File
@@ -3936,9 +3936,8 @@ a11y::AccType
nsTextFrame::AccessibleType()
{
if (IsEmpty()) {
nsAutoString renderedWhitespace;
GetRenderedText(&renderedWhitespace, nullptr, nullptr, 0, 1);
if (renderedWhitespace.IsEmpty()) {
RenderedText text = GetRenderedText();
if (text.mString.IsEmpty()) {
return a11y::eNoType;
}
}
@@ -4051,13 +4050,6 @@ public:
virtual void AddInlinePrefISize(nsRenderingContext *aRenderingContext,
InlinePrefISizeData *aData) override;
virtual nsresult GetRenderedText(nsAString* aString = nullptr,
gfxSkipChars* aSkipChars = nullptr,
gfxSkipCharsIterator* aSkipIter = nullptr,
uint32_t aSkippedStartOffset = 0,
uint32_t aSkippedMaxLength = UINT32_MAX) override
{ return NS_ERROR_NOT_IMPLEMENTED; } // Call on a primary text frame only
protected:
explicit nsContinuingTextFrame(nsStyleContext* aContext) : nsTextFrame(aContext) {}
nsIFrame* mPrevContinuation;
@@ -8750,79 +8742,151 @@ static char16_t TransformChar(const nsStyleText* aStyle, gfxTextRun* aTextRun,
return aChar;
}
nsresult nsTextFrame::GetRenderedText(nsAString* aAppendToString,
gfxSkipChars* aSkipChars,
gfxSkipCharsIterator* aSkipIter,
uint32_t aSkippedStartOffset,
uint32_t aSkippedMaxLength)
static bool
LineEndsInHardLineBreak(nsTextFrame* aFrame)
{
// The handling of aSkippedStartOffset and aSkippedMaxLength could be more efficient...
gfxSkipChars skipChars;
nsIFrame* lineContainer = FindLineContainer(aFrame);
nsBlockFrame* block = do_QueryFrame(lineContainer);
if (!block) {
// Weird situation where we have a line layout without a block.
// No soft breaks occur in this situation.
return true;
}
bool foundValidLine;
nsBlockInFlowLineIterator iter(block, aFrame, &foundValidLine);
if (!foundValidLine) {
NS_ERROR("Invalid line!");
return true;
}
return !iter.GetLine()->IsLineWrapped();
}
nsIFrame::RenderedText
nsTextFrame::GetRenderedText(uint32_t aStartOffset,
uint32_t aEndOffset,
TextOffsetType aOffsetType)
{
NS_ASSERTION(!GetPrevContinuation(), "Must be called on first-in-flow");
// The handling of offsets could be more efficient...
RenderedText result;
nsTextFrame* textFrame;
const nsTextFragment* textFrag = mContent->GetText();
uint32_t keptCharsLength = 0;
uint32_t validCharsLength = 0;
uint32_t offsetInRenderedString = 0;
bool haveOffsets = false;
// Build skipChars and copy text, for each text frame in this continuation block
for (textFrame = this; textFrame;
textFrame = static_cast<nsTextFrame*>(textFrame->GetNextContinuation())) {
// For each text frame continuation in this block ...
if (textFrame->GetStateBits() & NS_FRAME_IS_DIRTY) {
// We don't trust dirty frames, expecially when computing rendered text.
// We don't trust dirty frames, especially when computing rendered text.
break;
}
// Ensure the text run and grab the gfxSkipCharsIterator for it
gfxSkipCharsIterator iter =
textFrame->EnsureTextRun(nsTextFrame::eInflated);
if (!textFrame->mTextRun)
return NS_ERROR_FAILURE;
if (!textFrame->mTextRun) {
break;
}
gfxSkipCharsIterator tmpIter = iter;
// Skip to the start of the text run, past ignored chars at start of line
// XXX In the future we may decide to trim extra spaces before a hard line
// break, in which case we need to accurately detect those sitations and
// call GetTrimmedOffsets() with true to trim whitespace at the line's end
TrimmedOffsets trimmedContentOffsets = textFrame->GetTrimmedOffsets(textFrag, false);
int32_t startOfLineSkipChars = trimmedContentOffsets.mStart - textFrame->mContentOffset;
if (startOfLineSkipChars > 0) {
skipChars.SkipChars(startOfLineSkipChars);
iter.SetOriginalOffset(trimmedContentOffsets.mStart);
TrimmedOffsets trimmedOffsets = textFrame->GetTrimmedOffsets(textFrag,
textFrame->IsAtEndOfLine() && LineEndsInHardLineBreak(textFrame));
bool trimmedSignificantNewline =
trimmedOffsets.GetEnd() < GetContentEnd() &&
HasSignificantTerminalNewline();
uint32_t skippedToRenderedStringOffset = offsetInRenderedString -
tmpIter.ConvertOriginalToSkipped(trimmedOffsets.mStart);
uint32_t nextOffsetInRenderedString =
tmpIter.ConvertOriginalToSkipped(trimmedOffsets.GetEnd()) +
(trimmedSignificantNewline ? 1 : 0) + skippedToRenderedStringOffset;
if (aOffsetType == TextOffsetType::OFFSETS_IN_RENDERED_TEXT) {
if (nextOffsetInRenderedString <= aStartOffset) {
offsetInRenderedString = nextOffsetInRenderedString;
continue;
}
if (!haveOffsets) {
result.mOffsetWithinNodeText =
tmpIter.ConvertSkippedToOriginal(aStartOffset - skippedToRenderedStringOffset);
result.mOffsetWithinNodeRenderedText = aStartOffset;
haveOffsets = true;
}
if (offsetInRenderedString >= aEndOffset) {
break;
}
} else {
if (uint32_t(textFrame->GetContentEnd()) <= aStartOffset) {
offsetInRenderedString = nextOffsetInRenderedString;
continue;
}
if (!haveOffsets) {
result.mOffsetWithinNodeText = aStartOffset;
// Skip trimmed space when computed the rendered text offset.
int32_t clamped = std::max<int32_t>(aStartOffset, trimmedOffsets.mStart);
result.mOffsetWithinNodeRenderedText =
tmpIter.ConvertOriginalToSkipped(clamped) + skippedToRenderedStringOffset;
MOZ_ASSERT(result.mOffsetWithinNodeRenderedText >= offsetInRenderedString &&
result.mOffsetWithinNodeRenderedText <= INT32_MAX,
"Bad offset within rendered text");
haveOffsets = true;
}
if (uint32_t(textFrame->mContentOffset) >= aEndOffset) {
break;
}
}
int32_t startOffset;
int32_t endOffset;
if (aOffsetType == TextOffsetType::OFFSETS_IN_RENDERED_TEXT) {
startOffset =
tmpIter.ConvertSkippedToOriginal(aStartOffset - skippedToRenderedStringOffset);
endOffset =
tmpIter.ConvertSkippedToOriginal(aEndOffset - skippedToRenderedStringOffset);
} else {
startOffset = aStartOffset;
endOffset = std::min<uint32_t>(INT32_MAX, aEndOffset);
}
trimmedOffsets.mStart = std::max<uint32_t>(trimmedOffsets.mStart,
startOffset);
trimmedOffsets.mLength = std::min<uint32_t>(trimmedOffsets.GetEnd(),
endOffset) - trimmedOffsets.mStart;
if (trimmedOffsets.mLength <= 0) {
offsetInRenderedString = nextOffsetInRenderedString;
continue;
}
// Keep and copy the appropriate chars withing the caller's requested range
const nsStyleText* textStyle = textFrame->StyleText();
while (iter.GetOriginalOffset() < trimmedContentOffsets.GetEnd() &&
keptCharsLength < aSkippedMaxLength) {
// For each original char from content text
if (iter.IsOriginalCharSkipped() || ++validCharsLength <= aSkippedStartOffset) {
skipChars.SkipChar();
} else {
++keptCharsLength;
skipChars.KeepChar();
if (aAppendToString) {
aAppendToString->Append(
TransformChar(textStyle, textFrame->mTextRun, iter.GetSkippedOffset(),
textFrag->CharAt(iter.GetOriginalOffset())));
iter.SetOriginalOffset(trimmedOffsets.mStart);
while (iter.GetOriginalOffset() < trimmedOffsets.GetEnd()) {
char16_t ch = textFrag->CharAt(iter.GetOriginalOffset());
if (iter.IsOriginalCharSkipped()) {
if (ch == CH_SHY) {
// We should preserve soft hyphens. They can't be transformed.
result.mString.Append(ch);
}
} else {
result.mString.Append(
TransformChar(textStyle, textFrame->mTextRun,
iter.GetSkippedOffset(), ch));
}
iter.AdvanceOriginal(1);
}
if (keptCharsLength >= aSkippedMaxLength) {
break; // Already past the end, don't build string or gfxSkipCharsIter anymore
}
}
if (aSkipChars) {
aSkipChars->TakeFrom(&skipChars); // Copy skipChars into aSkipChars
if (aSkipIter) {
// Caller must provide both pointers in order to retrieve a gfxSkipCharsIterator,
// because the gfxSkipCharsIterator holds a weak pointer to the gfxSkipChars.
*aSkipIter = gfxSkipCharsIterator(*aSkipChars, GetContentLength());
if (trimmedSignificantNewline && GetContentEnd() <= endOffset) {
// A significant newline was trimmed off (we must be
// white-space:pre-line). Put it back.
result.mString.Append('\n');
}
offsetInRenderedString = nextOffsetInRenderedString;
}
return NS_OK;
if (!haveOffsets) {
result.mOffsetWithinNodeText = textFrag->GetLength();
result.mOffsetWithinNodeRenderedText = offsetInRenderedString;
}
return result;
}
nsIAtom*