Bug 842181 - Prevent text zoom from affecting SVG text. r=dbaron

This commit is contained in:
Cameron McCormack 2013-06-29 13:28:50 +10:00
parent 09f398a510
commit 07c9631694
13 changed files with 155 additions and 38 deletions

View File

@ -2079,6 +2079,9 @@ CanvasRenderingContext2D::SetFont(const nsAString& font,
return;
}
// add a rule to prevent text zoom from affecting the style
rules.AppendElement(new nsDisableTextZoomStyleRule);
nsRefPtr<nsStyleContext> sc =
styleSet->ResolveStyleForRules(parentContext, rules);
if (!sc) {
@ -2097,21 +2100,20 @@ CanvasRenderingContext2D::SetFont(const nsAString& font,
// use CSS pixels instead of dev pixels to avoid being affected by page zoom
const uint32_t aupcp = nsPresContext::AppUnitsPerCSSPixel();
// un-zoom the font size to avoid being affected by text-only zoom
//
// Purposely ignore the font size that respects the user's minimum
// font preference (fontStyle->mFont.size) in favor of the computed
// size (fontStyle->mSize). See
// https://bugzilla.mozilla.org/show_bug.cgi?id=698652.
const nscoord fontSize = nsStyleFont::UnZoomText(parentContext->PresContext(), fontStyle->mSize);
bool printerFont = (presShell->GetPresContext()->Type() == nsPresContext::eContext_PrintPreview ||
presShell->GetPresContext()->Type() == nsPresContext::eContext_Print);
// Purposely ignore the font size that respects the user's minimum
// font preference (fontStyle->mFont.size) in favor of the computed
// size (fontStyle->mSize). See
// https://bugzilla.mozilla.org/show_bug.cgi?id=698652.
MOZ_ASSERT(!fontStyle->mAllowZoom,
"expected text zoom to be disabled on this nsStyleFont");
gfxFontStyle style(fontStyle->mFont.style,
fontStyle->mFont.weight,
fontStyle->mFont.stretch,
NSAppUnitsToFloatPixels(fontSize, float(aupcp)),
NSAppUnitsToFloatPixels(fontStyle->mSize, float(aupcp)),
language,
fontStyle->mFont.sizeAdjust,
fontStyle->mFont.systemFont,

View File

@ -59,27 +59,26 @@ function main()
try {
// Verify computed style values at various points during animation.
// * Correct behavior is for the computed values of 'font-size' to be
// exactly twice as large as their corresponding specified values.
// (Mozilla's SVG code divides out the textZoom factor from these computed
// values when rendering, to obtain the correct font-size.)
// the same as their corresponding specified values, since text zoom
// should not affect SVG text elements.
// * I also include tests for an identical animation of the "stroke-width"
// property, which should _not_ be affected by textZoom.
var text = document.getElementsByTagName("text")[0];
var rect = document.getElementsByTagName("rect")[0];
verifyStyle(text, "font-size", "10px");
verifyStyle(text, "font-size", "5px");
verifyStyle(rect, "stroke-width", "5px");
svg.setCurrentTime(1);
verifyStyle(text, "font-size", "40px");
verifyStyle(text, "font-size", "20px");
verifyStyle(rect, "stroke-width", "20px");
svg.setCurrentTime(1.5);
verifyStyle(text, "font-size", "60px");
verifyStyle(text, "font-size", "30px");
verifyStyle(rect, "stroke-width", "30px");
svg.setCurrentTime(2);
verifyStyle(text, "font-size", "80px");
verifyStyle(text, "font-size", "40px");
verifyStyle(rect, "stroke-width", "40px");
svg.setCurrentTime(3);
verifyStyle(text, "font-size", "80px");
verifyStyle(text, "font-size", "40px");
verifyStyle(rect, "stroke-width", "40px");
} catch (e) {
// If anything goes wrong, make sure we restore textZoom before bubbling

View File

@ -142,7 +142,6 @@ nsIFrame*
NS_NewHTMLVideoFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
#include "nsSVGTextContainerFrame.h"
#include "nsSVGTextFrame2.h"
nsIFrame*
NS_NewSVGOuterSVGFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);

View File

@ -3615,6 +3615,16 @@ CSS_PROP_TABLE(
nullptr,
CSS_PROP_NO_OFFSET,
eStyleAnimType_None)
CSS_PROP_FONT(
-x-text-zoom,
_x_text_zoom,
TextZoom,
CSS_PROPERTY_PARSE_INACCESSIBLE,
"",
0,
nullptr,
CSS_PROP_NO_OFFSET,
eStyleAnimType_None)
#endif /* !defined(CSS_PROP_STUB_NOT_CSS) */
#endif /* !defined(CSS_PROP_EXCLUDE_INTERNAL) */

View File

@ -3819,7 +3819,10 @@ nsComputedDOMStyle::GetLineHeightCoord(nscoord& aCoord)
// font->mSize as the font size. Adjust for that. Also adjust for
// the text zoom, if any.
const nsStyleFont* font = StyleFont();
float fCoord = float(aCoord) / mPresShell->GetPresContext()->TextZoom();
float fCoord = float(aCoord);
if (font->mAllowZoom) {
fCoord /= mPresShell->GetPresContext()->TextZoom();
}
if (font->mFont.size != font->mSize) {
fCoord = fCoord * (float(font->mSize) / float(font->mFont.size));
}

View File

@ -2547,8 +2547,10 @@ ComputeScriptLevelSize(const nsStyleFont* aFont, const nsStyleFont* aParentFont,
}
// Compute actual value of minScriptSize
nscoord minScriptSize =
nsStyleFont::ZoomText(aPresContext, aParentFont->mScriptMinSize);
nscoord minScriptSize = aParentFont->mScriptMinSize;
if (aFont->mAllowZoom) {
minScriptSize = nsStyleFont::ZoomText(aPresContext, minScriptSize);
}
double scriptLevelScale =
pow(aParentFont->mScriptSizeMultiplier, scriptLevelChange);
@ -2874,7 +2876,7 @@ struct SetFontSizeCalcOps : public css::BasicCoordCalcOps,
mParentFont,
nullptr, mPresContext, mAtRoot,
true, mCanStoreInRuleTree);
if (!aValue.IsRelativeLengthUnit()) {
if (!aValue.IsRelativeLengthUnit() && mParentFont->mAllowZoom) {
size = nsStyleFont::ZoomText(mPresContext, size);
}
}
@ -2907,14 +2909,16 @@ nsRuleNode::SetFontSize(nsPresContext* aPresContext,
bool aAtRoot,
bool& aCanStoreInRuleTree)
{
bool zoom = false;
// If false, means that *aSize has not been zoomed. If true, means that
// *aSize has been zoomed iff aParentFont->mAllowZoom is true.
bool sizeIsZoomedAccordingToParent = false;
int32_t baseSize = (int32_t) aPresContext->
GetDefaultFont(aFont->mGenericID, aFont->mLanguage)->size;
const nsCSSValue* sizeValue = aRuleData->ValueForFontSize();
if (eCSSUnit_Enumerated == sizeValue->GetUnit()) {
int32_t value = sizeValue->GetIntValue();
zoom = true;
if ((NS_STYLE_FONT_SIZE_XXSMALL <= value) &&
(value <= NS_STYLE_FONT_SIZE_XXLARGE)) {
*aSize = CalcFontPointSize(value, baseSize,
@ -2933,8 +2937,10 @@ nsRuleNode::SetFontSize(nsPresContext* aPresContext,
// Note that relative units here use the parent's size unadjusted
// for scriptlevel changes. A scriptlevel change between us and the parent
// is simply ignored.
nscoord parentSize =
nsStyleFont::UnZoomText(aPresContext, aParentSize);
nscoord parentSize = aParentSize;
if (aParentFont->mAllowZoom) {
parentSize = nsStyleFont::UnZoomText(aPresContext, parentSize);
}
if (NS_STYLE_FONT_SIZE_LARGER == value) {
*aSize = FindNextLargerFontSize(parentSize,
@ -2958,7 +2964,8 @@ nsRuleNode::SetFontSize(nsPresContext* aPresContext,
sizeValue->GetUnit() == eCSSUnit_Percent ||
sizeValue->IsCalcUnit()) {
SetFontSizeCalcOps ops(aParentSize, aParentFont,
aPresContext, aAtRoot, aCanStoreInRuleTree);
aPresContext, aAtRoot,
aCanStoreInRuleTree);
*aSize = css::ComputeCalc(*sizeValue, ops);
if (*aSize < 0) {
NS_ABORT_IF_FALSE(sizeValue->IsCalcUnit(),
@ -2966,13 +2973,13 @@ nsRuleNode::SetFontSize(nsPresContext* aPresContext,
"by parser");
*aSize = 0;
}
// Zoom is handled inside the calc ops when needed.
zoom = false;
// The calc ops will always zoom its result according to the value
// of aParentFont->mAllowZoom.
sizeIsZoomedAccordingToParent = true;
}
else if (eCSSUnit_System_Font == sizeValue->GetUnit()) {
// this becomes our cascading size
*aSize = aSystemFont.size;
zoom = true;
}
else if (eCSSUnit_Inherit == sizeValue->GetUnit()) {
aCanStoreInRuleTree = false;
@ -2980,13 +2987,12 @@ nsRuleNode::SetFontSize(nsPresContext* aPresContext,
// to inherit and we don't want explicit "inherit" to differ from the
// default.
*aSize = aScriptLevelAdjustedParentSize;
zoom = false;
sizeIsZoomedAccordingToParent = true;
}
else if (eCSSUnit_Initial == sizeValue->GetUnit()) {
// The initial value is 'medium', which has magical sizing based on
// the generic font family, so do that here too.
*aSize = baseSize;
zoom = true;
} else {
NS_ASSERTION(eCSSUnit_Null == sizeValue->GetUnit(),
"What kind of font-size value is this?");
@ -3000,13 +3006,20 @@ nsRuleNode::SetFontSize(nsPresContext* aPresContext,
// store the data in the rule tree.
aCanStoreInRuleTree = false;
*aSize = aScriptLevelAdjustedParentSize;
sizeIsZoomedAccordingToParent = true;
} else {
return;
}
}
// We want to zoom the cascaded size so that em-based measurements,
// line-heights, etc., work.
if (zoom) {
bool currentlyZoomed = sizeIsZoomedAccordingToParent &&
aParentFont->mAllowZoom;
if (!currentlyZoomed && aFont->mAllowZoom) {
*aSize = nsStyleFont::ZoomText(aPresContext, *aSize);
} else if (currentlyZoomed && !aFont->mAllowZoom) {
*aSize = nsStyleFont::UnZoomText(aPresContext, *aSize);
}
}
@ -3027,6 +3040,21 @@ nsRuleNode::SetFont(nsPresContext* aPresContext, nsStyleContext* aContext,
{
bool atRoot = !aContext->GetParent();
// -x-text-zoom: none, inherit, initial
bool allowZoom;
const nsCSSValue* textZoomValue = aRuleData->ValueForTextZoom();
if (eCSSUnit_Inherit == textZoomValue->GetUnit()) {
allowZoom = aParentFont->mAllowZoom;
} else if (eCSSUnit_None == textZoomValue->GetUnit()) {
allowZoom = false;
} else {
MOZ_ASSERT(eCSSUnit_Initial == textZoomValue->GetUnit() ||
eCSSUnit_Null == textZoomValue->GetUnit(),
"unexpected unit");
allowZoom = true;
}
aFont->EnableZoom(aPresContext, allowZoom);
// mLanguage must be set before before any of the CalcLengthWith calls
// (direct calls or calls via SetFontSize) for the cases where |aParentFont|
// is the same as |aFont|.

View File

@ -99,6 +99,28 @@ nsInitialStyleRule::List(FILE* out, int32_t aIndent) const
}
#endif
NS_IMPL_ISUPPORTS1(nsDisableTextZoomStyleRule, nsIStyleRule)
/* virtual */ void
nsDisableTextZoomStyleRule::MapRuleInfoInto(nsRuleData* aRuleData)
{
if (!(aRuleData->mSIDs & NS_STYLE_INHERIT_BIT(Font)))
return;
nsCSSValue* value = aRuleData->ValueForTextZoom();
if (value->GetUnit() == eCSSUnit_Null)
value->SetNoneValue();
}
#ifdef DEBUG
/* virtual */ void
nsDisableTextZoomStyleRule::List(FILE* out, int32_t aIndent) const
{
for (int32_t index = aIndent; --index >= 0; ) fputs(" ", out);
fputs("[disable text zoom style rule] {}\n", out);
}
#endif
static const nsStyleSet::sheetType gCSSSheetTypes[] = {
nsStyleSet::eAgentSheet,
nsStyleSet::eUserSheet,
@ -148,6 +170,7 @@ nsStyleSet::Init(nsPresContext *aPresContext)
mFirstLineRule = new nsEmptyStyleRule;
mFirstLetterRule = new nsEmptyStyleRule;
mPlaceholderRule = new nsEmptyStyleRule;
mDisableTextZoomStyleRule = new nsDisableTextZoomStyleRule;
mRuleTree = nsRuleNode::CreateRootNode(aPresContext);
@ -1152,6 +1175,7 @@ nsStyleSet::ResolveStyleFor(Element* aElement,
aTreeMatchContext.ResetForUnvisitedMatching();
ElementRuleProcessorData data(PresContext(), aElement, &ruleWalker,
aTreeMatchContext);
WalkDisableTextZoomRule(aElement, &ruleWalker);
FileRules(EnumRulesMatching<ElementRuleProcessorData>, &data, aElement,
&ruleWalker);
@ -1288,6 +1312,14 @@ nsStyleSet::WalkRestrictionRule(nsCSSPseudoElements::Type aPseudoType,
aRuleWalker->Forward(mPlaceholderRule);
}
void
nsStyleSet::WalkDisableTextZoomRule(Element* aElement, nsRuleWalker* aRuleWalker)
{
aRuleWalker->SetLevel(eAgentSheet, false, false);
if (aElement->IsSVG(nsGkAtoms::text))
aRuleWalker->Forward(mDisableTextZoomStyleRule);
}
already_AddRefed<nsStyleContext>
nsStyleSet::ResolvePseudoElementStyle(Element* aParentElement,
nsCSSPseudoElements::Type aType,

View File

@ -53,6 +53,15 @@ class nsInitialStyleRule MOZ_FINAL : public nsIStyleRule
#endif
};
class nsDisableTextZoomStyleRule MOZ_FINAL : public nsIStyleRule
{
NS_DECL_ISUPPORTS
virtual void MapRuleInfoInto(nsRuleData* aRuleData) MOZ_OVERRIDE;
#ifdef DEBUG
virtual void List(FILE* out = stdout, int32_t aIndent = 0) const MOZ_OVERRIDE;
#endif
};
// The style set object is created by the document viewer and ownership is
// then handed off to the PresShell. Only the PresShell should delete a
// style set.
@ -336,6 +345,9 @@ class nsStyleSet
void WalkRestrictionRule(nsCSSPseudoElements::Type aPseudoType,
nsRuleWalker* aRuleWalker);
void WalkDisableTextZoomRule(mozilla::dom::Element* aElement,
nsRuleWalker* aRuleWalker);
#ifdef DEBUG
// Just like AddImportantRules except it doesn't actually add anything; it
// just asserts that there are no important rules between aCurrLevelNode and
@ -435,6 +447,10 @@ class nsStyleSet
// determining when context-sensitive values are in use.
nsRefPtr<nsInitialStyleRule> mInitialStyleRule;
// Style rule that sets the internal -x-text-zoom property on
// <svg:text> elements to disable the effect of text zooming.
nsRefPtr<nsDisableTextZoomStyleRule> mDisableTextZoomStyleRule;
// Old rule trees, which should only be non-empty between
// BeginReconstruct and EndReconstruct, but in case of bugs that cause
// style contexts to exist too long, may last longer.

View File

@ -100,6 +100,7 @@ nsStyleFont::nsStyleFont(const nsStyleFont& aSrc)
, mGenericID(aSrc.mGenericID)
, mScriptLevel(aSrc.mScriptLevel)
, mExplicitLanguage(aSrc.mExplicitLanguage)
, mAllowZoom(aSrc.mAllowZoom)
, mScriptUnconstrainedSize(aSrc.mScriptUnconstrainedSize)
, mScriptMinSize(aSrc.mScriptMinSize)
, mScriptSizeMultiplier(aSrc.mScriptSizeMultiplier)
@ -128,6 +129,7 @@ nsStyleFont::Init(nsPresContext* aPresContext)
NS_POINTS_TO_TWIPS(NS_MATHML_DEFAULT_SCRIPT_MIN_SIZE_PT));
mScriptLevel = 0;
mScriptSizeMultiplier = NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER;
mAllowZoom = true;
nsAutoString language;
aPresContext->Document()->GetContentLanguage(language);
@ -161,8 +163,30 @@ nsStyleFont::Destroy(nsPresContext* aContext) {
aContext->FreeToShell(sizeof(nsStyleFont), this);
}
void
nsStyleFont::EnableZoom(nsPresContext* aContext, bool aEnable)
{
if (mAllowZoom == aEnable) {
return;
}
mAllowZoom = aEnable;
if (mAllowZoom) {
mSize = nsStyleFont::ZoomText(aContext, mSize);
mFont.size = nsStyleFont::ZoomText(aContext, mFont.size);
mScriptUnconstrainedSize =
nsStyleFont::ZoomText(aContext, mScriptUnconstrainedSize);
} else {
mSize = nsStyleFont::UnZoomText(aContext, mSize);
mFont.size = nsStyleFont::UnZoomText(aContext, mFont.size);
mScriptUnconstrainedSize =
nsStyleFont::UnZoomText(aContext, mScriptUnconstrainedSize);
}
}
nsChangeHint nsStyleFont::CalcDifference(const nsStyleFont& aOther) const
{
MOZ_ASSERT(mAllowZoom == aOther.mAllowZoom,
"expected mAllowZoom to be the same on both nsStyleFonts");
if (mSize != aOther.mSize ||
mLanguage != aOther.mLanguage ||
mExplicitLanguage != aOther.mExplicitLanguage) {

View File

@ -86,6 +86,8 @@ public:
void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW;
void Destroy(nsPresContext* aContext);
void EnableZoom(nsPresContext* aContext, bool aEnable);
nsFont mFont; // [inherited]
nscoord mSize; // [inherited] Our "computed size". Can be different
// from mFont.size which is our "actual size" and is
@ -102,6 +104,10 @@ public:
// was mLanguage set based on a lang attribute in the document?
bool mExplicitLanguage; // [inherited]
// should calls to ZoomText() and UnZoomText() be made to the font
// size on this nsStyleFont?
bool mAllowZoom; // [inherited]
// The value mSize would have had if scriptminsize had never been applied
nscoord mScriptUnconstrainedSize;
nscoord mScriptMinSize; // [inherited] length

View File

@ -103,6 +103,7 @@ const char *gInaccessibleProperties[] = {
"-x-lang",
"-x-span",
"-x-system-font",
"-x-text-zoom",
"border-end-color-value",
"border-end-style-value",
"border-end-width-value",

View File

@ -1626,9 +1626,7 @@ nsSVGGlyphFrame::EnsureTextRun(float *aDrawScale, float *aMetricsScale,
// fonts in SVG to respond to the browser's "TextZoom"
// (Ctrl++,Ctrl+-)
nsPresContext *presContext = PresContext();
float textZoom = presContext->TextZoom();
double size =
presContext->AppUnitsToFloatCSSPixels(fontData->mSize) / textZoom;
double size = presContext->AppUnitsToFloatCSSPixels(fontData->mSize);
double textRunSize;
if (mTextRun) {

View File

@ -4980,8 +4980,7 @@ nsSVGTextFrame2::UpdateFontSizeScaleFactor()
}
}
float textZoom = presContext->TextZoom();
double minSize = presContext->AppUnitsToFloatCSSPixels(min) / textZoom;
double minSize = presContext->AppUnitsToFloatCSSPixels(min);
if (geometricPrecision) {
// We want to ensure minSize is scaled to PRECISE_SIZE.
@ -4989,7 +4988,7 @@ nsSVGTextFrame2::UpdateFontSizeScaleFactor()
return;
}
double maxSize = presContext->AppUnitsToFloatCSSPixels(max) / textZoom;
double maxSize = presContext->AppUnitsToFloatCSSPixels(max);
// The context scale is the ratio of the length of the transformed
// diagonal vector (1,1) to the length of the untransformed diagonal