Line data Source code
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/. */
5 :
6 : #include "gfxContext.h"
7 : #include "nsMathMLmoFrame.h"
8 : #include "nsPresContext.h"
9 : #include "nsContentUtils.h"
10 : #include "nsFrameSelection.h"
11 : #include "nsMathMLElement.h"
12 : #include <algorithm>
13 :
14 : //
15 : // <mo> -- operator, fence, or separator - implementation
16 : //
17 :
18 : // additional style context to be used by our MathMLChar.
19 : #define NS_MATHML_CHAR_STYLE_CONTEXT_INDEX 0
20 :
21 : nsIFrame*
22 0 : NS_NewMathMLmoFrame(nsIPresShell* aPresShell, nsStyleContext *aContext)
23 : {
24 0 : return new (aPresShell) nsMathMLmoFrame(aContext);
25 : }
26 :
27 0 : NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmoFrame)
28 :
29 0 : nsMathMLmoFrame::~nsMathMLmoFrame()
30 : {
31 0 : }
32 :
33 : static const char16_t kApplyFunction = char16_t(0x2061);
34 : static const char16_t kInvisibleTimes = char16_t(0x2062);
35 : static const char16_t kInvisibleSeparator = char16_t(0x2063);
36 : static const char16_t kInvisiblePlus = char16_t(0x2064);
37 :
38 : eMathMLFrameType
39 0 : nsMathMLmoFrame::GetMathMLFrameType()
40 : {
41 0 : return NS_MATHML_OPERATOR_IS_INVISIBLE(mFlags)
42 0 : ? eMathMLFrameType_OperatorInvisible
43 0 : : eMathMLFrameType_OperatorOrdinary;
44 : }
45 :
46 : // since a mouse click implies selection, we cannot just rely on the
47 : // frame's state bit in our child text frame. So we will first check
48 : // its selected state bit, and use this little helper to double check.
49 : bool
50 0 : nsMathMLmoFrame::IsFrameInSelection(nsIFrame* aFrame)
51 : {
52 0 : NS_ASSERTION(aFrame, "null arg");
53 0 : if (!aFrame || !aFrame->IsSelected())
54 0 : return false;
55 :
56 0 : const nsFrameSelection* frameSelection = aFrame->GetConstFrameSelection();
57 : UniquePtr<SelectionDetails> details =
58 0 : frameSelection->LookUpSelection(aFrame->GetContent(), 0, 1, true);
59 :
60 0 : return details != nullptr;
61 : }
62 :
63 : bool
64 0 : nsMathMLmoFrame::UseMathMLChar()
65 : {
66 0 : return (NS_MATHML_OPERATOR_GET_FORM(mFlags) &&
67 0 : NS_MATHML_OPERATOR_IS_MUTABLE(mFlags)) ||
68 0 : NS_MATHML_OPERATOR_IS_CENTERED(mFlags);
69 : }
70 :
71 : void
72 0 : nsMathMLmoFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
73 : const nsRect& aDirtyRect,
74 : const nsDisplayListSet& aLists)
75 : {
76 0 : bool useMathMLChar = UseMathMLChar();
77 :
78 0 : if (!useMathMLChar) {
79 : // let the base class do everything
80 0 : nsMathMLTokenFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
81 : } else {
82 0 : DisplayBorderBackgroundOutline(aBuilder, aLists);
83 :
84 : // make our char selected if our inner child text frame is selected
85 0 : bool isSelected = false;
86 0 : nsRect selectedRect;
87 0 : nsIFrame* firstChild = mFrames.FirstChild();
88 0 : if (IsFrameInSelection(firstChild)) {
89 0 : mMathMLChar.GetRect(selectedRect);
90 : // add a one pixel border (it renders better for operators like minus)
91 0 : selectedRect.Inflate(nsPresContext::CSSPixelsToAppUnits(1));
92 0 : isSelected = true;
93 : }
94 0 : mMathMLChar.Display(aBuilder, this, aLists, 0, isSelected ? &selectedRect : nullptr);
95 :
96 : #if defined(DEBUG) && defined(SHOW_BOUNDING_BOX)
97 : // for visual debug
98 : DisplayBoundingMetrics(aBuilder, this, mReference, mBoundingMetrics, aLists);
99 : #endif
100 : }
101 0 : }
102 :
103 : // get the text that we enclose and setup our nsMathMLChar
104 : void
105 0 : nsMathMLmoFrame::ProcessTextData()
106 : {
107 0 : mFlags = 0;
108 :
109 0 : nsAutoString data;
110 0 : nsContentUtils::GetNodeTextContent(mContent, false, data);
111 :
112 0 : data.CompressWhitespace();
113 0 : int32_t length = data.Length();
114 0 : char16_t ch = (length == 0) ? char16_t('\0') : data[0];
115 :
116 0 : if ((length == 1) &&
117 0 : (ch == kApplyFunction ||
118 0 : ch == kInvisibleSeparator ||
119 0 : ch == kInvisiblePlus ||
120 : ch == kInvisibleTimes)) {
121 0 : mFlags |= NS_MATHML_OPERATOR_INVISIBLE;
122 : }
123 :
124 : // don't bother doing anything special if we don't have a single child
125 0 : nsPresContext* presContext = PresContext();
126 0 : if (mFrames.GetLength() != 1) {
127 0 : data.Truncate(); // empty data to reset the char
128 0 : mMathMLChar.SetData(data);
129 0 : ResolveMathMLCharStyle(presContext, mContent, mStyleContext, &mMathMLChar);
130 0 : return;
131 : }
132 :
133 : // special... in math mode, the usual minus sign '-' looks too short, so
134 : // what we do here is to remap <mo>-</mo> to the official Unicode minus
135 : // sign (U+2212) which looks much better. For background on this, see
136 : // http://groups.google.com/groups?hl=en&th=66488daf1ade7635&rnum=1
137 0 : if (1 == length && ch == '-') {
138 0 : ch = 0x2212;
139 0 : data = ch;
140 : }
141 :
142 : // cache the special bits: mutable, accent, movablelimits, centered.
143 : // we need to do this in anticipation of other requirements, and these
144 : // bits don't change. Do not reset these bits unless the text gets changed.
145 :
146 : // lookup all the forms under which the operator is listed in the dictionary,
147 : // and record whether the operator has accent="true" or movablelimits="true"
148 : nsOperatorFlags flags[4];
149 : float lspace[4], rspace[4];
150 0 : nsMathMLOperators::LookupOperators(data, flags, lspace, rspace);
151 : nsOperatorFlags allFlags =
152 0 : flags[NS_MATHML_OPERATOR_FORM_INFIX] |
153 0 : flags[NS_MATHML_OPERATOR_FORM_POSTFIX] |
154 0 : flags[NS_MATHML_OPERATOR_FORM_PREFIX];
155 :
156 0 : mFlags |= allFlags & NS_MATHML_OPERATOR_ACCENT;
157 0 : mFlags |= allFlags & NS_MATHML_OPERATOR_MOVABLELIMITS;
158 :
159 : // see if this is an operator that should be centered to cater for
160 : // fonts that are not math-aware
161 0 : if (1 == length) {
162 0 : if ((ch == '+') || (ch == '=') || (ch == '*') ||
163 0 : (ch == 0x2212) || // −
164 0 : (ch == 0x2264) || // ≤
165 0 : (ch == 0x2265) || // ≥
166 : (ch == 0x00D7)) { // ×
167 0 : mFlags |= NS_MATHML_OPERATOR_CENTERED;
168 : }
169 : }
170 :
171 : // cache the operator
172 0 : mMathMLChar.SetData(data);
173 :
174 : // cache the native direction -- beware of bug 133429...
175 : // mEmbellishData.direction must always retain our native direction, whereas
176 : // mMathMLChar.GetStretchDirection() may change later, when Stretch() is called
177 0 : mEmbellishData.direction = mMathMLChar.GetStretchDirection();
178 :
179 : bool isMutable =
180 0 : NS_MATHML_OPERATOR_IS_LARGEOP(allFlags) ||
181 0 : (mEmbellishData.direction != NS_STRETCH_DIRECTION_UNSUPPORTED);
182 0 : if (isMutable)
183 0 : mFlags |= NS_MATHML_OPERATOR_MUTABLE;
184 :
185 0 : ResolveMathMLCharStyle(presContext, mContent, mStyleContext, &mMathMLChar);
186 : }
187 :
188 : // get our 'form' and lookup in the Operator Dictionary to fetch
189 : // our default data that may come from there. Then complete our setup
190 : // using attributes that we may have. To stay in sync, this function is
191 : // called very often. We depend on many things that may change around us.
192 : // However, we re-use unchanged values.
193 : void
194 0 : nsMathMLmoFrame::ProcessOperatorData()
195 : {
196 : // if we have been here before, we will just use our cached form
197 0 : nsOperatorFlags form = NS_MATHML_OPERATOR_GET_FORM(mFlags);
198 0 : nsAutoString value;
199 0 : float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this);
200 :
201 : // special bits are always kept in mFlags.
202 : // remember the mutable bit from ProcessTextData().
203 : // Some chars are listed under different forms in the dictionary,
204 : // and there could be a form under which the char is mutable.
205 : // If the char is the core of an embellished container, we will keep
206 : // it mutable irrespective of the form of the embellished container.
207 : // Also remember the other special bits that we want to carry forward.
208 0 : mFlags &= NS_MATHML_OPERATOR_MUTABLE |
209 : NS_MATHML_OPERATOR_ACCENT |
210 : NS_MATHML_OPERATOR_MOVABLELIMITS |
211 : NS_MATHML_OPERATOR_CENTERED |
212 0 : NS_MATHML_OPERATOR_INVISIBLE;
213 :
214 0 : if (!mEmbellishData.coreFrame) {
215 : // i.e., we haven't been here before, the default form is infix
216 0 : form = NS_MATHML_OPERATOR_FORM_INFIX;
217 :
218 : // reset everything so that we don't keep outdated values around
219 : // in case of dynamic changes
220 0 : mEmbellishData.flags = 0;
221 0 : mEmbellishData.coreFrame = nullptr;
222 0 : mEmbellishData.leadingSpace = 0;
223 0 : mEmbellishData.trailingSpace = 0;
224 0 : if (mMathMLChar.Length() != 1)
225 0 : mEmbellishData.direction = NS_STRETCH_DIRECTION_UNSUPPORTED;
226 : // else... retain the native direction obtained in ProcessTextData()
227 :
228 0 : if (!mFrames.FirstChild()) {
229 0 : return;
230 : }
231 :
232 0 : mEmbellishData.flags |= NS_MATHML_EMBELLISH_OPERATOR;
233 0 : mEmbellishData.coreFrame = this;
234 :
235 : // there are two particular things that we also need to record so that if our
236 : // parent is <mover>, <munder>, or <munderover>, they will treat us properly:
237 : // 1) do we have accent="true"
238 : // 2) do we have movablelimits="true"
239 :
240 : // they need the extra information to decide how to treat their scripts/limits
241 : // (note: <mover>, <munder>, or <munderover> need not necessarily be our
242 : // direct parent -- case of embellished operators)
243 :
244 : // default values from the Operator Dictionary were obtained in ProcessTextData()
245 : // and these special bits are always kept in mFlags
246 0 : if (NS_MATHML_OPERATOR_IS_ACCENT(mFlags))
247 0 : mEmbellishData.flags |= NS_MATHML_EMBELLISH_ACCENT;
248 0 : if (NS_MATHML_OPERATOR_IS_MOVABLELIMITS(mFlags))
249 0 : mEmbellishData.flags |= NS_MATHML_EMBELLISH_MOVABLELIMITS;
250 :
251 : // see if the accent attribute is there
252 0 : mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::accent_, value);
253 0 : if (value.EqualsLiteral("true"))
254 0 : mEmbellishData.flags |= NS_MATHML_EMBELLISH_ACCENT;
255 0 : else if (value.EqualsLiteral("false"))
256 0 : mEmbellishData.flags &= ~NS_MATHML_EMBELLISH_ACCENT;
257 :
258 : // see if the movablelimits attribute is there
259 0 : mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::movablelimits_, value);
260 0 : if (value.EqualsLiteral("true"))
261 0 : mEmbellishData.flags |= NS_MATHML_EMBELLISH_MOVABLELIMITS;
262 0 : else if (value.EqualsLiteral("false"))
263 0 : mEmbellishData.flags &= ~NS_MATHML_EMBELLISH_MOVABLELIMITS;
264 :
265 : // ---------------------------------------------------------------------
266 : // we will be called again to re-sync the rest of our state next time...
267 : // (nobody needs the other values below at this stage)
268 0 : mFlags |= form;
269 0 : return;
270 : }
271 :
272 0 : nsPresContext* presContext = PresContext();
273 :
274 : // beware of bug 133814 - there is a two-way dependency in the
275 : // embellished hierarchy: our embellished ancestors need to set
276 : // their flags based on some of our state (set above), and here we
277 : // need to re-sync our 'form' depending on our outermost embellished
278 : // container. A null form here means that an earlier attempt to stretch
279 : // our mMathMLChar failed, in which case we don't bother re-stretching again
280 0 : if (form) {
281 : // get our outermost embellished container and its parent.
282 : // (we ensure that we are the core, not just a sibling of the core)
283 0 : nsIFrame* embellishAncestor = this;
284 0 : nsEmbellishData embellishData;
285 0 : nsIFrame* parentAncestor = this;
286 0 : do {
287 0 : embellishAncestor = parentAncestor;
288 0 : parentAncestor = embellishAncestor->GetParent();
289 0 : GetEmbellishDataFrom(parentAncestor, embellishData);
290 0 : } while (embellishData.coreFrame == this);
291 :
292 : // flag if we have an embellished ancestor
293 0 : if (embellishAncestor != this)
294 0 : mFlags |= NS_MATHML_OPERATOR_EMBELLISH_ANCESTOR;
295 : else
296 0 : mFlags &= ~NS_MATHML_OPERATOR_EMBELLISH_ANCESTOR;
297 :
298 : // find the position of our outermost embellished container w.r.t
299 : // its siblings.
300 :
301 0 : nsIFrame* nextSibling = embellishAncestor->GetNextSibling();
302 0 : nsIFrame* prevSibling = embellishAncestor->GetPrevSibling();
303 :
304 : // flag to distinguish from a real infix. Set for (embellished) operators
305 : // that live in (inferred) mrows.
306 0 : nsIMathMLFrame* mathAncestor = do_QueryFrame(parentAncestor);
307 0 : bool zeroSpacing = false;
308 0 : if (mathAncestor) {
309 0 : zeroSpacing = !mathAncestor->IsMrowLike();
310 : } else {
311 0 : nsMathMLmathBlockFrame* blockFrame = do_QueryFrame(parentAncestor);
312 0 : if (blockFrame) {
313 0 : zeroSpacing = !blockFrame->IsMrowLike();
314 : }
315 : }
316 0 : if (zeroSpacing) {
317 0 : mFlags |= NS_MATHML_OPERATOR_EMBELLISH_ISOLATED;
318 : } else {
319 0 : mFlags &= ~NS_MATHML_OPERATOR_EMBELLISH_ISOLATED;
320 : }
321 :
322 : // find our form
323 0 : form = NS_MATHML_OPERATOR_FORM_INFIX;
324 0 : mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::form, value);
325 0 : if (!value.IsEmpty()) {
326 0 : if (value.EqualsLiteral("prefix"))
327 0 : form = NS_MATHML_OPERATOR_FORM_PREFIX;
328 0 : else if (value.EqualsLiteral("postfix"))
329 0 : form = NS_MATHML_OPERATOR_FORM_POSTFIX;
330 : }
331 : else {
332 : // set our form flag depending on the position
333 0 : if (!prevSibling && nextSibling)
334 0 : form = NS_MATHML_OPERATOR_FORM_PREFIX;
335 0 : else if (prevSibling && !nextSibling)
336 0 : form = NS_MATHML_OPERATOR_FORM_POSTFIX;
337 : }
338 0 : mFlags &= ~NS_MATHML_OPERATOR_FORM; // clear the old form bits
339 0 : mFlags |= form;
340 :
341 : // Use the default value suggested by the MathML REC.
342 : // http://www.w3.org/TR/MathML/chapter3.html#presm.mo.attrs
343 : // thickmathspace = 5/18em
344 0 : float lspace = 5.0f/18.0f;
345 0 : float rspace = 5.0f/18.0f;
346 : // lookup the operator dictionary
347 0 : nsAutoString data;
348 0 : mMathMLChar.GetData(data);
349 0 : nsMathMLOperators::LookupOperator(data, form, &mFlags, &lspace, &rspace);
350 : // Spacing is zero if our outermost embellished operator is not in an
351 : // inferred mrow.
352 0 : if (!NS_MATHML_OPERATOR_EMBELLISH_IS_ISOLATED(mFlags) &&
353 0 : (lspace || rspace)) {
354 : // Cache the default values of lspace and rspace.
355 : // since these values are relative to the 'em' unit, convert to twips now
356 : nscoord em;
357 : RefPtr<nsFontMetrics> fm =
358 0 : nsLayoutUtils::GetFontMetricsForFrame(this, fontSizeInflation);
359 0 : GetEmHeight(fm, em);
360 :
361 0 : mEmbellishData.leadingSpace = NSToCoordRound(lspace * em);
362 0 : mEmbellishData.trailingSpace = NSToCoordRound(rspace * em);
363 :
364 : // tuning if we don't want too much extra space when we are a script.
365 : // (with its fonts, TeX sets lspace=0 & rspace=0 as soon as scriptlevel>0.
366 : // Our fonts can be anything, so...)
367 0 : if (StyleFont()->mScriptLevel > 0 &&
368 0 : !NS_MATHML_OPERATOR_HAS_EMBELLISH_ANCESTOR(mFlags)) {
369 0 : mEmbellishData.leadingSpace /= 2;
370 0 : mEmbellishData.trailingSpace /= 2;
371 : }
372 : }
373 : }
374 :
375 : // If we are an accent without explicit lspace="." or rspace=".",
376 : // we will ignore our default leading/trailing space
377 :
378 : // lspace
379 : //
380 : // "Specifies the leading space appearing before the operator"
381 : //
382 : // values: length
383 : // default: set by dictionary (thickmathspace)
384 : //
385 : // XXXfredw Support for negative and relative values is not implemented
386 : // (bug 805926).
387 : // Relative values will give a multiple of the current leading space,
388 : // which is not necessarily the default one.
389 : //
390 0 : nscoord leadingSpace = mEmbellishData.leadingSpace;
391 0 : mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::lspace_, value);
392 0 : if (!value.IsEmpty()) {
393 0 : nsCSSValue cssValue;
394 0 : if (nsMathMLElement::ParseNumericValue(value, cssValue, 0,
395 0 : mContent->OwnerDoc())) {
396 0 : if ((eCSSUnit_Number == cssValue.GetUnit()) && !cssValue.GetFloatValue())
397 0 : leadingSpace = 0;
398 0 : else if (cssValue.IsLengthUnit())
399 0 : leadingSpace = CalcLength(presContext, mStyleContext, cssValue,
400 0 : fontSizeInflation);
401 0 : mFlags |= NS_MATHML_OPERATOR_LSPACE_ATTR;
402 : }
403 : }
404 :
405 : // rspace
406 : //
407 : // "Specifies the trailing space appearing after the operator"
408 : //
409 : // values: length
410 : // default: set by dictionary (thickmathspace)
411 : //
412 : // XXXfredw Support for negative and relative values is not implemented
413 : // (bug 805926).
414 : // Relative values will give a multiple of the current leading space,
415 : // which is not necessarily the default one.
416 : //
417 0 : nscoord trailingSpace = mEmbellishData.trailingSpace;
418 0 : mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::rspace_, value);
419 0 : if (!value.IsEmpty()) {
420 0 : nsCSSValue cssValue;
421 0 : if (nsMathMLElement::ParseNumericValue(value, cssValue, 0,
422 0 : mContent->OwnerDoc())) {
423 0 : if ((eCSSUnit_Number == cssValue.GetUnit()) && !cssValue.GetFloatValue())
424 0 : trailingSpace = 0;
425 0 : else if (cssValue.IsLengthUnit())
426 0 : trailingSpace = CalcLength(presContext, mStyleContext, cssValue,
427 0 : fontSizeInflation);
428 0 : mFlags |= NS_MATHML_OPERATOR_RSPACE_ATTR;
429 : }
430 : }
431 :
432 : // little extra tuning to round lspace & rspace to at least a pixel so that
433 : // operators don't look as if they are colliding with their operands
434 0 : if (leadingSpace || trailingSpace) {
435 0 : nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
436 0 : if (leadingSpace && leadingSpace < onePixel)
437 0 : leadingSpace = onePixel;
438 0 : if (trailingSpace && trailingSpace < onePixel)
439 0 : trailingSpace = onePixel;
440 : }
441 :
442 : // the values that we get from our attributes override the dictionary
443 0 : mEmbellishData.leadingSpace = leadingSpace;
444 0 : mEmbellishData.trailingSpace = trailingSpace;
445 :
446 : // Now see if there are user-defined attributes that override the dictionary.
447 : // XXX Bug 1197771 - forcing an attribute to true when it is false in the
448 : // dictionary can cause conflicts in the rest of the stretching algorithms
449 : // (e.g. all largeops are assumed to have a vertical direction)
450 :
451 : // For each attribute overriden by the user, turn off its bit flag.
452 : // symmetric|movablelimits|separator|largeop|accent|fence|stretchy|form
453 : // special: accent and movablelimits are handled above,
454 : // don't process them here
455 :
456 0 : mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::stretchy_, value);
457 0 : if (value.EqualsLiteral("false")) {
458 0 : mFlags &= ~NS_MATHML_OPERATOR_STRETCHY;
459 0 : } else if (value.EqualsLiteral("true")) {
460 0 : mFlags |= NS_MATHML_OPERATOR_STRETCHY;
461 : }
462 0 : if (NS_MATHML_OPERATOR_IS_FENCE(mFlags)) {
463 0 : mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::fence_, value);
464 0 : if (value.EqualsLiteral("false"))
465 0 : mFlags &= ~NS_MATHML_OPERATOR_FENCE;
466 : else
467 0 : mEmbellishData.flags |= NS_MATHML_EMBELLISH_FENCE;
468 : }
469 0 : mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::largeop_, value);
470 0 : if (value.EqualsLiteral("false")) {
471 0 : mFlags &= ~NS_MATHML_OPERATOR_LARGEOP;
472 0 : } else if (value.EqualsLiteral("true")) {
473 0 : mFlags |= NS_MATHML_OPERATOR_LARGEOP;
474 : }
475 0 : if (NS_MATHML_OPERATOR_IS_SEPARATOR(mFlags)) {
476 0 : mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::separator_, value);
477 0 : if (value.EqualsLiteral("false"))
478 0 : mFlags &= ~NS_MATHML_OPERATOR_SEPARATOR;
479 : else
480 0 : mEmbellishData.flags |= NS_MATHML_EMBELLISH_SEPARATOR;
481 : }
482 0 : mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::symmetric_, value);
483 0 : if (value.EqualsLiteral("false"))
484 0 : mFlags &= ~NS_MATHML_OPERATOR_SYMMETRIC;
485 0 : else if (value.EqualsLiteral("true"))
486 0 : mFlags |= NS_MATHML_OPERATOR_SYMMETRIC;
487 :
488 :
489 : // minsize
490 : //
491 : // "Specifies the minimum size of the operator when stretchy"
492 : //
493 : // values: length
494 : // default: set by dictionary (1em)
495 : //
496 : // We don't allow negative values.
497 : // Note: Contrary to other "length" values, unitless and percentage do not
498 : // give a multiple of the defaut value but a multiple of the operator at
499 : // normal size.
500 : //
501 0 : mMinSize = 0;
502 0 : mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::minsize_, value);
503 0 : if (!value.IsEmpty()) {
504 0 : nsCSSValue cssValue;
505 0 : if (nsMathMLElement::ParseNumericValue(value, cssValue,
506 : nsMathMLElement::
507 : PARSE_ALLOW_UNITLESS,
508 0 : mContent->OwnerDoc())) {
509 0 : nsCSSUnit unit = cssValue.GetUnit();
510 0 : if (eCSSUnit_Number == unit)
511 0 : mMinSize = cssValue.GetFloatValue();
512 0 : else if (eCSSUnit_Percent == unit)
513 0 : mMinSize = cssValue.GetPercentValue();
514 0 : else if (eCSSUnit_Null != unit) {
515 0 : mMinSize = float(CalcLength(presContext, mStyleContext, cssValue,
516 : fontSizeInflation));
517 0 : mFlags |= NS_MATHML_OPERATOR_MINSIZE_ABSOLUTE;
518 : }
519 : }
520 : }
521 :
522 : // maxsize
523 : //
524 : // "Specifies the maximum size of the operator when stretchy"
525 : //
526 : // values: length | "infinity"
527 : // default: set by dictionary (infinity)
528 : //
529 : // We don't allow negative values.
530 : // Note: Contrary to other "length" values, unitless and percentage do not
531 : // give a multiple of the defaut value but a multiple of the operator at
532 : // normal size.
533 : //
534 0 : mMaxSize = NS_MATHML_OPERATOR_SIZE_INFINITY;
535 0 : mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::maxsize_, value);
536 0 : if (!value.IsEmpty()) {
537 0 : nsCSSValue cssValue;
538 0 : if (nsMathMLElement::ParseNumericValue(value, cssValue,
539 : nsMathMLElement::
540 : PARSE_ALLOW_UNITLESS,
541 0 : mContent->OwnerDoc())) {
542 0 : nsCSSUnit unit = cssValue.GetUnit();
543 0 : if (eCSSUnit_Number == unit)
544 0 : mMaxSize = cssValue.GetFloatValue();
545 0 : else if (eCSSUnit_Percent == unit)
546 0 : mMaxSize = cssValue.GetPercentValue();
547 0 : else if (eCSSUnit_Null != unit) {
548 0 : mMaxSize = float(CalcLength(presContext, mStyleContext, cssValue,
549 : fontSizeInflation));
550 0 : mFlags |= NS_MATHML_OPERATOR_MAXSIZE_ABSOLUTE;
551 : }
552 : }
553 : }
554 : }
555 :
556 : static uint32_t
557 0 : GetStretchHint(nsOperatorFlags aFlags, nsPresentationData aPresentationData,
558 : bool aIsVertical, const nsStyleFont* aStyleFont)
559 : {
560 0 : uint32_t stretchHint = NS_STRETCH_NONE;
561 : // See if it is okay to stretch,
562 : // starting from what the Operator Dictionary said
563 0 : if (NS_MATHML_OPERATOR_IS_MUTABLE(aFlags)) {
564 : // set the largeop or largeopOnly flags to suitably cover all the
565 : // 8 possible cases depending on whether displaystyle, largeop,
566 : // stretchy are true or false (see bug 69325).
567 : // . largeopOnly is taken if largeop=true and stretchy=false
568 : // . largeop is taken if largeop=true and stretchy=true
569 0 : if (aStyleFont->mMathDisplay == NS_MATHML_DISPLAYSTYLE_BLOCK &&
570 0 : NS_MATHML_OPERATOR_IS_LARGEOP(aFlags)) {
571 0 : stretchHint = NS_STRETCH_LARGEOP; // (largeopOnly, not mask!)
572 0 : if (NS_MATHML_OPERATOR_IS_INTEGRAL(aFlags)) {
573 0 : stretchHint |= NS_STRETCH_INTEGRAL;
574 : }
575 0 : if (NS_MATHML_OPERATOR_IS_STRETCHY(aFlags)) {
576 0 : stretchHint |= NS_STRETCH_NEARER | NS_STRETCH_LARGER;
577 : }
578 : }
579 0 : else if(NS_MATHML_OPERATOR_IS_STRETCHY(aFlags)) {
580 0 : if (aIsVertical) {
581 : // TeX hint. Can impact some sloppy markups missing <mrow></mrow>
582 0 : stretchHint = NS_STRETCH_NEARER;
583 : }
584 : else {
585 0 : stretchHint = NS_STRETCH_NORMAL;
586 : }
587 : }
588 : // else if the stretchy and largeop attributes have been disabled,
589 : // the operator is not mutable
590 : }
591 0 : return stretchHint;
592 : }
593 :
594 : // NOTE: aDesiredStretchSize is an IN/OUT parameter
595 : // On input - it contains our current size
596 : // On output - the same size or the new size that we want
597 : NS_IMETHODIMP
598 0 : nsMathMLmoFrame::Stretch(DrawTarget* aDrawTarget,
599 : nsStretchDirection aStretchDirection,
600 : nsBoundingMetrics& aContainerSize,
601 : ReflowOutput& aDesiredStretchSize)
602 : {
603 0 : if (NS_MATHML_STRETCH_WAS_DONE(mPresentationData.flags)) {
604 0 : NS_WARNING("it is wrong to fire stretch more than once on a frame");
605 0 : return NS_OK;
606 : }
607 0 : mPresentationData.flags |= NS_MATHML_STRETCH_DONE;
608 :
609 0 : nsIFrame* firstChild = mFrames.FirstChild();
610 :
611 : // get the axis height;
612 0 : float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this);
613 : RefPtr<nsFontMetrics> fm =
614 0 : nsLayoutUtils::GetFontMetricsForFrame(this, fontSizeInflation);
615 : nscoord axisHeight, height;
616 0 : GetAxisHeight(aDrawTarget, fm, axisHeight);
617 :
618 : // get the leading to be left at the top and the bottom of the stretched char
619 : // this seems more reliable than using fm->GetLeading() on suspicious fonts
620 : nscoord em;
621 0 : GetEmHeight(fm, em);
622 0 : nscoord leading = NSToCoordRound(0.2f * em);
623 :
624 : // Operators that are stretchy, or those that are to be centered
625 : // to cater for fonts that are not math-aware, are handled by the MathMLChar
626 : // ('form' is reset if stretch fails -- i.e., we don't bother to stretch next time)
627 0 : bool useMathMLChar = UseMathMLChar();
628 :
629 0 : nsBoundingMetrics charSize;
630 0 : nsBoundingMetrics container = aDesiredStretchSize.mBoundingMetrics;
631 0 : bool isVertical = false;
632 :
633 0 : if (((aStretchDirection == NS_STRETCH_DIRECTION_VERTICAL) ||
634 0 : (aStretchDirection == NS_STRETCH_DIRECTION_DEFAULT)) &&
635 0 : (mEmbellishData.direction == NS_STRETCH_DIRECTION_VERTICAL)) {
636 0 : isVertical = true;
637 : }
638 :
639 : uint32_t stretchHint =
640 0 : GetStretchHint(mFlags, mPresentationData, isVertical, StyleFont());
641 :
642 0 : if (useMathMLChar) {
643 0 : nsBoundingMetrics initialSize = aDesiredStretchSize.mBoundingMetrics;
644 :
645 0 : if (stretchHint != NS_STRETCH_NONE) {
646 :
647 0 : container = aContainerSize;
648 :
649 : // some adjustments if the operator is symmetric and vertical
650 :
651 0 : if (isVertical && NS_MATHML_OPERATOR_IS_SYMMETRIC(mFlags)) {
652 : // we need to center about the axis
653 0 : nscoord delta = std::max(container.ascent - axisHeight,
654 0 : container.descent + axisHeight);
655 0 : container.ascent = delta + axisHeight;
656 0 : container.descent = delta - axisHeight;
657 :
658 : // get ready in case we encounter user-desired min-max size
659 0 : delta = std::max(initialSize.ascent - axisHeight,
660 0 : initialSize.descent + axisHeight);
661 0 : initialSize.ascent = delta + axisHeight;
662 0 : initialSize.descent = delta - axisHeight;
663 : }
664 :
665 : // check for user-desired min-max size
666 :
667 0 : if (mMaxSize != NS_MATHML_OPERATOR_SIZE_INFINITY && mMaxSize > 0.0f) {
668 : // if we are here, there is a user defined maxsize ...
669 : //XXX Set stretchHint = NS_STRETCH_NORMAL? to honor the maxsize as close as possible?
670 0 : if (NS_MATHML_OPERATOR_MAXSIZE_IS_ABSOLUTE(mFlags)) {
671 : // there is an explicit value like maxsize="20pt"
672 : // try to maintain the aspect ratio of the char
673 0 : float aspect = mMaxSize / float(initialSize.ascent + initialSize.descent);
674 0 : container.ascent =
675 0 : std::min(container.ascent, nscoord(initialSize.ascent * aspect));
676 0 : container.descent =
677 0 : std::min(container.descent, nscoord(initialSize.descent * aspect));
678 : // below we use a type cast instead of a conversion to avoid a VC++ bug
679 : // see http://support.microsoft.com/support/kb/articles/Q115/7/05.ASP
680 0 : container.width =
681 0 : std::min(container.width, (nscoord)mMaxSize);
682 : }
683 : else { // multiplicative value
684 0 : container.ascent =
685 0 : std::min(container.ascent, nscoord(initialSize.ascent * mMaxSize));
686 0 : container.descent =
687 0 : std::min(container.descent, nscoord(initialSize.descent * mMaxSize));
688 0 : container.width =
689 0 : std::min(container.width, nscoord(initialSize.width * mMaxSize));
690 : }
691 :
692 0 : if (isVertical && !NS_MATHML_OPERATOR_IS_SYMMETRIC(mFlags)) {
693 : // re-adjust to align the char with the bottom of the initial container
694 0 : height = container.ascent + container.descent;
695 0 : container.descent = aContainerSize.descent;
696 0 : container.ascent = height - container.descent;
697 : }
698 : }
699 :
700 0 : if (mMinSize > 0.0f) {
701 : // if we are here, there is a user defined minsize ...
702 : // always allow the char to stretch in its natural direction,
703 : // even if it is different from the caller's direction
704 0 : if (aStretchDirection != NS_STRETCH_DIRECTION_DEFAULT &&
705 0 : aStretchDirection != mEmbellishData.direction) {
706 0 : aStretchDirection = NS_STRETCH_DIRECTION_DEFAULT;
707 : // but when we are not honoring the requested direction
708 : // we should not use the caller's container size either
709 0 : container = initialSize;
710 : }
711 0 : if (NS_MATHML_OPERATOR_MINSIZE_IS_ABSOLUTE(mFlags)) {
712 : // there is an explicit value like minsize="20pt"
713 : // try to maintain the aspect ratio of the char
714 0 : float aspect = mMinSize / float(initialSize.ascent + initialSize.descent);
715 0 : container.ascent =
716 0 : std::max(container.ascent, nscoord(initialSize.ascent * aspect));
717 0 : container.descent =
718 0 : std::max(container.descent, nscoord(initialSize.descent * aspect));
719 0 : container.width =
720 0 : std::max(container.width, (nscoord)mMinSize);
721 : }
722 : else { // multiplicative value
723 0 : container.ascent =
724 0 : std::max(container.ascent, nscoord(initialSize.ascent * mMinSize));
725 0 : container.descent =
726 0 : std::max(container.descent, nscoord(initialSize.descent * mMinSize));
727 0 : container.width =
728 0 : std::max(container.width, nscoord(initialSize.width * mMinSize));
729 : }
730 :
731 0 : if (isVertical && !NS_MATHML_OPERATOR_IS_SYMMETRIC(mFlags)) {
732 : // re-adjust to align the char with the bottom of the initial container
733 0 : height = container.ascent + container.descent;
734 0 : container.descent = aContainerSize.descent;
735 0 : container.ascent = height - container.descent;
736 : }
737 : }
738 : }
739 :
740 : // let the MathMLChar stretch itself...
741 0 : nsresult res = mMathMLChar.Stretch(PresContext(), aDrawTarget,
742 : fontSizeInflation,
743 : aStretchDirection, container, charSize,
744 : stretchHint,
745 0 : StyleVisibility()->mDirection);
746 0 : if (NS_FAILED(res)) {
747 : // gracefully handle cases where stretching the char failed (i.e., GetBoundingMetrics failed)
748 : // clear our 'form' to behave as if the operator wasn't in the dictionary
749 0 : mFlags &= ~NS_MATHML_OPERATOR_FORM;
750 0 : useMathMLChar = false;
751 : }
752 : }
753 :
754 : // Place our children using the default method
755 : // This will allow our child text frame to get its DidReflow()
756 0 : nsresult rv = Place(aDrawTarget, true, aDesiredStretchSize);
757 0 : if (NS_MATHML_HAS_ERROR(mPresentationData.flags) || NS_FAILED(rv)) {
758 : // Make sure the child frames get their DidReflow() calls.
759 0 : DidReflowChildren(mFrames.FirstChild());
760 : }
761 :
762 0 : if (useMathMLChar) {
763 : // update our bounding metrics... it becomes that of our MathML char
764 0 : mBoundingMetrics = charSize;
765 :
766 : // if the returned direction is 'unsupported', the char didn't actually change.
767 : // So we do the centering only if necessary
768 0 : if (mMathMLChar.GetStretchDirection() != NS_STRETCH_DIRECTION_UNSUPPORTED ||
769 0 : NS_MATHML_OPERATOR_IS_CENTERED(mFlags)) {
770 :
771 : bool largeopOnly =
772 0 : (NS_STRETCH_LARGEOP & stretchHint) != 0 &&
773 0 : (NS_STRETCH_VARIABLE_MASK & stretchHint) == 0;
774 :
775 0 : if (isVertical || NS_MATHML_OPERATOR_IS_CENTERED(mFlags)) {
776 : // the desired size returned by mMathMLChar maybe different
777 : // from the size of the container.
778 : // the mMathMLChar.mRect.y calculation is subtle, watch out!!!
779 :
780 0 : height = mBoundingMetrics.ascent + mBoundingMetrics.descent;
781 0 : if (NS_MATHML_OPERATOR_IS_SYMMETRIC(mFlags) ||
782 0 : NS_MATHML_OPERATOR_IS_CENTERED(mFlags)) {
783 : // For symmetric and vertical operators, or for operators that are always
784 : // centered ('+', '*', etc) we want to center about the axis of the container
785 0 : mBoundingMetrics.descent = height/2 - axisHeight;
786 0 : } else if (!largeopOnly) {
787 : // Align the center of the char with the center of the container
788 0 : mBoundingMetrics.descent = height/2 +
789 0 : (container.ascent + container.descent)/2 - container.ascent;
790 : } // else align the baselines
791 0 : mBoundingMetrics.ascent = height - mBoundingMetrics.descent;
792 : }
793 : }
794 : }
795 :
796 : // Fixup for the final height.
797 : // On one hand, our stretchy height can sometimes be shorter than surrounding
798 : // ASCII chars, e.g., arrow symbols have |mBoundingMetrics.ascent + leading|
799 : // that is smaller than the ASCII's ascent, hence when painting the background
800 : // later, it won't look uniform along the line.
801 : // On the other hand, sometimes we may leave too much gap when our glyph happens
802 : // to come from a font with tall glyphs. For example, since CMEX10 has very tall
803 : // glyphs, its natural font metrics are large, even if we pick a small glyph
804 : // whose size is comparable to the size of a normal ASCII glyph.
805 : // So to avoid uneven spacing in either of these two cases, we use the height
806 : // of the ASCII font as a reference and try to match it if possible.
807 :
808 : // special case for accents... keep them short to improve mouse operations...
809 : // an accent can only be the non-first child of <mover>, <munder>, <munderover>
810 : bool isAccent =
811 0 : NS_MATHML_EMBELLISH_IS_ACCENT(mEmbellishData.flags);
812 0 : if (isAccent) {
813 0 : nsEmbellishData parentData;
814 0 : GetEmbellishDataFrom(GetParent(), parentData);
815 0 : isAccent =
816 0 : (NS_MATHML_EMBELLISH_IS_ACCENTOVER(parentData.flags) ||
817 0 : NS_MATHML_EMBELLISH_IS_ACCENTUNDER(parentData.flags)) &&
818 0 : parentData.coreFrame != this;
819 : }
820 0 : if (isAccent && firstChild) {
821 : // see bug 188467 for what is going on here
822 0 : nscoord dy = aDesiredStretchSize.BlockStartAscent() -
823 0 : (mBoundingMetrics.ascent + leading);
824 0 : aDesiredStretchSize.SetBlockStartAscent(mBoundingMetrics.ascent + leading);
825 0 : aDesiredStretchSize.Height() = aDesiredStretchSize.BlockStartAscent() +
826 0 : mBoundingMetrics.descent;
827 :
828 0 : firstChild->SetPosition(firstChild->GetPosition() - nsPoint(0, dy));
829 : }
830 0 : else if (useMathMLChar) {
831 0 : nscoord ascent = fm->MaxAscent();
832 0 : nscoord descent = fm->MaxDescent();
833 0 : aDesiredStretchSize.SetBlockStartAscent(std::max(mBoundingMetrics.ascent + leading, ascent));
834 0 : aDesiredStretchSize.Height() = aDesiredStretchSize.BlockStartAscent() +
835 0 : std::max(mBoundingMetrics.descent + leading, descent);
836 : }
837 0 : aDesiredStretchSize.Width() = mBoundingMetrics.width;
838 0 : aDesiredStretchSize.mBoundingMetrics = mBoundingMetrics;
839 0 : mReference.x = 0;
840 0 : mReference.y = aDesiredStretchSize.BlockStartAscent();
841 : // Place our mMathMLChar, its origin is in our coordinate system
842 0 : if (useMathMLChar) {
843 0 : nscoord dy = aDesiredStretchSize.BlockStartAscent() - mBoundingMetrics.ascent;
844 0 : mMathMLChar.SetRect(nsRect(0, dy, charSize.width, charSize.ascent + charSize.descent));
845 : }
846 :
847 : // Before we leave... there is a last item in the check-list:
848 : // If our parent is not embellished, it means we are the outermost embellished
849 : // container and so we put the spacing, otherwise we don't include the spacing,
850 : // the outermost embellished container will take care of it.
851 :
852 0 : if (!NS_MATHML_OPERATOR_HAS_EMBELLISH_ANCESTOR(mFlags)) {
853 :
854 : // Account the spacing if we are not an accent with explicit attributes
855 0 : nscoord leadingSpace = mEmbellishData.leadingSpace;
856 0 : if (isAccent && !NS_MATHML_OPERATOR_HAS_LSPACE_ATTR(mFlags)) {
857 0 : leadingSpace = 0;
858 : }
859 0 : nscoord trailingSpace = mEmbellishData.trailingSpace;
860 0 : if (isAccent && !NS_MATHML_OPERATOR_HAS_RSPACE_ATTR(mFlags)) {
861 0 : trailingSpace = 0;
862 : }
863 :
864 0 : mBoundingMetrics.width += leadingSpace + trailingSpace;
865 0 : aDesiredStretchSize.Width() = mBoundingMetrics.width;
866 0 : aDesiredStretchSize.mBoundingMetrics.width = mBoundingMetrics.width;
867 :
868 0 : nscoord dx = (StyleVisibility()->mDirection ?
869 0 : trailingSpace : leadingSpace);
870 0 : if (dx) {
871 : // adjust the offsets
872 0 : mBoundingMetrics.leftBearing += dx;
873 0 : mBoundingMetrics.rightBearing += dx;
874 0 : aDesiredStretchSize.mBoundingMetrics.leftBearing += dx;
875 0 : aDesiredStretchSize.mBoundingMetrics.rightBearing += dx;
876 :
877 0 : if (useMathMLChar) {
878 0 : nsRect rect;
879 0 : mMathMLChar.GetRect(rect);
880 0 : mMathMLChar.SetRect(nsRect(rect.x + dx, rect.y,
881 0 : rect.width, rect.height));
882 : }
883 : else {
884 0 : nsIFrame* childFrame = firstChild;
885 0 : while (childFrame) {
886 0 : childFrame->SetPosition(childFrame->GetPosition() +
887 0 : nsPoint(dx, 0));
888 0 : childFrame = childFrame->GetNextSibling();
889 : }
890 : }
891 : }
892 : }
893 :
894 : // Finished with these:
895 0 : ClearSavedChildMetrics();
896 : // Set our overflow area
897 0 : GatherAndStoreOverflow(&aDesiredStretchSize);
898 :
899 : // There used to be code here to change the height of the child frame to
900 : // change the caret height, but the text frame that manages the caret is now
901 : // not a direct child but wrapped in a block frame. See also bug 412033.
902 :
903 0 : return NS_OK;
904 : }
905 :
906 : NS_IMETHODIMP
907 0 : nsMathMLmoFrame::InheritAutomaticData(nsIFrame* aParent)
908 : {
909 : // retain our native direction, it only changes if our text content changes
910 0 : nsStretchDirection direction = mEmbellishData.direction;
911 0 : nsMathMLTokenFrame::InheritAutomaticData(aParent);
912 0 : ProcessTextData();
913 0 : mEmbellishData.direction = direction;
914 0 : return NS_OK;
915 : }
916 :
917 : NS_IMETHODIMP
918 0 : nsMathMLmoFrame::TransmitAutomaticData()
919 : {
920 : // this will cause us to re-sync our flags from scratch
921 : // but our returned 'form' is still not final (bug 133429), it will
922 : // be recomputed to its final value during the next call in Reflow()
923 0 : mEmbellishData.coreFrame = nullptr;
924 0 : ProcessOperatorData();
925 0 : return NS_OK;
926 : }
927 :
928 : void
929 0 : nsMathMLmoFrame::SetInitialChildList(ChildListID aListID,
930 : nsFrameList& aChildList)
931 : {
932 : // First, let the parent class do its work
933 0 : nsMathMLTokenFrame::SetInitialChildList(aListID, aChildList);
934 0 : ProcessTextData();
935 0 : }
936 :
937 : void
938 0 : nsMathMLmoFrame::Reflow(nsPresContext* aPresContext,
939 : ReflowOutput& aDesiredSize,
940 : const ReflowInput& aReflowInput,
941 : nsReflowStatus& aStatus)
942 : {
943 : // certain values use units that depend on our style context, so
944 : // it is safer to just process the whole lot here
945 0 : ProcessOperatorData();
946 :
947 0 : nsMathMLTokenFrame::Reflow(aPresContext, aDesiredSize,
948 0 : aReflowInput, aStatus);
949 0 : }
950 :
951 : nsresult
952 0 : nsMathMLmoFrame::Place(DrawTarget* aDrawTarget,
953 : bool aPlaceOrigin,
954 : ReflowOutput& aDesiredSize)
955 : {
956 0 : nsresult rv = nsMathMLTokenFrame::Place(aDrawTarget, aPlaceOrigin, aDesiredSize);
957 :
958 0 : if (NS_FAILED(rv)) {
959 0 : return rv;
960 : }
961 :
962 : /* Special behaviour for largeops.
963 : In MathML "stretchy" and displaystyle "largeop" are different notions,
964 : even if we use the same technique to draw them (picking size variants).
965 : So largeop display operators should be considered "non-stretchy" and
966 : thus their sizes should be taken into account for the stretch size of
967 : other elements.
968 :
969 : This is a preliminary stretch - exact sizing/placement is handled by the
970 : Stretch() method.
971 : */
972 :
973 0 : if (!aPlaceOrigin &&
974 0 : StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_BLOCK &&
975 0 : NS_MATHML_OPERATOR_IS_LARGEOP(mFlags) && UseMathMLChar()) {
976 0 : nsBoundingMetrics newMetrics;
977 0 : rv = mMathMLChar.Stretch(PresContext(), aDrawTarget,
978 : nsLayoutUtils::FontSizeInflationFor(this),
979 : NS_STRETCH_DIRECTION_VERTICAL,
980 : aDesiredSize.mBoundingMetrics, newMetrics,
981 0 : NS_STRETCH_LARGEOP, StyleVisibility()->mDirection);
982 :
983 0 : if (NS_FAILED(rv)) {
984 : // Just use the initial size
985 0 : return NS_OK;
986 : }
987 :
988 0 : aDesiredSize.mBoundingMetrics = newMetrics;
989 : /* Treat the ascent/descent values calculated in the TokenFrame place
990 : calculations as the minimum for aDesiredSize calculations, rather
991 : than fetching them from font metrics again.
992 : */
993 0 : aDesiredSize.SetBlockStartAscent(std::max(mBoundingMetrics.ascent,
994 0 : newMetrics.ascent));
995 0 : aDesiredSize.Height() = aDesiredSize.BlockStartAscent() +
996 0 : std::max(mBoundingMetrics.descent,
997 0 : newMetrics.descent);
998 0 : aDesiredSize.Width() = newMetrics.width;
999 0 : mBoundingMetrics = newMetrics;
1000 : }
1001 0 : return NS_OK;
1002 : }
1003 :
1004 : /* virtual */ void
1005 0 : nsMathMLmoFrame::MarkIntrinsicISizesDirty()
1006 : {
1007 : // if we get this, it may mean that something changed in the text
1008 : // content. So blow away everything an re-build the automatic data
1009 : // from the parent of our outermost embellished container (we ensure
1010 : // that we are the core, not just a sibling of the core)
1011 :
1012 0 : ProcessTextData();
1013 :
1014 0 : nsIFrame* target = this;
1015 0 : nsEmbellishData embellishData;
1016 0 : do {
1017 0 : target = target->GetParent();
1018 0 : GetEmbellishDataFrom(target, embellishData);
1019 0 : } while (embellishData.coreFrame == this);
1020 :
1021 : // we have automatic data to update in the children of the target frame
1022 : // XXXldb This should really be marking dirty rather than rebuilding
1023 : // so that we don't rebuild multiple times for the same change.
1024 0 : RebuildAutomaticDataForChildren(target);
1025 :
1026 0 : nsMathMLContainerFrame::MarkIntrinsicISizesDirty();
1027 0 : }
1028 :
1029 : /* virtual */ void
1030 0 : nsMathMLmoFrame::GetIntrinsicISizeMetrics(gfxContext* aRenderingContext,
1031 : ReflowOutput& aDesiredSize)
1032 : {
1033 0 : ProcessOperatorData();
1034 0 : if (UseMathMLChar()) {
1035 0 : uint32_t stretchHint = GetStretchHint(mFlags, mPresentationData, true,
1036 0 : StyleFont());
1037 0 : aDesiredSize.Width() = mMathMLChar.
1038 0 : GetMaxWidth(PresContext(), aRenderingContext->GetDrawTarget(),
1039 : nsLayoutUtils::FontSizeInflationFor(this),
1040 : stretchHint);
1041 : }
1042 : else {
1043 0 : nsMathMLTokenFrame::GetIntrinsicISizeMetrics(aRenderingContext,
1044 0 : aDesiredSize);
1045 : }
1046 :
1047 : // leadingSpace and trailingSpace are actually applied to the outermost
1048 : // embellished container but for determining total intrinsic width it should
1049 : // be safe to include it for the core here instead.
1050 0 : bool isRTL = StyleVisibility()->mDirection;
1051 0 : aDesiredSize.Width() +=
1052 0 : mEmbellishData.leadingSpace + mEmbellishData.trailingSpace;
1053 0 : aDesiredSize.mBoundingMetrics.width = aDesiredSize.Width();
1054 0 : if (isRTL) {
1055 0 : aDesiredSize.mBoundingMetrics.leftBearing += mEmbellishData.trailingSpace;
1056 0 : aDesiredSize.mBoundingMetrics.rightBearing += mEmbellishData.trailingSpace;
1057 : } else {
1058 0 : aDesiredSize.mBoundingMetrics.leftBearing += mEmbellishData.leadingSpace;
1059 0 : aDesiredSize.mBoundingMetrics.rightBearing += mEmbellishData.leadingSpace;
1060 : }
1061 0 : }
1062 :
1063 : nsresult
1064 0 : nsMathMLmoFrame::AttributeChanged(int32_t aNameSpaceID,
1065 : nsIAtom* aAttribute,
1066 : int32_t aModType)
1067 : {
1068 : // check if this is an attribute that can affect the embellished hierarchy
1069 : // in a significant way and re-layout the entire hierarchy.
1070 0 : if (nsGkAtoms::accent_ == aAttribute ||
1071 0 : nsGkAtoms::movablelimits_ == aAttribute) {
1072 :
1073 : // set the target as the parent of our outermost embellished container
1074 : // (we ensure that we are the core, not just a sibling of the core)
1075 0 : nsIFrame* target = this;
1076 0 : nsEmbellishData embellishData;
1077 0 : do {
1078 0 : target = target->GetParent();
1079 0 : GetEmbellishDataFrom(target, embellishData);
1080 0 : } while (embellishData.coreFrame == this);
1081 :
1082 : // we have automatic data to update in the children of the target frame
1083 0 : return ReLayoutChildren(target);
1084 : }
1085 :
1086 : return nsMathMLTokenFrame::
1087 0 : AttributeChanged(aNameSpaceID, aAttribute, aModType);
1088 : }
1089 :
1090 : // ----------------------
1091 : // No need to track the style context given to our MathML char.
1092 : // the Style System will use these to pass the proper style context to our MathMLChar
1093 : nsStyleContext*
1094 0 : nsMathMLmoFrame::GetAdditionalStyleContext(int32_t aIndex) const
1095 : {
1096 0 : switch (aIndex) {
1097 : case NS_MATHML_CHAR_STYLE_CONTEXT_INDEX:
1098 0 : return mMathMLChar.GetStyleContext();
1099 : default:
1100 0 : return nullptr;
1101 : }
1102 : }
1103 :
1104 : void
1105 0 : nsMathMLmoFrame::SetAdditionalStyleContext(int32_t aIndex,
1106 : nsStyleContext* aStyleContext)
1107 : {
1108 0 : switch (aIndex) {
1109 : case NS_MATHML_CHAR_STYLE_CONTEXT_INDEX:
1110 0 : mMathMLChar.SetStyleContext(aStyleContext);
1111 0 : break;
1112 : }
1113 0 : }
|