bug 523370 - fixing bogus OOM with empty double free lists. r=dmandelin
[mozilla-central.git] / layout / mathml / nsMathMLOperators.cpp
blob14c65ce2be3d6b4658c6f597ae816e230b7dd5b1
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is Mozilla MathML Project.
17 * The Initial Developer of the Original Code is
18 * The University Of Queensland.
19 * Portions created by the Initial Developer are Copyright (C) 1999
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * Roger B. Sidje <rbs@maths.uq.edu.au>
24 * Karl Tomlinson <karlt+@karlt.net>, Mozilla Corporation
26 * Alternatively, the contents of this file may be used under the terms of
27 * either of the GNU General Public License Version 2 or later (the "GPL"),
28 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
40 #include "nsCOMPtr.h"
41 #include "nsString.h"
42 #include "nsHashtable.h"
43 #include "nsTArray.h"
45 #include "nsIComponentManager.h"
46 #include "nsIPersistentProperties2.h"
47 #include "nsNetUtil.h"
48 #include "nsCRT.h"
50 #include "nsMathMLOperators.h"
52 // operator dictionary entry
53 struct OperatorData {
54 OperatorData(void)
55 : mFlags(0),
56 mLeftSpace(0.0f),
57 mRightSpace(0.0f)
61 // member data
62 nsString mStr;
63 nsOperatorFlags mFlags;
64 float mLeftSpace; // unit is em
65 float mRightSpace; // unit is em
69 The MathML REC says:
70 "If the operator does not occur in the dictionary with the specified form,
71 the renderer should use one of the forms which is available there, in the
72 order of preference: infix, postfix, prefix."
74 The following variable will be used to keep track of all possible forms
75 encountered in the Operator Dictionary.
77 static OperatorData* gOperatorFound[4];
79 static PRInt32 gTableRefCount = 0;
80 static PRInt32 gOperatorCount = 0;
81 static OperatorData* gOperatorArray = nsnull;
82 static nsHashtable* gOperatorTable = nsnull;
83 static PRBool gInitialized = PR_FALSE;
84 static nsTArray<OperatorData*>* gStretchyOperatorArray = nsnull;
85 static nsTArray<nsString>* gInvariantCharArray = nsnull;
87 static const PRUnichar kNullCh = PRUnichar('\0');
88 static const PRUnichar kDashCh = PRUnichar('#');
89 static const PRUnichar kEqualCh = PRUnichar('=');
90 static const PRUnichar kColonCh = PRUnichar(':');
92 static const char* const kMathVariant_name[] = {
93 "normal",
94 "bold",
95 "italic",
96 "bold-italic",
97 "sans-serif",
98 "bold-sans-serif",
99 "sans-serif-italic",
100 "sans-serif-bold-italic",
101 "monospace",
102 "script",
103 "bold-script",
104 "fraktur",
105 "bold-fraktur",
106 "double-struck"
109 void
110 SetProperty(OperatorData* aOperatorData,
111 nsString aName,
112 nsString aValue)
114 if (!aName.Length() || !aValue.Length())
115 return;
117 // XXX These ones are not kept in the dictionary
118 // Support for these requires nsString member variables
119 // maxsize (default: infinity)
120 // minsize (default: 1)
122 if (aValue.EqualsLiteral("true")) {
123 // see if we should enable flags with default value=false
124 if (aName.EqualsLiteral("fence"))
125 aOperatorData->mFlags |= NS_MATHML_OPERATOR_FENCE;
126 else if (aName.EqualsLiteral("accent"))
127 aOperatorData->mFlags |= NS_MATHML_OPERATOR_ACCENT;
128 else if (aName.EqualsLiteral("largeop"))
129 aOperatorData->mFlags |= NS_MATHML_OPERATOR_LARGEOP;
130 else if (aName.EqualsLiteral("separator"))
131 aOperatorData->mFlags |= NS_MATHML_OPERATOR_SEPARATOR;
132 else if (aName.EqualsLiteral("movablelimits"))
133 aOperatorData->mFlags |= NS_MATHML_OPERATOR_MOVABLELIMITS;
135 else if (aValue.EqualsLiteral("false")) {
136 // see if we should disable flags with default value=true
137 if (aName.EqualsLiteral("symmetric"))
138 aOperatorData->mFlags &= ~NS_MATHML_OPERATOR_SYMMETRIC;
140 else if (aName.EqualsLiteral("stretchy") &&
141 (1 == aOperatorData->mStr.Length())) {
142 if (aValue.EqualsLiteral("vertical"))
143 aOperatorData->mFlags |= NS_MATHML_OPERATOR_STRETCHY_VERT;
144 else if (aValue.EqualsLiteral("horizontal"))
145 aOperatorData->mFlags |= NS_MATHML_OPERATOR_STRETCHY_HORIZ;
146 else return; // invalid value
147 if (kNotFound == nsMathMLOperators::FindStretchyOperator(aOperatorData->mStr[0])) {
148 gStretchyOperatorArray->AppendElement(aOperatorData);
151 else {
152 PRInt32 i = 0;
153 float space = 0.0f;
154 PRBool isLeftSpace;
155 if (aName.EqualsLiteral("lspace"))
156 isLeftSpace = PR_TRUE;
157 else if (aName.EqualsLiteral("rspace"))
158 isLeftSpace = PR_FALSE;
159 else return; // input is not applicable
161 // See if it is a numeric value (unit is assumed to be 'em')
162 if (nsCRT::IsAsciiDigit(aValue[0])) {
163 PRInt32 error = 0;
164 space = aValue.ToFloat(&error);
165 if (error) return;
167 // See if it is one of the 'namedspace' (ranging 1/18em...7/18em)
168 else if (aValue.EqualsLiteral("veryverythinmathspace")) i = 1;
169 else if (aValue.EqualsLiteral("verythinmathspace")) i = 2;
170 else if (aValue.EqualsLiteral("thinmathspace")) i = 3;
171 else if (aValue.EqualsLiteral("mediummathspace")) i = 4;
172 else if (aValue.EqualsLiteral("thickmathspace")) i = 5;
173 else if (aValue.EqualsLiteral("verythickmathspace")) i = 6;
174 else if (aValue.EqualsLiteral("veryverythickmathspace")) i = 7;
176 if (0 != i) // it was a namedspace value
177 space = float(i)/float(18);
179 if (isLeftSpace)
180 aOperatorData->mLeftSpace = space;
181 else
182 aOperatorData->mRightSpace = space;
186 PRBool
187 SetOperator(OperatorData* aOperatorData,
188 nsOperatorFlags aForm,
189 const nsCString& aOperator,
190 nsString& aAttributes)
193 // aOperator is in the expanded format \uNNNN\uNNNN ...
194 // First compress these Unicode points to the internal nsString format
195 PRInt32 i = 0;
196 nsAutoString name, value;
197 PRInt32 len = aOperator.Length();
198 PRUnichar c = aOperator[i++];
199 PRUint32 state = 0;
200 PRUnichar uchar = 0;
201 while (i <= len) {
202 if (0 == state) {
203 if (c != '\\')
204 return PR_FALSE;
205 if (i < len)
206 c = aOperator[i];
207 i++;
208 if (('u' != c) && ('U' != c))
209 return PR_FALSE;
210 if (i < len)
211 c = aOperator[i];
212 i++;
213 state++;
215 else {
216 if (('0' <= c) && (c <= '9'))
217 uchar = (uchar << 4) | (c - '0');
218 else if (('a' <= c) && (c <= 'f'))
219 uchar = (uchar << 4) | (c - 'a' + 0x0a);
220 else if (('A' <= c) && (c <= 'F'))
221 uchar = (uchar << 4) | (c - 'A' + 0x0a);
222 else return PR_FALSE;
223 if (i < len)
224 c = aOperator[i];
225 i++;
226 state++;
227 if (5 == state) {
228 value.Append(uchar);
229 uchar = 0;
230 state = 0;
234 if (0 != state) return PR_FALSE;
236 // Quick return when the caller doesn't care about the attributes and just wants
237 // to know if this is a valid operator (this is the case at the first pass of the
238 // parsing of the dictionary in InitOperators())
239 if (!aForm) return PR_TRUE;
241 // Add operator to hash table (symmetric="true" by default for all operators)
242 aOperatorData->mFlags |= aForm | NS_MATHML_OPERATOR_SYMMETRIC;
243 aOperatorData->mStr.Assign(value);
244 value.AppendInt(aForm, 10);
245 nsStringKey key(value);
246 gOperatorTable->Put(&key, aOperatorData);
248 #ifdef NS_DEBUG
249 NS_LossyConvertUTF16toASCII str(aAttributes);
250 #endif
251 // Loop over the space-delimited list of attributes to get the name:value pairs
252 aAttributes.Append(kNullCh); // put an extra null at the end
253 PRUnichar* start = aAttributes.BeginWriting();
254 PRUnichar* end = start;
255 while ((kNullCh != *start) && (kDashCh != *start)) {
256 name.SetLength(0);
257 value.SetLength(0);
258 // skip leading space, the dash amounts to the end of the line
259 while ((kNullCh!=*start) && (kDashCh!=*start) && nsCRT::IsAsciiSpace(*start)) {
260 ++start;
262 end = start;
263 // look for ':' or '='
264 while ((kNullCh!=*end) && (kDashCh!=*end) && (kColonCh!=*end) && (kEqualCh!=*end)) {
265 ++end;
267 if ((kColonCh!=*end) && (kEqualCh!=*end)) {
268 #ifdef NS_DEBUG
269 printf("Bad MathML operator: %s\n", str.get());
270 #endif
271 return PR_TRUE;
273 *end = kNullCh; // end segment here
274 // this segment is the name
275 if (start < end) {
276 name.Assign(start);
278 start = ++end;
279 // look for space or end of line
280 while ((kNullCh!=*end) && (kDashCh!=*start) && !nsCRT::IsAsciiSpace(*end)) {
281 ++end;
283 *end = kNullCh; // end segment here
284 // this segment is the value
285 if (start < end) {
286 value.Assign(start);
288 SetProperty(aOperatorData, name, value);
289 start = ++end;
291 return PR_TRUE;
294 nsresult
295 InitOperators(void)
297 // Load the property file containing the Operator Dictionary
298 nsresult rv;
299 nsCOMPtr<nsIPersistentProperties> mathfontProp;
300 rv = NS_LoadPersistentPropertiesFromURISpec(getter_AddRefs(mathfontProp),
301 NS_LITERAL_CSTRING("resource://gre/res/fonts/mathfont.properties"));
302 if (NS_FAILED(rv)) return rv;
304 // Get the list of invariant chars
305 for (PRInt32 i = 0; i < eMATHVARIANT_COUNT; ++i) {
306 nsCAutoString key(NS_LITERAL_CSTRING("mathvariant."));
307 key.Append(kMathVariant_name[i]);
308 nsAutoString value;
309 mathfontProp->GetStringProperty(key, value);
310 gInvariantCharArray->AppendElement(value); // i.e., gInvariantCharArray[i] holds this list
313 // Parse the Operator Dictionary in two passes.
314 // The first pass is to count the number of operators; the second pass is to
315 // allocate the necessary space for them and to add them in the hash table.
316 for (PRInt32 pass = 1; pass <= 2; pass++) {
317 OperatorData dummyData;
318 OperatorData* operatorData = &dummyData;
319 nsCOMPtr<nsISimpleEnumerator> iterator;
320 if (NS_SUCCEEDED(mathfontProp->Enumerate(getter_AddRefs(iterator)))) {
321 PRBool more;
322 PRInt32 index = 0;
323 nsCAutoString name;
324 nsAutoString attributes;
325 while ((NS_SUCCEEDED(iterator->HasMoreElements(&more))) && more) {
326 nsCOMPtr<nsIPropertyElement> element;
327 if (NS_SUCCEEDED(iterator->GetNext(getter_AddRefs(element)))) {
328 if (NS_SUCCEEDED(element->GetKey(name)) &&
329 NS_SUCCEEDED(element->GetValue(attributes))) {
330 // expected key: operator.\uNNNN.{infix,postfix,prefix}
331 if ((21 <= name.Length()) && (0 == name.Find("operator.\\u"))) {
332 name.Cut(0, 9); // 9 is the length of "operator.";
333 PRInt32 len = name.Length();
334 nsOperatorFlags form = 0;
335 if (kNotFound != name.RFind(".infix")) {
336 form = NS_MATHML_OPERATOR_FORM_INFIX;
337 len -= 6; // 6 is the length of ".infix";
339 else if (kNotFound != name.RFind(".postfix")) {
340 form = NS_MATHML_OPERATOR_FORM_POSTFIX;
341 len -= 8; // 8 is the length of ".postfix";
343 else if (kNotFound != name.RFind(".prefix")) {
344 form = NS_MATHML_OPERATOR_FORM_PREFIX;
345 len -= 7; // 7 is the length of ".prefix";
347 else continue; // input is not applicable
348 name.SetLength(len);
349 if (2 == pass) { // allocate space and start the storage
350 if (!gOperatorArray) {
351 if (0 == gOperatorCount) return NS_ERROR_UNEXPECTED;
352 gOperatorArray = new OperatorData[gOperatorCount];
353 if (!gOperatorArray) return NS_ERROR_OUT_OF_MEMORY;
355 operatorData = &gOperatorArray[index];
357 else {
358 form = 0; // to quickly return from SetOperator() at pass 1
360 // See if the operator should be retained
361 if (SetOperator(operatorData, form, name, attributes)) {
362 index++;
363 if (1 == pass) gOperatorCount = index;
371 return NS_OK;
374 nsresult
375 InitGlobals()
377 gInitialized = PR_TRUE;
378 nsresult rv = NS_ERROR_OUT_OF_MEMORY;
379 gInvariantCharArray = new nsTArray<nsString>();
380 gStretchyOperatorArray = new nsTArray<OperatorData*>();
381 if (gInvariantCharArray && gStretchyOperatorArray) {
382 gOperatorTable = new nsHashtable();
383 if (gOperatorTable) {
384 rv = InitOperators();
387 if (NS_FAILED(rv))
388 nsMathMLOperators::CleanUp();
389 return rv;
392 void
393 nsMathMLOperators::CleanUp()
395 if (gInvariantCharArray) {
396 delete gInvariantCharArray;
397 gInvariantCharArray = nsnull;
399 if (gOperatorArray) {
400 delete[] gOperatorArray;
401 gOperatorArray = nsnull;
403 if (gStretchyOperatorArray) {
404 delete gStretchyOperatorArray;
405 gStretchyOperatorArray = nsnull;
407 if (gOperatorTable) {
408 delete gOperatorTable;
409 gOperatorTable = nsnull;
413 void
414 nsMathMLOperators::AddRefTable(void)
416 gTableRefCount++;
419 void
420 nsMathMLOperators::ReleaseTable(void)
422 if (0 == --gTableRefCount) {
423 CleanUp();
427 PRBool
428 nsMathMLOperators::LookupOperator(const nsString& aOperator,
429 const nsOperatorFlags aForm,
430 nsOperatorFlags* aFlags,
431 float* aLeftSpace,
432 float* aRightSpace)
434 if (!gInitialized) {
435 InitGlobals();
437 if (gOperatorTable) {
438 NS_ASSERTION(aFlags && aLeftSpace && aRightSpace, "bad usage");
439 NS_ASSERTION(aForm>=0 && aForm<4, "*** invalid call ***");
441 OperatorData* found;
442 PRInt32 form = NS_MATHML_OPERATOR_GET_FORM(aForm);
443 gOperatorFound[NS_MATHML_OPERATOR_FORM_INFIX] = nsnull;
444 gOperatorFound[NS_MATHML_OPERATOR_FORM_POSTFIX] = nsnull;
445 gOperatorFound[NS_MATHML_OPERATOR_FORM_PREFIX] = nsnull;
447 nsAutoString key(aOperator);
448 key.AppendInt(form, 10);
449 nsStringKey hkey(key);
450 gOperatorFound[form] = found = (OperatorData*)gOperatorTable->Get(&hkey);
452 // If not found, check if the operator exists perhaps in a different form,
453 // in the order of preference: infix, postfix, prefix
454 if (!found) {
455 if (form != NS_MATHML_OPERATOR_FORM_INFIX) {
456 form = NS_MATHML_OPERATOR_FORM_INFIX;
457 key.Assign(aOperator);
458 key.AppendInt(form, 10);
459 nsStringKey hashkey(key);
460 gOperatorFound[form] = found = (OperatorData*)gOperatorTable->Get(&hashkey);
462 if (!found) {
463 if (form != NS_MATHML_OPERATOR_FORM_POSTFIX) {
464 form = NS_MATHML_OPERATOR_FORM_POSTFIX;
465 key.Assign(aOperator);
466 key.AppendInt(form, 10);
467 nsStringKey hashkey(key);
468 gOperatorFound[form] = found = (OperatorData*)gOperatorTable->Get(&hashkey);
470 if (!found) {
471 if (form != NS_MATHML_OPERATOR_FORM_PREFIX) {
472 form = NS_MATHML_OPERATOR_FORM_PREFIX;
473 key.Assign(aOperator);
474 key.AppendInt(form, 10);
475 nsStringKey hashkey(key);
476 gOperatorFound[form] = found = (OperatorData*)gOperatorTable->Get(&hashkey);
481 if (found) {
482 NS_ASSERTION(found->mStr.Equals(aOperator), "bad setup");
483 *aLeftSpace = found->mLeftSpace;
484 *aRightSpace = found->mRightSpace;
485 *aFlags &= ~NS_MATHML_OPERATOR_FORM; // clear the form bits
486 *aFlags |= found->mFlags; // just add bits without overwriting
487 return PR_TRUE;
490 return PR_FALSE;
493 void
494 nsMathMLOperators::LookupOperators(const nsString& aOperator,
495 nsOperatorFlags* aFlags,
496 float* aLeftSpace,
497 float* aRightSpace)
499 if (!gInitialized) {
500 InitGlobals();
503 aFlags[NS_MATHML_OPERATOR_FORM_INFIX] = 0;
504 aLeftSpace[NS_MATHML_OPERATOR_FORM_INFIX] = 0.0f;
505 aRightSpace[NS_MATHML_OPERATOR_FORM_INFIX] = 0.0f;
507 aFlags[NS_MATHML_OPERATOR_FORM_POSTFIX] = 0;
508 aLeftSpace[NS_MATHML_OPERATOR_FORM_POSTFIX] = 0.0f;
509 aRightSpace[NS_MATHML_OPERATOR_FORM_POSTFIX] = 0.0f;
511 aFlags[NS_MATHML_OPERATOR_FORM_PREFIX] = 0;
512 aLeftSpace[NS_MATHML_OPERATOR_FORM_PREFIX] = 0.0f;
513 aRightSpace[NS_MATHML_OPERATOR_FORM_PREFIX] = 0.0f;
515 if (gOperatorTable) {
516 // a lookup with form=0 will put all the variants in gOperatorFound[]
517 float dummy;
518 nsOperatorFlags flags = 0;
519 LookupOperator(aOperator, /*form=*/0, &flags, &dummy, &dummy);
520 // if the operator was found, gOperatorFound contains all its variants
521 OperatorData* found;
522 found = gOperatorFound[NS_MATHML_OPERATOR_FORM_INFIX];
523 if (found) {
524 aFlags[NS_MATHML_OPERATOR_FORM_INFIX] = found->mFlags;
525 aLeftSpace[NS_MATHML_OPERATOR_FORM_INFIX] = found->mLeftSpace;
526 aRightSpace[NS_MATHML_OPERATOR_FORM_INFIX] = found->mRightSpace;
528 found = gOperatorFound[NS_MATHML_OPERATOR_FORM_POSTFIX];
529 if (found) {
530 aFlags[NS_MATHML_OPERATOR_FORM_POSTFIX] = found->mFlags;
531 aLeftSpace[NS_MATHML_OPERATOR_FORM_POSTFIX] = found->mLeftSpace;
532 aRightSpace[NS_MATHML_OPERATOR_FORM_POSTFIX] = found->mRightSpace;
534 found = gOperatorFound[NS_MATHML_OPERATOR_FORM_PREFIX];
535 if (found) {
536 aFlags[NS_MATHML_OPERATOR_FORM_PREFIX] = found->mFlags;
537 aLeftSpace[NS_MATHML_OPERATOR_FORM_PREFIX] = found->mLeftSpace;
538 aRightSpace[NS_MATHML_OPERATOR_FORM_PREFIX] = found->mRightSpace;
543 PRBool
544 nsMathMLOperators::IsMutableOperator(const nsString& aOperator)
546 if (!gInitialized) {
547 InitGlobals();
549 // lookup all the variants of the operator and return true if there
550 // is a variant that is stretchy or largeop
551 nsOperatorFlags flags[4];
552 float lspace[4], rspace[4];
553 nsMathMLOperators::LookupOperators(aOperator, flags, lspace, rspace);
554 nsOperatorFlags allFlags =
555 flags[NS_MATHML_OPERATOR_FORM_INFIX] |
556 flags[NS_MATHML_OPERATOR_FORM_POSTFIX] |
557 flags[NS_MATHML_OPERATOR_FORM_PREFIX];
558 return NS_MATHML_OPERATOR_IS_STRETCHY(allFlags) ||
559 NS_MATHML_OPERATOR_IS_LARGEOP(allFlags);
562 PRInt32
563 nsMathMLOperators::CountStretchyOperator()
565 if (!gInitialized) {
566 InitGlobals();
568 return (gStretchyOperatorArray) ? gStretchyOperatorArray->Length() : 0;
571 PRInt32
572 nsMathMLOperators::FindStretchyOperator(PRUnichar aOperator)
574 if (!gInitialized) {
575 InitGlobals();
577 if (gStretchyOperatorArray) {
578 for (PRUint32 k = 0; k < gStretchyOperatorArray->Length(); k++) {
579 OperatorData* data = gStretchyOperatorArray->ElementAt(k);
580 if (data && (aOperator == data->mStr[0])) {
581 return k;
585 return kNotFound;
588 nsStretchDirection
589 nsMathMLOperators::GetStretchyDirectionAt(PRInt32 aIndex)
591 NS_ASSERTION(gStretchyOperatorArray, "invalid call");
592 if (gStretchyOperatorArray) {
593 NS_ASSERTION(aIndex < PRInt32(gStretchyOperatorArray->Length()),
594 "invalid call");
595 OperatorData* data = gStretchyOperatorArray->ElementAt(aIndex);
596 if (data) {
597 if (NS_MATHML_OPERATOR_IS_STRETCHY_VERT(data->mFlags))
598 return NS_STRETCH_DIRECTION_VERTICAL;
599 else if (NS_MATHML_OPERATOR_IS_STRETCHY_HORIZ(data->mFlags))
600 return NS_STRETCH_DIRECTION_HORIZONTAL;
601 NS_ASSERTION(PR_FALSE, "*** bad setup ***");
604 return NS_STRETCH_DIRECTION_UNSUPPORTED;
607 void
608 nsMathMLOperators::DisableStretchyOperatorAt(PRInt32 aIndex)
610 NS_ASSERTION(gStretchyOperatorArray, "invalid call");
611 if (gStretchyOperatorArray) {
612 NS_ASSERTION(aIndex < PRInt32(gStretchyOperatorArray->Length()),
613 "invalid call");
614 (*gStretchyOperatorArray)[aIndex] = nsnull;
618 /* static */ eMATHVARIANT
619 nsMathMLOperators::LookupInvariantChar(const nsAString& aChar)
621 if (!gInitialized) {
622 InitGlobals();
624 if (gInvariantCharArray) {
625 for (PRInt32 i = gInvariantCharArray->Length()-1; i >= 0; --i) {
626 const nsString& list = gInvariantCharArray->ElementAt(i);
627 nsString::const_iterator start, end;
628 list.BeginReading(start);
629 list.EndReading(end);
630 // Style-invariant characters are at offset 3*j + 1.
631 if (FindInReadable(aChar, start, end) &&
632 start.size_backward() % 3 == 1) {
633 return eMATHVARIANT(i);
637 return eMATHVARIANT_NONE;
640 /* static */ const nsDependentSubstring
641 nsMathMLOperators::TransformVariantChar(const PRUnichar& aChar,
642 eMATHVARIANT aVariant)
644 if (!gInitialized) {
645 InitGlobals();
647 if (gInvariantCharArray) {
648 nsString list = gInvariantCharArray->ElementAt(aVariant);
649 PRInt32 index = list.FindChar(aChar);
650 // BMP characters are at offset 3*j
651 if (index != kNotFound && index % 3 == 0 && list.Length() - index >= 2 ) {
652 // The style-invariant character is the next character
653 // (and list should contain padding if the next character is in the BMP).
654 ++index;
655 PRUint32 len = NS_IS_HIGH_SURROGATE(list.CharAt(index)) ? 2 : 1;
656 return nsDependentSubstring(list, index, len);
659 return nsDependentSubstring(&aChar, &aChar + 1);