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