--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -175,34 +175,32 @@ TabWidthStore::ApplySpacing(gfxTextRun::
NS_DECLARE_FRAME_PROPERTY_DELETABLE(TabWidthProperty, TabWidthStore)
NS_DECLARE_FRAME_PROPERTY_WITHOUT_DTOR(OffsetToFrameProperty, nsTextFrame)
NS_DECLARE_FRAME_PROPERTY_WITHOUT_DTOR(UninflatedTextRunProperty, gfxTextRun)
NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(FontSizeInflationProperty, float)
+/**
+ * A glyph observer for the change of a font glyph in a text run.
+ *
+ * This is stored in {Simple, Complex}TextRunUserData.
+ */
class GlyphObserver : public gfxFont::GlyphChangeObserver {
public:
- GlyphObserver(gfxFont* aFont, nsTextFrame* aFrame)
- : gfxFont::GlyphChangeObserver(aFont), mFrame(aFrame) {}
+ GlyphObserver(gfxFont* aFont, gfxTextRun* aTextRun)
+ : gfxFont::GlyphChangeObserver(aFont), mTextRun(aTextRun) {
+ MOZ_ASSERT(aTextRun->GetUserData());
+ }
virtual void NotifyGlyphsChanged() override;
private:
- nsTextFrame* mFrame;
+ gfxTextRun* mTextRun;
};
-/**
- * This property is set on text frames with TEXT_IN_TEXTRUN_USER_DATA set that
- * have potentially-animated glyphs.
- * The only reason this list is in a property is to automatically destroy the
- * list when the frame is deleted, unregistering the observers.
- */
-NS_DECLARE_FRAME_PROPERTY_DELETABLE(TextFrameGlyphObservers,
- nsTArray<UniquePtr<GlyphObserver>>)
-
static const nsFrameState TEXT_REFLOW_FLAGS =
TEXT_FIRST_LETTER |
TEXT_START_OF_LINE |
TEXT_END_OF_LINE |
TEXT_HYPHEN_BREAK |
TEXT_TRIMMED_TRAILING_WHITESPACE |
TEXT_JUSTIFICATION_ENABLED |
TEXT_HAS_NONCOLLAPSED_CHARACTERS |
@@ -218,32 +216,66 @@ static const nsFrameState TEXT_WHITESPAC
*
* Text frames delegate work to gfxTextRun objects. The gfxTextRun object
* transforms text to positioned glyphs. It can report the geometry of the
* glyphs and paint them. Text frames configure gfxTextRuns by providing text,
* spacing, language, and other information.
*
* A gfxTextRun can cover more than one DOM text node. This is necessary to
* get kerning, ligatures and shaping for text that spans multiple text nodes
- * but is all the same font. The userdata for a gfxTextRun object is a
- * TextRunUserData* or an nsIFrame*.
- *
+ * but is all the same font.
+ *
+ * The userdata for a gfxTextRun object can be:
+ *
+ * - A nsTextFrame* in the case a text run maps to only one flow. In this
+ * case, the textrun's user data pointer is a pointer to mStartFrame for that
+ * flow, mDOMOffsetToBeforeTransformOffset is zero, and mContentLength is the
+ * length of the text node.
+ *
+ * - A SimpleTextRunUserData in the case a text run maps to one flow, but we
+ * still have to keep a list of glyph observers.
+ *
+ * - A ComplexTextRunUserData in the case a text run maps to multiple flows,
+ * but we need to keep a list of glyph observers.
+ *
+ * - A TextRunUserData in the case a text run maps multiple flows, but it
+ * doesn't have any glyph observer for changes in SVG fonts.
+ *
+ * You can differentiate between the four different cases with the
+ * TEXT_IS_SIMPLE_FLOW and TEXT_MIGHT_HAVE_GLYPH_CHANGES flags.
+ *
* We go to considerable effort to make sure things work even if in-flow
* siblings have different style contexts (i.e., first-letter and first-line).
- *
+ *
* Our convention is that unsigned integer character offsets are offsets into
* the transformed string. Signed integer character offsets are offsets into
* the DOM string.
- *
+ *
* XXX currently we don't handle hyphenated breaks between text frames where the
* hyphen occurs at the end of the first text frame, e.g.
* <b>Kit­</b>ty
*/
/**
+ * This is our user data for the textrun, when textRun->GetFlags() has
+ * TEXT_IS_SIMPLE_FLOW set, and also TEXT_MIGHT_HAVE_GLYPH_CHANGES.
+ *
+ * This allows having an array of observers if there are fonts whose glyphs
+ * might change, but also avoid allocation in the simple case that there aren't.
+ */
+struct SimpleTextRunUserData {
+ nsTArray<UniquePtr<GlyphObserver>> mGlyphObservers;
+ nsTextFrame* mFrame;
+ explicit SimpleTextRunUserData(nsTextFrame* aFrame)
+ : mFrame(aFrame)
+ {
+ }
+};
+
+/**
* We use an array of these objects to record which text frames
* are associated with the textrun. mStartFrame is the start of a list of
* text frames. Some sequence of its continuations are covered by the textrun.
* A content textnode can have at most one TextRunMappedFlow associated with it
* for a given textrun.
*
* mDOMOffsetToBeforeTransformOffset is added to DOM offsets for those frames to obtain
* the offset into the before-transformation text of the textrun. It can be
@@ -254,29 +286,37 @@ static const nsFrameState TEXT_WHITESPAC
struct TextRunMappedFlow {
nsTextFrame* mStartFrame;
int32_t mDOMOffsetToBeforeTransformOffset;
// The text mapped starts at mStartFrame->GetContentOffset() and is this long
uint32_t mContentLength;
};
/**
- * This is our user data for the textrun, when textRun->GetFlags() does not
- * have TEXT_IS_SIMPLE_FLOW set. When TEXT_IS_SIMPLE_FLOW is set, there is
- * just one flow, the textrun's user data pointer is a pointer to mStartFrame
- * for that flow, mDOMOffsetToBeforeTransformOffset is zero, and mContentLength
- * is the length of the text node.
+ * This is the type in the gfxTextRun's userdata field in the common case that
+ * the text run maps to multiple flows, but no fonts have been found with
+ * animatable glyphs.
+ *
+ * This way, we avoid allocating and constructing the extra nsTArray.
*/
struct TextRunUserData {
TextRunMappedFlow* mMappedFlows;
uint32_t mMappedFlowCount;
uint32_t mLastFlowIndex;
};
/**
+ * This is our user data for the textrun, when textRun->GetFlags() does not
+ * have TEXT_IS_SIMPLE_FLOW set and has the TEXT_MIGHT HAVE_GLYPH_CHANGES flag.
+ */
+struct ComplexTextRunUserData : public TextRunUserData {
+ nsTArray<UniquePtr<GlyphObserver>> mGlyphObservers;
+};
+
+/**
* This helper object computes colors used for painting, and also IME
* underline information. The data is computed lazily and cached as necessary.
* These live for just the duration of one paint operation.
*/
class nsTextPaintStyle {
public:
explicit nsTextPaintStyle(nsTextFrame* aFrame);
@@ -408,23 +448,93 @@ protected:
void InitSelectionStyle(int32_t aIndex);
bool EnsureSufficientContrast(nscolor *aForeColor, nscolor *aBackColor);
nscolor GetResolvedForeColor(nscolor aColor, nscolor aDefaultForeColor,
nscolor aBackColor);
};
+static TextRunUserData*
+CreateUserData(uint32_t aMappedFlowCount)
+{
+ TextRunUserData* data = static_cast<TextRunUserData*>
+ (moz_xmalloc(sizeof(TextRunUserData) +
+ aMappedFlowCount * sizeof(TextRunMappedFlow)));
+ data->mMappedFlows = reinterpret_cast<TextRunMappedFlow*>(data + 1);
+ data->mMappedFlowCount = aMappedFlowCount;
+ data->mLastFlowIndex = 0;
+ return data;
+}
+
static void
-DestroyUserData(void* aUserData)
-{
- TextRunUserData* userData = static_cast<TextRunUserData*>(aUserData);
- if (userData) {
- free(userData);
- }
+DestroyUserData(TextRunUserData* aUserData)
+{
+ if (aUserData) {
+ free(aUserData);
+ }
+}
+
+static ComplexTextRunUserData*
+CreateComplexUserData(uint32_t aMappedFlowCount)
+{
+ ComplexTextRunUserData* data = static_cast<ComplexTextRunUserData*>
+ (moz_xmalloc(sizeof(ComplexTextRunUserData) +
+ aMappedFlowCount * sizeof(TextRunMappedFlow)));
+ new (data) ComplexTextRunUserData();
+ data->mMappedFlows = reinterpret_cast<TextRunMappedFlow*>(data + 1);
+ data->mMappedFlowCount = aMappedFlowCount;
+ data->mLastFlowIndex = 0;
+ return data;
+}
+
+static void
+DestroyComplexUserData(ComplexTextRunUserData* aUserData)
+{
+ if (aUserData) {
+ aUserData->~ComplexTextRunUserData();
+ free(aUserData);
+ }
+}
+
+static void
+DestroyTextRunUserData(gfxTextRun* aTextRun)
+{
+ MOZ_ASSERT(aTextRun->GetUserData());
+ if (aTextRun->GetFlags() & nsTextFrameUtils::TEXT_IS_SIMPLE_FLOW) {
+ if (aTextRun->GetFlags() & nsTextFrameUtils::TEXT_MIGHT_HAVE_GLYPH_CHANGES) {
+ delete static_cast<SimpleTextRunUserData*>(aTextRun->GetUserData());
+ }
+ } else {
+ if (aTextRun->GetFlags() & nsTextFrameUtils::TEXT_MIGHT_HAVE_GLYPH_CHANGES) {
+ DestroyComplexUserData(
+ static_cast<ComplexTextRunUserData*>(aTextRun->GetUserData()));
+ } else {
+ DestroyUserData(
+ static_cast<TextRunUserData*>(aTextRun->GetUserData()));
+ }
+ }
+ aTextRun->ClearFlagBits(nsTextFrameUtils::TEXT_MIGHT_HAVE_GLYPH_CHANGES);
+ aTextRun->SetUserData(nullptr);
+}
+
+/**
+ * These are utility functions just for helping with the complexity related with
+ * the text runs user data.
+ */
+static nsTextFrame*
+GetFrameForSimpleFlow(gfxTextRun* aTextRun)
+{
+ MOZ_ASSERT(aTextRun->GetFlags() & nsTextFrameUtils::TEXT_IS_SIMPLE_FLOW,
+ "Not so simple flow?");
+ if (aTextRun->GetFlags() & nsTextFrameUtils::TEXT_MIGHT_HAVE_GLYPH_CHANGES) {
+ return static_cast<SimpleTextRunUserData*>(aTextRun->GetUserData())->mFrame;
+ }
+
+ return static_cast<nsTextFrame*>(aTextRun->GetUserData());
}
/**
* Remove |aTextRun| from the frame continuation chain starting at
* |aStartContinuation| if non-null, otherwise starting at |aFrame|.
* Unmark |aFrame| as a text run owner if it's the frame we start at.
* Return true if |aStartContinuation| is non-null and was found
* in the next-continuation chain of |aFrame|.
@@ -470,82 +580,76 @@ ClearAllTextRunReferences(nsTextFrame* a
* @note the caller is expected to take care of possibly destroying the
* text run if all userdata frames were reset (userdata is deallocated
* by this function though). The caller can detect this has occured by
* checking |aTextRun->GetUserData() == nullptr|.
*/
static void
UnhookTextRunFromFrames(gfxTextRun* aTextRun, nsTextFrame* aStartContinuation)
{
- if (!aTextRun->GetUserData())
+ if (!aTextRun->GetUserData()) {
return;
+ }
if (aTextRun->GetFlags() & nsTextFrameUtils::TEXT_IS_SIMPLE_FLOW) {
- nsTextFrame* userDataFrame = static_cast<nsTextFrame*>(
- static_cast<nsIFrame*>(aTextRun->GetUserData()));
+ nsTextFrame* userDataFrame = GetFrameForSimpleFlow(aTextRun);
nsFrameState whichTextRunState =
userDataFrame->GetTextRun(nsTextFrame::eInflated) == aTextRun
? TEXT_IN_TEXTRUN_USER_DATA
: TEXT_IN_UNINFLATED_TEXTRUN_USER_DATA;
DebugOnly<bool> found =
ClearAllTextRunReferences(userDataFrame, aTextRun,
aStartContinuation, whichTextRunState);
NS_ASSERTION(!aStartContinuation || found,
"aStartContinuation wasn't found in simple flow text run");
if (!(userDataFrame->GetStateBits() & whichTextRunState)) {
- aTextRun->SetUserData(nullptr);
+ DestroyTextRunUserData(aTextRun);
}
} else {
- TextRunUserData* userData =
- static_cast<TextRunUserData*>(aTextRun->GetUserData());
+ auto userData = static_cast<TextRunUserData*>(aTextRun->GetUserData());
int32_t destroyFromIndex = aStartContinuation ? -1 : 0;
for (uint32_t i = 0; i < userData->mMappedFlowCount; ++i) {
nsTextFrame* userDataFrame = userData->mMappedFlows[i].mStartFrame;
nsFrameState whichTextRunState =
userDataFrame->GetTextRun(nsTextFrame::eInflated) == aTextRun
? TEXT_IN_TEXTRUN_USER_DATA
: TEXT_IN_UNINFLATED_TEXTRUN_USER_DATA;
bool found =
ClearAllTextRunReferences(userDataFrame, aTextRun,
aStartContinuation, whichTextRunState);
if (found) {
if (userDataFrame->GetStateBits() & whichTextRunState) {
destroyFromIndex = i + 1;
- }
- else {
+ } else {
destroyFromIndex = i;
}
aStartContinuation = nullptr;
}
}
NS_ASSERTION(destroyFromIndex >= 0,
"aStartContinuation wasn't found in multi flow text run");
if (destroyFromIndex == 0) {
- DestroyUserData(userData);
- aTextRun->SetUserData(nullptr);
- }
- else {
+ DestroyTextRunUserData(aTextRun);
+ } else {
userData->mMappedFlowCount = uint32_t(destroyFromIndex);
if (userData->mLastFlowIndex >= uint32_t(destroyFromIndex)) {
userData->mLastFlowIndex = uint32_t(destroyFromIndex) - 1;
}
}
}
}
-void
-GlyphObserver::NotifyGlyphsChanged()
-{
- nsIPresShell* shell = mFrame->PresContext()->PresShell();
- for (nsIFrame* f = mFrame; f;
+static void
+InvalidateFrameDueToGlyphsChanged(nsIFrame* aFrame)
+{
+ MOZ_ASSERT(aFrame);
+
+ nsIPresShell* shell = aFrame->PresContext()->PresShell();
+ for (nsIFrame* f = aFrame; f;
f = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(f)) {
- if (f != mFrame && f->HasAnyStateBits(TEXT_IN_TEXTRUN_USER_DATA)) {
- // f will have its own GlyphObserver (if needed) so we can stop here.
- break;
- }
f->InvalidateFrame();
// If this is a non-display text frame within SVG <text>, we need
// to reflow the SVGTextFrame. (This is similar to reflowing the
// SVGTextFrame in response to style changes, in
// SVGTextFrame::DidSetStyleContext.)
if (f->IsSVGText() && f->GetStateBits() & NS_FRAME_IS_NONDISPLAY) {
auto svgTextFrame = static_cast<SVGTextFrame*>(
@@ -558,16 +662,30 @@ GlyphObserver::NotifyGlyphsChanged()
// we should probably do lazily here since there could be a lot
// of text frames affected and we'd like to coalesce the work. So that's
// not easy to do well.
shell->FrameNeedsReflow(f, nsIPresShell::eResize, NS_FRAME_IS_DIRTY);
}
}
}
+void
+GlyphObserver::NotifyGlyphsChanged()
+{
+ if (mTextRun->GetFlags() & nsTextFrameUtils::TEXT_IS_SIMPLE_FLOW) {
+ InvalidateFrameDueToGlyphsChanged(GetFrameForSimpleFlow(mTextRun));
+ return;
+ }
+
+ auto data = static_cast<TextRunUserData*>(mTextRun->GetUserData());
+ for (uint32_t i = 0; i < data->mMappedFlowCount; ++i) {
+ InvalidateFrameDueToGlyphsChanged(data->mMappedFlows[i].mStartFrame);
+ }
+}
+
int32_t nsTextFrame::GetContentEnd() const {
nsTextFrame* next = static_cast<nsTextFrame*>(GetNextContinuation());
return next ? next->GetContentOffset() : mContent->GetText()->GetLength();
}
struct FlowLengthProperty {
int32_t mStartOffset;
// The offset of the next fixed continuation after mStartOffset, or
@@ -739,69 +857,91 @@ IsAllWhitespace(const nsTextFragment* aF
if (ch == ' ' || ch == '\t' || ch == '\r' || (ch == '\n' && aAllowNewline))
continue;
return false;
}
return true;
}
static void
-CreateObserverForAnimatedGlyphs(nsTextFrame* aFrame, const nsTArray<gfxFont*>& aFonts)
-{
- if (!(aFrame->GetStateBits() & TEXT_IN_TEXTRUN_USER_DATA)) {
- // Maybe the textrun was created for uninflated text.
+ClearObserversFromTextRun(gfxTextRun* aTextRun)
+{
+ if (!(aTextRun->GetFlags() & nsTextFrameUtils::TEXT_MIGHT_HAVE_GLYPH_CHANGES)) {
return;
}
- nsTArray<UniquePtr<GlyphObserver>>* observers =
- new nsTArray<UniquePtr<GlyphObserver>>();
- for (uint32_t i = 0, count = aFonts.Length(); i < count; ++i) {
- observers->AppendElement(new GlyphObserver(aFonts[i], aFrame));
- }
- aFrame->Properties().Set(TextFrameGlyphObservers(), observers);
- // We are lazy and don't try to remove a property value that might be
- // obsolete due to style changes or font selection changes. That is
- // likely to be rarely needed, and we don't want to eat the overhead of
- // doing it for the overwhelmingly common case of no property existing.
- // (And we're out of state bits to conveniently use for a fast property
- // existence check.) The only downside is that in some rare cases we might
- // keep fonts alive for longer than necessary, or unnecessarily invalidate
- // frames.
+ if (aTextRun->GetFlags() & nsTextFrameUtils::TEXT_IS_SIMPLE_FLOW) {
+ static_cast<SimpleTextRunUserData*>(aTextRun->GetUserData())
+ ->mGlyphObservers.Clear();
+ } else {
+ static_cast<ComplexTextRunUserData*>(aTextRun->GetUserData())
+ ->mGlyphObservers.Clear();
+ }
}
static void
CreateObserversForAnimatedGlyphs(gfxTextRun* aTextRun)
{
if (!aTextRun->GetUserData()) {
return;
}
+
+ ClearObserversFromTextRun(aTextRun);
+
nsTArray<gfxFont*> fontsWithAnimatedGlyphs;
uint32_t numGlyphRuns;
const gfxTextRun::GlyphRun* glyphRuns =
aTextRun->GetGlyphRuns(&numGlyphRuns);
for (uint32_t i = 0; i < numGlyphRuns; ++i) {
gfxFont* font = glyphRuns[i].mFont;
if (font->GlyphsMayChange() && !fontsWithAnimatedGlyphs.Contains(font)) {
fontsWithAnimatedGlyphs.AppendElement(font);
}
}
if (fontsWithAnimatedGlyphs.IsEmpty()) {
+ // NB: Theoretically, we should clear the TEXT_MIGHT_HAVE_GLYPH_CHANGES
+ // here. That would involve de-allocating the simple user data struct if
+ // present too, and resetting the pointer to the frame. In practice, I
+ // don't think worth doing that work here, given the flag's only purpose is
+ // to distinguish what kind of user data is there.
return;
}
+ nsTArray<UniquePtr<GlyphObserver>>* observers;
+
if (aTextRun->GetFlags() & nsTextFrameUtils::TEXT_IS_SIMPLE_FLOW) {
- CreateObserverForAnimatedGlyphs(static_cast<nsTextFrame*>(
- static_cast<nsIFrame*>(aTextRun->GetUserData())), fontsWithAnimatedGlyphs);
+ // Swap the frame pointer for a just-allocated SimpleTextRunUserData if
+ // appropriate.
+ if (!(aTextRun->GetFlags() & nsTextFrameUtils::TEXT_MIGHT_HAVE_GLYPH_CHANGES)) {
+ auto frame = static_cast<nsTextFrame*>(aTextRun->GetUserData());
+ aTextRun->SetUserData(new SimpleTextRunUserData(frame));
+ }
+
+ auto data =
+ static_cast<SimpleTextRunUserData*>(aTextRun->GetUserData());
+ observers = &data->mGlyphObservers;
} else {
- TextRunUserData* userData =
- static_cast<TextRunUserData*>(aTextRun->GetUserData());
- for (uint32_t i = 0; i < userData->mMappedFlowCount; ++i) {
- CreateObserverForAnimatedGlyphs(userData->mMappedFlows[i].mStartFrame,
- fontsWithAnimatedGlyphs);
- }
+ if (!(aTextRun->GetFlags() & nsTextFrameUtils::TEXT_MIGHT_HAVE_GLYPH_CHANGES)) {
+ auto oldData = static_cast<TextRunUserData*>(aTextRun->GetUserData());
+ auto data = CreateComplexUserData(oldData->mMappedFlowCount);
+ data->mLastFlowIndex = oldData->mLastFlowIndex;
+ for (uint32_t i = 0; i < oldData->mMappedFlowCount; ++i) {
+ data->mMappedFlows[i] = oldData->mMappedFlows[i];
+ }
+ DestroyUserData(oldData);
+ aTextRun->SetUserData(data);
+ }
+ auto data = static_cast<ComplexTextRunUserData*>(aTextRun->GetUserData());
+ observers = &data->mGlyphObservers;
+ }
+
+ aTextRun->SetFlagBits(nsTextFrameUtils::TEXT_MIGHT_HAVE_GLYPH_CHANGES);
+
+ for (auto font : fontsWithAnimatedGlyphs) {
+ observers->AppendElement(new GlyphObserver(font, aTextRun));
}
}
/**
* This class accumulates state as we scan a paragraph of text. It detects
* textrun boundaries (changes from text to non-text, hard
* line breaks, and font changes) and builds a gfxTextRun at each boundary.
* It also detects linebreaker run boundaries (changes from text to non-text,
@@ -940,19 +1080,17 @@ public:
nsTransformedTextRun* transformedTextRun =
static_cast<nsTransformedTextRun*>(mTextRun);
transformedTextRun->SetCapitalization(aOffset + mOffsetIntoTextRun, aLength,
aCapitalize);
}
}
void Finish(gfxMissingFontRecorder* aMFR) {
- MOZ_ASSERT(!(mTextRun->GetFlags() &
- (gfxTextRunFactory::TEXT_UNUSED_FLAGS |
- nsTextFrameUtils::TEXT_UNUSED_FLAG)),
+ MOZ_ASSERT(!(mTextRun->GetFlags() & nsTextFrameUtils::TEXT_UNUSED_FLAG),
"Flag set that should never be set! (memory safety error?)");
if (mTextRun->GetFlags() & nsTextFrameUtils::TEXT_IS_TRANSFORMED) {
nsTransformedTextRun* transformedTextRun =
static_cast<nsTransformedTextRun*>(mTextRun);
transformedTextRun->FinishSettingProperties(mDrawTarget, aMFR);
}
// The way nsTransformedTextRun is implemented, its glyph runs aren't
// available until after nsTransformedTextRun::FinishSettingProperties()
@@ -1395,22 +1533,23 @@ ExpandBuffer(char16_t* aDest, uint8_t* a
++aSrc;
--aCount;
}
return aDest;
}
bool BuildTextRunsScanner::IsTextRunValidForMappedFlows(gfxTextRun* aTextRun)
{
- if (aTextRun->GetFlags() & nsTextFrameUtils::TEXT_IS_SIMPLE_FLOW)
+ if (aTextRun->GetFlags() & nsTextFrameUtils::TEXT_IS_SIMPLE_FLOW) {
return mMappedFlows.Length() == 1 &&
- mMappedFlows[0].mStartFrame == static_cast<nsTextFrame*>(aTextRun->GetUserData()) &&
+ mMappedFlows[0].mStartFrame == GetFrameForSimpleFlow(aTextRun) &&
mMappedFlows[0].mEndFrame == nullptr;
-
- TextRunUserData* userData = static_cast<TextRunUserData*>(aTextRun->GetUserData());
+ }
+
+ auto userData = static_cast<TextRunUserData*>(aTextRun->GetUserData());
if (userData->mMappedFlowCount != mMappedFlows.Length())
return false;
for (uint32_t i = 0; i < mMappedFlows.Length(); ++i) {
if (userData->mMappedFlows[i].mStartFrame != mMappedFlows[i].mStartFrame ||
int32_t(userData->mMappedFlows[i].mContentLength) !=
mMappedFlows[i].GetContentEnd() - mMappedFlows[i].mStartFrame->GetContentOffset())
return false;
}
@@ -1870,24 +2009,22 @@ BuildTextRunsScanner::BuildTextRunForFra
TextRunUserData* userDataToDestroy;
// If the situation is particularly simple (and common) we don't need to
// allocate userData.
if (mMappedFlows.Length() == 1 && !mMappedFlows[0].mEndFrame &&
mMappedFlows[0].mStartFrame->GetContentOffset() == 0) {
userData = &dummyData;
userDataToDestroy = nullptr;
dummyData.mMappedFlows = &dummyMappedFlow;
+ dummyData.mMappedFlowCount = mMappedFlows.Length();
+ dummyData.mLastFlowIndex = 0;
} else {
- userData = static_cast<TextRunUserData*>
- (moz_xmalloc(sizeof(TextRunUserData) + mMappedFlows.Length()*sizeof(TextRunMappedFlow)));
+ userData = CreateUserData(mMappedFlows.Length());
userDataToDestroy = userData;
- userData->mMappedFlows = reinterpret_cast<TextRunMappedFlow*>(userData + 1);
- }
- userData->mMappedFlowCount = mMappedFlows.Length();
- userData->mLastFlowIndex = 0;
+ }
uint32_t currentTransformedTextOffset = 0;
uint32_t nextBreakIndex = 0;
nsTextFrame* nextBreakBeforeFrame = GetNextBreakBeforeFrame(&nextBreakIndex);
bool isSVG = mLineContainer->IsSVGText();
bool enabledJustification =
(mLineContainer->StyleText()->mTextAlign == NS_STYLE_TEXT_ALIGN_JUSTIFY ||
@@ -2275,24 +2412,22 @@ BuildTextRunsScanner::SetupLineBreakerCo
TextRunUserData* userDataToDestroy;
// If the situation is particularly simple (and common) we don't need to
// allocate userData.
if (mMappedFlows.Length() == 1 && !mMappedFlows[0].mEndFrame &&
mMappedFlows[0].mStartFrame->GetContentOffset() == 0) {
userData = &dummyData;
userDataToDestroy = nullptr;
dummyData.mMappedFlows = &dummyMappedFlow;
+ dummyData.mMappedFlowCount = mMappedFlows.Length();
+ dummyData.mLastFlowIndex = 0;
} else {
- userData = static_cast<TextRunUserData*>
- (moz_xmalloc(sizeof(TextRunUserData) + mMappedFlows.Length()*sizeof(TextRunMappedFlow)));
+ userData = CreateUserData(mMappedFlows.Length());
userDataToDestroy = userData;
- userData->mMappedFlows = reinterpret_cast<TextRunMappedFlow*>(userData + 1);
- }
- userData->mMappedFlowCount = mMappedFlows.Length();
- userData->mLastFlowIndex = 0;
+ }
uint32_t nextBreakIndex = 0;
nsTextFrame* nextBreakBeforeFrame = GetNextBreakBeforeFrame(&nextBreakIndex);
const nsStyleText* textStyle = nullptr;
for (uint32_t i = 0; i < mMappedFlows.Length(); ++i) {
MappedFlow* mappedFlow = &mMappedFlows[i];
nsTextFrame* f = mappedFlow->mStartFrame;
@@ -2571,48 +2706,45 @@ BuildTextRunsScanner::AssignTextRun(gfxT
nsTextFrame* endFrame = mappedFlow->mEndFrame;
nsTextFrame* f;
for (f = startFrame; f != endFrame;
f = static_cast<nsTextFrame*>(f->GetNextContinuation())) {
#ifdef DEBUG_roc
if (f->GetTextRun(mWhichTextRun)) {
gfxTextRun* textRun = f->GetTextRun(mWhichTextRun);
if (textRun->GetFlags() & nsTextFrameUtils::TEXT_IS_SIMPLE_FLOW) {
- if (mMappedFlows[0].mStartFrame != static_cast<nsTextFrame*>(textRun->GetUserData())) {
+ if (mMappedFlows[0].mStartFrame != GetFrameForSimpleFlow(textRun)) {
NS_WARNING("REASSIGNING SIMPLE FLOW TEXT RUN!");
}
} else {
- TextRunUserData* userData =
- static_cast<TextRunUserData*>(textRun->GetUserData());
-
+ auto userData = static_cast<TextRunUserData*>(aTextRun->GetUserData());
if (userData->mMappedFlowCount >= mMappedFlows.Length() ||
userData->mMappedFlows[userData->mMappedFlowCount - 1].mStartFrame !=
- mMappedFlows[userData->mMappedFlowCount - 1].mStartFrame) {
+ mMappedFlows[userdata->mMappedFlowCount - 1].mStartFrame) {
NS_WARNING("REASSIGNING MULTIFLOW TEXT RUN (not append)!");
}
}
}
#endif
gfxTextRun* oldTextRun = f->GetTextRun(mWhichTextRun);
if (oldTextRun) {
nsTextFrame* firstFrame = nullptr;
uint32_t startOffset = 0;
if (oldTextRun->GetFlags() & nsTextFrameUtils::TEXT_IS_SIMPLE_FLOW) {
- firstFrame = static_cast<nsTextFrame*>(oldTextRun->GetUserData());
- }
- else {
- TextRunUserData* userData = static_cast<TextRunUserData*>(oldTextRun->GetUserData());
+ firstFrame = GetFrameForSimpleFlow(oldTextRun);
+ } else {
+ auto userData = static_cast<TextRunUserData*>(oldTextRun->GetUserData());
firstFrame = userData->mMappedFlows[0].mStartFrame;
if (MOZ_UNLIKELY(f != firstFrame)) {
- TextRunMappedFlow* flow = FindFlowForContent(userData, f->GetContent());
+ TextRunMappedFlow* flow = FindFlowForContent(userData,
+ f->GetContent());
if (flow) {
startOffset = flow->mDOMOffsetToBeforeTransformOffset;
- }
- else {
+ } else {
NS_ERROR("Can't find flow containing frame 'f'");
}
}
}
// Optimization: if |f| is the first frame in the flow then there are no
// prev-continuations that use |oldTextRun|.
nsTextFrame* clearFrom = nullptr;
@@ -2682,17 +2814,17 @@ nsTextFrame::EnsureTextRun(TextRunType a
if (textRun->GetFlags() & nsTextFrameUtils::TEXT_IS_SIMPLE_FLOW) {
if (aFlowEndInTextRun) {
*aFlowEndInTextRun = textRun->GetLength();
}
return gfxSkipCharsIterator(textRun->GetSkipChars(), 0, mContentOffset);
}
- TextRunUserData* userData = static_cast<TextRunUserData*>(textRun->GetUserData());
+ auto userData = static_cast<TextRunUserData*>(textRun->GetUserData());
TextRunMappedFlow* flow = FindFlowForContent(userData, mContent);
if (flow) {
// Since textruns can only contain one flow for a given content element,
// this must be our flow.
uint32_t flowIndex = flow - userData->mMappedFlows;
userData->mLastFlowIndex = flowIndex;
gfxSkipCharsIterator iter(textRun->GetSkipChars(),
flow->mDOMOffsetToBeforeTransformOffset, mContentOffset);