1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/mathml/nsMathMLOperators.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,464 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "nsMathMLOperators.h" 1.10 +#include "nsCOMPtr.h" 1.11 +#include "nsDataHashtable.h" 1.12 +#include "nsHashKeys.h" 1.13 +#include "nsTArray.h" 1.14 + 1.15 +#include "nsIPersistentProperties2.h" 1.16 +#include "nsNetUtil.h" 1.17 +#include "nsCRT.h" 1.18 + 1.19 +// operator dictionary entry 1.20 +struct OperatorData { 1.21 + OperatorData(void) 1.22 + : mFlags(0), 1.23 + mLeadingSpace(0.0f), 1.24 + mTrailingSpace(0.0f) 1.25 + { 1.26 + } 1.27 + 1.28 + // member data 1.29 + nsString mStr; 1.30 + nsOperatorFlags mFlags; 1.31 + float mLeadingSpace; // unit is em 1.32 + float mTrailingSpace; // unit is em 1.33 +}; 1.34 + 1.35 +static int32_t gTableRefCount = 0; 1.36 +static uint32_t gOperatorCount = 0; 1.37 +static OperatorData* gOperatorArray = nullptr; 1.38 +static nsDataHashtable<nsStringHashKey, OperatorData*>* gOperatorTable = nullptr; 1.39 +static bool gGlobalsInitialized = false; 1.40 + 1.41 +static const char16_t kDashCh = char16_t('#'); 1.42 +static const char16_t kColonCh = char16_t(':'); 1.43 + 1.44 +static void 1.45 +SetBooleanProperty(OperatorData* aOperatorData, 1.46 + nsString aName) 1.47 +{ 1.48 + if (aName.IsEmpty()) 1.49 + return; 1.50 + 1.51 + if (aName.EqualsLiteral("stretchy") && (1 == aOperatorData->mStr.Length())) 1.52 + aOperatorData->mFlags |= NS_MATHML_OPERATOR_STRETCHY; 1.53 + else if (aName.EqualsLiteral("fence")) 1.54 + aOperatorData->mFlags |= NS_MATHML_OPERATOR_FENCE; 1.55 + else if (aName.EqualsLiteral("accent")) 1.56 + aOperatorData->mFlags |= NS_MATHML_OPERATOR_ACCENT; 1.57 + else if (aName.EqualsLiteral("largeop")) 1.58 + aOperatorData->mFlags |= NS_MATHML_OPERATOR_LARGEOP; 1.59 + else if (aName.EqualsLiteral("separator")) 1.60 + aOperatorData->mFlags |= NS_MATHML_OPERATOR_SEPARATOR; 1.61 + else if (aName.EqualsLiteral("movablelimits")) 1.62 + aOperatorData->mFlags |= NS_MATHML_OPERATOR_MOVABLELIMITS; 1.63 + else if (aName.EqualsLiteral("symmetric")) 1.64 + aOperatorData->mFlags |= NS_MATHML_OPERATOR_SYMMETRIC; 1.65 + else if (aName.EqualsLiteral("integral")) 1.66 + aOperatorData->mFlags |= NS_MATHML_OPERATOR_INTEGRAL; 1.67 + else if (aName.EqualsLiteral("mirrorable")) 1.68 + aOperatorData->mFlags |= NS_MATHML_OPERATOR_MIRRORABLE; 1.69 +} 1.70 + 1.71 +static void 1.72 +SetProperty(OperatorData* aOperatorData, 1.73 + nsString aName, 1.74 + nsString aValue) 1.75 +{ 1.76 + if (aName.IsEmpty() || aValue.IsEmpty()) 1.77 + return; 1.78 + 1.79 + // XXX These ones are not kept in the dictionary 1.80 + // Support for these requires nsString member variables 1.81 + // maxsize (default: infinity) 1.82 + // minsize (default: 1) 1.83 + 1.84 + if (aName.EqualsLiteral("direction")) { 1.85 + if (aValue.EqualsLiteral("vertical")) 1.86 + aOperatorData->mFlags |= NS_MATHML_OPERATOR_DIRECTION_VERTICAL; 1.87 + else if (aValue.EqualsLiteral("horizontal")) 1.88 + aOperatorData->mFlags |= NS_MATHML_OPERATOR_DIRECTION_HORIZONTAL; 1.89 + else return; // invalid value 1.90 + } else { 1.91 + bool isLeadingSpace; 1.92 + if (aName.EqualsLiteral("lspace")) 1.93 + isLeadingSpace = true; 1.94 + else if (aName.EqualsLiteral("rspace")) 1.95 + isLeadingSpace = false; 1.96 + else return; // input is not applicable 1.97 + 1.98 + // aValue is assumed to be a digit from 0 to 7 1.99 + nsresult error = NS_OK; 1.100 + float space = aValue.ToFloat(&error) / 18.0; 1.101 + if (NS_FAILED(error)) return; 1.102 + 1.103 + if (isLeadingSpace) 1.104 + aOperatorData->mLeadingSpace = space; 1.105 + else 1.106 + aOperatorData->mTrailingSpace = space; 1.107 + } 1.108 +} 1.109 + 1.110 +static bool 1.111 +SetOperator(OperatorData* aOperatorData, 1.112 + nsOperatorFlags aForm, 1.113 + const nsCString& aOperator, 1.114 + nsString& aAttributes) 1.115 + 1.116 +{ 1.117 + static const char16_t kNullCh = char16_t('\0'); 1.118 + 1.119 + // aOperator is in the expanded format \uNNNN\uNNNN ... 1.120 + // First compress these Unicode points to the internal nsString format 1.121 + int32_t i = 0; 1.122 + nsAutoString name, value; 1.123 + int32_t len = aOperator.Length(); 1.124 + char16_t c = aOperator[i++]; 1.125 + uint32_t state = 0; 1.126 + char16_t uchar = 0; 1.127 + while (i <= len) { 1.128 + if (0 == state) { 1.129 + if (c != '\\') 1.130 + return false; 1.131 + if (i < len) 1.132 + c = aOperator[i]; 1.133 + i++; 1.134 + if (('u' != c) && ('U' != c)) 1.135 + return false; 1.136 + if (i < len) 1.137 + c = aOperator[i]; 1.138 + i++; 1.139 + state++; 1.140 + } 1.141 + else { 1.142 + if (('0' <= c) && (c <= '9')) 1.143 + uchar = (uchar << 4) | (c - '0'); 1.144 + else if (('a' <= c) && (c <= 'f')) 1.145 + uchar = (uchar << 4) | (c - 'a' + 0x0a); 1.146 + else if (('A' <= c) && (c <= 'F')) 1.147 + uchar = (uchar << 4) | (c - 'A' + 0x0a); 1.148 + else return false; 1.149 + if (i < len) 1.150 + c = aOperator[i]; 1.151 + i++; 1.152 + state++; 1.153 + if (5 == state) { 1.154 + value.Append(uchar); 1.155 + uchar = 0; 1.156 + state = 0; 1.157 + } 1.158 + } 1.159 + } 1.160 + if (0 != state) return false; 1.161 + 1.162 + // Quick return when the caller doesn't care about the attributes and just wants 1.163 + // to know if this is a valid operator (this is the case at the first pass of the 1.164 + // parsing of the dictionary in InitOperators()) 1.165 + if (!aForm) return true; 1.166 + 1.167 + // Add operator to hash table 1.168 + aOperatorData->mFlags |= aForm; 1.169 + aOperatorData->mStr.Assign(value); 1.170 + value.AppendInt(aForm, 10); 1.171 + gOperatorTable->Put(value, aOperatorData); 1.172 + 1.173 +#ifdef DEBUG 1.174 + NS_LossyConvertUTF16toASCII str(aAttributes); 1.175 +#endif 1.176 + // Loop over the space-delimited list of attributes to get the name:value pairs 1.177 + aAttributes.Append(kNullCh); // put an extra null at the end 1.178 + char16_t* start = aAttributes.BeginWriting(); 1.179 + char16_t* end = start; 1.180 + while ((kNullCh != *start) && (kDashCh != *start)) { 1.181 + name.SetLength(0); 1.182 + value.SetLength(0); 1.183 + // skip leading space, the dash amounts to the end of the line 1.184 + while ((kNullCh!=*start) && (kDashCh!=*start) && nsCRT::IsAsciiSpace(*start)) { 1.185 + ++start; 1.186 + } 1.187 + end = start; 1.188 + // look for ':' 1.189 + while ((kNullCh!=*end) && (kDashCh!=*end) && !nsCRT::IsAsciiSpace(*end) && 1.190 + (kColonCh!=*end)) { 1.191 + ++end; 1.192 + } 1.193 + // If ':' is not found, then it's a boolean property 1.194 + bool IsBooleanProperty = (kColonCh != *end); 1.195 + *end = kNullCh; // end segment here 1.196 + // this segment is the name 1.197 + if (start < end) { 1.198 + name.Assign(start); 1.199 + } 1.200 + if (IsBooleanProperty) { 1.201 + SetBooleanProperty(aOperatorData, name); 1.202 + } else { 1.203 + start = ++end; 1.204 + // look for space or end of line 1.205 + while ((kNullCh!=*end) && (kDashCh!=*end) && 1.206 + !nsCRT::IsAsciiSpace(*end)) { 1.207 + ++end; 1.208 + } 1.209 + *end = kNullCh; // end segment here 1.210 + if (start < end) { 1.211 + // this segment is the value 1.212 + value.Assign(start); 1.213 + } 1.214 + SetProperty(aOperatorData, name, value); 1.215 + } 1.216 + start = ++end; 1.217 + } 1.218 + return true; 1.219 +} 1.220 + 1.221 +static nsresult 1.222 +InitOperators(void) 1.223 +{ 1.224 + // Load the property file containing the Operator Dictionary 1.225 + nsresult rv; 1.226 + nsCOMPtr<nsIPersistentProperties> mathfontProp; 1.227 + rv = NS_LoadPersistentPropertiesFromURISpec(getter_AddRefs(mathfontProp), 1.228 + NS_LITERAL_CSTRING("resource://gre/res/fonts/mathfont.properties")); 1.229 + if (NS_FAILED(rv)) return rv; 1.230 + 1.231 + // Parse the Operator Dictionary in two passes. 1.232 + // The first pass is to count the number of operators; the second pass is to 1.233 + // allocate the necessary space for them and to add them in the hash table. 1.234 + for (int32_t pass = 1; pass <= 2; pass++) { 1.235 + OperatorData dummyData; 1.236 + OperatorData* operatorData = &dummyData; 1.237 + nsCOMPtr<nsISimpleEnumerator> iterator; 1.238 + if (NS_SUCCEEDED(mathfontProp->Enumerate(getter_AddRefs(iterator)))) { 1.239 + bool more; 1.240 + uint32_t index = 0; 1.241 + nsAutoCString name; 1.242 + nsAutoString attributes; 1.243 + while ((NS_SUCCEEDED(iterator->HasMoreElements(&more))) && more) { 1.244 + nsCOMPtr<nsISupports> supports; 1.245 + nsCOMPtr<nsIPropertyElement> element; 1.246 + if (NS_SUCCEEDED(iterator->GetNext(getter_AddRefs(supports)))) { 1.247 + element = do_QueryInterface(supports); 1.248 + if (NS_SUCCEEDED(element->GetKey(name)) && 1.249 + NS_SUCCEEDED(element->GetValue(attributes))) { 1.250 + // expected key: operator.\uNNNN.{infix,postfix,prefix} 1.251 + if ((21 <= name.Length()) && (0 == name.Find("operator.\\u"))) { 1.252 + name.Cut(0, 9); // 9 is the length of "operator."; 1.253 + int32_t len = name.Length(); 1.254 + nsOperatorFlags form = 0; 1.255 + if (kNotFound != name.RFind(".infix")) { 1.256 + form = NS_MATHML_OPERATOR_FORM_INFIX; 1.257 + len -= 6; // 6 is the length of ".infix"; 1.258 + } 1.259 + else if (kNotFound != name.RFind(".postfix")) { 1.260 + form = NS_MATHML_OPERATOR_FORM_POSTFIX; 1.261 + len -= 8; // 8 is the length of ".postfix"; 1.262 + } 1.263 + else if (kNotFound != name.RFind(".prefix")) { 1.264 + form = NS_MATHML_OPERATOR_FORM_PREFIX; 1.265 + len -= 7; // 7 is the length of ".prefix"; 1.266 + } 1.267 + else continue; // input is not applicable 1.268 + name.SetLength(len); 1.269 + if (2 == pass) { // allocate space and start the storage 1.270 + if (!gOperatorArray) { 1.271 + if (0 == gOperatorCount) return NS_ERROR_UNEXPECTED; 1.272 + gOperatorArray = new OperatorData[gOperatorCount]; 1.273 + if (!gOperatorArray) return NS_ERROR_OUT_OF_MEMORY; 1.274 + } 1.275 + operatorData = &gOperatorArray[index]; 1.276 + } 1.277 + else { 1.278 + form = 0; // to quickly return from SetOperator() at pass 1 1.279 + } 1.280 + // See if the operator should be retained 1.281 + if (SetOperator(operatorData, form, name, attributes)) { 1.282 + index++; 1.283 + if (1 == pass) gOperatorCount = index; 1.284 + } 1.285 + } 1.286 + } 1.287 + } 1.288 + } 1.289 + } 1.290 + } 1.291 + return NS_OK; 1.292 +} 1.293 + 1.294 +static nsresult 1.295 +InitGlobals() 1.296 +{ 1.297 + gGlobalsInitialized = true; 1.298 + nsresult rv = NS_ERROR_OUT_OF_MEMORY; 1.299 + gOperatorTable = new nsDataHashtable<nsStringHashKey, OperatorData*>(); 1.300 + if (gOperatorTable) { 1.301 + rv = InitOperators(); 1.302 + } 1.303 + if (NS_FAILED(rv)) 1.304 + nsMathMLOperators::CleanUp(); 1.305 + return rv; 1.306 +} 1.307 + 1.308 +void 1.309 +nsMathMLOperators::CleanUp() 1.310 +{ 1.311 + if (gOperatorArray) { 1.312 + delete[] gOperatorArray; 1.313 + gOperatorArray = nullptr; 1.314 + } 1.315 + if (gOperatorTable) { 1.316 + delete gOperatorTable; 1.317 + gOperatorTable = nullptr; 1.318 + } 1.319 +} 1.320 + 1.321 +void 1.322 +nsMathMLOperators::AddRefTable(void) 1.323 +{ 1.324 + gTableRefCount++; 1.325 +} 1.326 + 1.327 +void 1.328 +nsMathMLOperators::ReleaseTable(void) 1.329 +{ 1.330 + if (0 == --gTableRefCount) { 1.331 + CleanUp(); 1.332 + } 1.333 +} 1.334 + 1.335 +static OperatorData* 1.336 +GetOperatorData(const nsString& aOperator, nsOperatorFlags aForm) 1.337 +{ 1.338 + nsAutoString key(aOperator); 1.339 + key.AppendInt(aForm); 1.340 + return gOperatorTable->Get(key); 1.341 +} 1.342 + 1.343 +bool 1.344 +nsMathMLOperators::LookupOperator(const nsString& aOperator, 1.345 + const nsOperatorFlags aForm, 1.346 + nsOperatorFlags* aFlags, 1.347 + float* aLeadingSpace, 1.348 + float* aTrailingSpace) 1.349 +{ 1.350 + if (!gGlobalsInitialized) { 1.351 + InitGlobals(); 1.352 + } 1.353 + if (gOperatorTable) { 1.354 + NS_ASSERTION(aFlags && aLeadingSpace && aTrailingSpace, "bad usage"); 1.355 + NS_ASSERTION(aForm > 0 && aForm < 4, "*** invalid call ***"); 1.356 + 1.357 + // The MathML REC says: 1.358 + // If the operator does not occur in the dictionary with the specified form, 1.359 + // the renderer should use one of the forms which is available there, in the 1.360 + // order of preference: infix, postfix, prefix. 1.361 + 1.362 + OperatorData* found; 1.363 + int32_t form = NS_MATHML_OPERATOR_GET_FORM(aForm); 1.364 + if (!(found = GetOperatorData(aOperator, form))) { 1.365 + if (form == NS_MATHML_OPERATOR_FORM_INFIX || 1.366 + !(found = 1.367 + GetOperatorData(aOperator, NS_MATHML_OPERATOR_FORM_INFIX))) { 1.368 + if (form == NS_MATHML_OPERATOR_FORM_POSTFIX || 1.369 + !(found = 1.370 + GetOperatorData(aOperator, NS_MATHML_OPERATOR_FORM_POSTFIX))) { 1.371 + if (form != NS_MATHML_OPERATOR_FORM_PREFIX) { 1.372 + found = GetOperatorData(aOperator, NS_MATHML_OPERATOR_FORM_PREFIX); 1.373 + } 1.374 + } 1.375 + } 1.376 + } 1.377 + if (found) { 1.378 + NS_ASSERTION(found->mStr.Equals(aOperator), "bad setup"); 1.379 + *aLeadingSpace = found->mLeadingSpace; 1.380 + *aTrailingSpace = found->mTrailingSpace; 1.381 + *aFlags &= ~NS_MATHML_OPERATOR_FORM; // clear the form bits 1.382 + *aFlags |= found->mFlags; // just add bits without overwriting 1.383 + return true; 1.384 + } 1.385 + } 1.386 + return false; 1.387 +} 1.388 + 1.389 +void 1.390 +nsMathMLOperators::LookupOperators(const nsString& aOperator, 1.391 + nsOperatorFlags* aFlags, 1.392 + float* aLeadingSpace, 1.393 + float* aTrailingSpace) 1.394 +{ 1.395 + if (!gGlobalsInitialized) { 1.396 + InitGlobals(); 1.397 + } 1.398 + 1.399 + aFlags[NS_MATHML_OPERATOR_FORM_INFIX] = 0; 1.400 + aLeadingSpace[NS_MATHML_OPERATOR_FORM_INFIX] = 0.0f; 1.401 + aTrailingSpace[NS_MATHML_OPERATOR_FORM_INFIX] = 0.0f; 1.402 + 1.403 + aFlags[NS_MATHML_OPERATOR_FORM_POSTFIX] = 0; 1.404 + aLeadingSpace[NS_MATHML_OPERATOR_FORM_POSTFIX] = 0.0f; 1.405 + aTrailingSpace[NS_MATHML_OPERATOR_FORM_POSTFIX] = 0.0f; 1.406 + 1.407 + aFlags[NS_MATHML_OPERATOR_FORM_PREFIX] = 0; 1.408 + aLeadingSpace[NS_MATHML_OPERATOR_FORM_PREFIX] = 0.0f; 1.409 + aTrailingSpace[NS_MATHML_OPERATOR_FORM_PREFIX] = 0.0f; 1.410 + 1.411 + if (gOperatorTable) { 1.412 + OperatorData* found; 1.413 + found = GetOperatorData(aOperator, NS_MATHML_OPERATOR_FORM_INFIX); 1.414 + if (found) { 1.415 + aFlags[NS_MATHML_OPERATOR_FORM_INFIX] = found->mFlags; 1.416 + aLeadingSpace[NS_MATHML_OPERATOR_FORM_INFIX] = found->mLeadingSpace; 1.417 + aTrailingSpace[NS_MATHML_OPERATOR_FORM_INFIX] = found->mTrailingSpace; 1.418 + } 1.419 + found = GetOperatorData(aOperator, NS_MATHML_OPERATOR_FORM_POSTFIX); 1.420 + if (found) { 1.421 + aFlags[NS_MATHML_OPERATOR_FORM_POSTFIX] = found->mFlags; 1.422 + aLeadingSpace[NS_MATHML_OPERATOR_FORM_POSTFIX] = found->mLeadingSpace; 1.423 + aTrailingSpace[NS_MATHML_OPERATOR_FORM_POSTFIX] = found->mTrailingSpace; 1.424 + } 1.425 + found = GetOperatorData(aOperator, NS_MATHML_OPERATOR_FORM_PREFIX); 1.426 + if (found) { 1.427 + aFlags[NS_MATHML_OPERATOR_FORM_PREFIX] = found->mFlags; 1.428 + aLeadingSpace[NS_MATHML_OPERATOR_FORM_PREFIX] = found->mLeadingSpace; 1.429 + aTrailingSpace[NS_MATHML_OPERATOR_FORM_PREFIX] = found->mTrailingSpace; 1.430 + } 1.431 + } 1.432 +} 1.433 + 1.434 +/* static */ bool 1.435 +nsMathMLOperators::IsMirrorableOperator(const nsString& aOperator) 1.436 +{ 1.437 + // LookupOperator will search infix, postfix and prefix forms of aOperator and 1.438 + // return the first form found. It is assumed that all these forms have same 1.439 + // mirrorability. 1.440 + nsOperatorFlags flags = 0; 1.441 + float dummy; 1.442 + nsMathMLOperators::LookupOperator(aOperator, 1.443 + NS_MATHML_OPERATOR_FORM_INFIX, 1.444 + &flags, &dummy, &dummy); 1.445 + return NS_MATHML_OPERATOR_IS_MIRRORABLE(flags); 1.446 +} 1.447 + 1.448 +/* static */ nsStretchDirection 1.449 +nsMathMLOperators::GetStretchyDirection(const nsString& aOperator) 1.450 +{ 1.451 + // LookupOperator will search infix, postfix and prefix forms of aOperator and 1.452 + // return the first form found. It is assumed that all these forms have same 1.453 + // direction. 1.454 + nsOperatorFlags flags = 0; 1.455 + float dummy; 1.456 + nsMathMLOperators::LookupOperator(aOperator, 1.457 + NS_MATHML_OPERATOR_FORM_INFIX, 1.458 + &flags, &dummy, &dummy); 1.459 + 1.460 + if (NS_MATHML_OPERATOR_IS_DIRECTION_VERTICAL(flags)) { 1.461 + return NS_STRETCH_DIRECTION_VERTICAL; 1.462 + } else if (NS_MATHML_OPERATOR_IS_DIRECTION_HORIZONTAL(flags)) { 1.463 + return NS_STRETCH_DIRECTION_HORIZONTAL; 1.464 + } else { 1.465 + return NS_STRETCH_DIRECTION_UNSUPPORTED; 1.466 + } 1.467 +}