Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/layout/mathml/nsMathMLOperators.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "nsMathMLOperators.h"
8
#include "nsCOMPtr.h"
9
#include "nsDataHashtable.h"
10
#include "nsHashKeys.h"
11
#include "nsTArray.h"
12
13
#include "nsIPersistentProperties2.h"
14
#include "nsISimpleEnumerator.h"
15
#include "nsCRT.h"
16
17
// operator dictionary entry
18
struct OperatorData {
19
  OperatorData(void)
20
    : mFlags(0),
21
      mLeadingSpace(0.0f),
22
      mTrailingSpace(0.0f)
23
0
  {
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
SetBooleanProperty(OperatorData* aOperatorData,
44
                   nsString      aName)
45
0
{
46
0
  if (aName.IsEmpty())
47
0
    return;
48
0
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
0
}
68
69
static void
70
SetProperty(OperatorData* aOperatorData,
71
            nsString      aName,
72
            nsString      aValue)
73
0
{
74
0
  if (aName.IsEmpty() || aValue.IsEmpty())
75
0
    return;
76
0
77
0
  // XXX These ones are not kept in the dictionary
78
0
  // Support for these requires nsString member variables
79
0
  // maxsize (default: infinity)
80
0
  // minsize (default: 1)
81
0
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
0
  } else {
89
0
    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
0
96
0
    // 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
0
101
0
    if (isLeadingSpace)
102
0
      aOperatorData->mLeadingSpace = space;
103
0
    else
104
0
      aOperatorData->mTrailingSpace = space;
105
0
  }
106
0
}
107
108
static bool
109
SetOperator(OperatorData*   aOperatorData,
110
            nsOperatorFlags aForm,
111
            const nsCString& aOperator,
112
            nsString&        aAttributes)
113
114
0
{
115
0
  static const char16_t kNullCh = char16_t('\0');
116
0
117
0
  // aOperator is in the expanded format \uNNNN\uNNNN ...
118
0
  // 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
0
    }
139
0
    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
0
      }
156
0
    }
157
0
  }
158
0
  if (0 != state) return false;
159
0
160
0
  // Quick return when the caller doesn't care about the attributes and just wants
161
0
  // to know if this is a valid operator (this is the case at the first pass of the
162
0
  // parsing of the dictionary in InitOperators())
163
0
  if (!aForm) return true;
164
0
165
0
  // 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
0
171
#ifdef DEBUG
172
  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
0
    // 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
0
    }
185
0
    end = start;
186
0
    // look for ':'
187
0
    while ((kNullCh!=*end) && (kDashCh!=*end) && !nsCRT::IsAsciiSpace(*end) &&
188
0
           (kColonCh!=*end)) {
189
0
      ++end;
190
0
    }
191
0
    // If ':' is not found, then it's a boolean property
192
0
    bool IsBooleanProperty = (kColonCh != *end);
193
0
    *end = kNullCh; // end segment here
194
0
    // this segment is the name
195
0
    if (start < end) {
196
0
      name.Assign(start);
197
0
    }
198
0
    if (IsBooleanProperty) {
199
0
      SetBooleanProperty(aOperatorData, name);
200
0
    } else {
201
0
      start = ++end;
202
0
      // look for space or end of line
203
0
      while ((kNullCh!=*end) && (kDashCh!=*end) &&
204
0
             !nsCRT::IsAsciiSpace(*end)) {
205
0
        ++end;
206
0
      }
207
0
      *end = kNullCh; // end segment here
208
0
      if (start < end) {
209
0
        // this segment is the value
210
0
        value.Assign(start);
211
0
      }
212
0
      SetProperty(aOperatorData, name, value);
213
0
    }
214
0
    start = ++end;
215
0
  }
216
0
  return true;
217
0
}
218
219
static nsresult
220
InitOperators(void)
221
0
{
222
0
  // Load the property file containing the Operator Dictionary
223
0
  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
0
229
0
  if (NS_FAILED(rv)) return rv;
230
0
231
0
  // Parse the Operator Dictionary in two passes.
232
0
  // The first pass is to count the number of operators; the second pass is to
233
0
  // 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
0
      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
0
            // 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
0
              }
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
0
              }
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
0
              }
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
0
                }
275
0
                operatorData = &gOperatorArray[index];
276
0
              }
277
0
              else {
278
0
                form = 0; // to quickly return from SetOperator() at pass 1
279
0
              }
280
0
              // 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
0
              }
285
0
            }
286
0
          }
287
0
        }
288
0
      }
289
0
    }
290
0
  }
291
0
  return NS_OK;
292
0
}
293
294
static nsresult
295
InitOperatorGlobals()
296
0
{
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
0
  }
303
0
  if (NS_FAILED(rv))
304
0
    nsMathMLOperators::CleanUp();
305
0
  return rv;
306
0
}
307
308
void
309
nsMathMLOperators::CleanUp()
310
0
{
311
0
  if (gOperatorArray) {
312
0
    delete[] gOperatorArray;
313
0
    gOperatorArray = nullptr;
314
0
  }
315
0
  if (gOperatorTable) {
316
0
    delete gOperatorTable;
317
0
    gOperatorTable = nullptr;
318
0
  }
319
0
}
320
321
void
322
nsMathMLOperators::AddRefTable(void)
323
3
{
324
3
  gTableRefCount++;
325
3
}
326
327
void
328
nsMathMLOperators::ReleaseTable(void)
329
0
{
330
0
  if (0 == --gTableRefCount) {
331
0
    CleanUp();
332
0
  }
333
0
}
334
335
static OperatorData*
336
GetOperatorData(const nsString& aOperator, nsOperatorFlags aForm)
337
0
{
338
0
  nsAutoString key(aOperator);
339
0
  key.AppendInt(aForm);
340
0
  return gOperatorTable->Get(key);
341
0
}
342
343
bool
344
nsMathMLOperators::LookupOperator(const nsString&       aOperator,
345
                                  const nsOperatorFlags aForm,
346
                                  nsOperatorFlags*      aFlags,
347
                                  float*                aLeadingSpace,
348
                                  float*                aTrailingSpace)
349
0
{
350
0
  if (!gGlobalsInitialized) {
351
0
    InitOperatorGlobals();
352
0
  }
353
0
  if (gOperatorTable) {
354
0
    NS_ASSERTION(aFlags && aLeadingSpace && aTrailingSpace, "bad usage");
355
0
    NS_ASSERTION(aForm > 0 && aForm < 4, "*** invalid call ***");
356
0
357
0
    // The MathML REC says:
358
0
    // If the operator does not occur in the dictionary with the specified form,
359
0
    // the renderer should use one of the forms which is available there, in the
360
0
    // order of preference: infix, postfix, prefix.
361
0
362
0
    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
0
          !(found =
367
0
            GetOperatorData(aOperator, NS_MATHML_OPERATOR_FORM_INFIX))) {
368
0
        if (form == NS_MATHML_OPERATOR_FORM_POSTFIX ||
369
0
            !(found =
370
0
              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
0
          }
374
0
        }
375
0
      }
376
0
    }
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
0
    }
385
0
  }
386
0
  return false;
387
0
}
388
389
void
390
nsMathMLOperators::LookupOperators(const nsString&       aOperator,
391
                                   nsOperatorFlags*      aFlags,
392
                                   float*                aLeadingSpace,
393
                                   float*                aTrailingSpace)
394
0
{
395
0
  if (!gGlobalsInitialized) {
396
0
    InitOperatorGlobals();
397
0
  }
398
0
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
0
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
0
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
0
411
0
  if (gOperatorTable) {
412
0
    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
0
    }
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
0
    }
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
0
    }
431
0
  }
432
0
}
433
434
/* static */ bool
435
nsMathMLOperators::IsMirrorableOperator(const nsString& aOperator)
436
0
{
437
0
  // LookupOperator will search infix, postfix and prefix forms of aOperator and
438
0
  // return the first form found. It is assumed that all these forms have same
439
0
  // mirrorability.
440
0
  nsOperatorFlags flags = 0;
441
0
  float dummy;
442
0
  nsMathMLOperators::LookupOperator(aOperator,
443
0
                                    NS_MATHML_OPERATOR_FORM_INFIX,
444
0
                                    &flags, &dummy, &dummy);
445
0
  return NS_MATHML_OPERATOR_IS_MIRRORABLE(flags);
446
0
}
447
448
/* static */ nsStretchDirection
449
nsMathMLOperators::GetStretchyDirection(const nsString& aOperator)
450
0
{
451
0
  // LookupOperator will search infix, postfix and prefix forms of aOperator and
452
0
  // return the first form found. It is assumed that all these forms have same
453
0
  // direction.
454
0
  nsOperatorFlags flags = 0;
455
0
  float dummy;
456
0
  nsMathMLOperators::LookupOperator(aOperator,
457
0
                                    NS_MATHML_OPERATOR_FORM_INFIX,
458
0
                                    &flags, &dummy, &dummy);
459
0
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
0
  } else {
465
0
    return NS_STRETCH_DIRECTION_UNSUPPORTED;
466
0
  }
467
0
}