/src/mozilla-central/dom/mathml/nsMathMLElement.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 | | |
8 | | #include "nsMathMLElement.h" |
9 | | #include "base/compiler_specific.h" |
10 | | #include "mozilla/ArrayUtils.h" |
11 | | #include "mozilla/FontPropertyTypes.h" |
12 | | #include "mozilla/TextUtils.h" |
13 | | #include "nsGkAtoms.h" |
14 | | #include "nsITableCellLayout.h" // for MAX_COLSPAN / MAX_ROWSPAN |
15 | | #include "nsLayoutStylesheetCache.h" |
16 | | #include "nsCSSValue.h" |
17 | | #include "nsMappedAttributes.h" |
18 | | #include "nsStyleConsts.h" |
19 | | #include "nsIDocument.h" |
20 | | #include "nsIPresShell.h" |
21 | | #include "nsPresContext.h" |
22 | | #include "mozAutoDocUpdate.h" |
23 | | #include "nsIScriptError.h" |
24 | | #include "nsContentUtils.h" |
25 | | #include "nsIURI.h" |
26 | | |
27 | | #include "mozilla/EventDispatcher.h" |
28 | | #include "mozilla/EventStates.h" |
29 | | #include "mozilla/MappedDeclarations.h" |
30 | | #include "mozilla/dom/ElementBinding.h" |
31 | | |
32 | | using namespace mozilla; |
33 | | using namespace mozilla::dom; |
34 | | |
35 | | //---------------------------------------------------------------------- |
36 | | // nsISupports methods: |
37 | | |
38 | | NS_IMPL_ISUPPORTS_INHERITED(nsMathMLElement, nsMathMLElementBase, |
39 | | Link) |
40 | | |
41 | | static nsresult |
42 | | WarnDeprecated(const char16_t* aDeprecatedAttribute, |
43 | | const char16_t* aFavoredAttribute, nsIDocument* aDocument) |
44 | 0 | { |
45 | 0 | const char16_t *argv[] = |
46 | 0 | { aDeprecatedAttribute, aFavoredAttribute }; |
47 | 0 | return nsContentUtils:: |
48 | 0 | ReportToConsole(nsIScriptError::warningFlag, |
49 | 0 | NS_LITERAL_CSTRING("MathML"), aDocument, |
50 | 0 | nsContentUtils::eMATHML_PROPERTIES, |
51 | 0 | "DeprecatedSupersededBy", argv, 2); |
52 | 0 | } |
53 | | |
54 | | static nsresult |
55 | | ReportLengthParseError(const nsString& aValue, nsIDocument* aDocument) |
56 | 0 | { |
57 | 0 | const char16_t *arg = aValue.get(); |
58 | 0 | return nsContentUtils:: |
59 | 0 | ReportToConsole(nsIScriptError::errorFlag, |
60 | 0 | NS_LITERAL_CSTRING("MathML"), aDocument, |
61 | 0 | nsContentUtils::eMATHML_PROPERTIES, |
62 | 0 | "LengthParsingError", &arg, 1); |
63 | 0 | } |
64 | | |
65 | | static nsresult |
66 | | ReportParseErrorNoTag(const nsString& aValue, |
67 | | nsAtom* aAtom, |
68 | | nsIDocument* aDocument) |
69 | 0 | { |
70 | 0 | const char16_t *argv[] = |
71 | 0 | { aValue.get(), aAtom->GetUTF16String() }; |
72 | 0 | return nsContentUtils:: |
73 | 0 | ReportToConsole(nsIScriptError::errorFlag, |
74 | 0 | NS_LITERAL_CSTRING("MathML"), aDocument, |
75 | 0 | nsContentUtils::eMATHML_PROPERTIES, |
76 | 0 | "AttributeParsingErrorNoTag", argv, 2); |
77 | 0 | } |
78 | | |
79 | | nsMathMLElement::nsMathMLElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) |
80 | | : nsMathMLElementBase(std::move(aNodeInfo)), |
81 | | ALLOW_THIS_IN_INITIALIZER_LIST(Link(this)), |
82 | | mIncrementScriptLevel(false) |
83 | 0 | { |
84 | 0 | } |
85 | | |
86 | | nsMathMLElement::nsMathMLElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo) |
87 | | : nsMathMLElementBase(std::move(aNodeInfo)), |
88 | | ALLOW_THIS_IN_INITIALIZER_LIST(Link(this)), |
89 | | mIncrementScriptLevel(false) |
90 | 0 | { |
91 | 0 | } |
92 | | |
93 | | nsresult |
94 | | nsMathMLElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, |
95 | | nsIContent* aBindingParent) |
96 | 0 | { |
97 | 0 | Link::ResetLinkState(false, Link::ElementHasHref()); |
98 | 0 |
|
99 | 0 | nsresult rv = nsMathMLElementBase::BindToTree(aDocument, aParent, |
100 | 0 | aBindingParent); |
101 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
102 | 0 |
|
103 | 0 | if (aDocument) { |
104 | 0 | aDocument->RegisterPendingLinkUpdate(this); |
105 | 0 | } |
106 | 0 |
|
107 | 0 | // Set the bit in the document for telemetry. |
108 | 0 | if (nsIDocument* doc = GetComposedDoc()) { |
109 | 0 | doc->SetMathMLEnabled(); |
110 | 0 | } |
111 | 0 |
|
112 | 0 | return rv; |
113 | 0 | } |
114 | | |
115 | | void |
116 | | nsMathMLElement::UnbindFromTree(bool aDeep, bool aNullParent) |
117 | 0 | { |
118 | 0 | // If this link is ever reinserted into a document, it might |
119 | 0 | // be under a different xml:base, so forget the cached state now. |
120 | 0 | Link::ResetLinkState(false, Link::ElementHasHref()); |
121 | 0 |
|
122 | 0 | nsMathMLElementBase::UnbindFromTree(aDeep, aNullParent); |
123 | 0 | } |
124 | | |
125 | | bool |
126 | | nsMathMLElement::ParseAttribute(int32_t aNamespaceID, |
127 | | nsAtom* aAttribute, |
128 | | const nsAString& aValue, |
129 | | nsIPrincipal* aMaybeScriptedPrincipal, |
130 | | nsAttrValue& aResult) |
131 | 0 | { |
132 | 0 | MOZ_ASSERT(IsMathMLElement()); |
133 | 0 |
|
134 | 0 | if (aNamespaceID == kNameSpaceID_None) { |
135 | 0 | if (mNodeInfo->Equals(nsGkAtoms::math) && aAttribute == nsGkAtoms::mode) { |
136 | 0 | WarnDeprecated(nsGkAtoms::mode->GetUTF16String(), |
137 | 0 | nsGkAtoms::display->GetUTF16String(), OwnerDoc()); |
138 | 0 | } |
139 | 0 | if (aAttribute == nsGkAtoms::color) { |
140 | 0 | WarnDeprecated(nsGkAtoms::color->GetUTF16String(), |
141 | 0 | nsGkAtoms::mathcolor_->GetUTF16String(), OwnerDoc()); |
142 | 0 | } |
143 | 0 | if (aAttribute == nsGkAtoms::color || |
144 | 0 | aAttribute == nsGkAtoms::mathcolor_ || |
145 | 0 | aAttribute == nsGkAtoms::background || |
146 | 0 | aAttribute == nsGkAtoms::mathbackground_) { |
147 | 0 | return aResult.ParseColor(aValue); |
148 | 0 | } |
149 | 0 | if (mNodeInfo->Equals(nsGkAtoms::mtd_)) { |
150 | 0 | if (aAttribute == nsGkAtoms::columnspan_) { |
151 | 0 | aResult.ParseClampedNonNegativeInt(aValue, 1, 1, MAX_COLSPAN); |
152 | 0 | return true; |
153 | 0 | } |
154 | 0 | if (aAttribute == nsGkAtoms::rowspan) { |
155 | 0 | aResult.ParseClampedNonNegativeInt(aValue, 1, 0, MAX_ROWSPAN); |
156 | 0 | return true; |
157 | 0 | } |
158 | 0 | } |
159 | 0 | } |
160 | 0 |
|
161 | 0 | return nsMathMLElementBase::ParseAttribute(aNamespaceID, aAttribute, |
162 | 0 | aValue, |
163 | 0 | aMaybeScriptedPrincipal, |
164 | 0 | aResult); |
165 | 0 | } |
166 | | |
167 | | static Element::MappedAttributeEntry sMtableStyles[] = { |
168 | | { &nsGkAtoms::width }, |
169 | | { nullptr } |
170 | | }; |
171 | | |
172 | | static Element::MappedAttributeEntry sTokenStyles[] = { |
173 | | { &nsGkAtoms::mathsize_ }, |
174 | | { &nsGkAtoms::fontsize_ }, |
175 | | { &nsGkAtoms::color }, |
176 | | { &nsGkAtoms::fontfamily_ }, |
177 | | { &nsGkAtoms::fontstyle_ }, |
178 | | { &nsGkAtoms::fontweight_ }, |
179 | | { &nsGkAtoms::mathvariant_}, |
180 | | { nullptr } |
181 | | }; |
182 | | |
183 | | static Element::MappedAttributeEntry sEnvironmentStyles[] = { |
184 | | { &nsGkAtoms::scriptlevel_ }, |
185 | | { &nsGkAtoms::scriptminsize_ }, |
186 | | { &nsGkAtoms::scriptsizemultiplier_ }, |
187 | | { &nsGkAtoms::background }, |
188 | | { nullptr } |
189 | | }; |
190 | | |
191 | | static Element::MappedAttributeEntry sCommonPresStyles[] = { |
192 | | { &nsGkAtoms::mathcolor_ }, |
193 | | { &nsGkAtoms::mathbackground_ }, |
194 | | { nullptr } |
195 | | }; |
196 | | |
197 | | static Element::MappedAttributeEntry sDirStyles[] = { |
198 | | { &nsGkAtoms::dir }, |
199 | | { nullptr } |
200 | | }; |
201 | | |
202 | | bool |
203 | | nsMathMLElement::IsAttributeMapped(const nsAtom* aAttribute) const |
204 | 0 | { |
205 | 0 | MOZ_ASSERT(IsMathMLElement()); |
206 | 0 |
|
207 | 0 | static const MappedAttributeEntry* const mtableMap[] = { |
208 | 0 | sMtableStyles, |
209 | 0 | sCommonPresStyles |
210 | 0 | }; |
211 | 0 | static const MappedAttributeEntry* const tokenMap[] = { |
212 | 0 | sTokenStyles, |
213 | 0 | sCommonPresStyles, |
214 | 0 | sDirStyles |
215 | 0 | }; |
216 | 0 | static const MappedAttributeEntry* const mstyleMap[] = { |
217 | 0 | sTokenStyles, |
218 | 0 | sEnvironmentStyles, |
219 | 0 | sCommonPresStyles, |
220 | 0 | sDirStyles |
221 | 0 | }; |
222 | 0 | static const MappedAttributeEntry* const commonPresMap[] = { |
223 | 0 | sCommonPresStyles |
224 | 0 | }; |
225 | 0 | static const MappedAttributeEntry* const mrowMap[] = { |
226 | 0 | sCommonPresStyles, |
227 | 0 | sDirStyles |
228 | 0 | }; |
229 | 0 |
|
230 | 0 | // We don't support mglyph (yet). |
231 | 0 | if (IsAnyOfMathMLElements(nsGkAtoms::ms_, nsGkAtoms::mi_, nsGkAtoms::mn_, |
232 | 0 | nsGkAtoms::mo_, nsGkAtoms::mtext_, |
233 | 0 | nsGkAtoms::mspace_)) |
234 | 0 | return FindAttributeDependence(aAttribute, tokenMap); |
235 | 0 | if (IsAnyOfMathMLElements(nsGkAtoms::mstyle_, nsGkAtoms::math)) |
236 | 0 | return FindAttributeDependence(aAttribute, mstyleMap); |
237 | 0 | |
238 | 0 | if (mNodeInfo->Equals(nsGkAtoms::mtable_)) |
239 | 0 | return FindAttributeDependence(aAttribute, mtableMap); |
240 | 0 | |
241 | 0 | if (mNodeInfo->Equals(nsGkAtoms::mrow_)) |
242 | 0 | return FindAttributeDependence(aAttribute, mrowMap); |
243 | 0 | |
244 | 0 | if (IsAnyOfMathMLElements(nsGkAtoms::maction_, |
245 | 0 | nsGkAtoms::maligngroup_, |
246 | 0 | nsGkAtoms::malignmark_, |
247 | 0 | nsGkAtoms::menclose_, |
248 | 0 | nsGkAtoms::merror_, |
249 | 0 | nsGkAtoms::mfenced_, |
250 | 0 | nsGkAtoms::mfrac_, |
251 | 0 | nsGkAtoms::mover_, |
252 | 0 | nsGkAtoms::mpadded_, |
253 | 0 | nsGkAtoms::mphantom_, |
254 | 0 | nsGkAtoms::mprescripts_, |
255 | 0 | nsGkAtoms::mroot_, |
256 | 0 | nsGkAtoms::msqrt_, |
257 | 0 | nsGkAtoms::msub_, |
258 | 0 | nsGkAtoms::msubsup_, |
259 | 0 | nsGkAtoms::msup_, |
260 | 0 | nsGkAtoms::mtd_, |
261 | 0 | nsGkAtoms::mtr_, |
262 | 0 | nsGkAtoms::munder_, |
263 | 0 | nsGkAtoms::munderover_, |
264 | 0 | nsGkAtoms::none)) { |
265 | 0 | return FindAttributeDependence(aAttribute, commonPresMap); |
266 | 0 | } |
267 | 0 | |
268 | 0 | return false; |
269 | 0 | } |
270 | | |
271 | | nsMapRuleToAttributesFunc |
272 | | nsMathMLElement::GetAttributeMappingFunction() const |
273 | 0 | { |
274 | 0 | // It doesn't really matter what our tag is here, because only attributes |
275 | 0 | // that satisfy IsAttributeMapped will be stored in the mapped attributes |
276 | 0 | // list and available to the mapping function |
277 | 0 | return &MapMathMLAttributesInto; |
278 | 0 | } |
279 | | |
280 | | /* static */ bool |
281 | | nsMathMLElement::ParseNamedSpaceValue(const nsString& aString, |
282 | | nsCSSValue& aCSSValue, |
283 | | uint32_t aFlags) |
284 | 0 | { |
285 | 0 | int32_t i = 0; |
286 | 0 | // See if it is one of the 'namedspace' (ranging -7/18em, -6/18, ... 7/18em) |
287 | 0 | if (aString.EqualsLiteral("veryverythinmathspace")) { |
288 | 0 | i = 1; |
289 | 0 | } else if (aString.EqualsLiteral("verythinmathspace")) { |
290 | 0 | i = 2; |
291 | 0 | } else if (aString.EqualsLiteral("thinmathspace")) { |
292 | 0 | i = 3; |
293 | 0 | } else if (aString.EqualsLiteral("mediummathspace")) { |
294 | 0 | i = 4; |
295 | 0 | } else if (aString.EqualsLiteral("thickmathspace")) { |
296 | 0 | i = 5; |
297 | 0 | } else if (aString.EqualsLiteral("verythickmathspace")) { |
298 | 0 | i = 6; |
299 | 0 | } else if (aString.EqualsLiteral("veryverythickmathspace")) { |
300 | 0 | i = 7; |
301 | 0 | } else if (aFlags & PARSE_ALLOW_NEGATIVE) { |
302 | 0 | if (aString.EqualsLiteral("negativeveryverythinmathspace")) { |
303 | 0 | i = -1; |
304 | 0 | } else if (aString.EqualsLiteral("negativeverythinmathspace")) { |
305 | 0 | i = -2; |
306 | 0 | } else if (aString.EqualsLiteral("negativethinmathspace")) { |
307 | 0 | i = -3; |
308 | 0 | } else if (aString.EqualsLiteral("negativemediummathspace")) { |
309 | 0 | i = -4; |
310 | 0 | } else if (aString.EqualsLiteral("negativethickmathspace")) { |
311 | 0 | i = -5; |
312 | 0 | } else if (aString.EqualsLiteral("negativeverythickmathspace")) { |
313 | 0 | i = -6; |
314 | 0 | } else if (aString.EqualsLiteral("negativeveryverythickmathspace")) { |
315 | 0 | i = -7; |
316 | 0 | } |
317 | 0 | } |
318 | 0 | if (0 != i) { |
319 | 0 | aCSSValue.SetFloatValue(float(i)/float(18), eCSSUnit_EM); |
320 | 0 | return true; |
321 | 0 | } |
322 | 0 | |
323 | 0 | return false; |
324 | 0 | } |
325 | | |
326 | | // The REC says: |
327 | | // |
328 | | // "Most presentation elements have attributes that accept values representing |
329 | | // lengths to be used for size, spacing or similar properties. The syntax of a |
330 | | // length is specified as |
331 | | // |
332 | | // number | number unit | namedspace |
333 | | // |
334 | | // There should be no space between the number and the unit of a length." |
335 | | // |
336 | | // "A trailing '%' represents a percent of the default value. The default |
337 | | // value, or how it is obtained, is listed in the table of attributes for each |
338 | | // element. [...] A number without a unit is intepreted as a multiple of the |
339 | | // default value." |
340 | | // |
341 | | // "The possible units in MathML are: |
342 | | // |
343 | | // Unit Description |
344 | | // em an em (font-relative unit traditionally used for horizontal lengths) |
345 | | // ex an ex (font-relative unit traditionally used for vertical lengths) |
346 | | // px pixels, or size of a pixel in the current display |
347 | | // in inches (1 inch = 2.54 centimeters) |
348 | | // cm centimeters |
349 | | // mm millimeters |
350 | | // pt points (1 point = 1/72 inch) |
351 | | // pc picas (1 pica = 12 points) |
352 | | // % percentage of default value" |
353 | | // |
354 | | // The numbers are defined that way: |
355 | | // - unsigned-number: "a string of decimal digits with up to one decimal point |
356 | | // (U+002E), representing a non-negative terminating decimal number (a type of |
357 | | // rational number)" |
358 | | // - number: "an optional prefix of '-' (U+002D), followed by an unsigned |
359 | | // number, representing a terminating decimal number (a type of rational |
360 | | // number)" |
361 | | // |
362 | | /* static */ bool |
363 | | nsMathMLElement::ParseNumericValue(const nsString& aString, |
364 | | nsCSSValue& aCSSValue, |
365 | | uint32_t aFlags, |
366 | | nsIDocument* aDocument) |
367 | 0 | { |
368 | 0 | nsAutoString str(aString); |
369 | 0 | str.CompressWhitespace(); // aString is const in this code... |
370 | 0 |
|
371 | 0 | int32_t stringLength = str.Length(); |
372 | 0 | if (!stringLength) { |
373 | 0 | if (!(aFlags & PARSE_SUPPRESS_WARNINGS)) { |
374 | 0 | ReportLengthParseError(aString, aDocument); |
375 | 0 | } |
376 | 0 | return false; |
377 | 0 | } |
378 | 0 |
|
379 | 0 | if (ParseNamedSpaceValue(str, aCSSValue, aFlags)) { |
380 | 0 | return true; |
381 | 0 | } |
382 | 0 | |
383 | 0 | nsAutoString number, unit; |
384 | 0 |
|
385 | 0 | // see if the negative sign is there |
386 | 0 | int32_t i = 0; |
387 | 0 | char16_t c = str[0]; |
388 | 0 | if (c == '-') { |
389 | 0 | number.Append(c); |
390 | 0 | i++; |
391 | 0 | } |
392 | 0 |
|
393 | 0 | // Gather up characters that make up the number |
394 | 0 | bool gotDot = false; |
395 | 0 | for ( ; i < stringLength; i++) { |
396 | 0 | c = str[i]; |
397 | 0 | if (gotDot && c == '.') { |
398 | 0 | if (!(aFlags & PARSE_SUPPRESS_WARNINGS)) { |
399 | 0 | ReportLengthParseError(aString, aDocument); |
400 | 0 | } |
401 | 0 | return false; // two dots encountered |
402 | 0 | } |
403 | 0 | else if (c == '.') |
404 | 0 | gotDot = true; |
405 | 0 | else if (!IsAsciiDigit(c)) { |
406 | 0 | str.Right(unit, stringLength - i); |
407 | 0 | // some authors leave blanks before the unit, but that shouldn't |
408 | 0 | // be allowed, so don't CompressWhitespace on 'unit'. |
409 | 0 | break; |
410 | 0 | } |
411 | 0 | number.Append(c); |
412 | 0 | } |
413 | 0 |
|
414 | 0 | // Convert number to floating point |
415 | 0 | nsresult errorCode; |
416 | 0 | float floatValue = number.ToFloat(&errorCode); |
417 | 0 | if (NS_FAILED(errorCode)) { |
418 | 0 | if (!(aFlags & PARSE_SUPPRESS_WARNINGS)) { |
419 | 0 | ReportLengthParseError(aString, aDocument); |
420 | 0 | } |
421 | 0 | return false; |
422 | 0 | } |
423 | 0 | if (floatValue < 0 && !(aFlags & PARSE_ALLOW_NEGATIVE)) { |
424 | 0 | if (!(aFlags & PARSE_SUPPRESS_WARNINGS)) { |
425 | 0 | ReportLengthParseError(aString, aDocument); |
426 | 0 | } |
427 | 0 | return false; |
428 | 0 | } |
429 | 0 |
|
430 | 0 | nsCSSUnit cssUnit; |
431 | 0 | if (unit.IsEmpty()) { |
432 | 0 | if (aFlags & PARSE_ALLOW_UNITLESS) { |
433 | 0 | // no explicit unit, this is a number that will act as a multiplier |
434 | 0 | if (!(aFlags & PARSE_SUPPRESS_WARNINGS)) { |
435 | 0 | nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, |
436 | 0 | NS_LITERAL_CSTRING("MathML"), aDocument, |
437 | 0 | nsContentUtils::eMATHML_PROPERTIES, |
438 | 0 | "UnitlessValuesAreDeprecated"); |
439 | 0 | } |
440 | 0 | if (aFlags & CONVERT_UNITLESS_TO_PERCENT) { |
441 | 0 | aCSSValue.SetPercentValue(floatValue); |
442 | 0 | return true; |
443 | 0 | } |
444 | 0 | else |
445 | 0 | cssUnit = eCSSUnit_Number; |
446 | 0 | } else { |
447 | 0 | // We are supposed to have a unit, but there isn't one. |
448 | 0 | // If the value is 0 we can just call it "pixels" otherwise |
449 | 0 | // this is illegal. |
450 | 0 | if (floatValue != 0.0) { |
451 | 0 | if (!(aFlags & PARSE_SUPPRESS_WARNINGS)) { |
452 | 0 | ReportLengthParseError(aString, aDocument); |
453 | 0 | } |
454 | 0 | return false; |
455 | 0 | } |
456 | 0 | cssUnit = eCSSUnit_Pixel; |
457 | 0 | } |
458 | 0 | } |
459 | 0 | else if (unit.EqualsLiteral("%")) { |
460 | 0 | aCSSValue.SetPercentValue(floatValue / 100.0f); |
461 | 0 | return true; |
462 | 0 | } |
463 | 0 | else if (unit.EqualsLiteral("em")) cssUnit = eCSSUnit_EM; |
464 | 0 | else if (unit.EqualsLiteral("ex")) cssUnit = eCSSUnit_XHeight; |
465 | 0 | else if (unit.EqualsLiteral("px")) cssUnit = eCSSUnit_Pixel; |
466 | 0 | else if (unit.EqualsLiteral("in")) cssUnit = eCSSUnit_Inch; |
467 | 0 | else if (unit.EqualsLiteral("cm")) cssUnit = eCSSUnit_Centimeter; |
468 | 0 | else if (unit.EqualsLiteral("mm")) cssUnit = eCSSUnit_Millimeter; |
469 | 0 | else if (unit.EqualsLiteral("pt")) cssUnit = eCSSUnit_Point; |
470 | 0 | else if (unit.EqualsLiteral("pc")) cssUnit = eCSSUnit_Pica; |
471 | 0 | else if (unit.EqualsLiteral("q")) cssUnit = eCSSUnit_Quarter; |
472 | 0 | else { // unexpected unit |
473 | 0 | if (!(aFlags & PARSE_SUPPRESS_WARNINGS)) { |
474 | 0 | ReportLengthParseError(aString, aDocument); |
475 | 0 | } |
476 | 0 | return false; |
477 | 0 | } |
478 | 0 |
|
479 | 0 | aCSSValue.SetFloatValue(floatValue, cssUnit); |
480 | 0 | return true; |
481 | 0 | } |
482 | | |
483 | | void |
484 | | nsMathMLElement::MapMathMLAttributesInto(const nsMappedAttributes* aAttributes, |
485 | | MappedDeclarations& aDecls) |
486 | 0 | { |
487 | 0 | // scriptsizemultiplier |
488 | 0 | // |
489 | 0 | // "Specifies the multiplier to be used to adjust font size due to changes |
490 | 0 | // in scriptlevel. |
491 | 0 | // |
492 | 0 | // values: number |
493 | 0 | // default: 0.71 |
494 | 0 | // |
495 | 0 | const nsAttrValue* value = |
496 | 0 | aAttributes->GetAttr(nsGkAtoms::scriptsizemultiplier_); |
497 | 0 | if (value && value->Type() == nsAttrValue::eString && |
498 | 0 | !aDecls.PropertyIsSet(eCSSProperty__moz_script_size_multiplier)) { |
499 | 0 | nsAutoString str(value->GetStringValue()); |
500 | 0 | str.CompressWhitespace(); |
501 | 0 | // MathML numbers can't have leading '+' |
502 | 0 | if (str.Length() > 0 && str.CharAt(0) != '+') { |
503 | 0 | nsresult errorCode; |
504 | 0 | float floatValue = str.ToFloat(&errorCode); |
505 | 0 | // Negative scriptsizemultipliers are not parsed |
506 | 0 | if (NS_SUCCEEDED(errorCode) && floatValue >= 0.0f) { |
507 | 0 | aDecls.SetNumberValue(eCSSProperty__moz_script_size_multiplier, floatValue); |
508 | 0 | } else { |
509 | 0 | ReportParseErrorNoTag(str, |
510 | 0 | nsGkAtoms::scriptsizemultiplier_, |
511 | 0 | aDecls.Document()); |
512 | 0 | } |
513 | 0 | } |
514 | 0 | } |
515 | 0 |
|
516 | 0 | // scriptminsize |
517 | 0 | // |
518 | 0 | // "Specifies the minimum font size allowed due to changes in scriptlevel. |
519 | 0 | // Note that this does not limit the font size due to changes to mathsize." |
520 | 0 | // |
521 | 0 | // values: length |
522 | 0 | // default: 8pt |
523 | 0 | // |
524 | 0 | // We don't allow negative values. |
525 | 0 | // Unitless and percent values give a multiple of the default value. |
526 | 0 | // |
527 | 0 | value = aAttributes->GetAttr(nsGkAtoms::scriptminsize_); |
528 | 0 | if (value && value->Type() == nsAttrValue::eString && |
529 | 0 | !aDecls.PropertyIsSet(eCSSProperty__moz_script_min_size)) { |
530 | 0 | nsCSSValue scriptMinSize; |
531 | 0 | ParseNumericValue(value->GetStringValue(), scriptMinSize, |
532 | 0 | PARSE_ALLOW_UNITLESS | CONVERT_UNITLESS_TO_PERCENT, |
533 | 0 | aDecls.Document()); |
534 | 0 |
|
535 | 0 | if (scriptMinSize.GetUnit() == eCSSUnit_Percent) { |
536 | 0 | scriptMinSize.SetFloatValue(8.0 * scriptMinSize.GetPercentValue(), |
537 | 0 | eCSSUnit_Point); |
538 | 0 | } |
539 | 0 | if (scriptMinSize.GetUnit() != eCSSUnit_Null) { |
540 | 0 | aDecls.SetLengthValue(eCSSProperty__moz_script_min_size, scriptMinSize); |
541 | 0 | } |
542 | 0 | } |
543 | 0 |
|
544 | 0 | // scriptlevel |
545 | 0 | // |
546 | 0 | // "Changes the scriptlevel in effect for the children. When the value is |
547 | 0 | // given without a sign, it sets scriptlevel to the specified value; when a |
548 | 0 | // sign is given, it increments ("+") or decrements ("-") the current |
549 | 0 | // value. (Note that large decrements can result in negative values of |
550 | 0 | // scriptlevel, but these values are considered legal.)" |
551 | 0 | // |
552 | 0 | // values: ( "+" | "-" )? unsigned-integer |
553 | 0 | // default: inherited |
554 | 0 | // |
555 | 0 | value = aAttributes->GetAttr(nsGkAtoms::scriptlevel_); |
556 | 0 | if (value && value->Type() == nsAttrValue::eString && |
557 | 0 | !aDecls.PropertyIsSet(eCSSProperty__moz_script_level)) { |
558 | 0 | nsAutoString str(value->GetStringValue()); |
559 | 0 | str.CompressWhitespace(); |
560 | 0 | if (str.Length() > 0) { |
561 | 0 | nsresult errorCode; |
562 | 0 | int32_t intValue = str.ToInteger(&errorCode); |
563 | 0 | if (NS_SUCCEEDED(errorCode)) { |
564 | 0 | // This is kind of cheesy ... if the scriptlevel has a sign, |
565 | 0 | // then it's a relative value and we store the nsCSSValue as an |
566 | 0 | // Integer to indicate that. Otherwise we store it as a Number |
567 | 0 | // to indicate that the scriptlevel is absolute. |
568 | 0 | char16_t ch = str.CharAt(0); |
569 | 0 | if (ch == '+' || ch == '-') { |
570 | 0 | aDecls.SetIntValue(eCSSProperty__moz_script_level, intValue); |
571 | 0 | } else { |
572 | 0 | aDecls.SetNumberValue(eCSSProperty__moz_script_level, intValue); |
573 | 0 | } |
574 | 0 | } else { |
575 | 0 | ReportParseErrorNoTag(str, |
576 | 0 | nsGkAtoms::scriptlevel_, |
577 | 0 | aDecls.Document()); |
578 | 0 | } |
579 | 0 | } |
580 | 0 | } |
581 | 0 |
|
582 | 0 | // mathsize |
583 | 0 | // |
584 | 0 | // "Specifies the size to display the token content. The values 'small' and |
585 | 0 | // 'big' choose a size smaller or larger than the current font size, but |
586 | 0 | // leave the exact proportions unspecified; 'normal' is allowed for |
587 | 0 | // completeness, but since it is equivalent to '100%' or '1em', it has no |
588 | 0 | // effect." |
589 | 0 | // |
590 | 0 | // values: "small" | "normal" | "big" | length |
591 | 0 | // default: inherited |
592 | 0 | // |
593 | 0 | // fontsize |
594 | 0 | // |
595 | 0 | // "Specified the size for the token. Deprecated in favor of mathsize." |
596 | 0 | // |
597 | 0 | // values: length |
598 | 0 | // default: inherited |
599 | 0 | // |
600 | 0 | // In both cases, we don't allow negative values. |
601 | 0 | // Unitless values give a multiple of the default value. |
602 | 0 | // |
603 | 0 | bool parseSizeKeywords = true; |
604 | 0 | value = aAttributes->GetAttr(nsGkAtoms::mathsize_); |
605 | 0 | if (!value) { |
606 | 0 | parseSizeKeywords = false; |
607 | 0 | value = aAttributes->GetAttr(nsGkAtoms::fontsize_); |
608 | 0 | if (value) { |
609 | 0 | WarnDeprecated(nsGkAtoms::fontsize_->GetUTF16String(), |
610 | 0 | nsGkAtoms::mathsize_->GetUTF16String(), |
611 | 0 | aDecls.Document()); |
612 | 0 | } |
613 | 0 | } |
614 | 0 | if (value && value->Type() == nsAttrValue::eString && |
615 | 0 | !aDecls.PropertyIsSet(eCSSProperty_font_size)) { |
616 | 0 | nsAutoString str(value->GetStringValue()); |
617 | 0 | nsCSSValue fontSize; |
618 | 0 | if (!ParseNumericValue(str, fontSize, PARSE_SUPPRESS_WARNINGS | |
619 | 0 | PARSE_ALLOW_UNITLESS | CONVERT_UNITLESS_TO_PERCENT, |
620 | 0 | nullptr) |
621 | 0 | && parseSizeKeywords) { |
622 | 0 | static const char sizes[3][7] = { "small", "normal", "big" }; |
623 | 0 | static const int32_t values[MOZ_ARRAY_LENGTH(sizes)] = { |
624 | 0 | NS_STYLE_FONT_SIZE_SMALL, NS_STYLE_FONT_SIZE_MEDIUM, |
625 | 0 | NS_STYLE_FONT_SIZE_LARGE |
626 | 0 | }; |
627 | 0 | str.CompressWhitespace(); |
628 | 0 | for (uint32_t i = 0; i < ArrayLength(sizes); ++i) { |
629 | 0 | if (str.EqualsASCII(sizes[i])) { |
630 | 0 | aDecls.SetKeywordValue(eCSSProperty_font_size, values[i]); |
631 | 0 | break; |
632 | 0 | } |
633 | 0 | } |
634 | 0 | } else if (fontSize.GetUnit() == eCSSUnit_Percent) { |
635 | 0 | aDecls.SetPercentValue(eCSSProperty_font_size, |
636 | 0 | fontSize.GetPercentValue()); |
637 | 0 | } else if (fontSize.GetUnit() != eCSSUnit_Null) { |
638 | 0 | aDecls.SetLengthValue(eCSSProperty_font_size, fontSize); |
639 | 0 | } |
640 | 0 | } |
641 | 0 |
|
642 | 0 | // fontfamily |
643 | 0 | // |
644 | 0 | // "Should be the name of a font that may be available to a MathML renderer, |
645 | 0 | // or a CSS font specification; See Section 6.5 Using CSS with MathML and |
646 | 0 | // CSS for more information. Deprecated in favor of mathvariant." |
647 | 0 | // |
648 | 0 | // values: string |
649 | 0 | // |
650 | 0 | value = aAttributes->GetAttr(nsGkAtoms::fontfamily_); |
651 | 0 | if (value) { |
652 | 0 | WarnDeprecated(nsGkAtoms::fontfamily_->GetUTF16String(), |
653 | 0 | nsGkAtoms::mathvariant_->GetUTF16String(), |
654 | 0 | aDecls.Document()); |
655 | 0 | } |
656 | 0 | if (value && value->Type() == nsAttrValue::eString && |
657 | 0 | !aDecls.PropertyIsSet(eCSSProperty_font_family)) { |
658 | 0 | aDecls.SetFontFamily(value->GetStringValue()); |
659 | 0 | } |
660 | 0 |
|
661 | 0 | // fontstyle |
662 | 0 | // |
663 | 0 | // "Specified the font style to use for the token. Deprecated in favor of |
664 | 0 | // mathvariant." |
665 | 0 | // |
666 | 0 | // values: "normal" | "italic" |
667 | 0 | // default: normal (except on <mi>) |
668 | 0 | // |
669 | 0 | // Note that the font-style property is reset in layout/style/ when |
670 | 0 | // -moz-math-variant is specified. |
671 | 0 | value = aAttributes->GetAttr(nsGkAtoms::fontstyle_); |
672 | 0 | if (value) { |
673 | 0 | WarnDeprecated(nsGkAtoms::fontstyle_->GetUTF16String(), |
674 | 0 | nsGkAtoms::mathvariant_->GetUTF16String(), |
675 | 0 | aDecls.Document()); |
676 | 0 | if (value->Type() == nsAttrValue::eString && |
677 | 0 | !aDecls.PropertyIsSet(eCSSProperty_font_style)) { |
678 | 0 | nsAutoString str(value->GetStringValue()); |
679 | 0 | str.CompressWhitespace(); |
680 | 0 | // FIXME(emilio): This should use FontSlantStyle or what not. Or even |
681 | 0 | // better, it looks deprecated since forever, we should just kill it. |
682 | 0 | if (str.EqualsASCII("normal")) { |
683 | 0 | aDecls.SetKeywordValue(eCSSProperty_font_style, NS_FONT_STYLE_NORMAL); |
684 | 0 | } else if (str.EqualsASCII("italic")) { |
685 | 0 | aDecls.SetKeywordValue(eCSSProperty_font_style, NS_FONT_STYLE_ITALIC); |
686 | 0 | } |
687 | 0 | } |
688 | 0 | } |
689 | 0 |
|
690 | 0 | // fontweight |
691 | 0 | // |
692 | 0 | // "Specified the font weight for the token. Deprecated in favor of |
693 | 0 | // mathvariant." |
694 | 0 | // |
695 | 0 | // values: "normal" | "bold" |
696 | 0 | // default: normal |
697 | 0 | // |
698 | 0 | // Note that the font-weight property is reset in layout/style/ when |
699 | 0 | // -moz-math-variant is specified. |
700 | 0 | value = aAttributes->GetAttr(nsGkAtoms::fontweight_); |
701 | 0 | if (value) { |
702 | 0 | WarnDeprecated(nsGkAtoms::fontweight_->GetUTF16String(), |
703 | 0 | nsGkAtoms::mathvariant_->GetUTF16String(), |
704 | 0 | aDecls.Document()); |
705 | 0 | if (value->Type() == nsAttrValue::eString && |
706 | 0 | !aDecls.PropertyIsSet(eCSSProperty_font_weight)) { |
707 | 0 | nsAutoString str(value->GetStringValue()); |
708 | 0 | str.CompressWhitespace(); |
709 | 0 | if (str.EqualsASCII("normal")) { |
710 | 0 | aDecls.SetKeywordValue(eCSSProperty_font_weight, |
711 | 0 | FontWeight::Normal().ToFloat()); |
712 | 0 | } else if (str.EqualsASCII("bold")) { |
713 | 0 | aDecls.SetKeywordValue(eCSSProperty_font_weight, |
714 | 0 | FontWeight::Bold().ToFloat()); |
715 | 0 | } |
716 | 0 | } |
717 | 0 | } |
718 | 0 |
|
719 | 0 | // mathvariant |
720 | 0 | // |
721 | 0 | // "Specifies the logical class of the token. Note that this class is more |
722 | 0 | // than styling, it typically conveys semantic intent;" |
723 | 0 | // |
724 | 0 | // values: "normal" | "bold" | "italic" | "bold-italic" | "double-struck" | |
725 | 0 | // "bold-fraktur" | "script" | "bold-script" | "fraktur" | "sans-serif" | |
726 | 0 | // "bold-sans-serif" | "sans-serif-italic" | "sans-serif-bold-italic" | |
727 | 0 | // "monospace" | "initial" | "tailed" | "looped" | "stretched" |
728 | 0 | // default: normal (except on <mi>) |
729 | 0 | // |
730 | 0 | value = aAttributes->GetAttr(nsGkAtoms::mathvariant_); |
731 | 0 | if (value && value->Type() == nsAttrValue::eString && |
732 | 0 | !aDecls.PropertyIsSet(eCSSProperty__moz_math_variant)) { |
733 | 0 | nsAutoString str(value->GetStringValue()); |
734 | 0 | str.CompressWhitespace(); |
735 | 0 | static const char sizes[19][23] = { |
736 | 0 | "normal", "bold", "italic", "bold-italic", "script", "bold-script", |
737 | 0 | "fraktur", "double-struck", "bold-fraktur", "sans-serif", |
738 | 0 | "bold-sans-serif", "sans-serif-italic", "sans-serif-bold-italic", |
739 | 0 | "monospace", "initial", "tailed", "looped", "stretched" |
740 | 0 | }; |
741 | 0 | static const int32_t values[MOZ_ARRAY_LENGTH(sizes)] = { |
742 | 0 | NS_MATHML_MATHVARIANT_NORMAL, NS_MATHML_MATHVARIANT_BOLD, |
743 | 0 | NS_MATHML_MATHVARIANT_ITALIC, NS_MATHML_MATHVARIANT_BOLD_ITALIC, |
744 | 0 | NS_MATHML_MATHVARIANT_SCRIPT, NS_MATHML_MATHVARIANT_BOLD_SCRIPT, |
745 | 0 | NS_MATHML_MATHVARIANT_FRAKTUR, NS_MATHML_MATHVARIANT_DOUBLE_STRUCK, |
746 | 0 | NS_MATHML_MATHVARIANT_BOLD_FRAKTUR, NS_MATHML_MATHVARIANT_SANS_SERIF, |
747 | 0 | NS_MATHML_MATHVARIANT_BOLD_SANS_SERIF, |
748 | 0 | NS_MATHML_MATHVARIANT_SANS_SERIF_ITALIC, |
749 | 0 | NS_MATHML_MATHVARIANT_SANS_SERIF_BOLD_ITALIC, |
750 | 0 | NS_MATHML_MATHVARIANT_MONOSPACE, NS_MATHML_MATHVARIANT_INITIAL, |
751 | 0 | NS_MATHML_MATHVARIANT_TAILED, NS_MATHML_MATHVARIANT_LOOPED, |
752 | 0 | NS_MATHML_MATHVARIANT_STRETCHED |
753 | 0 | }; |
754 | 0 | for (uint32_t i = 0; i < ArrayLength(sizes); ++i) { |
755 | 0 | if (str.EqualsASCII(sizes[i])) { |
756 | 0 | aDecls.SetKeywordValue(eCSSProperty__moz_math_variant, values[i]); |
757 | 0 | break; |
758 | 0 | } |
759 | 0 | } |
760 | 0 | } |
761 | 0 |
|
762 | 0 | // mathbackground |
763 | 0 | // |
764 | 0 | // "Specifies the background color to be used to fill in the bounding box of |
765 | 0 | // the element and its children. The default, 'transparent', lets the |
766 | 0 | // background color, if any, used in the current rendering context to show |
767 | 0 | // through." |
768 | 0 | // |
769 | 0 | // values: color | "transparent" |
770 | 0 | // default: "transparent" |
771 | 0 | // |
772 | 0 | // background |
773 | 0 | // |
774 | 0 | // "Specified the background color to be used to fill in the bounding box of |
775 | 0 | // the element and its children. Deprecated in favor of mathbackground." |
776 | 0 | // |
777 | 0 | // values: color | "transparent" |
778 | 0 | // default: "transparent" |
779 | 0 | // |
780 | 0 | value = aAttributes->GetAttr(nsGkAtoms::mathbackground_); |
781 | 0 | if (!value) { |
782 | 0 | value = aAttributes->GetAttr(nsGkAtoms::background); |
783 | 0 | if (value) { |
784 | 0 | WarnDeprecated(nsGkAtoms::background->GetUTF16String(), |
785 | 0 | nsGkAtoms::mathbackground_->GetUTF16String(), |
786 | 0 | aDecls.Document()); |
787 | 0 | } |
788 | 0 | } |
789 | 0 | if (value) { |
790 | 0 | nscolor color; |
791 | 0 | if (value->GetColorValue(color)) { |
792 | 0 | aDecls.SetColorValueIfUnset(eCSSProperty_background_color, color); |
793 | 0 | } |
794 | 0 | } |
795 | 0 |
|
796 | 0 | // mathcolor |
797 | 0 | // |
798 | 0 | // "Specifies the foreground color to use when drawing the components of this |
799 | 0 | // element, such as the content for token elements or any lines, surds, or |
800 | 0 | // other decorations. It also establishes the default mathcolor used for |
801 | 0 | // child elements when used on a layout element." |
802 | 0 | // |
803 | 0 | // values: color |
804 | 0 | // default: inherited |
805 | 0 | // |
806 | 0 | // color |
807 | 0 | // |
808 | 0 | // "Specified the color for the token. Deprecated in favor of mathcolor." |
809 | 0 | // |
810 | 0 | // values: color |
811 | 0 | // default: inherited |
812 | 0 | // |
813 | 0 | value = aAttributes->GetAttr(nsGkAtoms::mathcolor_); |
814 | 0 | if (!value) { |
815 | 0 | value = aAttributes->GetAttr(nsGkAtoms::color); |
816 | 0 | if (value) { |
817 | 0 | WarnDeprecated(nsGkAtoms::color->GetUTF16String(), |
818 | 0 | nsGkAtoms::mathcolor_->GetUTF16String(), |
819 | 0 | aDecls.Document()); |
820 | 0 | } |
821 | 0 | } |
822 | 0 | nscolor color; |
823 | 0 | if (value && value->GetColorValue(color)) { |
824 | 0 | aDecls.SetColorValueIfUnset(eCSSProperty_color, color); |
825 | 0 | } |
826 | 0 |
|
827 | 0 | // width |
828 | 0 | // |
829 | 0 | // "Specifies the desired width of the entire table and is intended for |
830 | 0 | // visual user agents. When the value is a percentage value, the value is |
831 | 0 | // relative to the horizontal space a MathML renderer has available for the |
832 | 0 | // math element. When the value is "auto", the MathML renderer should |
833 | 0 | // calculate the table width from its contents using whatever layout |
834 | 0 | // algorithm it chooses. " |
835 | 0 | // |
836 | 0 | // values: "auto" | length |
837 | 0 | // default: auto |
838 | 0 | // |
839 | 0 | if (!aDecls.PropertyIsSet(eCSSProperty_width)) { |
840 | 0 | const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::width); |
841 | 0 | nsCSSValue width; |
842 | 0 | // This does not handle auto and unitless values |
843 | 0 | if (value && value->Type() == nsAttrValue::eString) { |
844 | 0 | ParseNumericValue(value->GetStringValue(), width, 0, aDecls.Document()); |
845 | 0 | if (width.GetUnit() == eCSSUnit_Percent) { |
846 | 0 | aDecls.SetPercentValue(eCSSProperty_width, |
847 | 0 | width.GetPercentValue()); |
848 | 0 | } else if (width.GetUnit() != eCSSUnit_Null) { |
849 | 0 | aDecls.SetLengthValue(eCSSProperty_width, width); |
850 | 0 | } |
851 | 0 | } |
852 | 0 | } |
853 | 0 |
|
854 | 0 | // dir |
855 | 0 | // |
856 | 0 | // Overall Directionality of Mathematics Formulas: |
857 | 0 | // "The overall directionality for a formula, basically the direction of the |
858 | 0 | // Layout Schemata, is specified by the dir attribute on the containing math |
859 | 0 | // element (see Section 2.2 The Top-Level math Element). The default is ltr. |
860 | 0 | // [...] The overall directionality is usually set on the math, but may also |
861 | 0 | // be switched for individual subformula by using the dir attribute on mrow |
862 | 0 | // or mstyle elements." |
863 | 0 | // |
864 | 0 | // Bidirectional Layout in Token Elements: |
865 | 0 | // "Specifies the initial directionality for text within the token: |
866 | 0 | // ltr (Left To Right) or rtl (Right To Left). This attribute should only be |
867 | 0 | // needed in rare cases involving weak or neutral characters; |
868 | 0 | // see Section 3.1.5.1 Overall Directionality of Mathematics Formulas for |
869 | 0 | // further discussion. It has no effect on mspace." |
870 | 0 | // |
871 | 0 | // values: "ltr" | "rtl" |
872 | 0 | // default: inherited |
873 | 0 | // |
874 | 0 | value = aAttributes->GetAttr(nsGkAtoms::dir); |
875 | 0 | if (value && value->Type() == nsAttrValue::eString && |
876 | 0 | !aDecls.PropertyIsSet(eCSSProperty_direction)) { |
877 | 0 | nsAutoString str(value->GetStringValue()); |
878 | 0 | static const char dirs[][4] = { "ltr", "rtl" }; |
879 | 0 | static const int32_t dirValues[MOZ_ARRAY_LENGTH(dirs)] = { |
880 | 0 | NS_STYLE_DIRECTION_LTR, NS_STYLE_DIRECTION_RTL |
881 | 0 | }; |
882 | 0 | for (uint32_t i = 0; i < ArrayLength(dirs); ++i) { |
883 | 0 | if (str.EqualsASCII(dirs[i])) { |
884 | 0 | aDecls.SetKeywordValue(eCSSProperty_direction, dirValues[i]); |
885 | 0 | break; |
886 | 0 | } |
887 | 0 | } |
888 | 0 | } |
889 | 0 | } |
890 | | |
891 | | void |
892 | | nsMathMLElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) |
893 | 0 | { |
894 | 0 | Element::GetEventTargetParent(aVisitor); |
895 | 0 |
|
896 | 0 | GetEventTargetParentForLinks(aVisitor); |
897 | 0 | } |
898 | | |
899 | | nsresult |
900 | | nsMathMLElement::PostHandleEvent(EventChainPostVisitor& aVisitor) |
901 | 0 | { |
902 | 0 | return PostHandleEventForLinks(aVisitor); |
903 | 0 | } |
904 | | |
905 | | NS_IMPL_ELEMENT_CLONE(nsMathMLElement) |
906 | | |
907 | | EventStates |
908 | | nsMathMLElement::IntrinsicState() const |
909 | 0 | { |
910 | 0 | return Link::LinkState() | nsMathMLElementBase::IntrinsicState() | |
911 | 0 | (mIncrementScriptLevel ? |
912 | 0 | NS_EVENT_STATE_INCREMENT_SCRIPT_LEVEL : EventStates()); |
913 | 0 | } |
914 | | |
915 | | bool |
916 | | nsMathMLElement::IsNodeOfType(uint32_t aFlags) const |
917 | 0 | { |
918 | 0 | return false; |
919 | 0 | } |
920 | | |
921 | | void |
922 | | nsMathMLElement::SetIncrementScriptLevel(bool aIncrementScriptLevel, |
923 | | bool aNotify) |
924 | 0 | { |
925 | 0 | if (aIncrementScriptLevel == mIncrementScriptLevel) |
926 | 0 | return; |
927 | 0 | mIncrementScriptLevel = aIncrementScriptLevel; |
928 | 0 |
|
929 | 0 | NS_ASSERTION(aNotify, "We always notify!"); |
930 | 0 |
|
931 | 0 | UpdateState(true); |
932 | 0 | } |
933 | | |
934 | | bool |
935 | | nsMathMLElement::IsFocusableInternal(int32_t* aTabIndex, bool aWithMouse) |
936 | 0 | { |
937 | 0 | nsCOMPtr<nsIURI> uri; |
938 | 0 | if (IsLink(getter_AddRefs(uri))) { |
939 | 0 | if (aTabIndex) { |
940 | 0 | *aTabIndex = ((sTabFocusModel & eTabFocus_linksMask) == 0 ? -1 : 0); |
941 | 0 | } |
942 | 0 | return true; |
943 | 0 | } |
944 | 0 |
|
945 | 0 | if (aTabIndex) { |
946 | 0 | *aTabIndex = -1; |
947 | 0 | } |
948 | 0 |
|
949 | 0 | return false; |
950 | 0 | } |
951 | | |
952 | | bool |
953 | | nsMathMLElement::IsLink(nsIURI** aURI) const |
954 | 0 | { |
955 | 0 | // http://www.w3.org/TR/2010/REC-MathML3-20101021/chapter6.html#interf.link |
956 | 0 | // The REC says that the following elements should not be linking elements: |
957 | 0 | if (IsAnyOfMathMLElements(nsGkAtoms::mprescripts_, nsGkAtoms::none, |
958 | 0 | nsGkAtoms::malignmark_, nsGkAtoms::maligngroup_)) { |
959 | 0 | *aURI = nullptr; |
960 | 0 | return false; |
961 | 0 | } |
962 | 0 | |
963 | 0 | bool hasHref = false; |
964 | 0 | const nsAttrValue* href = mAttrs.GetAttr(nsGkAtoms::href, |
965 | 0 | kNameSpaceID_None); |
966 | 0 | if (href) { |
967 | 0 | // MathML href |
968 | 0 | // The REC says: "When user agents encounter MathML elements with both href |
969 | 0 | // and xlink:href attributes, the href attribute should take precedence." |
970 | 0 | hasHref = true; |
971 | 0 | } else { |
972 | 0 | // To be a clickable XLink for styling and interaction purposes, we require: |
973 | 0 | // |
974 | 0 | // xlink:href - must be set |
975 | 0 | // xlink:type - must be unset or set to "" or set to "simple" |
976 | 0 | // xlink:show - must be unset or set to "", "new" or "replace" |
977 | 0 | // xlink:actuate - must be unset or set to "" or "onRequest" |
978 | 0 | // |
979 | 0 | // For any other values, we're either not a *clickable* XLink, or the end |
980 | 0 | // result is poorly specified. Either way, we return false. |
981 | 0 |
|
982 | 0 | static Element::AttrValuesArray sTypeVals[] = |
983 | 0 | { &nsGkAtoms::_empty, &nsGkAtoms::simple, nullptr }; |
984 | 0 |
|
985 | 0 | static Element::AttrValuesArray sShowVals[] = |
986 | 0 | { &nsGkAtoms::_empty, &nsGkAtoms::_new, &nsGkAtoms::replace, nullptr }; |
987 | 0 |
|
988 | 0 | static Element::AttrValuesArray sActuateVals[] = |
989 | 0 | { &nsGkAtoms::_empty, &nsGkAtoms::onRequest, nullptr }; |
990 | 0 |
|
991 | 0 | // Optimization: check for href first for early return |
992 | 0 | href = mAttrs.GetAttr(nsGkAtoms::href, kNameSpaceID_XLink); |
993 | 0 | if (href && |
994 | 0 | FindAttrValueIn(kNameSpaceID_XLink, nsGkAtoms::type, |
995 | 0 | sTypeVals, eCaseMatters) != |
996 | 0 | Element::ATTR_VALUE_NO_MATCH && |
997 | 0 | FindAttrValueIn(kNameSpaceID_XLink, nsGkAtoms::show, |
998 | 0 | sShowVals, eCaseMatters) != |
999 | 0 | Element::ATTR_VALUE_NO_MATCH && |
1000 | 0 | FindAttrValueIn(kNameSpaceID_XLink, nsGkAtoms::actuate, |
1001 | 0 | sActuateVals, eCaseMatters) != |
1002 | 0 | Element::ATTR_VALUE_NO_MATCH) { |
1003 | 0 | hasHref = true; |
1004 | 0 | } |
1005 | 0 | } |
1006 | 0 |
|
1007 | 0 | if (hasHref) { |
1008 | 0 | nsCOMPtr<nsIURI> baseURI = GetBaseURI(); |
1009 | 0 | // Get absolute URI |
1010 | 0 | nsAutoString hrefStr; |
1011 | 0 | href->ToString(hrefStr); |
1012 | 0 | nsContentUtils::NewURIWithDocumentCharset(aURI, hrefStr, |
1013 | 0 | OwnerDoc(), baseURI); |
1014 | 0 | // must promise out param is non-null if we return true |
1015 | 0 | return !!*aURI; |
1016 | 0 | } |
1017 | 0 | |
1018 | 0 | *aURI = nullptr; |
1019 | 0 | return false; |
1020 | 0 | } |
1021 | | |
1022 | | void |
1023 | | nsMathMLElement::GetLinkTarget(nsAString& aTarget) |
1024 | 0 | { |
1025 | 0 | const nsAttrValue* target = mAttrs.GetAttr(nsGkAtoms::target, |
1026 | 0 | kNameSpaceID_XLink); |
1027 | 0 | if (target) { |
1028 | 0 | target->ToString(aTarget); |
1029 | 0 | } |
1030 | 0 |
|
1031 | 0 | if (aTarget.IsEmpty()) { |
1032 | 0 |
|
1033 | 0 | static Element::AttrValuesArray sShowVals[] = |
1034 | 0 | { &nsGkAtoms::_new, &nsGkAtoms::replace, nullptr }; |
1035 | 0 |
|
1036 | 0 | switch (FindAttrValueIn(kNameSpaceID_XLink, nsGkAtoms::show, |
1037 | 0 | sShowVals, eCaseMatters)) { |
1038 | 0 | case 0: |
1039 | 0 | aTarget.AssignLiteral("_blank"); |
1040 | 0 | return; |
1041 | 0 | case 1: |
1042 | 0 | return; |
1043 | 0 | } |
1044 | 0 | OwnerDoc()->GetBaseTarget(aTarget); |
1045 | 0 | } |
1046 | 0 | } |
1047 | | |
1048 | | already_AddRefed<nsIURI> |
1049 | | nsMathMLElement::GetHrefURI() const |
1050 | 0 | { |
1051 | 0 | nsCOMPtr<nsIURI> hrefURI; |
1052 | 0 | return IsLink(getter_AddRefs(hrefURI)) ? hrefURI.forget() : nullptr; |
1053 | 0 | } |
1054 | | |
1055 | | nsresult |
1056 | | nsMathMLElement::AfterSetAttr(int32_t aNameSpaceID, nsAtom* aName, |
1057 | | const nsAttrValue* aValue, |
1058 | | const nsAttrValue* aOldValue, |
1059 | | nsIPrincipal* aSubjectPrincipal, |
1060 | | bool aNotify) |
1061 | 0 | { |
1062 | 0 | // It is important that this be done after the attribute is set/unset. |
1063 | 0 | // We will need the updated attribute value because notifying the document |
1064 | 0 | // that content states have changed will call IntrinsicState, which will try |
1065 | 0 | // to get updated information about the visitedness from Link. |
1066 | 0 | if (aName == nsGkAtoms::href && |
1067 | 0 | (aNameSpaceID == kNameSpaceID_None || |
1068 | 0 | aNameSpaceID == kNameSpaceID_XLink)) { |
1069 | 0 | if (aValue && aNameSpaceID == kNameSpaceID_XLink) { |
1070 | 0 | WarnDeprecated(u"xlink:href", u"href", OwnerDoc()); |
1071 | 0 | } |
1072 | 0 | // Note: When unsetting href, there may still be another href since there |
1073 | 0 | // are 2 possible namespaces. |
1074 | 0 | Link::ResetLinkState(aNotify, aValue || Link::ElementHasHref()); |
1075 | 0 | } |
1076 | 0 |
|
1077 | 0 | return nsMathMLElementBase::AfterSetAttr(aNameSpaceID, aName, aValue, |
1078 | 0 | aOldValue, aSubjectPrincipal, aNotify); |
1079 | 0 | } |
1080 | | |
1081 | | JSObject* |
1082 | | nsMathMLElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) |
1083 | 0 | { |
1084 | 0 | return Element_Binding::Wrap(aCx, this, aGivenProto); |
1085 | 0 | } |