Files
palemoon27/layout/mathml/nsMathMLOperators.cpp
T
roytam1 1e24b22ef4 remove nsINetUtil_ESR_38, fixup and import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1150366 - Remove duplicate d3.js. r=jsantell (71f991da6)
- Bug 1175352 - Refactor LoadInfo arguments to be more self contained (r=bent) (3f7d08164)
- Bug 1181533 - Add nsCRTGlue.h and prtime.h includes to nsDownloadManager.cpp to fix --disable-safe-browsing build bustage. r=froydnj (af338edf1)
- Bug 905127 - Part 1 - Make some functions from nsNetUtil not inline. r=jduell Bug 905127 - Part 2 - remove unnecessary nsNetUtil.h includes r=jduell Bug 905127 - Make some functions from nsNetUtil not inline. r=jduell (bc47d5b3a)
- Bug 1117650 - Part 1: Move all CORS tests into dom/security/test. r=sicking (6f727387c)
- Bug 1117650 - Part 2: Move all MixedContent tests into dom/security/test. r=tanvi (73f273829)
- Bug 1004703 - ignore 'unsafe-inline' if nonce- or hash-source specifed - tests (r=sstamm) (ebf411159)
- Bug 921493 - CSP: test whitelisting of scheme-relative sources (r=dveditz) (f0618b189)
- Bug 1139569 - Optimize update function for element editors in markup view;r=mratcliffe (c60d38dca)
- Partil of Bug 1139644 - Make markupview use sdk timers instead of individual timers for each window;r=pbrosset (8bc1a4d66)
- Bug 1146568 - Avoid unsafe CPOW usage warnings in browser_markupview_tag_edit_08.js; r=bgrins Making use of the devtools test frame-script to set the test node's attributes instead of using a CPOW. (61d7eed64)
- Bug 1146568 - Avoid unsafe CPOW usage in markupview tests, in assertAttributes helper; r=bgrins (efc492d5d)
- Bug 858038 - Allow moving elements in the markupview by drag/drop; r=pbrosset (51d79c15c)
- Bug 1139644 - Flash only relevant attributes in markup view when changed;r=pbrosset (29807fa46)
- Bug 1147128 - Make sure attribute shows up in markup view after removing and setting to the previous value;r=mratcliffe (1947f7417)
- Bug 1147325 - Clear box model timer on markup view destroy. r=pbrosset Prevents 'this._inspector.toolbox is null' spam in mochitest-dt (1d6808b4a)
- Bug 921102 - 1 - Linkify URIs in the inspector; r=tromey, miker This first part adds a parser for node attributes which, given some node information and an attribute name, generates a small AST-like array of objects that tells which parts of the attribute (if any) are links, and what they link to. Using this, the markup-view generates the right HTML structure to display these parts as links. This part 1 doesn't yet allow users to follow these links. (c500e270e)
- Bug 1139667 - CSP tests for fetch(). r=ckirschb (df030457f)
- Bug 1086999 - CSP: Asterisk (*) wildcard should not allow blob:, data:, or filesystem: when matching source expressions - tests (r=sstamm) (dee0f2239)
- Bug 1117650 - Part 3: Move all CSP tests into dom/security/test. r=sstamm (29c95475c)
- Bug 1146566 - 1 - Use devtools common frame-script in markupview tests and add helper; r=bgrins (b10e42468)
- Bug 1146566 - 2 - Avoid using CPOWs in test browser_markupview_tag_edit_03.js; r=bgrins This change updates the browser_markupview_tag_edit_03.js test by making use of the getDomElementInfo message listener to retrieve information about the tested nodes without having to go through CPOWs. (07f0ee3ff)
- Bug 1036324 - Adds option to walker.parents() to not traverse DocShellTreeItems of different types (24123a5d2)
- Bug 921102 - 2 - Markup-view tests for attribute links; r=miker (f6da81799)
- Bug 901250 - Add scroll into view menu item for the inspector. r=bgrins (bfc7518b1)
- Bug 994055 - Part 1: Add a toggle sidebar panel button to the inspector. r=miker This adds a new icon, next to the searchbox (like in the debugger) to expand or collapse the sidebar panel in the inspector. The state is *not* persisted yet when the toolbox is closed. The button is hidden when the toolbox host switches to 'side'. (87730e20a)
- Bug 994055 - Part 2: Add tests for the inspector sidebar toggle button. r=miker (2872c3fbd)
- Bug 921102 - 3 - Open/copy markup-view attribute links; r=bgrins This part adds contextual menu items that become enabled when the user right clicks on an attribute that has a link. Depending on the nature of the link, a new tab will be opened or a node selected. The user can also choose to copy the link in the clipboard. (9b93485f6)
- Bug 921102 - 4 - Tests for the open/copy links on markup-view attributes; r=bgrins (861ba51f6)
2021-05-21 09:14:47 +08:00

470 lines
15 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 "nsDataHashtable.h"
#include "nsHashKeys.h"
#include "nsTArray.h"
#include "nsIPersistentProperties2.h"
#include "nsISimpleEnumerator.h"
#include "nsContentUtils.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 nsDataHashtable<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->Put(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),
NS_LITERAL_CSTRING("resource://gre/res/fonts/mathfont.properties"),
nsContentUtils::GetSystemPrincipal(),
nsIContentPolicy::TYPE_OTHER);
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];
if (!gOperatorArray) return NS_ERROR_OUT_OF_MEMORY;
}
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
InitGlobals()
{
gGlobalsInitialized = true;
nsresult rv = NS_ERROR_OUT_OF_MEMORY;
gOperatorTable = new nsDataHashtable<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) {
InitGlobals();
}
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) {
InitGlobals();
}
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;
}
}