Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : #include "nsMathMLOperators.h"
7 : #include "nsCOMPtr.h"
8 : #include "nsDataHashtable.h"
9 : #include "nsHashKeys.h"
10 : #include "nsTArray.h"
11 :
12 : #include "nsIPersistentProperties2.h"
13 : #include "nsISimpleEnumerator.h"
14 : #include "nsContentUtils.h"
15 : #include "nsCRT.h"
16 :
17 : // operator dictionary entry
18 0 : struct OperatorData {
19 0 : OperatorData(void)
20 0 : : mFlags(0),
21 : mLeadingSpace(0.0f),
22 0 : mTrailingSpace(0.0f)
23 : {
24 0 : }
25 :
26 : // member data
27 : nsString mStr;
28 : nsOperatorFlags mFlags;
29 : float mLeadingSpace; // unit is em
30 : float mTrailingSpace; // unit is em
31 : };
32 :
33 : static int32_t gTableRefCount = 0;
34 : static uint32_t gOperatorCount = 0;
35 : static OperatorData* gOperatorArray = nullptr;
36 : static nsDataHashtable<nsStringHashKey, OperatorData*>* gOperatorTable = nullptr;
37 : static bool gGlobalsInitialized = false;
38 :
39 : static const char16_t kDashCh = char16_t('#');
40 : static const char16_t kColonCh = char16_t(':');
41 :
42 : static void
43 0 : SetBooleanProperty(OperatorData* aOperatorData,
44 : nsString aName)
45 : {
46 0 : if (aName.IsEmpty())
47 0 : return;
48 :
49 0 : if (aName.EqualsLiteral("stretchy") && (1 == aOperatorData->mStr.Length()))
50 0 : aOperatorData->mFlags |= NS_MATHML_OPERATOR_STRETCHY;
51 0 : else if (aName.EqualsLiteral("fence"))
52 0 : aOperatorData->mFlags |= NS_MATHML_OPERATOR_FENCE;
53 0 : else if (aName.EqualsLiteral("accent"))
54 0 : aOperatorData->mFlags |= NS_MATHML_OPERATOR_ACCENT;
55 0 : else if (aName.EqualsLiteral("largeop"))
56 0 : aOperatorData->mFlags |= NS_MATHML_OPERATOR_LARGEOP;
57 0 : else if (aName.EqualsLiteral("separator"))
58 0 : aOperatorData->mFlags |= NS_MATHML_OPERATOR_SEPARATOR;
59 0 : else if (aName.EqualsLiteral("movablelimits"))
60 0 : aOperatorData->mFlags |= NS_MATHML_OPERATOR_MOVABLELIMITS;
61 0 : else if (aName.EqualsLiteral("symmetric"))
62 0 : aOperatorData->mFlags |= NS_MATHML_OPERATOR_SYMMETRIC;
63 0 : else if (aName.EqualsLiteral("integral"))
64 0 : aOperatorData->mFlags |= NS_MATHML_OPERATOR_INTEGRAL;
65 0 : else if (aName.EqualsLiteral("mirrorable"))
66 0 : aOperatorData->mFlags |= NS_MATHML_OPERATOR_MIRRORABLE;
67 : }
68 :
69 : static void
70 0 : SetProperty(OperatorData* aOperatorData,
71 : nsString aName,
72 : nsString aValue)
73 : {
74 0 : if (aName.IsEmpty() || aValue.IsEmpty())
75 0 : return;
76 :
77 : // XXX These ones are not kept in the dictionary
78 : // Support for these requires nsString member variables
79 : // maxsize (default: infinity)
80 : // minsize (default: 1)
81 :
82 0 : if (aName.EqualsLiteral("direction")) {
83 0 : if (aValue.EqualsLiteral("vertical"))
84 0 : aOperatorData->mFlags |= NS_MATHML_OPERATOR_DIRECTION_VERTICAL;
85 0 : else if (aValue.EqualsLiteral("horizontal"))
86 0 : aOperatorData->mFlags |= NS_MATHML_OPERATOR_DIRECTION_HORIZONTAL;
87 0 : else return; // invalid value
88 : } else {
89 : bool isLeadingSpace;
90 0 : if (aName.EqualsLiteral("lspace"))
91 0 : isLeadingSpace = true;
92 0 : else if (aName.EqualsLiteral("rspace"))
93 0 : isLeadingSpace = false;
94 0 : else return; // input is not applicable
95 :
96 : // aValue is assumed to be a digit from 0 to 7
97 0 : nsresult error = NS_OK;
98 0 : float space = aValue.ToFloat(&error) / 18.0;
99 0 : if (NS_FAILED(error)) return;
100 :
101 0 : if (isLeadingSpace)
102 0 : aOperatorData->mLeadingSpace = space;
103 : else
104 0 : aOperatorData->mTrailingSpace = space;
105 : }
106 : }
107 :
108 : static bool
109 0 : SetOperator(OperatorData* aOperatorData,
110 : nsOperatorFlags aForm,
111 : const nsCString& aOperator,
112 : nsString& aAttributes)
113 :
114 : {
115 : static const char16_t kNullCh = char16_t('\0');
116 :
117 : // aOperator is in the expanded format \uNNNN\uNNNN ...
118 : // First compress these Unicode points to the internal nsString format
119 0 : int32_t i = 0;
120 0 : nsAutoString name, value;
121 0 : int32_t len = aOperator.Length();
122 0 : char16_t c = aOperator[i++];
123 0 : uint32_t state = 0;
124 0 : char16_t uchar = 0;
125 0 : while (i <= len) {
126 0 : if (0 == state) {
127 0 : if (c != '\\')
128 0 : return false;
129 0 : if (i < len)
130 0 : c = aOperator[i];
131 0 : i++;
132 0 : if (('u' != c) && ('U' != c))
133 0 : return false;
134 0 : if (i < len)
135 0 : c = aOperator[i];
136 0 : i++;
137 0 : state++;
138 : }
139 : else {
140 0 : if (('0' <= c) && (c <= '9'))
141 0 : uchar = (uchar << 4) | (c - '0');
142 0 : else if (('a' <= c) && (c <= 'f'))
143 0 : uchar = (uchar << 4) | (c - 'a' + 0x0a);
144 0 : else if (('A' <= c) && (c <= 'F'))
145 0 : uchar = (uchar << 4) | (c - 'A' + 0x0a);
146 0 : else return false;
147 0 : if (i < len)
148 0 : c = aOperator[i];
149 0 : i++;
150 0 : state++;
151 0 : if (5 == state) {
152 0 : value.Append(uchar);
153 0 : uchar = 0;
154 0 : state = 0;
155 : }
156 : }
157 : }
158 0 : if (0 != state) return false;
159 :
160 : // Quick return when the caller doesn't care about the attributes and just wants
161 : // to know if this is a valid operator (this is the case at the first pass of the
162 : // parsing of the dictionary in InitOperators())
163 0 : if (!aForm) return true;
164 :
165 : // Add operator to hash table
166 0 : aOperatorData->mFlags |= aForm;
167 0 : aOperatorData->mStr.Assign(value);
168 0 : value.AppendInt(aForm, 10);
169 0 : gOperatorTable->Put(value, aOperatorData);
170 :
171 : #ifdef DEBUG
172 0 : NS_LossyConvertUTF16toASCII str(aAttributes);
173 : #endif
174 : // Loop over the space-delimited list of attributes to get the name:value pairs
175 0 : aAttributes.Append(kNullCh); // put an extra null at the end
176 0 : char16_t* start = aAttributes.BeginWriting();
177 0 : char16_t* end = start;
178 0 : while ((kNullCh != *start) && (kDashCh != *start)) {
179 0 : name.SetLength(0);
180 0 : value.SetLength(0);
181 : // skip leading space, the dash amounts to the end of the line
182 0 : while ((kNullCh!=*start) && (kDashCh!=*start) && nsCRT::IsAsciiSpace(*start)) {
183 0 : ++start;
184 : }
185 0 : end = start;
186 : // look for ':'
187 0 : while ((kNullCh!=*end) && (kDashCh!=*end) && !nsCRT::IsAsciiSpace(*end) &&
188 0 : (kColonCh!=*end)) {
189 0 : ++end;
190 : }
191 : // If ':' is not found, then it's a boolean property
192 0 : bool IsBooleanProperty = (kColonCh != *end);
193 0 : *end = kNullCh; // end segment here
194 : // this segment is the name
195 0 : if (start < end) {
196 0 : name.Assign(start);
197 : }
198 0 : if (IsBooleanProperty) {
199 0 : SetBooleanProperty(aOperatorData, name);
200 : } else {
201 0 : start = ++end;
202 : // look for space or end of line
203 0 : while ((kNullCh!=*end) && (kDashCh!=*end) &&
204 0 : !nsCRT::IsAsciiSpace(*end)) {
205 0 : ++end;
206 : }
207 0 : *end = kNullCh; // end segment here
208 0 : if (start < end) {
209 : // this segment is the value
210 0 : value.Assign(start);
211 : }
212 0 : SetProperty(aOperatorData, name, value);
213 : }
214 0 : start = ++end;
215 : }
216 0 : return true;
217 : }
218 :
219 : static nsresult
220 0 : InitOperators(void)
221 : {
222 : // Load the property file containing the Operator Dictionary
223 : nsresult rv;
224 0 : nsCOMPtr<nsIPersistentProperties> mathfontProp;
225 0 : rv = NS_LoadPersistentPropertiesFromURISpec(
226 0 : getter_AddRefs(mathfontProp),
227 0 : NS_LITERAL_CSTRING("resource://gre/res/fonts/mathfont.properties"));
228 :
229 0 : if (NS_FAILED(rv)) return rv;
230 :
231 : // Parse the Operator Dictionary in two passes.
232 : // The first pass is to count the number of operators; the second pass is to
233 : // allocate the necessary space for them and to add them in the hash table.
234 0 : for (int32_t pass = 1; pass <= 2; pass++) {
235 0 : OperatorData dummyData;
236 0 : OperatorData* operatorData = &dummyData;
237 0 : nsCOMPtr<nsISimpleEnumerator> iterator;
238 0 : if (NS_SUCCEEDED(mathfontProp->Enumerate(getter_AddRefs(iterator)))) {
239 : bool more;
240 0 : uint32_t index = 0;
241 0 : nsAutoCString name;
242 0 : nsAutoString attributes;
243 0 : while ((NS_SUCCEEDED(iterator->HasMoreElements(&more))) && more) {
244 0 : nsCOMPtr<nsISupports> supports;
245 0 : nsCOMPtr<nsIPropertyElement> element;
246 0 : if (NS_SUCCEEDED(iterator->GetNext(getter_AddRefs(supports)))) {
247 0 : element = do_QueryInterface(supports);
248 0 : if (NS_SUCCEEDED(element->GetKey(name)) &&
249 0 : NS_SUCCEEDED(element->GetValue(attributes))) {
250 : // expected key: operator.\uNNNN.{infix,postfix,prefix}
251 0 : if ((21 <= name.Length()) && (0 == name.Find("operator.\\u"))) {
252 0 : name.Cut(0, 9); // 9 is the length of "operator.";
253 0 : int32_t len = name.Length();
254 0 : nsOperatorFlags form = 0;
255 0 : if (kNotFound != name.RFind(".infix")) {
256 0 : form = NS_MATHML_OPERATOR_FORM_INFIX;
257 0 : len -= 6; // 6 is the length of ".infix";
258 : }
259 0 : else if (kNotFound != name.RFind(".postfix")) {
260 0 : form = NS_MATHML_OPERATOR_FORM_POSTFIX;
261 0 : len -= 8; // 8 is the length of ".postfix";
262 : }
263 0 : else if (kNotFound != name.RFind(".prefix")) {
264 0 : form = NS_MATHML_OPERATOR_FORM_PREFIX;
265 0 : len -= 7; // 7 is the length of ".prefix";
266 : }
267 0 : else continue; // input is not applicable
268 0 : name.SetLength(len);
269 0 : if (2 == pass) { // allocate space and start the storage
270 0 : if (!gOperatorArray) {
271 0 : if (0 == gOperatorCount) return NS_ERROR_UNEXPECTED;
272 0 : gOperatorArray = new OperatorData[gOperatorCount];
273 0 : if (!gOperatorArray) return NS_ERROR_OUT_OF_MEMORY;
274 : }
275 0 : operatorData = &gOperatorArray[index];
276 : }
277 : else {
278 0 : form = 0; // to quickly return from SetOperator() at pass 1
279 : }
280 : // See if the operator should be retained
281 0 : if (SetOperator(operatorData, form, name, attributes)) {
282 0 : index++;
283 0 : if (1 == pass) gOperatorCount = index;
284 : }
285 : }
286 : }
287 : }
288 : }
289 : }
290 : }
291 0 : return NS_OK;
292 : }
293 :
294 : static nsresult
295 0 : InitOperatorGlobals()
296 : {
297 0 : gGlobalsInitialized = true;
298 0 : nsresult rv = NS_ERROR_OUT_OF_MEMORY;
299 0 : gOperatorTable = new nsDataHashtable<nsStringHashKey, OperatorData*>();
300 0 : if (gOperatorTable) {
301 0 : rv = InitOperators();
302 : }
303 0 : if (NS_FAILED(rv))
304 0 : nsMathMLOperators::CleanUp();
305 0 : return rv;
306 : }
307 :
308 : void
309 0 : nsMathMLOperators::CleanUp()
310 : {
311 0 : if (gOperatorArray) {
312 0 : delete[] gOperatorArray;
313 0 : gOperatorArray = nullptr;
314 : }
315 0 : if (gOperatorTable) {
316 0 : delete gOperatorTable;
317 0 : gOperatorTable = nullptr;
318 : }
319 0 : }
320 :
321 : void
322 3 : nsMathMLOperators::AddRefTable(void)
323 : {
324 3 : gTableRefCount++;
325 3 : }
326 :
327 : void
328 0 : nsMathMLOperators::ReleaseTable(void)
329 : {
330 0 : if (0 == --gTableRefCount) {
331 0 : CleanUp();
332 : }
333 0 : }
334 :
335 : static OperatorData*
336 0 : GetOperatorData(const nsString& aOperator, nsOperatorFlags aForm)
337 : {
338 0 : nsAutoString key(aOperator);
339 0 : key.AppendInt(aForm);
340 0 : return gOperatorTable->Get(key);
341 : }
342 :
343 : bool
344 0 : nsMathMLOperators::LookupOperator(const nsString& aOperator,
345 : const nsOperatorFlags aForm,
346 : nsOperatorFlags* aFlags,
347 : float* aLeadingSpace,
348 : float* aTrailingSpace)
349 : {
350 0 : if (!gGlobalsInitialized) {
351 0 : InitOperatorGlobals();
352 : }
353 0 : if (gOperatorTable) {
354 0 : NS_ASSERTION(aFlags && aLeadingSpace && aTrailingSpace, "bad usage");
355 0 : NS_ASSERTION(aForm > 0 && aForm < 4, "*** invalid call ***");
356 :
357 : // The MathML REC says:
358 : // If the operator does not occur in the dictionary with the specified form,
359 : // the renderer should use one of the forms which is available there, in the
360 : // order of preference: infix, postfix, prefix.
361 :
362 : OperatorData* found;
363 0 : int32_t form = NS_MATHML_OPERATOR_GET_FORM(aForm);
364 0 : if (!(found = GetOperatorData(aOperator, form))) {
365 0 : if (form == NS_MATHML_OPERATOR_FORM_INFIX ||
366 : !(found =
367 : GetOperatorData(aOperator, NS_MATHML_OPERATOR_FORM_INFIX))) {
368 0 : if (form == NS_MATHML_OPERATOR_FORM_POSTFIX ||
369 : !(found =
370 : GetOperatorData(aOperator, NS_MATHML_OPERATOR_FORM_POSTFIX))) {
371 0 : if (form != NS_MATHML_OPERATOR_FORM_PREFIX) {
372 0 : found = GetOperatorData(aOperator, NS_MATHML_OPERATOR_FORM_PREFIX);
373 : }
374 : }
375 : }
376 : }
377 0 : if (found) {
378 0 : NS_ASSERTION(found->mStr.Equals(aOperator), "bad setup");
379 0 : *aLeadingSpace = found->mLeadingSpace;
380 0 : *aTrailingSpace = found->mTrailingSpace;
381 0 : *aFlags &= ~NS_MATHML_OPERATOR_FORM; // clear the form bits
382 0 : *aFlags |= found->mFlags; // just add bits without overwriting
383 0 : return true;
384 : }
385 : }
386 0 : return false;
387 : }
388 :
389 : void
390 0 : nsMathMLOperators::LookupOperators(const nsString& aOperator,
391 : nsOperatorFlags* aFlags,
392 : float* aLeadingSpace,
393 : float* aTrailingSpace)
394 : {
395 0 : if (!gGlobalsInitialized) {
396 0 : InitOperatorGlobals();
397 : }
398 :
399 0 : aFlags[NS_MATHML_OPERATOR_FORM_INFIX] = 0;
400 0 : aLeadingSpace[NS_MATHML_OPERATOR_FORM_INFIX] = 0.0f;
401 0 : aTrailingSpace[NS_MATHML_OPERATOR_FORM_INFIX] = 0.0f;
402 :
403 0 : aFlags[NS_MATHML_OPERATOR_FORM_POSTFIX] = 0;
404 0 : aLeadingSpace[NS_MATHML_OPERATOR_FORM_POSTFIX] = 0.0f;
405 0 : aTrailingSpace[NS_MATHML_OPERATOR_FORM_POSTFIX] = 0.0f;
406 :
407 0 : aFlags[NS_MATHML_OPERATOR_FORM_PREFIX] = 0;
408 0 : aLeadingSpace[NS_MATHML_OPERATOR_FORM_PREFIX] = 0.0f;
409 0 : aTrailingSpace[NS_MATHML_OPERATOR_FORM_PREFIX] = 0.0f;
410 :
411 0 : if (gOperatorTable) {
412 : OperatorData* found;
413 0 : found = GetOperatorData(aOperator, NS_MATHML_OPERATOR_FORM_INFIX);
414 0 : if (found) {
415 0 : aFlags[NS_MATHML_OPERATOR_FORM_INFIX] = found->mFlags;
416 0 : aLeadingSpace[NS_MATHML_OPERATOR_FORM_INFIX] = found->mLeadingSpace;
417 0 : aTrailingSpace[NS_MATHML_OPERATOR_FORM_INFIX] = found->mTrailingSpace;
418 : }
419 0 : found = GetOperatorData(aOperator, NS_MATHML_OPERATOR_FORM_POSTFIX);
420 0 : if (found) {
421 0 : aFlags[NS_MATHML_OPERATOR_FORM_POSTFIX] = found->mFlags;
422 0 : aLeadingSpace[NS_MATHML_OPERATOR_FORM_POSTFIX] = found->mLeadingSpace;
423 0 : aTrailingSpace[NS_MATHML_OPERATOR_FORM_POSTFIX] = found->mTrailingSpace;
424 : }
425 0 : found = GetOperatorData(aOperator, NS_MATHML_OPERATOR_FORM_PREFIX);
426 0 : if (found) {
427 0 : aFlags[NS_MATHML_OPERATOR_FORM_PREFIX] = found->mFlags;
428 0 : aLeadingSpace[NS_MATHML_OPERATOR_FORM_PREFIX] = found->mLeadingSpace;
429 0 : aTrailingSpace[NS_MATHML_OPERATOR_FORM_PREFIX] = found->mTrailingSpace;
430 : }
431 : }
432 0 : }
433 :
434 : /* static */ bool
435 0 : nsMathMLOperators::IsMirrorableOperator(const nsString& aOperator)
436 : {
437 : // LookupOperator will search infix, postfix and prefix forms of aOperator and
438 : // return the first form found. It is assumed that all these forms have same
439 : // mirrorability.
440 0 : nsOperatorFlags flags = 0;
441 : float dummy;
442 : nsMathMLOperators::LookupOperator(aOperator,
443 : NS_MATHML_OPERATOR_FORM_INFIX,
444 0 : &flags, &dummy, &dummy);
445 0 : return NS_MATHML_OPERATOR_IS_MIRRORABLE(flags);
446 : }
447 :
448 : /* static */ nsStretchDirection
449 0 : nsMathMLOperators::GetStretchyDirection(const nsString& aOperator)
450 : {
451 : // LookupOperator will search infix, postfix and prefix forms of aOperator and
452 : // return the first form found. It is assumed that all these forms have same
453 : // direction.
454 0 : nsOperatorFlags flags = 0;
455 : float dummy;
456 : nsMathMLOperators::LookupOperator(aOperator,
457 : NS_MATHML_OPERATOR_FORM_INFIX,
458 0 : &flags, &dummy, &dummy);
459 :
460 0 : if (NS_MATHML_OPERATOR_IS_DIRECTION_VERTICAL(flags)) {
461 0 : return NS_STRETCH_DIRECTION_VERTICAL;
462 0 : } else if (NS_MATHML_OPERATOR_IS_DIRECTION_HORIZONTAL(flags)) {
463 0 : return NS_STRETCH_DIRECTION_HORIZONTAL;
464 : } else {
465 0 : return NS_STRETCH_DIRECTION_UNSUPPORTED;
466 : }
467 : }
|