Make nsRuleNode's use of font metrics for ch and ex units pass the correct language. (Bug 678671, patch 3) r=roc

This makes these users (which are exceptions within layout, although
low-level enough that it won't matter for font inflation work) call
through to GetMetricsFor explicitly with the correct language, rather
than using the broken nsPresContext::GetMetricsFor and its
charset-detected language.

This improves the correctness of our behavior for 'ch' and 'ex' CSS
units when the font selection (or defaults) are language-dependent.  It
should also reduce the number of unique sets of font metrics requested
(which helps nsFontCache effectiveness).
This commit is contained in:
L. David Baron 2011-08-14 10:08:04 -07:00
Родитель 4695681430
Коммит 0e94875a83
4 изменённых файлов: 136 добавлений и 20 удалений

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

@ -184,6 +184,7 @@ static void EnsureBlockDisplay(PRUint8& display)
static nscoord CalcLengthWith(const nsCSSValue& aValue,
nscoord aFontSize,
const nsStyleFont* aStyleFont,
const nsStyleVisibility* aStyleVisibility,
nsStyleContext* aStyleContext,
nsPresContext* aPresContext,
PRBool aUseProvidedRootEmSize,
@ -196,6 +197,7 @@ struct CalcLengthCalcOps : public css::BasicCoordCalcOps,
// All of the parameters to CalcLengthWith except aValue.
const nscoord mFontSize;
const nsStyleFont* const mStyleFont;
const nsStyleVisibility* const mStyleVisibility;
nsStyleContext* const mStyleContext;
nsPresContext* const mPresContext;
const PRBool mUseProvidedRootEmSize;
@ -203,11 +205,13 @@ struct CalcLengthCalcOps : public css::BasicCoordCalcOps,
PRBool& mCanStoreInRuleTree;
CalcLengthCalcOps(nscoord aFontSize, const nsStyleFont* aStyleFont,
const nsStyleVisibility* aStyleVisibility,
nsStyleContext* aStyleContext, nsPresContext* aPresContext,
PRBool aUseProvidedRootEmSize, PRBool aUseUserFontSet,
PRBool& aCanStoreInRuleTree)
: mFontSize(aFontSize),
mStyleFont(aStyleFont),
mStyleVisibility(aStyleVisibility),
mStyleContext(aStyleContext),
mPresContext(aPresContext),
mUseProvidedRootEmSize(aUseProvidedRootEmSize),
@ -218,8 +222,8 @@ struct CalcLengthCalcOps : public css::BasicCoordCalcOps,
result_type ComputeLeafValue(const nsCSSValue& aValue)
{
return CalcLengthWith(aValue, mFontSize, mStyleFont, mStyleContext,
mPresContext, mUseProvidedRootEmSize,
return CalcLengthWith(aValue, mFontSize, mStyleFont, mStyleVisibility,
mStyleContext, mPresContext, mUseProvidedRootEmSize,
mUseUserFontSet, mCanStoreInRuleTree);
}
};
@ -229,9 +233,34 @@ static inline nscoord ScaleCoord(const nsCSSValue &aValue, float factor)
return NSToCoordRoundWithClamp(aValue.GetFloatValue() * factor);
}
already_AddRefed<nsFontMetrics>
GetMetricsFor(nsPresContext* aPresContext,
nsStyleContext* aStyleContext,
const nsStyleFont* aStyleFont,
const nsStyleVisibility* aStyleVisibility,
nscoord aFontSize, // overrides value from aStyleFont
PRBool aUseUserFontSet)
{
nsFont font = aStyleFont->mFont;
font.size = aFontSize;
gfxUserFontSet *fs = nsnull;
if (aUseUserFontSet) {
fs = aPresContext->GetUserFontSet();
}
nsRefPtr<nsFontMetrics> fm;
if (!aStyleVisibility) {
aStyleVisibility = aStyleContext->GetStyleVisibility();
}
aPresContext->DeviceContext()->GetMetricsFor(font,
aStyleVisibility->mLanguage,
fs, *getter_AddRefs(fm));
return fm.forget();
}
static nscoord CalcLengthWith(const nsCSSValue& aValue,
nscoord aFontSize,
const nsStyleFont* aStyleFont,
const nsStyleVisibility* aStyleVisibility,
nsStyleContext* aStyleContext,
nsPresContext* aPresContext,
PRBool aUseProvidedRootEmSize,
@ -243,8 +272,10 @@ static nscoord CalcLengthWith(const nsCSSValue& aValue,
{
NS_ASSERTION(aValue.IsLengthUnit() || aValue.IsCalcUnit(),
"not a length or calc unit");
NS_ASSERTION(aStyleFont || aStyleContext, "Must have style data");
NS_ASSERTION(!aStyleFont || !aStyleContext, "Duplicate sources of data");
NS_ASSERTION((aStyleFont && aStyleVisibility) || aStyleContext,
"Must have style data");
NS_ASSERTION((!aStyleFont && !aStyleVisibility) || !aStyleContext,
"Duplicate sources of data");
NS_ASSERTION(aPresContext, "Must have prescontext");
if (aValue.IsFixedLengthUnit()) {
@ -305,17 +336,15 @@ static nscoord CalcLengthWith(const nsCSSValue& aValue,
// XXX scale against font metrics height instead?
}
case eCSSUnit_XHeight: {
nsFont font = styleFont->mFont;
font.size = aFontSize;
nsRefPtr<nsFontMetrics> fm =
aPresContext->GetMetricsFor(font, aUseUserFontSet);
GetMetricsFor(aPresContext, aStyleContext, styleFont,
aStyleVisibility, aFontSize, aUseUserFontSet);
return ScaleCoord(aValue, float(fm->XHeight()));
}
case eCSSUnit_Char: {
nsFont font = styleFont->mFont;
font.size = aFontSize;
nsRefPtr<nsFontMetrics> fm =
aPresContext->GetMetricsFor(font, aUseUserFontSet);
GetMetricsFor(aPresContext, aStyleContext, styleFont,
aStyleVisibility, aFontSize, aUseUserFontSet);
gfxFloat zeroWidth = (fm->GetThebesFontGroup()->GetFontAt(0)
->GetMetrics().zeroOrAveCharWidth);
@ -333,7 +362,8 @@ static nscoord CalcLengthWith(const nsCSSValue& aValue,
case eCSSUnit_Calc_Times_L:
case eCSSUnit_Calc_Times_R:
case eCSSUnit_Calc_Divided: {
CalcLengthCalcOps ops(aFontSize, aStyleFont, aStyleContext, aPresContext,
CalcLengthCalcOps ops(aFontSize, aStyleFont, aStyleVisibility,
aStyleContext, aPresContext,
aUseProvidedRootEmSize, aUseUserFontSet,
aCanStoreInRuleTree);
return css::ComputeCalc(aValue, ops);
@ -353,7 +383,8 @@ nsRuleNode::CalcLength(const nsCSSValue& aValue,
{
NS_ASSERTION(aStyleContext, "Must have style data");
return CalcLengthWith(aValue, -1, nsnull, aStyleContext, aPresContext,
return CalcLengthWith(aValue, -1, nsnull, nsnull,
aStyleContext, aPresContext,
PR_FALSE, PR_TRUE, aCanStoreInRuleTree);
}
@ -372,8 +403,10 @@ nsRuleNode::CalcLengthWithInitialFont(nsPresContext* aPresContext,
const nsCSSValue& aValue)
{
nsStyleFont defaultFont(aPresContext);
nsStyleVisibility defaultVisibility(aPresContext); // FIXME: best language?
PRBool canStoreInRuleTree;
return CalcLengthWith(aValue, -1, &defaultFont, nsnull, aPresContext,
return CalcLengthWith(aValue, -1, &defaultFont, &defaultVisibility,
nsnull, aPresContext,
PR_TRUE, PR_FALSE, canStoreInRuleTree);
}
@ -2442,15 +2475,18 @@ struct SetFontSizeCalcOps : public css::BasicCoordCalcOps,
// The parameters beyond aValue that we need for CalcLengthWith.
const nscoord mParentSize;
const nsStyleFont* const mParentFont;
const nsStyleVisibility* const mLanguageVisibility;
nsPresContext* const mPresContext;
const PRBool mAtRoot;
PRBool& mCanStoreInRuleTree;
SetFontSizeCalcOps(nscoord aParentSize, const nsStyleFont* aParentFont,
const nsStyleVisibility* aLanguageVisibility,
nsPresContext* aPresContext, PRBool aAtRoot,
PRBool& aCanStoreInRuleTree)
: mParentSize(aParentSize),
mParentFont(aParentFont),
mLanguageVisibility(aLanguageVisibility),
mPresContext(aPresContext),
mAtRoot(aAtRoot),
mCanStoreInRuleTree(aCanStoreInRuleTree)
@ -2464,7 +2500,8 @@ struct SetFontSizeCalcOps : public css::BasicCoordCalcOps,
// Note that font-based length units use the parent's size
// unadjusted for scriptlevel changes. A scriptlevel change
// between us and the parent is simply ignored.
size = CalcLengthWith(aValue, mParentSize, mParentFont,
size = CalcLengthWith(aValue, mParentSize,
mParentFont, mLanguageVisibility,
nsnull, mPresContext, mAtRoot,
PR_TRUE, mCanStoreInRuleTree);
if (!aValue.IsRelativeLengthUnit()) {
@ -2492,6 +2529,7 @@ nsRuleNode::SetFontSize(nsPresContext* aPresContext,
const nsRuleData* aRuleData,
const nsStyleFont* aFont,
const nsStyleFont* aParentFont,
const nsStyleVisibility* aLanguageVisibility,
nscoord* aSize,
const nsFont& aSystemFont,
nscoord aParentSize,
@ -2550,8 +2588,8 @@ nsRuleNode::SetFontSize(nsPresContext* aPresContext,
else if (sizeValue->IsLengthUnit() ||
sizeValue->GetUnit() == eCSSUnit_Percent ||
sizeValue->IsCalcUnit()) {
SetFontSizeCalcOps ops(aParentSize, aParentFont, aPresContext, aAtRoot,
aCanStoreInRuleTree);
SetFontSizeCalcOps ops(aParentSize, aParentFont, aLanguageVisibility,
aPresContext, aAtRoot, aCanStoreInRuleTree);
*aSize = css::ComputeCalc(*sizeValue, ops);
if (*aSize < 0) {
NS_ABORT_IF_FALSE(sizeValue->IsCalcUnit(),
@ -2622,6 +2660,12 @@ nsRuleNode::SetFont(nsPresContext* aPresContext, nsStyleContext* aContext,
const nsFont* defaultVariableFont =
aPresContext->GetDefaultFont(kPresContext_DefaultVariableFont_ID);
PRBool atRoot = !aContext->GetParent();
// We get '%', 'em', 'ex', and 'ch' units from aParentFont. For 'ex'
// and 'ch' units we use font metrics, which depend on language. It
// makes sense to use the language from the same style context as
// aParentFont.
const nsStyleVisibility *languageVisibility =
(atRoot ? aContext : aContext->GetParent())->GetStyleVisibility();
// -moz-system-font: enum (never inherit!)
nsFont systemFont;
@ -2810,7 +2854,8 @@ nsRuleNode::SetFont(nsPresContext* aPresContext, nsStyleContext* aContext,
// to the parent font, or the size definitions are circular and we
//
aFont->mScriptMinSize =
CalcLengthWith(*scriptMinSizeValue, aParentFont->mSize, aParentFont,
CalcLengthWith(*scriptMinSizeValue, aParentFont->mSize,
aParentFont, languageVisibility,
nsnull, aPresContext, atRoot, PR_TRUE,
aCanStoreInRuleTree);
}
@ -2878,7 +2923,8 @@ nsRuleNode::SetFont(nsPresContext* aPresContext, nsStyleContext* aContext,
&scriptLevelAdjustedUnconstrainedParentSize);
NS_ASSERTION(!aUsedStartStruct || aFont->mScriptUnconstrainedSize == aFont->mSize,
"If we have a start struct, we should have reset everything coming in here");
SetFontSize(aPresContext, aRuleData, aFont, aParentFont, &aFont->mSize,
SetFontSize(aPresContext, aRuleData, aFont, aParentFont,
languageVisibility, &aFont->mSize,
systemFont, aParentFont->mSize, scriptLevelAdjustedParentSize,
aUsedStartStruct, atRoot, aCanStoreInRuleTree);
if (aParentFont->mSize == aParentFont->mScriptUnconstrainedSize &&
@ -2891,8 +2937,8 @@ nsRuleNode::SetFont(nsPresContext* aPresContext, nsStyleContext* aContext,
aFont->mScriptUnconstrainedSize = aFont->mSize;
} else {
SetFontSize(aPresContext, aRuleData, aFont, aParentFont,
&aFont->mScriptUnconstrainedSize, systemFont,
aParentFont->mScriptUnconstrainedSize,
languageVisibility, &aFont->mScriptUnconstrainedSize,
systemFont, aParentFont->mScriptUnconstrainedSize,
scriptLevelAdjustedUnconstrainedParentSize,
aUsedStartStruct, atRoot, aCanStoreInRuleTree);
}
@ -4556,6 +4602,10 @@ nsRuleNode::ComputeVisibilityData(void* aStartStruct,
COMPUTE_START_INHERITED(Visibility, (mPresContext),
visibility, parentVisibility)
// IMPORTANT: No properties in this struct have lengths in them. We
// depend on this since CalcLengthWith can call GetStyleVisibility()
// to get the language for resolving fonts!
// direction: enum, inherit, initial
SetDiscrete(*aRuleData->ValueForDirection(), visibility->mDirection,
canStoreInRuleTree,

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

@ -600,6 +600,7 @@ protected:
const nsRuleData* aRuleData,
const nsStyleFont* aFont,
const nsStyleFont* aParentFont,
const nsStyleVisibility* aLanguageVisibility,
nscoord* aSize,
const nsFont& aSystemFont,
nscoord aParentSize,

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

@ -133,6 +133,7 @@ _TEST_FILES = test_acid3_test46.html \
file_bug645998-1.css \
file_bug645998-2.css \
test_cascade.html \
test_ch_ex_no_infloops.html \
test_compute_data_with_start_struct.html \
test_computed_style.html \
test_computed_style_no_pseudo.html \

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

@ -0,0 +1,64 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=678671
-->
<head>
<title>Test for Bug 678671</title>
<script type="application/javascript" src="/MochiKit/packed.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="property_database.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=678671">Mozilla Bug 678671</a>
<p id="display"></p>
<div id="content"></div>
<script type="application/javascript">
/** Test for Bug 678671 **/
/**
* Test 'ex' and 'ch' units in every place we possible can to make
* sure they don't cause an infinite loop.
*/
var content = document.getElementById("content");
var cs = getComputedStyle(content, "");
for (var prop in gCSSProperties) {
var info = gCSSProperties[prop];
if (info.backend_only) {
continue;
}
function test_val(v) {
content.style.setProperty(prop, v, "");
isnot(get_computed_value(cs, prop), "",
"Setting '" + prop + "' to '" + v + "' should not cause infinite loop");
}
test_val('3ex');
test_val('2ch');
function test_replaced_values(value_list) {
// For each item in value_list, if it looks like it has a dimension
// in it, replace those dimensions with 3ex and 2ch and test it.
for (var i = 0; i < value_list.length; ++i) {
var value = value_list[i];
function try_replace(withval) {
var rep = value.replace(/[0-9.]+[a-zA-Z]+/g, withval)
if (rep != value) {
test_val(rep);
}
}
try_replace('3ex');
try_replace('2ch');
}
}
test_replaced_values(info.initial_values);
test_replaced_values(info.other_values);
content.style.removeProperty(prop);
}
</script>
</pre>
</body>
</html>