forked from mirrors/gecko-dev
		
	 ad01a10a3b
			
		
	
	
		ad01a10a3b
		
	
	
	
	
		
			
			Note that this patch only transforms the use of the nsDataHashtable type alias to a directly equivalent use of nsTHashMap. It does not change the specification of the hash key type to make use of the key class deduction that nsTHashMap allows for in some cases. That can be done in a separate step, but requires more attention. Differential Revision: https://phabricator.services.mozilla.com/D106008
		
			
				
	
	
		
			426 lines
		
	
	
	
		
			15 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			426 lines
		
	
	
	
		
			15 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 | |
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| #include "nsMathMLOperators.h"
 | |
| #include "nsCOMPtr.h"
 | |
| #include "nsTHashMap.h"
 | |
| #include "nsHashKeys.h"
 | |
| #include "nsNetUtil.h"
 | |
| #include "nsTArray.h"
 | |
| 
 | |
| #include "nsIPersistentProperties2.h"
 | |
| #include "nsISimpleEnumerator.h"
 | |
| #include "nsCRT.h"
 | |
| 
 | |
| // operator dictionary entry
 | |
| struct OperatorData {
 | |
|   OperatorData(void) : mFlags(0), mLeadingSpace(0.0f), mTrailingSpace(0.0f) {}
 | |
| 
 | |
|   // member data
 | |
|   nsString mStr;
 | |
|   nsOperatorFlags mFlags;
 | |
|   float mLeadingSpace;   // unit is em
 | |
|   float mTrailingSpace;  // unit is em
 | |
| };
 | |
| 
 | |
| static int32_t gTableRefCount = 0;
 | |
| static uint32_t gOperatorCount = 0;
 | |
| static OperatorData* gOperatorArray = nullptr;
 | |
| static nsTHashMap<nsStringHashKey, OperatorData*>* gOperatorTable = nullptr;
 | |
| static bool gGlobalsInitialized = false;
 | |
| 
 | |
| static const char16_t kDashCh = char16_t('#');
 | |
| static const char16_t kColonCh = char16_t(':');
 | |
| 
 | |
| static void SetBooleanProperty(OperatorData* aOperatorData, nsString aName) {
 | |
|   if (aName.IsEmpty()) return;
 | |
| 
 | |
|   if (aName.EqualsLiteral("stretchy") && (1 == aOperatorData->mStr.Length()))
 | |
|     aOperatorData->mFlags |= NS_MATHML_OPERATOR_STRETCHY;
 | |
|   else if (aName.EqualsLiteral("fence"))
 | |
|     aOperatorData->mFlags |= NS_MATHML_OPERATOR_FENCE;
 | |
|   else if (aName.EqualsLiteral("accent"))
 | |
|     aOperatorData->mFlags |= NS_MATHML_OPERATOR_ACCENT;
 | |
|   else if (aName.EqualsLiteral("largeop"))
 | |
|     aOperatorData->mFlags |= NS_MATHML_OPERATOR_LARGEOP;
 | |
|   else if (aName.EqualsLiteral("separator"))
 | |
|     aOperatorData->mFlags |= NS_MATHML_OPERATOR_SEPARATOR;
 | |
|   else if (aName.EqualsLiteral("movablelimits"))
 | |
|     aOperatorData->mFlags |= NS_MATHML_OPERATOR_MOVABLELIMITS;
 | |
|   else if (aName.EqualsLiteral("symmetric"))
 | |
|     aOperatorData->mFlags |= NS_MATHML_OPERATOR_SYMMETRIC;
 | |
|   else if (aName.EqualsLiteral("integral"))
 | |
|     aOperatorData->mFlags |= NS_MATHML_OPERATOR_INTEGRAL;
 | |
|   else if (aName.EqualsLiteral("mirrorable"))
 | |
|     aOperatorData->mFlags |= NS_MATHML_OPERATOR_MIRRORABLE;
 | |
| }
 | |
| 
 | |
| static void SetProperty(OperatorData* aOperatorData, nsString aName,
 | |
|                         nsString aValue) {
 | |
|   if (aName.IsEmpty() || aValue.IsEmpty()) return;
 | |
| 
 | |
|   // XXX These ones are not kept in the dictionary
 | |
|   // Support for these requires nsString member variables
 | |
|   // maxsize (default: infinity)
 | |
|   // minsize (default: 1)
 | |
| 
 | |
|   if (aName.EqualsLiteral("direction")) {
 | |
|     if (aValue.EqualsLiteral("vertical"))
 | |
|       aOperatorData->mFlags |= NS_MATHML_OPERATOR_DIRECTION_VERTICAL;
 | |
|     else if (aValue.EqualsLiteral("horizontal"))
 | |
|       aOperatorData->mFlags |= NS_MATHML_OPERATOR_DIRECTION_HORIZONTAL;
 | |
|     else
 | |
|       return;  // invalid value
 | |
|   } else {
 | |
|     bool isLeadingSpace;
 | |
|     if (aName.EqualsLiteral("lspace"))
 | |
|       isLeadingSpace = true;
 | |
|     else if (aName.EqualsLiteral("rspace"))
 | |
|       isLeadingSpace = false;
 | |
|     else
 | |
|       return;  // input is not applicable
 | |
| 
 | |
|     // aValue is assumed to be a digit from 0 to 7
 | |
|     nsresult error = NS_OK;
 | |
|     float space = aValue.ToFloat(&error) / 18.0;
 | |
|     if (NS_FAILED(error)) return;
 | |
| 
 | |
|     if (isLeadingSpace)
 | |
|       aOperatorData->mLeadingSpace = space;
 | |
|     else
 | |
|       aOperatorData->mTrailingSpace = space;
 | |
|   }
 | |
| }
 | |
| 
 | |
| static bool SetOperator(OperatorData* aOperatorData, nsOperatorFlags aForm,
 | |
|                         const nsCString& aOperator, nsString& aAttributes)
 | |
| 
 | |
| {
 | |
|   static const char16_t kNullCh = char16_t('\0');
 | |
| 
 | |
|   // aOperator is in the expanded format \uNNNN\uNNNN ...
 | |
|   // First compress these Unicode points to the internal nsString format
 | |
|   int32_t i = 0;
 | |
|   nsAutoString name, value;
 | |
|   int32_t len = aOperator.Length();
 | |
|   char16_t c = aOperator[i++];
 | |
|   uint32_t state = 0;
 | |
|   char16_t uchar = 0;
 | |
|   while (i <= len) {
 | |
|     if (0 == state) {
 | |
|       if (c != '\\') return false;
 | |
|       if (i < len) c = aOperator[i];
 | |
|       i++;
 | |
|       if (('u' != c) && ('U' != c)) return false;
 | |
|       if (i < len) c = aOperator[i];
 | |
|       i++;
 | |
|       state++;
 | |
|     } else {
 | |
|       if (('0' <= c) && (c <= '9'))
 | |
|         uchar = (uchar << 4) | (c - '0');
 | |
|       else if (('a' <= c) && (c <= 'f'))
 | |
|         uchar = (uchar << 4) | (c - 'a' + 0x0a);
 | |
|       else if (('A' <= c) && (c <= 'F'))
 | |
|         uchar = (uchar << 4) | (c - 'A' + 0x0a);
 | |
|       else
 | |
|         return false;
 | |
|       if (i < len) c = aOperator[i];
 | |
|       i++;
 | |
|       state++;
 | |
|       if (5 == state) {
 | |
|         value.Append(uchar);
 | |
|         uchar = 0;
 | |
|         state = 0;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   if (0 != state) return false;
 | |
| 
 | |
|   // Quick return when the caller doesn't care about the attributes and just
 | |
|   // wants to know if this is a valid operator (this is the case at the first
 | |
|   // pass of the parsing of the dictionary in InitOperators())
 | |
|   if (!aForm) return true;
 | |
| 
 | |
|   // Add operator to hash table
 | |
|   aOperatorData->mFlags |= aForm;
 | |
|   aOperatorData->mStr.Assign(value);
 | |
|   value.AppendInt(aForm, 10);
 | |
|   gOperatorTable->InsertOrUpdate(value, aOperatorData);
 | |
| 
 | |
| #ifdef DEBUG
 | |
|   NS_LossyConvertUTF16toASCII str(aAttributes);
 | |
| #endif
 | |
|   // Loop over the space-delimited list of attributes to get the name:value
 | |
|   // pairs
 | |
|   aAttributes.Append(kNullCh);  // put an extra null at the end
 | |
|   char16_t* start = aAttributes.BeginWriting();
 | |
|   char16_t* end = start;
 | |
|   while ((kNullCh != *start) && (kDashCh != *start)) {
 | |
|     name.SetLength(0);
 | |
|     value.SetLength(0);
 | |
|     // skip leading space, the dash amounts to the end of the line
 | |
|     while ((kNullCh != *start) && (kDashCh != *start) &&
 | |
|            nsCRT::IsAsciiSpace(*start)) {
 | |
|       ++start;
 | |
|     }
 | |
|     end = start;
 | |
|     // look for ':'
 | |
|     while ((kNullCh != *end) && (kDashCh != *end) &&
 | |
|            !nsCRT::IsAsciiSpace(*end) && (kColonCh != *end)) {
 | |
|       ++end;
 | |
|     }
 | |
|     // If ':' is not found, then it's a boolean property
 | |
|     bool IsBooleanProperty = (kColonCh != *end);
 | |
|     *end = kNullCh;  // end segment here
 | |
|     // this segment is the name
 | |
|     if (start < end) {
 | |
|       name.Assign(start);
 | |
|     }
 | |
|     if (IsBooleanProperty) {
 | |
|       SetBooleanProperty(aOperatorData, name);
 | |
|     } else {
 | |
|       start = ++end;
 | |
|       // look for space or end of line
 | |
|       while ((kNullCh != *end) && (kDashCh != *end) &&
 | |
|              !nsCRT::IsAsciiSpace(*end)) {
 | |
|         ++end;
 | |
|       }
 | |
|       *end = kNullCh;  // end segment here
 | |
|       if (start < end) {
 | |
|         // this segment is the value
 | |
|         value.Assign(start);
 | |
|       }
 | |
|       SetProperty(aOperatorData, name, value);
 | |
|     }
 | |
|     start = ++end;
 | |
|   }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| static nsresult InitOperators(void) {
 | |
|   // Load the property file containing the Operator Dictionary
 | |
|   nsresult rv;
 | |
|   nsCOMPtr<nsIPersistentProperties> mathfontProp;
 | |
|   rv = NS_LoadPersistentPropertiesFromURISpec(
 | |
|       getter_AddRefs(mathfontProp),
 | |
|       "resource://gre/res/fonts/mathfont.properties"_ns);
 | |
| 
 | |
|   if (NS_FAILED(rv)) return rv;
 | |
| 
 | |
|   // Parse the Operator Dictionary in two passes.
 | |
|   // The first pass is to count the number of operators; the second pass is to
 | |
|   // allocate the necessary space for them and to add them in the hash table.
 | |
|   for (int32_t pass = 1; pass <= 2; pass++) {
 | |
|     OperatorData dummyData;
 | |
|     OperatorData* operatorData = &dummyData;
 | |
|     nsCOMPtr<nsISimpleEnumerator> iterator;
 | |
|     if (NS_SUCCEEDED(mathfontProp->Enumerate(getter_AddRefs(iterator)))) {
 | |
|       bool more;
 | |
|       uint32_t index = 0;
 | |
|       nsAutoCString name;
 | |
|       nsAutoString attributes;
 | |
|       while ((NS_SUCCEEDED(iterator->HasMoreElements(&more))) && more) {
 | |
|         nsCOMPtr<nsISupports> supports;
 | |
|         nsCOMPtr<nsIPropertyElement> element;
 | |
|         if (NS_SUCCEEDED(iterator->GetNext(getter_AddRefs(supports)))) {
 | |
|           element = do_QueryInterface(supports);
 | |
|           if (NS_SUCCEEDED(element->GetKey(name)) &&
 | |
|               NS_SUCCEEDED(element->GetValue(attributes))) {
 | |
|             // expected key: operator.\uNNNN.{infix,postfix,prefix}
 | |
|             if ((21 <= name.Length()) && (0 == name.Find("operator.\\u"))) {
 | |
|               name.Cut(0, 9);  // 9 is the length of "operator.";
 | |
|               int32_t len = name.Length();
 | |
|               nsOperatorFlags form = 0;
 | |
|               if (kNotFound != name.RFind(".infix")) {
 | |
|                 form = NS_MATHML_OPERATOR_FORM_INFIX;
 | |
|                 len -= 6;  // 6 is the length of ".infix";
 | |
|               } else if (kNotFound != name.RFind(".postfix")) {
 | |
|                 form = NS_MATHML_OPERATOR_FORM_POSTFIX;
 | |
|                 len -= 8;  // 8 is the length of ".postfix";
 | |
|               } else if (kNotFound != name.RFind(".prefix")) {
 | |
|                 form = NS_MATHML_OPERATOR_FORM_PREFIX;
 | |
|                 len -= 7;  // 7 is the length of ".prefix";
 | |
|               } else
 | |
|                 continue;  // input is not applicable
 | |
|               name.SetLength(len);
 | |
|               if (2 == pass) {  // allocate space and start the storage
 | |
|                 if (!gOperatorArray) {
 | |
|                   if (0 == gOperatorCount) return NS_ERROR_UNEXPECTED;
 | |
|                   gOperatorArray = new OperatorData[gOperatorCount];
 | |
|                 }
 | |
|                 operatorData = &gOperatorArray[index];
 | |
|               } else {
 | |
|                 form = 0;  // to quickly return from SetOperator() at pass 1
 | |
|               }
 | |
|               // See if the operator should be retained
 | |
|               if (SetOperator(operatorData, form, name, attributes)) {
 | |
|                 index++;
 | |
|                 if (1 == pass) gOperatorCount = index;
 | |
|               }
 | |
|             }
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| static nsresult InitOperatorGlobals() {
 | |
|   gGlobalsInitialized = true;
 | |
|   nsresult rv = NS_ERROR_OUT_OF_MEMORY;
 | |
|   gOperatorTable = new nsTHashMap<nsStringHashKey, OperatorData*>();
 | |
|   if (gOperatorTable) {
 | |
|     rv = InitOperators();
 | |
|   }
 | |
|   if (NS_FAILED(rv)) nsMathMLOperators::CleanUp();
 | |
|   return rv;
 | |
| }
 | |
| 
 | |
| void nsMathMLOperators::CleanUp() {
 | |
|   if (gOperatorArray) {
 | |
|     delete[] gOperatorArray;
 | |
|     gOperatorArray = nullptr;
 | |
|   }
 | |
|   if (gOperatorTable) {
 | |
|     delete gOperatorTable;
 | |
|     gOperatorTable = nullptr;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void nsMathMLOperators::AddRefTable(void) { gTableRefCount++; }
 | |
| 
 | |
| void nsMathMLOperators::ReleaseTable(void) {
 | |
|   if (0 == --gTableRefCount) {
 | |
|     CleanUp();
 | |
|   }
 | |
| }
 | |
| 
 | |
| static OperatorData* GetOperatorData(const nsString& aOperator,
 | |
|                                      nsOperatorFlags aForm) {
 | |
|   nsAutoString key(aOperator);
 | |
|   key.AppendInt(aForm);
 | |
|   return gOperatorTable->Get(key);
 | |
| }
 | |
| 
 | |
| bool nsMathMLOperators::LookupOperator(const nsString& aOperator,
 | |
|                                        const nsOperatorFlags aForm,
 | |
|                                        nsOperatorFlags* aFlags,
 | |
|                                        float* aLeadingSpace,
 | |
|                                        float* aTrailingSpace) {
 | |
|   if (!gGlobalsInitialized) {
 | |
|     InitOperatorGlobals();
 | |
|   }
 | |
|   if (gOperatorTable) {
 | |
|     NS_ASSERTION(aFlags && aLeadingSpace && aTrailingSpace, "bad usage");
 | |
|     NS_ASSERTION(aForm > 0 && aForm < 4, "*** invalid call ***");
 | |
| 
 | |
|     // The MathML REC says:
 | |
|     // If the operator does not occur in the dictionary with the specified form,
 | |
|     // the renderer should use one of the forms which is available there, in the
 | |
|     // order of preference: infix, postfix, prefix.
 | |
| 
 | |
|     OperatorData* found;
 | |
|     int32_t form = NS_MATHML_OPERATOR_GET_FORM(aForm);
 | |
|     if (!(found = GetOperatorData(aOperator, form))) {
 | |
|       if (form == NS_MATHML_OPERATOR_FORM_INFIX ||
 | |
|           !(found =
 | |
|                 GetOperatorData(aOperator, NS_MATHML_OPERATOR_FORM_INFIX))) {
 | |
|         if (form == NS_MATHML_OPERATOR_FORM_POSTFIX ||
 | |
|             !(found = GetOperatorData(aOperator,
 | |
|                                       NS_MATHML_OPERATOR_FORM_POSTFIX))) {
 | |
|           if (form != NS_MATHML_OPERATOR_FORM_PREFIX) {
 | |
|             found = GetOperatorData(aOperator, NS_MATHML_OPERATOR_FORM_PREFIX);
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|     if (found) {
 | |
|       NS_ASSERTION(found->mStr.Equals(aOperator), "bad setup");
 | |
|       *aLeadingSpace = found->mLeadingSpace;
 | |
|       *aTrailingSpace = found->mTrailingSpace;
 | |
|       *aFlags &= ~NS_MATHML_OPERATOR_FORM;  // clear the form bits
 | |
|       *aFlags |= found->mFlags;             // just add bits without overwriting
 | |
|       return true;
 | |
|     }
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| void nsMathMLOperators::LookupOperators(const nsString& aOperator,
 | |
|                                         nsOperatorFlags* aFlags,
 | |
|                                         float* aLeadingSpace,
 | |
|                                         float* aTrailingSpace) {
 | |
|   if (!gGlobalsInitialized) {
 | |
|     InitOperatorGlobals();
 | |
|   }
 | |
| 
 | |
|   aFlags[NS_MATHML_OPERATOR_FORM_INFIX] = 0;
 | |
|   aLeadingSpace[NS_MATHML_OPERATOR_FORM_INFIX] = 0.0f;
 | |
|   aTrailingSpace[NS_MATHML_OPERATOR_FORM_INFIX] = 0.0f;
 | |
| 
 | |
|   aFlags[NS_MATHML_OPERATOR_FORM_POSTFIX] = 0;
 | |
|   aLeadingSpace[NS_MATHML_OPERATOR_FORM_POSTFIX] = 0.0f;
 | |
|   aTrailingSpace[NS_MATHML_OPERATOR_FORM_POSTFIX] = 0.0f;
 | |
| 
 | |
|   aFlags[NS_MATHML_OPERATOR_FORM_PREFIX] = 0;
 | |
|   aLeadingSpace[NS_MATHML_OPERATOR_FORM_PREFIX] = 0.0f;
 | |
|   aTrailingSpace[NS_MATHML_OPERATOR_FORM_PREFIX] = 0.0f;
 | |
| 
 | |
|   if (gOperatorTable) {
 | |
|     OperatorData* found;
 | |
|     found = GetOperatorData(aOperator, NS_MATHML_OPERATOR_FORM_INFIX);
 | |
|     if (found) {
 | |
|       aFlags[NS_MATHML_OPERATOR_FORM_INFIX] = found->mFlags;
 | |
|       aLeadingSpace[NS_MATHML_OPERATOR_FORM_INFIX] = found->mLeadingSpace;
 | |
|       aTrailingSpace[NS_MATHML_OPERATOR_FORM_INFIX] = found->mTrailingSpace;
 | |
|     }
 | |
|     found = GetOperatorData(aOperator, NS_MATHML_OPERATOR_FORM_POSTFIX);
 | |
|     if (found) {
 | |
|       aFlags[NS_MATHML_OPERATOR_FORM_POSTFIX] = found->mFlags;
 | |
|       aLeadingSpace[NS_MATHML_OPERATOR_FORM_POSTFIX] = found->mLeadingSpace;
 | |
|       aTrailingSpace[NS_MATHML_OPERATOR_FORM_POSTFIX] = found->mTrailingSpace;
 | |
|     }
 | |
|     found = GetOperatorData(aOperator, NS_MATHML_OPERATOR_FORM_PREFIX);
 | |
|     if (found) {
 | |
|       aFlags[NS_MATHML_OPERATOR_FORM_PREFIX] = found->mFlags;
 | |
|       aLeadingSpace[NS_MATHML_OPERATOR_FORM_PREFIX] = found->mLeadingSpace;
 | |
|       aTrailingSpace[NS_MATHML_OPERATOR_FORM_PREFIX] = found->mTrailingSpace;
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| /* static */
 | |
| bool nsMathMLOperators::IsMirrorableOperator(const nsString& aOperator) {
 | |
|   // LookupOperator will search infix, postfix and prefix forms of aOperator and
 | |
|   // return the first form found. It is assumed that all these forms have same
 | |
|   // mirrorability.
 | |
|   nsOperatorFlags flags = 0;
 | |
|   float dummy;
 | |
|   nsMathMLOperators::LookupOperator(aOperator, NS_MATHML_OPERATOR_FORM_INFIX,
 | |
|                                     &flags, &dummy, &dummy);
 | |
|   return NS_MATHML_OPERATOR_IS_MIRRORABLE(flags);
 | |
| }
 | |
| 
 | |
| /* static */
 | |
| nsStretchDirection nsMathMLOperators::GetStretchyDirection(
 | |
|     const nsString& aOperator) {
 | |
|   // LookupOperator will search infix, postfix and prefix forms of aOperator and
 | |
|   // return the first form found. It is assumed that all these forms have same
 | |
|   // direction.
 | |
|   nsOperatorFlags flags = 0;
 | |
|   float dummy;
 | |
|   nsMathMLOperators::LookupOperator(aOperator, NS_MATHML_OPERATOR_FORM_INFIX,
 | |
|                                     &flags, &dummy, &dummy);
 | |
| 
 | |
|   if (NS_MATHML_OPERATOR_IS_DIRECTION_VERTICAL(flags)) {
 | |
|     return NS_STRETCH_DIRECTION_VERTICAL;
 | |
|   } else if (NS_MATHML_OPERATOR_IS_DIRECTION_HORIZONTAL(flags)) {
 | |
|     return NS_STRETCH_DIRECTION_HORIZONTAL;
 | |
|   } else {
 | |
|     return NS_STRETCH_DIRECTION_UNSUPPORTED;
 | |
|   }
 | |
| }
 |