LCOV - code coverage report
Current view: top level - layout/mathml - nsMathMLOperators.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 3 252 1.2 %
Date: 2017-07-14 16:53:18 Functions: 1 15 6.7 %
Legend: Lines: hit not hit

          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             : }

Generated by: LCOV version 1.13