1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsMathMLOperators.h"
8 #include "nsDataHashtable.h"
9 #include "nsHashKeys.h"
12 #include "nsIPersistentProperties2.h"
13 #include "nsNetUtil.h"
16 // operator dictionary entry
27 nsOperatorFlags mFlags
;
28 float mLeadingSpace
; // unit is em
29 float mTrailingSpace
; // unit is em
32 static int32_t gTableRefCount
= 0;
33 static uint32_t gOperatorCount
= 0;
34 static OperatorData
* gOperatorArray
= nullptr;
35 static nsDataHashtable
<nsStringHashKey
, OperatorData
*>* gOperatorTable
= nullptr;
36 static bool gGlobalsInitialized
= false;
38 static const char16_t kDashCh
= char16_t('#');
39 static const char16_t kColonCh
= char16_t(':');
42 SetBooleanProperty(OperatorData
* aOperatorData
,
48 if (aName
.EqualsLiteral("stretchy") && (1 == aOperatorData
->mStr
.Length()))
49 aOperatorData
->mFlags
|= NS_MATHML_OPERATOR_STRETCHY
;
50 else if (aName
.EqualsLiteral("fence"))
51 aOperatorData
->mFlags
|= NS_MATHML_OPERATOR_FENCE
;
52 else if (aName
.EqualsLiteral("accent"))
53 aOperatorData
->mFlags
|= NS_MATHML_OPERATOR_ACCENT
;
54 else if (aName
.EqualsLiteral("largeop"))
55 aOperatorData
->mFlags
|= NS_MATHML_OPERATOR_LARGEOP
;
56 else if (aName
.EqualsLiteral("separator"))
57 aOperatorData
->mFlags
|= NS_MATHML_OPERATOR_SEPARATOR
;
58 else if (aName
.EqualsLiteral("movablelimits"))
59 aOperatorData
->mFlags
|= NS_MATHML_OPERATOR_MOVABLELIMITS
;
60 else if (aName
.EqualsLiteral("symmetric"))
61 aOperatorData
->mFlags
|= NS_MATHML_OPERATOR_SYMMETRIC
;
62 else if (aName
.EqualsLiteral("integral"))
63 aOperatorData
->mFlags
|= NS_MATHML_OPERATOR_INTEGRAL
;
64 else if (aName
.EqualsLiteral("mirrorable"))
65 aOperatorData
->mFlags
|= NS_MATHML_OPERATOR_MIRRORABLE
;
69 SetProperty(OperatorData
* aOperatorData
,
73 if (aName
.IsEmpty() || aValue
.IsEmpty())
76 // XXX These ones are not kept in the dictionary
77 // Support for these requires nsString member variables
78 // maxsize (default: infinity)
79 // minsize (default: 1)
81 if (aName
.EqualsLiteral("direction")) {
82 if (aValue
.EqualsLiteral("vertical"))
83 aOperatorData
->mFlags
|= NS_MATHML_OPERATOR_DIRECTION_VERTICAL
;
84 else if (aValue
.EqualsLiteral("horizontal"))
85 aOperatorData
->mFlags
|= NS_MATHML_OPERATOR_DIRECTION_HORIZONTAL
;
86 else return; // invalid value
89 if (aName
.EqualsLiteral("lspace"))
90 isLeadingSpace
= true;
91 else if (aName
.EqualsLiteral("rspace"))
92 isLeadingSpace
= false;
93 else return; // input is not applicable
95 // aValue is assumed to be a digit from 0 to 7
96 nsresult error
= NS_OK
;
97 float space
= aValue
.ToFloat(&error
) / 18.0;
98 if (NS_FAILED(error
)) return;
101 aOperatorData
->mLeadingSpace
= space
;
103 aOperatorData
->mTrailingSpace
= space
;
108 SetOperator(OperatorData
* aOperatorData
,
109 nsOperatorFlags aForm
,
110 const nsCString
& aOperator
,
111 nsString
& aAttributes
)
114 static const char16_t kNullCh
= char16_t('\0');
116 // aOperator is in the expanded format \uNNNN\uNNNN ...
117 // First compress these Unicode points to the internal nsString format
119 nsAutoString name
, value
;
120 int32_t len
= aOperator
.Length();
121 char16_t c
= aOperator
[i
++];
131 if (('u' != c
) && ('U' != c
))
139 if (('0' <= c
) && (c
<= '9'))
140 uchar
= (uchar
<< 4) | (c
- '0');
141 else if (('a' <= c
) && (c
<= 'f'))
142 uchar
= (uchar
<< 4) | (c
- 'a' + 0x0a);
143 else if (('A' <= c
) && (c
<= 'F'))
144 uchar
= (uchar
<< 4) | (c
- 'A' + 0x0a);
157 if (0 != state
) return false;
159 // Quick return when the caller doesn't care about the attributes and just wants
160 // to know if this is a valid operator (this is the case at the first pass of the
161 // parsing of the dictionary in InitOperators())
162 if (!aForm
) return true;
164 // Add operator to hash table
165 aOperatorData
->mFlags
|= aForm
;
166 aOperatorData
->mStr
.Assign(value
);
167 value
.AppendInt(aForm
, 10);
168 gOperatorTable
->Put(value
, aOperatorData
);
171 NS_LossyConvertUTF16toASCII
str(aAttributes
);
173 // Loop over the space-delimited list of attributes to get the name:value pairs
174 aAttributes
.Append(kNullCh
); // put an extra null at the end
175 char16_t
* start
= aAttributes
.BeginWriting();
176 char16_t
* end
= start
;
177 while ((kNullCh
!= *start
) && (kDashCh
!= *start
)) {
180 // skip leading space, the dash amounts to the end of the line
181 while ((kNullCh
!=*start
) && (kDashCh
!=*start
) && nsCRT::IsAsciiSpace(*start
)) {
186 while ((kNullCh
!=*end
) && (kDashCh
!=*end
) && !nsCRT::IsAsciiSpace(*end
) &&
190 // If ':' is not found, then it's a boolean property
191 bool IsBooleanProperty
= (kColonCh
!= *end
);
192 *end
= kNullCh
; // end segment here
193 // this segment is the name
197 if (IsBooleanProperty
) {
198 SetBooleanProperty(aOperatorData
, name
);
201 // look for space or end of line
202 while ((kNullCh
!=*end
) && (kDashCh
!=*end
) &&
203 !nsCRT::IsAsciiSpace(*end
)) {
206 *end
= kNullCh
; // end segment here
208 // this segment is the value
211 SetProperty(aOperatorData
, name
, value
);
221 // Load the property file containing the Operator Dictionary
223 nsCOMPtr
<nsIPersistentProperties
> mathfontProp
;
224 rv
= NS_LoadPersistentPropertiesFromURISpec(getter_AddRefs(mathfontProp
),
225 NS_LITERAL_CSTRING("resource://gre/res/fonts/mathfont.properties"));
226 if (NS_FAILED(rv
)) return rv
;
228 // Parse the Operator Dictionary in two passes.
229 // The first pass is to count the number of operators; the second pass is to
230 // allocate the necessary space for them and to add them in the hash table.
231 for (int32_t pass
= 1; pass
<= 2; pass
++) {
232 OperatorData dummyData
;
233 OperatorData
* operatorData
= &dummyData
;
234 nsCOMPtr
<nsISimpleEnumerator
> iterator
;
235 if (NS_SUCCEEDED(mathfontProp
->Enumerate(getter_AddRefs(iterator
)))) {
239 nsAutoString attributes
;
240 while ((NS_SUCCEEDED(iterator
->HasMoreElements(&more
))) && more
) {
241 nsCOMPtr
<nsISupports
> supports
;
242 nsCOMPtr
<nsIPropertyElement
> element
;
243 if (NS_SUCCEEDED(iterator
->GetNext(getter_AddRefs(supports
)))) {
244 element
= do_QueryInterface(supports
);
245 if (NS_SUCCEEDED(element
->GetKey(name
)) &&
246 NS_SUCCEEDED(element
->GetValue(attributes
))) {
247 // expected key: operator.\uNNNN.{infix,postfix,prefix}
248 if ((21 <= name
.Length()) && (0 == name
.Find("operator.\\u"))) {
249 name
.Cut(0, 9); // 9 is the length of "operator.";
250 int32_t len
= name
.Length();
251 nsOperatorFlags form
= 0;
252 if (kNotFound
!= name
.RFind(".infix")) {
253 form
= NS_MATHML_OPERATOR_FORM_INFIX
;
254 len
-= 6; // 6 is the length of ".infix";
256 else if (kNotFound
!= name
.RFind(".postfix")) {
257 form
= NS_MATHML_OPERATOR_FORM_POSTFIX
;
258 len
-= 8; // 8 is the length of ".postfix";
260 else if (kNotFound
!= name
.RFind(".prefix")) {
261 form
= NS_MATHML_OPERATOR_FORM_PREFIX
;
262 len
-= 7; // 7 is the length of ".prefix";
264 else continue; // input is not applicable
266 if (2 == pass
) { // allocate space and start the storage
267 if (!gOperatorArray
) {
268 if (0 == gOperatorCount
) return NS_ERROR_UNEXPECTED
;
269 gOperatorArray
= new OperatorData
[gOperatorCount
];
270 if (!gOperatorArray
) return NS_ERROR_OUT_OF_MEMORY
;
272 operatorData
= &gOperatorArray
[index
];
275 form
= 0; // to quickly return from SetOperator() at pass 1
277 // See if the operator should be retained
278 if (SetOperator(operatorData
, form
, name
, attributes
)) {
280 if (1 == pass
) gOperatorCount
= index
;
294 gGlobalsInitialized
= true;
295 nsresult rv
= NS_ERROR_OUT_OF_MEMORY
;
296 gOperatorTable
= new nsDataHashtable
<nsStringHashKey
, OperatorData
*>();
297 if (gOperatorTable
) {
298 rv
= InitOperators();
301 nsMathMLOperators::CleanUp();
306 nsMathMLOperators::CleanUp()
308 if (gOperatorArray
) {
309 delete[] gOperatorArray
;
310 gOperatorArray
= nullptr;
312 if (gOperatorTable
) {
313 delete gOperatorTable
;
314 gOperatorTable
= nullptr;
319 nsMathMLOperators::AddRefTable(void)
325 nsMathMLOperators::ReleaseTable(void)
327 if (0 == --gTableRefCount
) {
333 GetOperatorData(const nsString
& aOperator
, nsOperatorFlags aForm
)
335 nsAutoString
key(aOperator
);
336 key
.AppendInt(aForm
);
337 return gOperatorTable
->Get(key
);
341 nsMathMLOperators::LookupOperator(const nsString
& aOperator
,
342 const nsOperatorFlags aForm
,
343 nsOperatorFlags
* aFlags
,
344 float* aLeadingSpace
,
345 float* aTrailingSpace
)
347 if (!gGlobalsInitialized
) {
350 if (gOperatorTable
) {
351 NS_ASSERTION(aFlags
&& aLeadingSpace
&& aTrailingSpace
, "bad usage");
352 NS_ASSERTION(aForm
> 0 && aForm
< 4, "*** invalid call ***");
354 // The MathML REC says:
355 // If the operator does not occur in the dictionary with the specified form,
356 // the renderer should use one of the forms which is available there, in the
357 // order of preference: infix, postfix, prefix.
360 int32_t form
= NS_MATHML_OPERATOR_GET_FORM(aForm
);
361 if (!(found
= GetOperatorData(aOperator
, form
))) {
362 if (form
== NS_MATHML_OPERATOR_FORM_INFIX
||
364 GetOperatorData(aOperator
, NS_MATHML_OPERATOR_FORM_INFIX
))) {
365 if (form
== NS_MATHML_OPERATOR_FORM_POSTFIX
||
367 GetOperatorData(aOperator
, NS_MATHML_OPERATOR_FORM_POSTFIX
))) {
368 if (form
!= NS_MATHML_OPERATOR_FORM_PREFIX
) {
369 found
= GetOperatorData(aOperator
, NS_MATHML_OPERATOR_FORM_PREFIX
);
375 NS_ASSERTION(found
->mStr
.Equals(aOperator
), "bad setup");
376 *aLeadingSpace
= found
->mLeadingSpace
;
377 *aTrailingSpace
= found
->mTrailingSpace
;
378 *aFlags
&= ~NS_MATHML_OPERATOR_FORM
; // clear the form bits
379 *aFlags
|= found
->mFlags
; // just add bits without overwriting
387 nsMathMLOperators::LookupOperators(const nsString
& aOperator
,
388 nsOperatorFlags
* aFlags
,
389 float* aLeadingSpace
,
390 float* aTrailingSpace
)
392 if (!gGlobalsInitialized
) {
396 aFlags
[NS_MATHML_OPERATOR_FORM_INFIX
] = 0;
397 aLeadingSpace
[NS_MATHML_OPERATOR_FORM_INFIX
] = 0.0f
;
398 aTrailingSpace
[NS_MATHML_OPERATOR_FORM_INFIX
] = 0.0f
;
400 aFlags
[NS_MATHML_OPERATOR_FORM_POSTFIX
] = 0;
401 aLeadingSpace
[NS_MATHML_OPERATOR_FORM_POSTFIX
] = 0.0f
;
402 aTrailingSpace
[NS_MATHML_OPERATOR_FORM_POSTFIX
] = 0.0f
;
404 aFlags
[NS_MATHML_OPERATOR_FORM_PREFIX
] = 0;
405 aLeadingSpace
[NS_MATHML_OPERATOR_FORM_PREFIX
] = 0.0f
;
406 aTrailingSpace
[NS_MATHML_OPERATOR_FORM_PREFIX
] = 0.0f
;
408 if (gOperatorTable
) {
410 found
= GetOperatorData(aOperator
, NS_MATHML_OPERATOR_FORM_INFIX
);
412 aFlags
[NS_MATHML_OPERATOR_FORM_INFIX
] = found
->mFlags
;
413 aLeadingSpace
[NS_MATHML_OPERATOR_FORM_INFIX
] = found
->mLeadingSpace
;
414 aTrailingSpace
[NS_MATHML_OPERATOR_FORM_INFIX
] = found
->mTrailingSpace
;
416 found
= GetOperatorData(aOperator
, NS_MATHML_OPERATOR_FORM_POSTFIX
);
418 aFlags
[NS_MATHML_OPERATOR_FORM_POSTFIX
] = found
->mFlags
;
419 aLeadingSpace
[NS_MATHML_OPERATOR_FORM_POSTFIX
] = found
->mLeadingSpace
;
420 aTrailingSpace
[NS_MATHML_OPERATOR_FORM_POSTFIX
] = found
->mTrailingSpace
;
422 found
= GetOperatorData(aOperator
, NS_MATHML_OPERATOR_FORM_PREFIX
);
424 aFlags
[NS_MATHML_OPERATOR_FORM_PREFIX
] = found
->mFlags
;
425 aLeadingSpace
[NS_MATHML_OPERATOR_FORM_PREFIX
] = found
->mLeadingSpace
;
426 aTrailingSpace
[NS_MATHML_OPERATOR_FORM_PREFIX
] = found
->mTrailingSpace
;
432 nsMathMLOperators::IsMirrorableOperator(const nsString
& aOperator
)
434 // LookupOperator will search infix, postfix and prefix forms of aOperator and
435 // return the first form found. It is assumed that all these forms have same
437 nsOperatorFlags flags
= 0;
439 nsMathMLOperators::LookupOperator(aOperator
,
440 NS_MATHML_OPERATOR_FORM_INFIX
,
441 &flags
, &dummy
, &dummy
);
442 return NS_MATHML_OPERATOR_IS_MIRRORABLE(flags
);
445 /* static */ nsStretchDirection
446 nsMathMLOperators::GetStretchyDirection(const nsString
& aOperator
)
448 // LookupOperator will search infix, postfix and prefix forms of aOperator and
449 // return the first form found. It is assumed that all these forms have same
451 nsOperatorFlags flags
= 0;
453 nsMathMLOperators::LookupOperator(aOperator
,
454 NS_MATHML_OPERATOR_FORM_INFIX
,
455 &flags
, &dummy
, &dummy
);
457 if (NS_MATHML_OPERATOR_IS_DIRECTION_VERTICAL(flags
)) {
458 return NS_STRETCH_DIRECTION_VERTICAL
;
459 } else if (NS_MATHML_OPERATOR_IS_DIRECTION_HORIZONTAL(flags
)) {
460 return NS_STRETCH_DIRECTION_HORIZONTAL
;
462 return NS_STRETCH_DIRECTION_UNSUPPORTED
;