Bug 785860 - fix sts preload list tests to skip private mode tests if private browsin...
[gecko.git] / layout / mathml / nsMathMLTokenFrame.cpp
blobdddd671981e7a6b33c3d3e58ce8e55d2325b3857
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsCOMPtr.h"
7 #include "nsFrame.h"
8 #include "nsPresContext.h"
9 #include "nsStyleContext.h"
10 #include "nsStyleConsts.h"
11 #include "nsContentUtils.h"
12 #include "nsCSSFrameConstructor.h"
13 #include "nsMathMLTokenFrame.h"
15 nsIFrame*
16 NS_NewMathMLTokenFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
18 return new (aPresShell) nsMathMLTokenFrame(aContext);
21 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLTokenFrame)
23 nsMathMLTokenFrame::~nsMathMLTokenFrame()
27 NS_IMETHODIMP
28 nsMathMLTokenFrame::InheritAutomaticData(nsIFrame* aParent)
30 // let the base class get the default from our parent
31 nsMathMLContainerFrame::InheritAutomaticData(aParent);
33 if (mContent->Tag() != nsGkAtoms::mspace_) {
34 // see if the directionality attribute is there
35 nsMathMLFrame::FindAttrDirectionality(mContent, mPresentationData);
38 ProcessTextData();
40 return NS_OK;
43 eMathMLFrameType
44 nsMathMLTokenFrame::GetMathMLFrameType()
46 // treat everything other than <mi> as ordinary...
47 if (mContent->Tag() != nsGkAtoms::mi_) {
48 return eMathMLFrameType_Ordinary;
51 // for <mi>, distinguish between italic and upright...
52 nsAutoString style;
53 // mathvariant overrides fontstyle
54 // http://www.w3.org/TR/2003/REC-MathML2-20031021/chapter3.html#presm.deprecatt
55 mContent->GetAttr(kNameSpaceID_None,
56 nsGkAtoms::_moz_math_fontstyle_, style) ||
57 GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::mathvariant_,
58 style) ||
59 GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::fontstyle_,
60 style);
62 if (style.EqualsLiteral("italic") || style.EqualsLiteral("bold-italic") ||
63 style.EqualsLiteral("script") || style.EqualsLiteral("bold-script") ||
64 style.EqualsLiteral("sans-serif-italic") ||
65 style.EqualsLiteral("sans-serif-bold-italic")) {
66 return eMathMLFrameType_ItalicIdentifier;
68 else if(style.EqualsLiteral("invariant")) {
69 nsAutoString data;
70 nsContentUtils::GetNodeTextContent(mContent, false, data);
71 eMATHVARIANT variant = nsMathMLOperators::LookupInvariantChar(data);
73 switch (variant) {
74 case eMATHVARIANT_italic:
75 case eMATHVARIANT_bold_italic:
76 case eMATHVARIANT_script:
77 case eMATHVARIANT_bold_script:
78 case eMATHVARIANT_sans_serif_italic:
79 case eMATHVARIANT_sans_serif_bold_italic:
80 return eMathMLFrameType_ItalicIdentifier;
81 default:
82 ; // fall through to upright
85 return eMathMLFrameType_UprightIdentifier;
88 static void
89 CompressWhitespace(nsIContent* aContent)
91 uint32_t numKids = aContent->GetChildCount();
92 for (uint32_t kid = 0; kid < numKids; kid++) {
93 nsIContent* cont = aContent->GetChildAt(kid);
94 if (cont && cont->IsNodeOfType(nsINode::eTEXT)) {
95 nsAutoString text;
96 cont->AppendTextTo(text);
97 text.CompressWhitespace();
98 cont->SetText(text, false); // not meant to be used if notify is needed
103 NS_IMETHODIMP
104 nsMathMLTokenFrame::Init(nsIContent* aContent,
105 nsIFrame* aParent,
106 nsIFrame* aPrevInFlow)
108 // leading and trailing whitespace doesn't count -- bug 15402
109 // brute force removal for people who do <mi> a </mi> instead of <mi>a</mi>
110 // XXX the best fix is to skip these in nsTextFrame
111 CompressWhitespace(aContent);
113 // let the base class do its Init()
114 return nsMathMLContainerFrame::Init(aContent, aParent, aPrevInFlow);
117 NS_IMETHODIMP
118 nsMathMLTokenFrame::SetInitialChildList(ChildListID aListID,
119 nsFrameList& aChildList)
121 // First, let the base class do its work
122 nsresult rv = nsMathMLContainerFrame::SetInitialChildList(aListID, aChildList);
123 if (NS_FAILED(rv))
124 return rv;
126 SetQuotes(false);
127 ProcessTextData();
128 return rv;
131 nsresult
132 nsMathMLTokenFrame::Reflow(nsPresContext* aPresContext,
133 nsHTMLReflowMetrics& aDesiredSize,
134 const nsHTMLReflowState& aReflowState,
135 nsReflowStatus& aStatus)
137 nsresult rv = NS_OK;
139 // initializations needed for empty markup like <mtag></mtag>
140 aDesiredSize.width = aDesiredSize.height = 0;
141 aDesiredSize.ascent = 0;
142 aDesiredSize.mBoundingMetrics = nsBoundingMetrics();
144 nsSize availSize(aReflowState.ComputedWidth(), NS_UNCONSTRAINEDSIZE);
145 nsIFrame* childFrame = GetFirstPrincipalChild();
146 while (childFrame) {
147 // ask our children to compute their bounding metrics
148 nsHTMLReflowMetrics childDesiredSize(aDesiredSize.mFlags
149 | NS_REFLOW_CALC_BOUNDING_METRICS);
150 nsHTMLReflowState childReflowState(aPresContext, aReflowState,
151 childFrame, availSize);
152 rv = ReflowChild(childFrame, aPresContext, childDesiredSize,
153 childReflowState, aStatus);
154 //NS_ASSERTION(NS_FRAME_IS_COMPLETE(aStatus), "bad status");
155 if (NS_FAILED(rv)) {
156 // Call DidReflow() for the child frames we successfully did reflow.
157 DidReflowChildren(GetFirstPrincipalChild(), childFrame);
158 return rv;
161 SaveReflowAndBoundingMetricsFor(childFrame, childDesiredSize,
162 childDesiredSize.mBoundingMetrics);
164 childFrame = childFrame->GetNextSibling();
168 // place and size children
169 FinalizeReflow(*aReflowState.rendContext, aDesiredSize);
171 aStatus = NS_FRAME_COMPLETE;
172 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
173 return NS_OK;
176 // For token elements, mBoundingMetrics is computed at the ReflowToken
177 // pass, it is not computed here because our children may be text frames
178 // that do not implement the GetBoundingMetrics() interface.
179 /* virtual */ nsresult
180 nsMathMLTokenFrame::Place(nsRenderingContext& aRenderingContext,
181 bool aPlaceOrigin,
182 nsHTMLReflowMetrics& aDesiredSize)
184 mBoundingMetrics = nsBoundingMetrics();
185 for (nsIFrame* childFrame = GetFirstPrincipalChild(); childFrame;
186 childFrame = childFrame->GetNextSibling()) {
187 nsHTMLReflowMetrics childSize;
188 GetReflowAndBoundingMetricsFor(childFrame, childSize,
189 childSize.mBoundingMetrics, nullptr);
190 // compute and cache the bounding metrics
191 mBoundingMetrics += childSize.mBoundingMetrics;
194 nsRefPtr<nsFontMetrics> fm;
195 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm));
196 nscoord ascent = fm->MaxAscent();
197 nscoord descent = fm->MaxDescent();
199 aDesiredSize.mBoundingMetrics = mBoundingMetrics;
200 aDesiredSize.width = mBoundingMetrics.width;
201 aDesiredSize.ascent = NS_MAX(mBoundingMetrics.ascent, ascent);
202 aDesiredSize.height = aDesiredSize.ascent +
203 NS_MAX(mBoundingMetrics.descent, descent);
205 if (aPlaceOrigin) {
206 nscoord dy, dx = 0;
207 for (nsIFrame* childFrame = GetFirstPrincipalChild(); childFrame;
208 childFrame = childFrame->GetNextSibling()) {
209 nsHTMLReflowMetrics childSize;
210 GetReflowAndBoundingMetricsFor(childFrame, childSize,
211 childSize.mBoundingMetrics);
213 // place and size the child; (dx,0) makes the caret happy - bug 188146
214 dy = childSize.height == 0 ? 0 : aDesiredSize.ascent - childSize.ascent;
215 FinishReflowChild(childFrame, PresContext(), nullptr, childSize, dx, dy, 0);
216 dx += childSize.width;
220 SetReference(nsPoint(0, aDesiredSize.ascent));
222 return NS_OK;
225 /* virtual */ void
226 nsMathMLTokenFrame::MarkIntrinsicWidthsDirty()
228 // this could be called due to changes in the nsTextFrame beneath us
229 // when something changed in the text content. So re-process our text
230 ProcessTextData();
232 nsMathMLContainerFrame::MarkIntrinsicWidthsDirty();
235 NS_IMETHODIMP
236 nsMathMLTokenFrame::AttributeChanged(int32_t aNameSpaceID,
237 nsIAtom* aAttribute,
238 int32_t aModType)
240 if (nsGkAtoms::lquote_ == aAttribute ||
241 nsGkAtoms::rquote_ == aAttribute) {
242 SetQuotes(true);
245 return nsMathMLContainerFrame::
246 AttributeChanged(aNameSpaceID, aAttribute, aModType);
249 void
250 nsMathMLTokenFrame::ProcessTextData()
252 // see if the style changes from normal to italic or vice-versa
253 if (!SetTextStyle())
254 return;
256 // explicitly request a re-resolve to pick up the change of style
257 PresContext()->PresShell()->FrameConstructor()->
258 PostRestyleEvent(mContent->AsElement(), eRestyle_Subtree, NS_STYLE_HINT_NONE);
261 ///////////////////////////////////////////////////////////////////////////
262 // For <mi>, if the content is not a single character, turn the font to
263 // normal (this function will also query attributes from the mstyle hierarchy)
264 // Returns true if there is a style change.
266 // http://www.w3.org/TR/2003/REC-MathML2-20031021/chapter3.html#presm.commatt
268 // "It is important to note that only certain combinations of
269 // character data and mathvariant attribute values make sense.
270 // ...
271 // By design, the only cases that have an unambiguous
272 // interpretation are exactly the ones that correspond to SMP Math
273 // Alphanumeric Symbol characters, which are enumerated in Section
274 // 6.2.3 Mathematical Alphanumeric Symbols Characters. In all other
275 // cases, it is suggested that renderers ignore the value of the
276 // mathvariant attribute if it is present."
278 // There are no corresponding characters for mathvariant=normal, suggesting
279 // that this value should be ignored, but this (from the same section of
280 // Chapter 3) implies that font-style should not be inherited, but set to
281 // normal for mathvariant=normal:
283 // "In particular, inheritance of the mathvariant attribute does not follow
284 // the CSS model. The default value for this attribute is "normal"
285 // (non-slanted) for all tokens except mi. ... (The deprecated fontslant
286 // attribute also behaves this way.)"
288 bool
289 nsMathMLTokenFrame::SetTextStyle()
291 if (mContent->Tag() != nsGkAtoms::mi_)
292 return false;
294 if (!mFrames.FirstChild())
295 return false;
297 // Get the text content that we enclose and its length
298 nsAutoString data;
299 nsContentUtils::GetNodeTextContent(mContent, false, data);
300 int32_t length = data.Length();
301 if (!length)
302 return false;
304 nsAutoString fontstyle;
305 bool isSingleCharacter =
306 length == 1 ||
307 (length == 2 && NS_IS_HIGH_SURROGATE(data[0]));
308 if (isSingleCharacter &&
309 nsMathMLOperators::LookupInvariantChar(data) != eMATHVARIANT_NONE) {
310 // bug 65951 - a non-stylable character has its own intrinsic appearance
311 fontstyle.AssignLiteral("invariant");
313 else {
314 // Attributes override the default behavior.
315 nsAutoString value;
316 if (!(GetAttribute(mContent, mPresentationData.mstyle,
317 nsGkAtoms::mathvariant_, value) ||
318 GetAttribute(mContent, mPresentationData.mstyle,
319 nsGkAtoms::fontstyle_, value))) {
320 if (!isSingleCharacter) {
321 fontstyle.AssignLiteral("normal");
323 else if (length == 1 && // BMP
324 !nsMathMLOperators::
325 TransformVariantChar(data[0], eMATHVARIANT_italic).
326 Equals(data)) {
327 // Transformation exists. Try to make the BMP character look like the
328 // styled character using the style system until bug 114365 is resolved.
329 fontstyle.AssignLiteral("italic");
331 // else single character but there is no corresponding Math Alphanumeric
332 // Symbol character: "ignore the value of the [default] mathvariant
333 // attribute".
337 // set the _moz-math-font-style attribute without notifying that we want a reflow
338 if (fontstyle.IsEmpty()) {
339 if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::_moz_math_fontstyle_)) {
340 mContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::_moz_math_fontstyle_,
341 false);
342 return true;
345 else if (!mContent->AttrValueIs(kNameSpaceID_None,
346 nsGkAtoms::_moz_math_fontstyle_,
347 fontstyle, eCaseMatters)) {
348 mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::_moz_math_fontstyle_,
349 fontstyle, false);
350 return true;
353 return false;
356 ///////////////////////////////////////////////////////////////////////////
357 // For <ms>, it is assumed that the mathml.css file contains two rules:
358 // ms:before { content: open-quote; }
359 // ms:after { content: close-quote; }
360 // With these two rules, the frame construction code will
361 // create inline frames that contain text frames which themselves
362 // contain the text content of the quotes.
363 // So the main idea in this code is to see if there are lquote and
364 // rquote attributes. If these are there, we ovewrite the default
365 // quotes in the text frames.
366 // XXX this is somewhat bogus, we probably should map lquote and rquote
367 // to 'content' style rules
369 // But what if the mathml.css file wasn't loaded?
370 // We also check that we are not relying on null pointers...
372 static void
373 SetQuote(nsIFrame* aFrame, nsString& aValue, bool aNotify)
375 if (!aFrame)
376 return;
378 nsIFrame* textFrame = aFrame->GetFirstPrincipalChild();
379 if (!textFrame)
380 return;
382 nsIContent* quoteContent = textFrame->GetContent();
383 if (!quoteContent->IsNodeOfType(nsINode::eTEXT))
384 return;
386 quoteContent->SetText(aValue, aNotify);
389 void
390 nsMathMLTokenFrame::SetQuotes(bool aNotify)
392 if (mContent->Tag() != nsGkAtoms::ms_)
393 return;
395 nsAutoString value;
396 // lquote
397 if (GetAttribute(mContent, mPresentationData.mstyle,
398 nsGkAtoms::lquote_, value)) {
399 SetQuote(nsLayoutUtils::GetBeforeFrame(this), value, aNotify);
401 // rquote
402 if (GetAttribute(mContent, mPresentationData.mstyle,
403 nsGkAtoms::rquote_, value)) {
404 SetQuote(nsLayoutUtils::GetAfterFrame(this), value, aNotify);