Files
palemoon27/layout/mathml/nsMathMLmpaddedFrame.cpp
T
roytam1 212144c57f import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1153936 - nsIHttpChannelInteral attribute to opt out of alt-svc on per channel basis r=hurley (0e5667321)
- Bug 1137287 - Part 2: Send non-200/404 synthesized responses via the parent HTTP implementation for proper processing. r=mayhemer (3c38a1908)
- Bug 1137287 - Part 0: Test for synthesized redirects. r=jdm (6f12f95de)
- Bug 1137287 - Build fix. rs=KWierso for a CLOSED TREE (85694256a)
- Bug 1095098 - move do_QueryObject templates into their own header; r=froydnj (5d349c248)
- Bug 1095098 - followup - add back some static analysis attributes lost in a rebase; r=me (917a6b5da)
- Bug 1159303 - Reduce noise due to sort operations warnings. r=bent (cac3c3e12)
- Bug 1164559 - Part 1: Remove instances of #ifdef PR_LOGGING in storage. r=froydnj (5dd0f92b0)
- Bug 1164559 - Part 2: Wrap expensive calls in PR_LOG_TEST. r=froydnj (54e94d1ce)
- Bug 1148833 part 1 - Remove nsMathMLContainerFrame::WillReflow, reset the NS_MATHML_ERROR bit at the start of Reflow instead. r=roc (803abb8d9)
- Bug 1148833 part 2 - Makes sure gLogModule is initialized by calling GetLogModuleInfo(). r=roc (722e2019d)
- Bug 1148833 part 3 - Remove nsIFrame::WillReflow and add a non-virtual MarkInReflow method instead that sets NS_FRAME_IN_REFLOW. Call it at the start of Reflow(). r=roc (9cceb221d)
- Bug 1148833 part 4 - Fix indentation of some Reflow params (white-space changes only). (b2ba3e18a)
- Bug 1158546 - Remove nsDisplayHeaderFooter::mFrame in favor of nsDisplayItem::mFrame; r=roc (f6a20967f)
- Bug 1162673 - Part 1: Remove instances of #ifdef PR_LOGGING in layout. r=froydnj (453a452fc)
- Bug 1162673 - Part 2: Wrap expensive calls in PR_LOG_TEST. r=froydnj (d745eab19)
- Bug 1144031 - fix use of uninitialized variable, r=mcmanus (327dc1af9)
- Bug 1144270 : Update remaining callers of newChannel to newChannel2 in netwerk/ (r=mcmanus) (60c21d7cb)
- Bug 1161558 cleanup some nsIObserver shutdown paths r=bagder (eb8441bb9)
- Bug 1162336 - Part 1: Remove instances of #ifdef PR_LOGGING in netwerk. r=froydnj (8cadc40a2)
- Bug 1162336 - Part 2: Wrap expensive calls in PR_LOG_TEST. r=froydnj (4e26e4886)
- Bug 1163194 - Part 1: Remove instances of #ifdef PR_LOGGING in dom/xul. r=froydnj (4f840564b)
- Bug 1163194 - Part 2: Wrap expensive calls in PR_LOG_TEST. r=froydnj (653707f34)
- Bug 1153737: Avoid unnecessary uses of mozilla::pkix::ScopedPtr, r=keeler (fd9eb9aa2)
- Bug 1038072 - signature verification for JAR files unpacked into a directory. r=keeler (32469e1dd)
- Bug 1124076 - Properly detect certs when loaded and prompt to import them. r=sworkman/dkeeler (d860e3cac)
- Bug 1124076 followup - fix the build when PR_LOGGING is not defined. r=mrbkap (657b18bf2)
- Bug 1162691 - Part 1: Remove instances of #ifdef PR_LOGGING in security. r=froydnj (98a916e82)
- Bug 1162691 - Part 2: Wrap expensive calls in PR_LOG_TEST. r=froydnj (000c2fe42)
- bug 1147085 - remove nsINSSCertCache (replace it with nsIX509CertDB.getCerts()) r=Cykesiopka (88f7eba23)
- Bug 1149888 - Make PLDHashTable::mRecursionLevel atomic, r=froydnj. Pushing on CLOSED TREE with a=ryanvm. (25d8e2da1)
- Bug 1050035 (part 1, attempt 2) - Lazily allocate PLDHashTable::mEntryStore. r=froydnj. (195615f16)
- Bug 1159972 - Remove the fallible version of PL_DHashTableInit(). r=froydnj. (d31806eeb)
2020-05-30 12:49:06 +08:00

450 lines
16 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsMathMLmpaddedFrame.h"
#include "nsMathMLElement.h"
#include "mozilla/gfx/2D.h"
#include <algorithm>
//
// <mpadded> -- adjust space around content - implementation
//
#define NS_MATHML_SIGN_INVALID -1 // if the attribute is not there
#define NS_MATHML_SIGN_UNSPECIFIED 0
#define NS_MATHML_SIGN_MINUS 1
#define NS_MATHML_SIGN_PLUS 2
#define NS_MATHML_PSEUDO_UNIT_UNSPECIFIED 0
#define NS_MATHML_PSEUDO_UNIT_ITSELF 1 // special
#define NS_MATHML_PSEUDO_UNIT_WIDTH 2
#define NS_MATHML_PSEUDO_UNIT_HEIGHT 3
#define NS_MATHML_PSEUDO_UNIT_DEPTH 4
#define NS_MATHML_PSEUDO_UNIT_NAMEDSPACE 5
nsIFrame*
NS_NewMathMLmpaddedFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
{
return new (aPresShell) nsMathMLmpaddedFrame(aContext);
}
NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmpaddedFrame)
nsMathMLmpaddedFrame::~nsMathMLmpaddedFrame()
{
}
NS_IMETHODIMP
nsMathMLmpaddedFrame::InheritAutomaticData(nsIFrame* aParent)
{
// let the base class get the default from our parent
nsMathMLContainerFrame::InheritAutomaticData(aParent);
mPresentationData.flags |= NS_MATHML_STRETCH_ALL_CHILDREN_VERTICALLY;
return NS_OK;
}
void
nsMathMLmpaddedFrame::ProcessAttributes()
{
/*
parse the attributes
width = [+|-] unsigned-number (% [pseudo-unit] | pseudo-unit | h-unit | namedspace)
height = [+|-] unsigned-number (% [pseudo-unit] | pseudo-unit | v-unit | namedspace)
depth = [+|-] unsigned-number (% [pseudo-unit] | pseudo-unit | v-unit | namedspace)
lspace = [+|-] unsigned-number (% [pseudo-unit] | pseudo-unit | h-unit | namedspace)
voffset= [+|-] unsigned-number (% [pseudo-unit] | pseudo-unit | v-unit | namedspace)
*/
nsAutoString value;
// width
mWidthSign = NS_MATHML_SIGN_INVALID;
mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::width, value);
if (!value.IsEmpty()) {
if (!ParseAttribute(value, mWidthSign, mWidth, mWidthPseudoUnit)) {
ReportParseError(nsGkAtoms::width->GetUTF16String(), value.get());
}
}
// height
mHeightSign = NS_MATHML_SIGN_INVALID;
mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::height, value);
if (!value.IsEmpty()) {
if (!ParseAttribute(value, mHeightSign, mHeight, mHeightPseudoUnit)) {
ReportParseError(nsGkAtoms::height->GetUTF16String(), value.get());
}
}
// depth
mDepthSign = NS_MATHML_SIGN_INVALID;
mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::depth_, value);
if (!value.IsEmpty()) {
if (!ParseAttribute(value, mDepthSign, mDepth, mDepthPseudoUnit)) {
ReportParseError(nsGkAtoms::depth_->GetUTF16String(), value.get());
}
}
// lspace
mLeadingSpaceSign = NS_MATHML_SIGN_INVALID;
mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::lspace_, value);
if (!value.IsEmpty()) {
if (!ParseAttribute(value, mLeadingSpaceSign, mLeadingSpace,
mLeadingSpacePseudoUnit)) {
ReportParseError(nsGkAtoms::lspace_->GetUTF16String(), value.get());
}
}
// voffset
mVerticalOffsetSign = NS_MATHML_SIGN_INVALID;
mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::voffset_, value);
if (!value.IsEmpty()) {
if (!ParseAttribute(value, mVerticalOffsetSign, mVerticalOffset,
mVerticalOffsetPseudoUnit)) {
ReportParseError(nsGkAtoms::voffset_->GetUTF16String(), value.get());
}
}
}
// parse an input string in the following format (see bug 148326 for testcases):
// [+|-] unsigned-number (% [pseudo-unit] | pseudo-unit | css-unit | namedspace)
bool
nsMathMLmpaddedFrame::ParseAttribute(nsString& aString,
int32_t& aSign,
nsCSSValue& aCSSValue,
int32_t& aPseudoUnit)
{
aCSSValue.Reset();
aSign = NS_MATHML_SIGN_INVALID;
aPseudoUnit = NS_MATHML_PSEUDO_UNIT_UNSPECIFIED;
aString.CompressWhitespace(); // aString is not a const in this code
int32_t stringLength = aString.Length();
if (!stringLength)
return false;
nsAutoString number, unit;
//////////////////////
// see if the sign is there
int32_t i = 0;
if (aString[0] == '+') {
aSign = NS_MATHML_SIGN_PLUS;
i++;
}
else if (aString[0] == '-') {
aSign = NS_MATHML_SIGN_MINUS;
i++;
}
else
aSign = NS_MATHML_SIGN_UNSPECIFIED;
// get the number
bool gotDot = false, gotPercent = false;
for (; i < stringLength; i++) {
char16_t c = aString[i];
if (gotDot && c == '.') {
// error - two dots encountered
aSign = NS_MATHML_SIGN_INVALID;
return false;
}
if (c == '.')
gotDot = true;
else if (!nsCRT::IsAsciiDigit(c)) {
break;
}
number.Append(c);
}
// catch error if we didn't enter the loop above... we could simply initialize
// floatValue = 1, to cater for cases such as width="height", but that wouldn't
// be in line with the spec which requires an explicit number
if (number.IsEmpty()) {
aSign = NS_MATHML_SIGN_INVALID;
return false;
}
nsresult errorCode;
float floatValue = number.ToFloat(&errorCode);
if (NS_FAILED(errorCode)) {
aSign = NS_MATHML_SIGN_INVALID;
return false;
}
// see if this is a percentage-based value
if (i < stringLength && aString[i] == '%') {
i++;
gotPercent = true;
}
// the remainder now should be a css-unit, or a pseudo-unit, or a named-space
aString.Right(unit, stringLength - i);
if (unit.IsEmpty()) {
if (gotPercent) {
// case ["+"|"-"] unsigned-number "%"
aCSSValue.SetPercentValue(floatValue / 100.0f);
aPseudoUnit = NS_MATHML_PSEUDO_UNIT_ITSELF;
return true;
} else {
// case ["+"|"-"] unsigned-number
// XXXfredw: should we allow non-zero unitless values? See bug 757703.
if (!floatValue) {
aCSSValue.SetFloatValue(floatValue, eCSSUnit_Number);
aPseudoUnit = NS_MATHML_PSEUDO_UNIT_ITSELF;
return true;
}
}
}
else if (unit.EqualsLiteral("width")) aPseudoUnit = NS_MATHML_PSEUDO_UNIT_WIDTH;
else if (unit.EqualsLiteral("height")) aPseudoUnit = NS_MATHML_PSEUDO_UNIT_HEIGHT;
else if (unit.EqualsLiteral("depth")) aPseudoUnit = NS_MATHML_PSEUDO_UNIT_DEPTH;
else if (!gotPercent) { // percentage can only apply to a pseudo-unit
// see if the unit is a named-space
if (nsMathMLElement::ParseNamedSpaceValue(unit, aCSSValue,
nsMathMLElement::
PARSE_ALLOW_NEGATIVE)) {
// re-scale properly, and we know that the unit of the named-space is 'em'
floatValue *= aCSSValue.GetFloatValue();
aCSSValue.SetFloatValue(floatValue, eCSSUnit_EM);
aPseudoUnit = NS_MATHML_PSEUDO_UNIT_NAMEDSPACE;
return true;
}
// see if the input was just a CSS value
// We are not supposed to have a unitless, percent, negative or namedspace
// value here.
number.Append(unit); // leave the sign out if it was there
if (nsMathMLElement::ParseNumericValue(number, aCSSValue,
nsMathMLElement::
PARSE_SUPPRESS_WARNINGS, nullptr))
return true;
}
// if we enter here, we have a number that will act as a multiplier on a pseudo-unit
if (aPseudoUnit != NS_MATHML_PSEUDO_UNIT_UNSPECIFIED) {
if (gotPercent)
aCSSValue.SetPercentValue(floatValue / 100.0f);
else
aCSSValue.SetFloatValue(floatValue, eCSSUnit_Number);
return true;
}
#ifdef DEBUG
printf("mpadded: attribute with bad numeric value: %s\n",
NS_LossyConvertUTF16toASCII(aString).get());
#endif
// if we reach here, it means we encounter an unexpected input
aSign = NS_MATHML_SIGN_INVALID;
return false;
}
void
nsMathMLmpaddedFrame::UpdateValue(int32_t aSign,
int32_t aPseudoUnit,
const nsCSSValue& aCSSValue,
const nsHTMLReflowMetrics& aDesiredSize,
nscoord& aValueToUpdate,
float aFontSizeInflation) const
{
nsCSSUnit unit = aCSSValue.GetUnit();
if (NS_MATHML_SIGN_INVALID != aSign && eCSSUnit_Null != unit) {
nscoord scaler = 0, amount = 0;
if (eCSSUnit_Percent == unit || eCSSUnit_Number == unit) {
switch(aPseudoUnit) {
case NS_MATHML_PSEUDO_UNIT_WIDTH:
scaler = aDesiredSize.Width();
break;
case NS_MATHML_PSEUDO_UNIT_HEIGHT:
scaler = aDesiredSize.BlockStartAscent();
break;
case NS_MATHML_PSEUDO_UNIT_DEPTH:
scaler = aDesiredSize.Height() - aDesiredSize.BlockStartAscent();
break;
default:
// if we ever reach here, it would mean something is wrong
// somewhere with the setup and/or the caller
NS_ERROR("Unexpected Pseudo Unit");
return;
}
}
if (eCSSUnit_Number == unit)
amount = NSToCoordRound(float(scaler) * aCSSValue.GetFloatValue());
else if (eCSSUnit_Percent == unit)
amount = NSToCoordRound(float(scaler) * aCSSValue.GetPercentValue());
else
amount = CalcLength(PresContext(), mStyleContext, aCSSValue,
aFontSizeInflation);
if (NS_MATHML_SIGN_PLUS == aSign)
aValueToUpdate += amount;
else if (NS_MATHML_SIGN_MINUS == aSign)
aValueToUpdate -= amount;
else
aValueToUpdate = amount;
}
}
void
nsMathMLmpaddedFrame::Reflow(nsPresContext* aPresContext,
nsHTMLReflowMetrics& aDesiredSize,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
mPresentationData.flags &= ~NS_MATHML_ERROR;
ProcessAttributes();
///////////////
// Let the base class format our content like an inferred mrow
nsMathMLContainerFrame::Reflow(aPresContext, aDesiredSize,
aReflowState, aStatus);
//NS_ASSERTION(NS_FRAME_IS_COMPLETE(aStatus), "bad status");
}
/* virtual */ nsresult
nsMathMLmpaddedFrame::Place(nsRenderingContext& aRenderingContext,
bool aPlaceOrigin,
nsHTMLReflowMetrics& aDesiredSize)
{
nsresult rv =
nsMathMLContainerFrame::Place(aRenderingContext, false, aDesiredSize);
if (NS_MATHML_HAS_ERROR(mPresentationData.flags) || NS_FAILED(rv)) {
DidReflowChildren(GetFirstPrincipalChild());
return rv;
}
nscoord height = aDesiredSize.BlockStartAscent();
nscoord depth = aDesiredSize.Height() - aDesiredSize.BlockStartAscent();
// The REC says:
//
// "The lspace attribute ('leading' space) specifies the horizontal location
// of the positioning point of the child content with respect to the
// positioning point of the mpadded element. By default they coincide, and
// therefore absolute values for lspace have the same effect as relative
// values."
//
// "MathML renderers should ensure that, except for the effects of the
// attributes, the relative spacing between the contents of the mpadded
// element and surrounding MathML elements would not be modified by replacing
// an mpadded element with an mrow element with the same content, even if
// linebreaking occurs within the mpadded element."
//
// (http://www.w3.org/TR/MathML/chapter3.html#presm.mpadded)
//
// "In those discussions, the terms leading and trailing are used to specify
// a side of an object when which side to use depends on the directionality;
// ie. leading means left in LTR but right in RTL."
// (http://www.w3.org/TR/MathML/chapter3.html#presm.bidi.math)
nscoord lspace = 0;
// In MathML3, "width" will be the bounding box width and "advancewidth" will
// refer "to the horizontal distance between the positioning point of the
// mpadded and the positioning point for the following content". MathML2
// doesn't make the distinction.
nscoord width = aDesiredSize.Width();
nscoord voffset = 0;
int32_t pseudoUnit;
nscoord initialWidth = width;
float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this);
// update width
pseudoUnit = (mWidthPseudoUnit == NS_MATHML_PSEUDO_UNIT_ITSELF)
? NS_MATHML_PSEUDO_UNIT_WIDTH : mWidthPseudoUnit;
UpdateValue(mWidthSign, pseudoUnit, mWidth,
aDesiredSize, width, fontSizeInflation);
width = std::max(0, width);
// update "height" (this is the ascent in the terminology of the REC)
pseudoUnit = (mHeightPseudoUnit == NS_MATHML_PSEUDO_UNIT_ITSELF)
? NS_MATHML_PSEUDO_UNIT_HEIGHT : mHeightPseudoUnit;
UpdateValue(mHeightSign, pseudoUnit, mHeight,
aDesiredSize, height, fontSizeInflation);
height = std::max(0, height);
// update "depth" (this is the descent in the terminology of the REC)
pseudoUnit = (mDepthPseudoUnit == NS_MATHML_PSEUDO_UNIT_ITSELF)
? NS_MATHML_PSEUDO_UNIT_DEPTH : mDepthPseudoUnit;
UpdateValue(mDepthSign, pseudoUnit, mDepth,
aDesiredSize, depth, fontSizeInflation);
depth = std::max(0, depth);
// update lspace
if (mLeadingSpacePseudoUnit != NS_MATHML_PSEUDO_UNIT_ITSELF) {
pseudoUnit = mLeadingSpacePseudoUnit;
UpdateValue(mLeadingSpaceSign, pseudoUnit, mLeadingSpace,
aDesiredSize, lspace, fontSizeInflation);
}
// update voffset
if (mVerticalOffsetPseudoUnit != NS_MATHML_PSEUDO_UNIT_ITSELF) {
pseudoUnit = mVerticalOffsetPseudoUnit;
UpdateValue(mVerticalOffsetSign, pseudoUnit, mVerticalOffset,
aDesiredSize, voffset, fontSizeInflation);
}
// do the padding now that we have everything
// The idea here is to maintain the invariant that <mpadded>...</mpadded> (i.e.,
// with no attributes) looks the same as <mrow>...</mrow>. But when there are
// attributes, tweak our metrics and move children to achieve the desired visual
// effects.
if ((StyleVisibility()->mDirection ?
mWidthSign : mLeadingSpaceSign) != NS_MATHML_SIGN_INVALID) {
// there was padding on the left. dismiss the left italic correction now
// (so that our parent won't correct us)
mBoundingMetrics.leftBearing = 0;
}
if ((StyleVisibility()->mDirection ?
mLeadingSpaceSign : mWidthSign) != NS_MATHML_SIGN_INVALID) {
// there was padding on the right. dismiss the right italic correction now
// (so that our parent won't correct us)
mBoundingMetrics.width = width;
mBoundingMetrics.rightBearing = mBoundingMetrics.width;
}
nscoord dx = (StyleVisibility()->mDirection ?
width - initialWidth - lspace : lspace);
aDesiredSize.SetBlockStartAscent(height);
aDesiredSize.Width() = mBoundingMetrics.width;
aDesiredSize.Height() = depth + aDesiredSize.BlockStartAscent();
mBoundingMetrics.ascent = height;
mBoundingMetrics.descent = depth;
aDesiredSize.mBoundingMetrics = mBoundingMetrics;
mReference.x = 0;
mReference.y = aDesiredSize.BlockStartAscent();
if (aPlaceOrigin) {
// Finish reflowing child frames, positioning their origins.
PositionRowChildFrames(dx, aDesiredSize.BlockStartAscent() - voffset);
}
return NS_OK;
}
/* virtual */ nsresult
nsMathMLmpaddedFrame::MeasureForWidth(nsRenderingContext& aRenderingContext,
nsHTMLReflowMetrics& aDesiredSize)
{
ProcessAttributes();
return Place(aRenderingContext, false, aDesiredSize);
}