gecko/layout/mathml/nsMathMLTokenFrame.cpp
L. David Baron 2c717f398d Switch lots of callers to using nsLayoutUtils::GetFontMetricsForFrame and nsLayoutUtils::GetFontMetricsForStyleContext. (Bug 678671, patch 1) r=roc
This changes a number of callers that were previously bypassing the use
of the correct language (with either no language or the charset-detected
language on the pres context via nsPresContext::GetMetricsFor) and/or
the correct user font set to pass the correct values, which should
improve the correctness of the behavior of whatever they were using the
fonts for, and also reduce the number of unique sets of font metrics
requested (which helps nsFontCache effectiveness).
2011-08-14 10:08:04 -07:00

436 lines
15 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla MathML Project.
*
* The Initial Developer of the Original Code is
* The University of Queensland.
* Portions created by the Initial Developer are Copyright (C) 1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Roger B. Sidje <rbs@maths.uq.edu.au>
* Karl Tomlinson <karlt+@karlt.net>, Mozilla Corporation
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsCOMPtr.h"
#include "nsFrame.h"
#include "nsPresContext.h"
#include "nsStyleContext.h"
#include "nsStyleConsts.h"
#include "nsContentUtils.h"
#include "nsCSSFrameConstructor.h"
#include "nsMathMLTokenFrame.h"
nsIFrame*
NS_NewMathMLTokenFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
{
return new (aPresShell) nsMathMLTokenFrame(aContext);
}
NS_IMPL_FRAMEARENA_HELPERS(nsMathMLTokenFrame)
nsMathMLTokenFrame::~nsMathMLTokenFrame()
{
}
NS_IMETHODIMP
nsMathMLTokenFrame::InheritAutomaticData(nsIFrame* aParent)
{
// let the base class get the default from our parent
nsMathMLContainerFrame::InheritAutomaticData(aParent);
ProcessTextData();
return NS_OK;
}
eMathMLFrameType
nsMathMLTokenFrame::GetMathMLFrameType()
{
// treat everything other than <mi> as ordinary...
if (mContent->Tag() != nsGkAtoms::mi_) {
return eMathMLFrameType_Ordinary;
}
// for <mi>, distinguish between italic and upright...
nsAutoString style;
// mathvariant overrides fontstyle
// http://www.w3.org/TR/2003/REC-MathML2-20031021/chapter3.html#presm.deprecatt
mContent->GetAttr(kNameSpaceID_None,
nsGkAtoms::_moz_math_fontstyle_, style) ||
GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::mathvariant_,
style) ||
GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::fontstyle_,
style);
if (style.EqualsLiteral("italic") || style.EqualsLiteral("bold-italic") ||
style.EqualsLiteral("script") || style.EqualsLiteral("bold-script") ||
style.EqualsLiteral("sans-serif-italic") ||
style.EqualsLiteral("sans-serif-bold-italic")) {
return eMathMLFrameType_ItalicIdentifier;
}
else if(style.EqualsLiteral("invariant")) {
nsAutoString data;
nsContentUtils::GetNodeTextContent(mContent, PR_FALSE, data);
eMATHVARIANT variant = nsMathMLOperators::LookupInvariantChar(data);
switch (variant) {
case eMATHVARIANT_italic:
case eMATHVARIANT_bold_italic:
case eMATHVARIANT_script:
case eMATHVARIANT_bold_script:
case eMATHVARIANT_sans_serif_italic:
case eMATHVARIANT_sans_serif_bold_italic:
return eMathMLFrameType_ItalicIdentifier;
default:
; // fall through to upright
}
}
return eMathMLFrameType_UprightIdentifier;
}
static void
CompressWhitespace(nsIContent* aContent)
{
PRUint32 numKids = aContent->GetChildCount();
for (PRUint32 kid = 0; kid < numKids; kid++) {
nsIContent* cont = aContent->GetChildAt(kid);
if (cont && cont->IsNodeOfType(nsINode::eTEXT)) {
nsAutoString text;
cont->AppendTextTo(text);
text.CompressWhitespace();
cont->SetText(text, PR_FALSE); // not meant to be used if notify is needed
}
}
}
NS_IMETHODIMP
nsMathMLTokenFrame::Init(nsIContent* aContent,
nsIFrame* aParent,
nsIFrame* aPrevInFlow)
{
// leading and trailing whitespace doesn't count -- bug 15402
// brute force removal for people who do <mi> a </mi> instead of <mi>a</mi>
// XXX the best fix is to skip these in nsTextFrame
CompressWhitespace(aContent);
// let the base class do its Init()
return nsMathMLContainerFrame::Init(aContent, aParent, aPrevInFlow);
}
NS_IMETHODIMP
nsMathMLTokenFrame::SetInitialChildList(nsIAtom* aListName,
nsFrameList& aChildList)
{
// First, let the base class do its work
nsresult rv = nsMathMLContainerFrame::SetInitialChildList(aListName, aChildList);
if (NS_FAILED(rv))
return rv;
SetQuotes(PR_FALSE);
ProcessTextData();
return rv;
}
nsresult
nsMathMLTokenFrame::Reflow(nsPresContext* aPresContext,
nsHTMLReflowMetrics& aDesiredSize,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
nsresult rv = NS_OK;
// initializations needed for empty markup like <mtag></mtag>
aDesiredSize.width = aDesiredSize.height = 0;
aDesiredSize.ascent = 0;
aDesiredSize.mBoundingMetrics = nsBoundingMetrics();
nsSize availSize(aReflowState.ComputedWidth(), NS_UNCONSTRAINEDSIZE);
nsIFrame* childFrame = GetFirstChild(nsnull);
while (childFrame) {
// ask our children to compute their bounding metrics
nsHTMLReflowMetrics childDesiredSize(aDesiredSize.mFlags
| NS_REFLOW_CALC_BOUNDING_METRICS);
nsHTMLReflowState childReflowState(aPresContext, aReflowState,
childFrame, availSize);
rv = ReflowChild(childFrame, aPresContext, childDesiredSize,
childReflowState, aStatus);
//NS_ASSERTION(NS_FRAME_IS_COMPLETE(aStatus), "bad status");
if (NS_FAILED(rv)) {
// Call DidReflow() for the child frames we successfully did reflow.
DidReflowChildren(GetFirstChild(nsnull), childFrame);
return rv;
}
SaveReflowAndBoundingMetricsFor(childFrame, childDesiredSize,
childDesiredSize.mBoundingMetrics);
childFrame = childFrame->GetNextSibling();
}
// place and size children
FinalizeReflow(*aReflowState.rendContext, aDesiredSize);
aStatus = NS_FRAME_COMPLETE;
NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
return NS_OK;
}
// For token elements, mBoundingMetrics is computed at the ReflowToken
// pass, it is not computed here because our children may be text frames
// that do not implement the GetBoundingMetrics() interface.
/* virtual */ nsresult
nsMathMLTokenFrame::Place(nsRenderingContext& aRenderingContext,
PRBool aPlaceOrigin,
nsHTMLReflowMetrics& aDesiredSize)
{
mBoundingMetrics = nsBoundingMetrics();
for (nsIFrame* childFrame = GetFirstChild(nsnull); childFrame;
childFrame = childFrame->GetNextSibling()) {
nsHTMLReflowMetrics childSize;
GetReflowAndBoundingMetricsFor(childFrame, childSize,
childSize.mBoundingMetrics, nsnull);
// compute and cache the bounding metrics
mBoundingMetrics += childSize.mBoundingMetrics;
}
nsRefPtr<nsFontMetrics> fm;
nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm));
nscoord ascent = fm->MaxAscent();
nscoord descent = fm->MaxDescent();
aDesiredSize.mBoundingMetrics = mBoundingMetrics;
aDesiredSize.width = mBoundingMetrics.width;
aDesiredSize.ascent = NS_MAX(mBoundingMetrics.ascent, ascent);
aDesiredSize.height = aDesiredSize.ascent +
NS_MAX(mBoundingMetrics.descent, descent);
if (aPlaceOrigin) {
nscoord dy, dx = 0;
for (nsIFrame* childFrame = GetFirstChild(nsnull); childFrame;
childFrame = childFrame->GetNextSibling()) {
nsHTMLReflowMetrics childSize;
GetReflowAndBoundingMetricsFor(childFrame, childSize,
childSize.mBoundingMetrics);
// place and size the child; (dx,0) makes the caret happy - bug 188146
dy = childSize.height == 0 ? 0 : aDesiredSize.ascent - childSize.ascent;
FinishReflowChild(childFrame, PresContext(), nsnull, childSize, dx, dy, 0);
dx += childSize.width;
}
}
SetReference(nsPoint(0, aDesiredSize.ascent));
return NS_OK;
}
/* virtual */ void
nsMathMLTokenFrame::MarkIntrinsicWidthsDirty()
{
// this could be called due to changes in the nsTextFrame beneath us
// when something changed in the text content. So re-process our text
ProcessTextData();
nsMathMLContainerFrame::MarkIntrinsicWidthsDirty();
}
NS_IMETHODIMP
nsMathMLTokenFrame::AttributeChanged(PRInt32 aNameSpaceID,
nsIAtom* aAttribute,
PRInt32 aModType)
{
if (nsGkAtoms::lquote_ == aAttribute ||
nsGkAtoms::rquote_ == aAttribute) {
SetQuotes(PR_TRUE);
}
return nsMathMLContainerFrame::
AttributeChanged(aNameSpaceID, aAttribute, aModType);
}
void
nsMathMLTokenFrame::ProcessTextData()
{
// see if the style changes from normal to italic or vice-versa
if (!SetTextStyle())
return;
// explicitly request a re-resolve to pick up the change of style
PresContext()->PresShell()->FrameConstructor()->
PostRestyleEvent(mContent->AsElement(), eRestyle_Subtree, NS_STYLE_HINT_NONE);
}
///////////////////////////////////////////////////////////////////////////
// For <mi>, if the content is not a single character, turn the font to
// normal (this function will also query attributes from the mstyle hierarchy)
// Returns PR_TRUE if there is a style change.
//
// http://www.w3.org/TR/2003/REC-MathML2-20031021/chapter3.html#presm.commatt
//
// "It is important to note that only certain combinations of
// character data and mathvariant attribute values make sense.
// ...
// By design, the only cases that have an unambiguous
// interpretation are exactly the ones that correspond to SMP Math
// Alphanumeric Symbol characters, which are enumerated in Section
// 6.2.3 Mathematical Alphanumeric Symbols Characters. In all other
// cases, it is suggested that renderers ignore the value of the
// mathvariant attribute if it is present."
//
// There are no corresponding characters for mathvariant=normal, suggesting
// that this value should be ignored, but this (from the same section of
// Chapter 3) implies that font-style should not be inherited, but set to
// normal for mathvariant=normal:
//
// "In particular, inheritance of the mathvariant attribute does not follow
// the CSS model. The default value for this attribute is "normal"
// (non-slanted) for all tokens except mi. ... (The deprecated fontslant
// attribute also behaves this way.)"
PRBool
nsMathMLTokenFrame::SetTextStyle()
{
if (mContent->Tag() != nsGkAtoms::mi_)
return PR_FALSE;
if (!mFrames.FirstChild())
return PR_FALSE;
// Get the text content that we enclose and its length
nsAutoString data;
nsContentUtils::GetNodeTextContent(mContent, PR_FALSE, data);
PRInt32 length = data.Length();
if (!length)
return PR_FALSE;
nsAutoString fontstyle;
PRBool isSingleCharacter =
length == 1 ||
(length == 2 && NS_IS_HIGH_SURROGATE(data[0]));
if (isSingleCharacter &&
nsMathMLOperators::LookupInvariantChar(data) != eMATHVARIANT_NONE) {
// bug 65951 - a non-stylable character has its own intrinsic appearance
fontstyle.AssignLiteral("invariant");
}
else {
// Attributes override the default behavior.
nsAutoString value;
if (!(GetAttribute(mContent, mPresentationData.mstyle,
nsGkAtoms::mathvariant_, value) ||
GetAttribute(mContent, mPresentationData.mstyle,
nsGkAtoms::fontstyle_, value))) {
if (!isSingleCharacter) {
fontstyle.AssignLiteral("normal");
}
else if (length == 1 && // BMP
!nsMathMLOperators::
TransformVariantChar(data[0], eMATHVARIANT_italic).
Equals(data)) {
// Transformation exists. Try to make the BMP character look like the
// styled character using the style system until bug 114365 is resolved.
fontstyle.AssignLiteral("italic");
}
// else single character but there is no corresponding Math Alphanumeric
// Symbol character: "ignore the value of the [default] mathvariant
// attribute".
}
}
// set the _moz-math-font-style attribute without notifying that we want a reflow
if (fontstyle.IsEmpty()) {
if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::_moz_math_fontstyle_)) {
mContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::_moz_math_fontstyle_,
PR_FALSE);
return PR_TRUE;
}
}
else if (!mContent->AttrValueIs(kNameSpaceID_None,
nsGkAtoms::_moz_math_fontstyle_,
fontstyle, eCaseMatters)) {
mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::_moz_math_fontstyle_,
fontstyle, PR_FALSE);
return PR_TRUE;
}
return PR_FALSE;
}
///////////////////////////////////////////////////////////////////////////
// For <ms>, it is assumed that the mathml.css file contains two rules:
// ms:before { content: open-quote; }
// ms:after { content: close-quote; }
// With these two rules, the frame construction code will
// create inline frames that contain text frames which themselves
// contain the text content of the quotes.
// So the main idea in this code is to see if there are lquote and
// rquote attributes. If these are there, we ovewrite the default
// quotes in the text frames.
// XXX this is somewhat bogus, we probably should map lquote and rquote
// to 'content' style rules
//
// But what if the mathml.css file wasn't loaded?
// We also check that we are not relying on null pointers...
static void
SetQuote(nsIFrame* aFrame, nsString& aValue, PRBool aNotify)
{
if (!aFrame)
return;
nsIFrame* textFrame = aFrame->GetFirstChild(nsnull);
if (!textFrame)
return;
nsIContent* quoteContent = textFrame->GetContent();
if (!quoteContent->IsNodeOfType(nsINode::eTEXT))
return;
quoteContent->SetText(aValue, aNotify);
}
void
nsMathMLTokenFrame::SetQuotes(PRBool aNotify)
{
if (mContent->Tag() != nsGkAtoms::ms_)
return;
nsAutoString value;
// lquote
if (GetAttribute(mContent, mPresentationData.mstyle,
nsGkAtoms::lquote_, value)) {
SetQuote(nsLayoutUtils::GetBeforeFrame(this), value, aNotify);
}
// rquote
if (GetAttribute(mContent, mPresentationData.mstyle,
nsGkAtoms::rquote_, value)) {
SetQuote(nsLayoutUtils::GetAfterFrame(this), value, aNotify);
}
}