blob: 18b772ea28a8a27a50d929d226ff2861f1136c1d [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/core/mathml/mathml_element.h"
#include "third_party/blink/renderer/bindings/core/v8/js_event_handler_for_content_attribute.h"
#include "third_party/blink/renderer/core/css/css_property_name.h"
#include "third_party/blink/renderer/core/css/parser/css_parser.h"
#include "third_party/blink/renderer/core/css_value_keywords.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/dom/node_computed_style.h"
#include "third_party/blink/renderer/core/html/html_element.h"
#include "third_party/blink/renderer/platform/wtf/text/character_visitor.h"
#include "third_party/blink/renderer/platform/wtf/text/string_to_number.h"
namespace blink {
MathMLElement::MathMLElement(const QualifiedName& tagName,
Document& document,
ConstructionType constructionType)
: Element(tagName, &document, constructionType) {}
MathMLElement::~MathMLElement() {}
static inline bool IsValidDirAttribute(const AtomicString& value) {
return EqualIgnoringASCIICase(value, "ltr") ||
EqualIgnoringASCIICase(value, "rtl");
}
// Keywords from MathML3 and CSS font-size are skipped.
static inline bool IsDisallowedMathSizeAttribute(const AtomicString& value) {
return EqualIgnoringASCIICase(value, "medium") ||
value.EndsWith("large", kTextCaseASCIIInsensitive) ||
value.EndsWith("small", kTextCaseASCIIInsensitive) ||
EqualIgnoringASCIICase(value, "smaller") ||
EqualIgnoringASCIICase(value, "larger");
}
bool MathMLElement::IsPresentationAttribute(const QualifiedName& name) const {
if (name == html_names::kDirAttr || name == mathml_names::kMathsizeAttr ||
name == mathml_names::kMathcolorAttr ||
name == mathml_names::kMathbackgroundAttr ||
name == mathml_names::kMathvariantAttr ||
name == mathml_names::kScriptlevelAttr ||
name == mathml_names::kDisplayAttr ||
name == mathml_names::kDisplaystyleAttr)
return true;
return Element::IsPresentationAttribute(name);
}
namespace {
bool ParseScriptLevel(const AtomicString& attributeValue,
unsigned& scriptLevel,
bool& add) {
String value = attributeValue;
if (value.StartsWith("+") || value.StartsWith("-")) {
add = true;
value = value.Right(1);
}
return WTF::VisitCharacters(
value, [&](const auto* position, unsigned length) {
WTF::NumberParsingResult result;
WTF::NumberParsingOptions options(
WTF::NumberParsingOptions::kAcceptMinusZeroForUnsigned);
scriptLevel = CharactersToUInt(position, length, options, &result);
return result == WTF::NumberParsingResult::kSuccess;
});
}
} // namespace
void MathMLElement::CollectStyleForPresentationAttribute(
const QualifiedName& name,
const AtomicString& value,
MutableCSSPropertyValueSet* style) {
if (name == html_names::kDirAttr) {
if (IsValidDirAttribute(value)) {
AddPropertyToPresentationAttributeStyle(style, CSSPropertyID::kDirection,
value);
}
} else if (name == mathml_names::kMathsizeAttr) {
if (!IsDisallowedMathSizeAttribute(value)) {
AddPropertyToPresentationAttributeStyle(style, CSSPropertyID::kFontSize,
value);
}
} else if (name == mathml_names::kMathbackgroundAttr) {
AddPropertyToPresentationAttributeStyle(
style, CSSPropertyID::kBackgroundColor, value);
} else if (name == mathml_names::kMathcolorAttr) {
AddPropertyToPresentationAttributeStyle(style, CSSPropertyID::kColor,
value);
} else if (name == mathml_names::kScriptlevelAttr) {
unsigned scriptLevel = 0;
bool add = false;
if (ParseScriptLevel(value, scriptLevel, add)) {
if (add) {
AddPropertyToPresentationAttributeStyle(
style, CSSPropertyID::kMathDepth, "add(" + value + ")");
} else {
AddPropertyToPresentationAttributeStyle(
style, CSSPropertyID::kMathDepth, scriptLevel,
CSSPrimitiveValue::UnitType::kNumber);
}
}
} else if (name == mathml_names::kDisplayAttr &&
HasTagName(mathml_names::kMathTag)) {
if (EqualIgnoringASCIICase(value, "inline")) {
AddPropertyToPresentationAttributeStyle(style, CSSPropertyID::kDisplay,
CSSValueID::kMath);
AddPropertyToPresentationAttributeStyle(style, CSSPropertyID::kMathStyle,
CSSValueID::kCompact);
} else if (EqualIgnoringASCIICase(value, "block")) {
AddPropertyToPresentationAttributeStyle(style, CSSPropertyID::kDisplay,
"block math");
AddPropertyToPresentationAttributeStyle(style, CSSPropertyID::kMathStyle,
CSSValueID::kNormal);
}
} else if (name == mathml_names::kDisplaystyleAttr) {
if (EqualIgnoringASCIICase(value, "false")) {
AddPropertyToPresentationAttributeStyle(style, CSSPropertyID::kMathStyle,
CSSValueID::kCompact);
} else if (EqualIgnoringASCIICase(value, "true")) {
AddPropertyToPresentationAttributeStyle(style, CSSPropertyID::kMathStyle,
CSSValueID::kNormal);
}
} else if (name == mathml_names::kMathvariantAttr) {
// TODO(crbug.com/1076420): this needs to handle all mathvariant values.
if (EqualIgnoringASCIICase(value, "normal")) {
AddPropertyToPresentationAttributeStyle(
style, CSSPropertyID::kTextTransform, CSSValueID::kNone);
}
} else {
Element::CollectStyleForPresentationAttribute(name, value, style);
}
}
void MathMLElement::ParseAttribute(const AttributeModificationParams& param) {
const AtomicString& event_name =
HTMLElement::EventNameForAttributeName(param.name);
if (!event_name.IsNull()) {
SetAttributeEventListener(
event_name, JSEventHandlerForContentAttribute::Create(
GetExecutionContext(), param.name, param.new_value));
return;
}
Element::ParseAttribute(param);
}
base::Optional<bool> MathMLElement::BooleanAttribute(
const QualifiedName& name) const {
const AtomicString& value = FastGetAttribute(name);
if (EqualIgnoringASCIICase(value, "true"))
return true;
if (EqualIgnoringASCIICase(value, "false"))
return false;
return base::nullopt;
}
base::Optional<Length> MathMLElement::AddMathLengthToComputedStyle(
const CSSToLengthConversionData& conversion_data,
const QualifiedName& attr_name,
AllowPercentages allow_percentages) {
if (!FastHasAttribute(attr_name))
return base::nullopt;
auto value = FastGetAttribute(attr_name);
const CSSPrimitiveValue* parsed_value = CSSParser::ParseLengthPercentage(
value,
StrictCSSParserContext(GetExecutionContext()->GetSecureContextMode()));
if (!parsed_value || parsed_value->IsCalculated() ||
(parsed_value->IsPercentage() &&
(!value.EndsWith('%') || allow_percentages == AllowPercentages::kNo)))
return base::nullopt;
return parsed_value->ConvertToLength(conversion_data);
}
bool MathMLElement::IsTokenElement() const {
return HasTagName(mathml_names::kMiTag) || HasTagName(mathml_names::kMoTag) ||
HasTagName(mathml_names::kMnTag) ||
HasTagName(mathml_names::kMtextTag) ||
HasTagName(mathml_names::kMsTag);
}
} // namespace blink