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