mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 21:31:04 +00:00
Build font data structure by walking the necessary text. (Bug 706193, patch 3) r=roc
Compute the amount of text in the scope of an nsFontInflationData object. This walks the text that's inside of the block formatting context at which this object is rooted, excluding the text that's inside any nested BFC. Using the amount of text, the font sizes of the text, and the line threshold preference, we compute whether to enable font size inflation within that block formatting context.
This commit is contained in:
parent
90733eea84
commit
1f0b12c8b8
@ -39,8 +39,13 @@
|
||||
|
||||
#include "nsFontInflationData.h"
|
||||
#include "FramePropertyTable.h"
|
||||
#include "nsTextFragment.h"
|
||||
#include "nsIFormControlFrame.h"
|
||||
#include "nsHTMLReflowState.h"
|
||||
#include "nsTextFrameUtils.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::layout;
|
||||
|
||||
static void
|
||||
DestroyFontInflationData(void *aPropertyValue)
|
||||
@ -55,7 +60,283 @@ nsFontInflationData::FindFontInflationDataFor(const nsIFrame *aFrame)
|
||||
{
|
||||
// We have one set of font inflation data per block formatting context.
|
||||
const nsIFrame *bfc = FlowRootFor(aFrame);
|
||||
NS_ASSERTION(bfc->GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT,
|
||||
"should have found a flow root");
|
||||
|
||||
return static_cast<nsFontInflationData*>(
|
||||
bfc->Properties().Get(FontInflationDataProperty()));
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
nsFontInflationData::UpdateFontInflationDataWidthFor(const nsHTMLReflowState& aReflowState)
|
||||
{
|
||||
nsIFrame *bfc = aReflowState.frame;
|
||||
NS_ASSERTION(bfc->GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT,
|
||||
"should have been given a flow root");
|
||||
FrameProperties bfcProps(bfc->Properties());
|
||||
nsFontInflationData *data = static_cast<nsFontInflationData*>(
|
||||
bfcProps.Get(FontInflationDataProperty()));
|
||||
if (!data) {
|
||||
data = new nsFontInflationData(bfc);
|
||||
bfcProps.Set(FontInflationDataProperty(), data);
|
||||
}
|
||||
|
||||
data->UpdateWidth(aReflowState);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
nsFontInflationData::MarkFontInflationDataTextDirty(nsIFrame *aBFCFrame)
|
||||
{
|
||||
NS_ASSERTION(aBFCFrame->GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT,
|
||||
"should have been given a flow root");
|
||||
|
||||
FrameProperties bfcProps(aBFCFrame->Properties());
|
||||
nsFontInflationData *data = static_cast<nsFontInflationData*>(
|
||||
bfcProps.Get(FontInflationDataProperty()));
|
||||
if (data) {
|
||||
data->MarkTextDirty();
|
||||
}
|
||||
}
|
||||
|
||||
nsFontInflationData::nsFontInflationData(nsIFrame *aBFCFrame)
|
||||
: mBFCFrame(aBFCFrame)
|
||||
, mTextAmount(0)
|
||||
, mTextThreshold(0)
|
||||
, mInflationEnabled(false)
|
||||
, mTextDirty(true)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the closest common ancestor between aFrame1 and aFrame2, except
|
||||
* treating the parent of a frame as the first-in-flow of its parent (so
|
||||
* the result doesn't change when breaking changes).
|
||||
*
|
||||
* aKnownCommonAncestor is a known common ancestor of both.
|
||||
*/
|
||||
static nsIFrame*
|
||||
NearestCommonAncestorFirstInFlow(nsIFrame *aFrame1, nsIFrame *aFrame2,
|
||||
nsIFrame *aKnownCommonAncestor)
|
||||
{
|
||||
aFrame1 = aFrame1->GetFirstInFlow();
|
||||
aFrame2 = aFrame2->GetFirstInFlow();
|
||||
aKnownCommonAncestor = aKnownCommonAncestor->GetFirstInFlow();
|
||||
|
||||
nsAutoTArray<nsIFrame*, 32> ancestors1, ancestors2;
|
||||
for (nsIFrame *f = aFrame1; f != aKnownCommonAncestor;
|
||||
(f = f->GetParent()) && (f = f->GetFirstInFlow())) {
|
||||
ancestors1.AppendElement(f);
|
||||
}
|
||||
for (nsIFrame *f = aFrame2; f != aKnownCommonAncestor;
|
||||
(f = f->GetParent()) && (f = f->GetFirstInFlow())) {
|
||||
ancestors2.AppendElement(f);
|
||||
}
|
||||
|
||||
nsIFrame *result = aKnownCommonAncestor;
|
||||
PRUint32 i1 = ancestors1.Length(),
|
||||
i2 = ancestors2.Length();
|
||||
while (i1-- != 0 && i2-- != 0) {
|
||||
if (ancestors1[i1] != ancestors2[i2]) {
|
||||
break;
|
||||
}
|
||||
result = ancestors1[i1];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static nscoord
|
||||
ComputeDescendantWidth(const nsHTMLReflowState& aAncestorReflowState,
|
||||
nsIFrame *aDescendantFrame)
|
||||
{
|
||||
nsIFrame *ancestorFrame = aAncestorReflowState.frame->GetFirstInFlow();
|
||||
if (aDescendantFrame == ancestorFrame) {
|
||||
return aAncestorReflowState.ComputedWidth();
|
||||
}
|
||||
|
||||
AutoInfallibleTArray<nsIFrame*, 16> frames;
|
||||
for (nsIFrame *f = aDescendantFrame; f != ancestorFrame;
|
||||
f = f->GetParent()) {
|
||||
frames.AppendElement(f);
|
||||
}
|
||||
|
||||
PRUint32 len = frames.Length();
|
||||
nsHTMLReflowState *reflowStates = static_cast<nsHTMLReflowState*>
|
||||
(moz_xmalloc(sizeof(nsHTMLReflowState) * len));
|
||||
nsPresContext *presContext = aDescendantFrame->PresContext();
|
||||
for (PRUint32 i = 0; i < len; ++i) {
|
||||
const nsHTMLReflowState &parentReflowState =
|
||||
(i == 0) ? aAncestorReflowState : reflowStates[i - 1];
|
||||
nsSize availSize(parentReflowState.ComputedWidth(), NS_UNCONSTRAINEDSIZE);
|
||||
nsIFrame *frame = frames[len - i - 1];
|
||||
NS_ABORT_IF_FALSE(frame->GetParent()->GetFirstInFlow() ==
|
||||
parentReflowState.frame->GetFirstInFlow(),
|
||||
"bad logic in this function");
|
||||
new (reflowStates + i) nsHTMLReflowState(presContext, parentReflowState,
|
||||
frame, availSize);
|
||||
}
|
||||
|
||||
NS_ABORT_IF_FALSE(reflowStates[len - 1].frame == aDescendantFrame,
|
||||
"bad logic in this function");
|
||||
nscoord result = reflowStates[len - 1].ComputedWidth();
|
||||
|
||||
for (PRUint32 i = len; i-- != 0; ) {
|
||||
reflowStates[i].~nsHTMLReflowState();
|
||||
}
|
||||
moz_free(reflowStates);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
nsFontInflationData::UpdateWidth(const nsHTMLReflowState &aReflowState)
|
||||
{
|
||||
nsIFrame *bfc = aReflowState.frame;
|
||||
NS_ASSERTION(bfc->GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT,
|
||||
"must be block formatting context");
|
||||
|
||||
nsIFrame *firstInflatableDescendant =
|
||||
FindEdgeInflatableFrameIn(bfc, eFromStart);
|
||||
if (!firstInflatableDescendant) {
|
||||
mTextAmount = 0;
|
||||
mTextThreshold = 0; // doesn't matter
|
||||
mTextDirty = false;
|
||||
mInflationEnabled = false;
|
||||
return;
|
||||
}
|
||||
nsIFrame *lastInflatableDescendant =
|
||||
FindEdgeInflatableFrameIn(bfc, eFromEnd);
|
||||
NS_ABORT_IF_FALSE(!firstInflatableDescendant == !lastInflatableDescendant,
|
||||
"null-ness should match; NearestCommonAncestorFirstInFlow"
|
||||
" will crash when passed null");
|
||||
|
||||
// Particularly when we're computing for the root BFC, the width of
|
||||
// nca might differ significantly for the width of bfc.
|
||||
nsIFrame *nca = NearestCommonAncestorFirstInFlow(firstInflatableDescendant,
|
||||
lastInflatableDescendant,
|
||||
bfc);
|
||||
while (!nsLayoutUtils::IsContainerForFontSizeInflation(nca)) {
|
||||
nca = nca->GetParent();
|
||||
}
|
||||
|
||||
nscoord newNCAWidth = ComputeDescendantWidth(aReflowState, nca);
|
||||
|
||||
// See comment above "font.size.inflation.lineThreshold" in
|
||||
// modules/libpref/src/init/all.js .
|
||||
PRUint32 lineThreshold = nsLayoutUtils::FontSizeInflationLineThreshold();
|
||||
nscoord newTextThreshold = (newNCAWidth * lineThreshold) / 100;
|
||||
|
||||
if (mTextThreshold <= mTextAmount && mTextAmount < newTextThreshold) {
|
||||
// Because we truncate our scan when we hit sufficient text, we now
|
||||
// need to rescan.
|
||||
mTextDirty = true;
|
||||
}
|
||||
|
||||
mTextThreshold = newTextThreshold;
|
||||
mInflationEnabled = mTextAmount >= mTextThreshold;
|
||||
}
|
||||
|
||||
/* static */ nsIFrame*
|
||||
nsFontInflationData::FindEdgeInflatableFrameIn(nsIFrame* aFrame,
|
||||
SearchDirection aDirection)
|
||||
{
|
||||
// NOTE: This function has a similar structure to ScanTextIn!
|
||||
|
||||
// FIXME: Should probably only scan the text that's actually going to
|
||||
// be inflated!
|
||||
|
||||
nsIFormControlFrame* fcf = do_QueryFrame(aFrame);
|
||||
if (fcf) {
|
||||
return aFrame;
|
||||
}
|
||||
|
||||
// FIXME: aDirection!
|
||||
nsAutoTArray<FrameChildList, 4> lists;
|
||||
aFrame->GetChildLists(&lists);
|
||||
for (PRUint32 i = 0, len = lists.Length(); i < len; ++i) {
|
||||
const nsFrameList& list =
|
||||
lists[(aDirection == eFromStart) ? i : len - i - 1].mList;
|
||||
for (nsIFrame *kid = (aDirection == eFromStart) ? list.FirstChild()
|
||||
: list.LastChild();
|
||||
kid;
|
||||
kid = (aDirection == eFromStart) ? kid->GetNextSibling()
|
||||
: kid->GetPrevSibling()) {
|
||||
if (kid->GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT) {
|
||||
// Goes in a different set of inflation data.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (kid->GetType() == nsGkAtoms::textFrame) {
|
||||
nsIContent *content = kid->GetContent();
|
||||
if (content && kid == content->GetPrimaryFrame()) {
|
||||
PRUint32 len = nsTextFrameUtils::
|
||||
ComputeApproximateLengthWithWhitespaceCompression(
|
||||
content, kid->GetStyleText());
|
||||
if (len != 0) {
|
||||
return kid;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
nsIFrame *kidResult =
|
||||
FindEdgeInflatableFrameIn(kid, aDirection);
|
||||
if (kidResult) {
|
||||
return kidResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
void
|
||||
nsFontInflationData::ScanText()
|
||||
{
|
||||
mTextDirty = false;
|
||||
mTextAmount = 0;
|
||||
ScanTextIn(mBFCFrame);
|
||||
mInflationEnabled = mTextAmount >= mTextThreshold;
|
||||
}
|
||||
|
||||
void
|
||||
nsFontInflationData::ScanTextIn(nsIFrame *aFrame)
|
||||
{
|
||||
// NOTE: This function has a similar structure to FindEdgeInflatableFrameIn!
|
||||
|
||||
// FIXME: Should probably only scan the text that's actually going to
|
||||
// be inflated!
|
||||
|
||||
nsIFrame::ChildListIterator lists(aFrame);
|
||||
for (; !lists.IsDone(); lists.Next()) {
|
||||
nsFrameList::Enumerator kids(lists.CurrentList());
|
||||
for (; !kids.AtEnd(); kids.Next()) {
|
||||
nsIFrame *kid = kids.get();
|
||||
if (kid->GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT) {
|
||||
// Goes in a different set of inflation data.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (kid->GetType() == nsGkAtoms::textFrame) {
|
||||
nsIContent *content = kid->GetContent();
|
||||
if (content && kid == content->GetPrimaryFrame()) {
|
||||
PRUint32 len = nsTextFrameUtils::
|
||||
ComputeApproximateLengthWithWhitespaceCompression(
|
||||
content, kid->GetStyleText());
|
||||
if (len != 0) {
|
||||
nscoord fontSize = kid->GetStyleFont()->mFont.size;
|
||||
if (fontSize > 0) {
|
||||
mTextAmount += fontSize * len;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// recursive step
|
||||
ScanTextIn(kid);
|
||||
}
|
||||
|
||||
if (mTextAmount >= mTextThreshold) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -44,14 +44,47 @@
|
||||
#include "nsLayoutUtils.h"
|
||||
#include "nsBlockFrame.h"
|
||||
|
||||
struct nsHTMLReflowState;
|
||||
|
||||
class nsFontInflationData
|
||||
{
|
||||
public:
|
||||
|
||||
static nsFontInflationData* FindFontInflationDataFor(const nsIFrame *aFrame);
|
||||
|
||||
static void
|
||||
UpdateFontInflationDataWidthFor(const nsHTMLReflowState& aReflowState);
|
||||
|
||||
static void MarkFontInflationDataTextDirty(nsIFrame *aFrame);
|
||||
|
||||
bool InflationEnabled() {
|
||||
if (mTextDirty) {
|
||||
ScanText();
|
||||
}
|
||||
return mInflationEnabled;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
nsFontInflationData(nsIFrame* aBFCFrame);
|
||||
|
||||
nsFontInflationData(const nsFontInflationData&) MOZ_DELETE;
|
||||
void operator=(const nsFontInflationData&) MOZ_DELETE;
|
||||
|
||||
void UpdateWidth(const nsHTMLReflowState &aReflowState);
|
||||
enum SearchDirection { eFromStart, eFromEnd };
|
||||
static nsIFrame* FindEdgeInflatableFrameIn(nsIFrame *aFrame,
|
||||
SearchDirection aDirection);
|
||||
|
||||
void MarkTextDirty() { mTextDirty = true; }
|
||||
void ScanText();
|
||||
// Scan text in the subtree rooted at aFrame. Increment mTextAmount
|
||||
// by multiplying the number of characters found by the font size
|
||||
// (yielding the width that would be occupied by the characters if
|
||||
// they were all em squares). But stop scanning if mTextAmount
|
||||
// crosses mTextThreshold.
|
||||
void ScanTextIn(nsIFrame *aFrame);
|
||||
|
||||
static const nsIFrame* FlowRootFor(const nsIFrame *aFrame)
|
||||
{
|
||||
while (!(aFrame->GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT)) {
|
||||
@ -60,6 +93,10 @@ private:
|
||||
return aFrame;
|
||||
}
|
||||
|
||||
nsIFrame *mBFCFrame;
|
||||
nscoord mTextAmount, mTextThreshold;
|
||||
bool mInflationEnabled; // for this BFC
|
||||
bool mTextDirty;
|
||||
};
|
||||
|
||||
#endif /* !defined(nsFontInflationData_h_) */
|
||||
|
@ -127,6 +127,7 @@
|
||||
#include "nsRenderingContext.h"
|
||||
#include "CSSCalc.h"
|
||||
#include "nsAbsoluteContainingBlock.h"
|
||||
#include "nsFontInflationData.h"
|
||||
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/LookAndFeel.h"
|
||||
@ -3518,6 +3519,10 @@ nsFrame::MarkIntrinsicWidthsDirty()
|
||||
CoordNeedsRecalc(metrics->mFlex);
|
||||
CoordNeedsRecalc(metrics->mAscent);
|
||||
}
|
||||
|
||||
if (GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT) {
|
||||
nsFontInflationData::MarkFontInflationDataTextDirty(this);
|
||||
}
|
||||
}
|
||||
|
||||
/* virtual */ nscoord
|
||||
|
@ -56,9 +56,8 @@
|
||||
#include "nsIPercentHeightObserver.h"
|
||||
#include "nsLayoutUtils.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#ifdef IBMBIDI
|
||||
#include "nsBidiUtils.h"
|
||||
#endif
|
||||
#include "nsFontInflationData.h"
|
||||
|
||||
#ifdef NS_DEBUG
|
||||
#undef NOISY_VERTICAL_ALIGN
|
||||
@ -316,6 +315,12 @@ nsHTMLReflowState::Init(nsPresContext* aPresContext,
|
||||
"have unconstrained width; this should only result from "
|
||||
"very large sizes, not attempts at intrinsic width "
|
||||
"calculation");
|
||||
|
||||
if (frame->GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT) {
|
||||
// Create our font inflation data if we don't have it already, and
|
||||
// give it our current width information.
|
||||
nsFontInflationData::UpdateFontInflationDataWidthFor(*this);
|
||||
}
|
||||
}
|
||||
|
||||
void nsHTMLReflowState::InitCBReflowState()
|
||||
|
@ -43,6 +43,8 @@
|
||||
#include "gfxFont.h"
|
||||
#include "nsUnicharUtils.h"
|
||||
#include "nsBidiUtils.h"
|
||||
#include "nsIContent.h"
|
||||
#include "nsStyleStruct.h"
|
||||
|
||||
// XXX TODO implement transform of backslash to yen that nsTextTransform does
|
||||
// when requested by PresContext->LanguageSpecificTransformType(). Do it with
|
||||
@ -248,6 +250,47 @@ nsTextFrameUtils::TransformText(const PRUint8* aText, PRUint32 aLength,
|
||||
return aOutput;
|
||||
}
|
||||
|
||||
PRUint32
|
||||
nsTextFrameUtils::ComputeApproximateLengthWithWhitespaceCompression(
|
||||
nsIContent *aContent, const nsStyleText *aStyleText)
|
||||
{
|
||||
const nsTextFragment *frag = aContent->GetText();
|
||||
// This is an approximation so we don't really need anything
|
||||
// too fancy here.
|
||||
PRUint32 len;
|
||||
if (aStyleText->WhiteSpaceIsSignificant()) {
|
||||
len = frag->GetLength();
|
||||
} else {
|
||||
bool is2b = frag->Is2b();
|
||||
union {
|
||||
const char *s1b;
|
||||
const PRUnichar *s2b;
|
||||
} u;
|
||||
if (is2b) {
|
||||
u.s2b = frag->Get2b();
|
||||
} else {
|
||||
u.s1b = frag->Get1b();
|
||||
}
|
||||
bool prevWS = true; // more important to ignore blocks with
|
||||
// only whitespace than get inline boundaries
|
||||
// exactly right
|
||||
len = 0;
|
||||
for (PRUint32 i = 0, i_end = frag->GetLength(); i < i_end; ++i) {
|
||||
PRUnichar c = is2b ? u.s2b[i] : u.s1b[i];
|
||||
if (c == ' ' || c == '\n' || c == '\t' || c == '\r') {
|
||||
if (!prevWS) {
|
||||
++len;
|
||||
}
|
||||
prevWS = true;
|
||||
} else {
|
||||
++len;
|
||||
prevWS = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
bool nsSkipCharsRunIterator::NextRun() {
|
||||
do {
|
||||
if (mRunLength) {
|
||||
|
@ -43,6 +43,9 @@
|
||||
#include "gfxSkipChars.h"
|
||||
#include "nsTextFragment.h"
|
||||
|
||||
class nsIContent;
|
||||
struct nsStyleText;
|
||||
|
||||
#define BIG_TEXT_NODE_SIZE 4096
|
||||
|
||||
#define CH_NBSP 160
|
||||
@ -147,6 +150,10 @@ public:
|
||||
aArray->AppendElement(aOffset);
|
||||
}
|
||||
|
||||
static PRUint32
|
||||
ComputeApproximateLengthWithWhitespaceCompression(nsIContent *aContent,
|
||||
const nsStyleText
|
||||
*aStyleText);
|
||||
};
|
||||
|
||||
class nsSkipCharsRunIterator {
|
||||
|
Loading…
Reference in New Issue
Block a user