layout/mathml/nsMathMLOperators.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     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"
     7 #include "nsCOMPtr.h"
     8 #include "nsDataHashtable.h"
     9 #include "nsHashKeys.h"
    10 #include "nsTArray.h"
    12 #include "nsIPersistentProperties2.h"
    13 #include "nsNetUtil.h"
    14 #include "nsCRT.h"
    16 // operator dictionary entry
    17 struct OperatorData {
    18   OperatorData(void)
    19     : mFlags(0),
    20       mLeadingSpace(0.0f),
    21       mTrailingSpace(0.0f)
    22   {
    23   }
    25   // member data
    26   nsString        mStr;
    27   nsOperatorFlags mFlags;
    28   float           mLeadingSpace;   // unit is em
    29   float           mTrailingSpace;  // unit is em
    30 };
    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(':');
    41 static void
    42 SetBooleanProperty(OperatorData* aOperatorData,
    43                    nsString      aName)
    44 {
    45   if (aName.IsEmpty())
    46     return;
    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;
    66 }
    68 static void
    69 SetProperty(OperatorData* aOperatorData,
    70             nsString      aName,
    71             nsString      aValue)
    72 {
    73   if (aName.IsEmpty() || aValue.IsEmpty())
    74     return;
    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
    87   } else {
    88     bool isLeadingSpace;
    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;
   100     if (isLeadingSpace)
   101       aOperatorData->mLeadingSpace = space;
   102     else
   103       aOperatorData->mTrailingSpace = space;
   104   }
   105 }
   107 static bool
   108 SetOperator(OperatorData*   aOperatorData,
   109             nsOperatorFlags aForm,
   110             const nsCString& aOperator,
   111             nsString&        aAttributes)
   113 {
   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
   118   int32_t i = 0;
   119   nsAutoString name, value;
   120   int32_t len = aOperator.Length();
   121   char16_t c = aOperator[i++];
   122   uint32_t state  = 0;
   123   char16_t uchar = 0;
   124   while (i <= len) {
   125     if (0 == state) {
   126       if (c != '\\')
   127         return false;
   128       if (i < len)
   129         c = aOperator[i];
   130       i++;
   131       if (('u' != c) && ('U' != c))
   132         return false;
   133       if (i < len)
   134         c = aOperator[i];
   135       i++;
   136       state++;
   137     }
   138     else {
   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);
   145       else return false;
   146       if (i < len)
   147         c = aOperator[i];
   148       i++;
   149       state++;
   150       if (5 == state) {
   151         value.Append(uchar);
   152         uchar = 0;
   153         state = 0;
   154       }
   155     }
   156   }
   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);
   170 #ifdef DEBUG
   171   NS_LossyConvertUTF16toASCII str(aAttributes);
   172 #endif
   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)) {
   178     name.SetLength(0);
   179     value.SetLength(0);
   180     // skip leading space, the dash amounts to the end of the line
   181     while ((kNullCh!=*start) && (kDashCh!=*start) && nsCRT::IsAsciiSpace(*start)) {
   182       ++start;
   183     }
   184     end = start;
   185     // look for ':'
   186     while ((kNullCh!=*end) && (kDashCh!=*end) && !nsCRT::IsAsciiSpace(*end) &&
   187            (kColonCh!=*end)) {
   188       ++end;
   189     }
   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
   194     if (start < end) {
   195       name.Assign(start);
   196     }
   197     if (IsBooleanProperty) {
   198       SetBooleanProperty(aOperatorData, name);
   199     } else {
   200       start = ++end;
   201       // look for space or end of line
   202       while ((kNullCh!=*end) && (kDashCh!=*end) &&
   203              !nsCRT::IsAsciiSpace(*end)) {
   204         ++end;
   205       }
   206       *end = kNullCh; // end segment here
   207       if (start < end) {
   208         // this segment is the value
   209         value.Assign(start);
   210       }
   211       SetProperty(aOperatorData, name, value);
   212     }
   213     start = ++end;
   214   }
   215   return true;
   216 }
   218 static nsresult
   219 InitOperators(void)
   220 {
   221   // Load the property file containing the Operator Dictionary
   222   nsresult rv;
   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)))) {
   236       bool more;
   237       uint32_t index = 0;
   238       nsAutoCString name;
   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";
   255               }
   256               else if (kNotFound != name.RFind(".postfix")) {
   257                 form = NS_MATHML_OPERATOR_FORM_POSTFIX;
   258                 len -= 8; // 8 is the length of ".postfix";
   259               }
   260               else if (kNotFound != name.RFind(".prefix")) {
   261                 form = NS_MATHML_OPERATOR_FORM_PREFIX;
   262                 len -= 7; // 7 is the length of ".prefix";
   263               }
   264               else continue; // input is not applicable
   265               name.SetLength(len);
   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;
   271                 }
   272                 operatorData = &gOperatorArray[index];
   273               }
   274               else {
   275                 form = 0; // to quickly return from SetOperator() at pass 1
   276               }
   277               // See if the operator should be retained
   278               if (SetOperator(operatorData, form, name, attributes)) {
   279                 index++;
   280                 if (1 == pass) gOperatorCount = index;
   281               }
   282             }
   283           }
   284         }
   285       }
   286     }
   287   }
   288   return NS_OK;
   289 }
   291 static nsresult
   292 InitGlobals()
   293 {
   294   gGlobalsInitialized = true;
   295   nsresult rv = NS_ERROR_OUT_OF_MEMORY;
   296   gOperatorTable = new nsDataHashtable<nsStringHashKey, OperatorData*>();
   297   if (gOperatorTable) {
   298     rv = InitOperators();
   299   }
   300   if (NS_FAILED(rv))
   301     nsMathMLOperators::CleanUp();
   302   return rv;
   303 }
   305 void
   306 nsMathMLOperators::CleanUp()
   307 {
   308   if (gOperatorArray) {
   309     delete[] gOperatorArray;
   310     gOperatorArray = nullptr;
   311   }
   312   if (gOperatorTable) {
   313     delete gOperatorTable;
   314     gOperatorTable = nullptr;
   315   }
   316 }
   318 void
   319 nsMathMLOperators::AddRefTable(void)
   320 {
   321   gTableRefCount++;
   322 }
   324 void
   325 nsMathMLOperators::ReleaseTable(void)
   326 {
   327   if (0 == --gTableRefCount) {
   328     CleanUp();
   329   }
   330 }
   332 static OperatorData*
   333 GetOperatorData(const nsString& aOperator, nsOperatorFlags aForm)
   334 {
   335   nsAutoString key(aOperator);
   336   key.AppendInt(aForm);
   337   return gOperatorTable->Get(key);
   338 }
   340 bool
   341 nsMathMLOperators::LookupOperator(const nsString&       aOperator,
   342                                   const nsOperatorFlags aForm,
   343                                   nsOperatorFlags*      aFlags,
   344                                   float*                aLeadingSpace,
   345                                   float*                aTrailingSpace)
   346 {
   347   if (!gGlobalsInitialized) {
   348     InitGlobals();
   349   }
   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.
   359     OperatorData* found;
   360     int32_t form = NS_MATHML_OPERATOR_GET_FORM(aForm);
   361     if (!(found = GetOperatorData(aOperator, form))) {
   362       if (form == NS_MATHML_OPERATOR_FORM_INFIX ||
   363           !(found =
   364             GetOperatorData(aOperator, NS_MATHML_OPERATOR_FORM_INFIX))) {
   365         if (form == NS_MATHML_OPERATOR_FORM_POSTFIX ||
   366             !(found =
   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);
   370           }
   371         }
   372       }
   373     }
   374     if (found) {
   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
   380       return true;
   381     }
   382   }
   383   return false;
   384 }
   386 void
   387 nsMathMLOperators::LookupOperators(const nsString&       aOperator,
   388                                    nsOperatorFlags*      aFlags,
   389                                    float*                aLeadingSpace,
   390                                    float*                aTrailingSpace)
   391 {
   392   if (!gGlobalsInitialized) {
   393     InitGlobals();
   394   }
   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) {
   409     OperatorData* found;
   410     found = GetOperatorData(aOperator, NS_MATHML_OPERATOR_FORM_INFIX);
   411     if (found) {
   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;
   415     }
   416     found = GetOperatorData(aOperator, NS_MATHML_OPERATOR_FORM_POSTFIX);
   417     if (found) {
   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;
   421     }
   422     found = GetOperatorData(aOperator, NS_MATHML_OPERATOR_FORM_PREFIX);
   423     if (found) {
   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;
   427     }
   428   }
   429 }
   431 /* static */ bool
   432 nsMathMLOperators::IsMirrorableOperator(const nsString& aOperator)
   433 {
   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
   436   // mirrorability.
   437   nsOperatorFlags flags = 0;
   438   float dummy;
   439   nsMathMLOperators::LookupOperator(aOperator,
   440                                     NS_MATHML_OPERATOR_FORM_INFIX,
   441                                     &flags, &dummy, &dummy);
   442   return NS_MATHML_OPERATOR_IS_MIRRORABLE(flags);
   443 }
   445 /* static */ nsStretchDirection
   446 nsMathMLOperators::GetStretchyDirection(const nsString& aOperator)
   447 {
   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
   450   // direction.
   451   nsOperatorFlags flags = 0;
   452   float dummy;
   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;
   461   } else {
   462     return NS_STRETCH_DIRECTION_UNSUPPORTED;
   463   }
   464 }

mercurial