Index: Source/core/html/HTMLMetaElement-in.cpp |
diff --git a/Source/core/html/HTMLMetaElement-in.cpp b/Source/core/html/HTMLMetaElement-in.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..8761610c0741654ae8012a40411d326caff59081 |
--- /dev/null |
+++ b/Source/core/html/HTMLMetaElement-in.cpp |
@@ -0,0 +1,600 @@ |
+/* |
+ * Copyright (C) 1999 Lars Knoll (knoll@kde.org) |
+ * (C) 1999 Antti Koivisto (koivisto@kde.org) |
+ * (C) 2001 Dirk Mueller (mueller@kde.org) |
+ * Copyright (C) 2003, 2010 Apple Inc. All rights reserved. |
+ * Copyright (C) 2013 Intel Corporation. All rights reserved. |
+ * |
+ * This library is free software; you can redistribute it and/or |
+ * modify it under the terms of the GNU Library General Public |
+ * License as published by the Free Software Foundation; either |
+ * version 2 of the License, or (at your option) any later version. |
+ * |
+ * This library is distributed in the hope that it will be useful, |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+ * Library General Public License for more details. |
+ * |
+ * You should have received a copy of the GNU Library General Public License |
+ * along with this library; see the file COPYING.LIB. If not, write to |
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
+ * Boston, MA 02110-1301, USA. |
+ */ |
+ |
+#include "config.h" |
+#include "core/html/HTMLMetaElement.h" |
+ |
+#include "HTMLNames.h" |
+#include "RuntimeEnabledFeatures.h" |
+#include "core/css/CSSStyleSheet.h" |
+#include "core/css/CSSValuePool.h" |
+#include "core/css/MediaList.h" |
+#include "core/css/StylePropertySet.h" |
+#include "core/css/StyleSheetContents.h" |
+#include "core/dom/Document.h" |
+#include "core/dom/DocumentStyleSheetCollection.h" |
+#include "wtf/text/StringBuilder.h" |
+#include "wtf/text/TextPosition.h" |
+ |
+namespace WebCore { |
+ |
+using namespace HTMLNames; |
+using namespace std; |
+ |
+inline HTMLMetaElement::HTMLMetaElement(const QualifiedName& tagName, Document* document) |
+ : HTMLElement(tagName, document) |
+{ |
+ ASSERT(hasTagName(metaTag)); |
+ ScriptWrappable::init(this); |
+} |
+ |
+HTMLMetaElement::~HTMLMetaElement() |
+{ |
+ removeStyleSheet(); |
+} |
+ |
+PassRefPtr<HTMLMetaElement> HTMLMetaElement::create(Document* document) |
+{ |
+ return adoptRef(new HTMLMetaElement(metaTag, document)); |
+} |
+ |
+PassRefPtr<HTMLMetaElement> HTMLMetaElement::create(const QualifiedName& tagName, Document* document) |
+{ |
+ return adoptRef(new HTMLMetaElement(tagName, document)); |
+} |
+ |
+void HTMLMetaElement::removedFrom(ContainerNode* insertionPoint) |
+{ |
+ HTMLElement::removedFrom(insertionPoint); |
+ removeStyleSheet(); |
+} |
+ |
+Node::InsertionNotificationRequest HTMLMetaElement::insertedInto(ContainerNode* insertionPoint) |
+{ |
+ HTMLElement::insertedInto(insertionPoint); |
+ if (insertionPoint->inDocument()) |
+ process(); |
+ return InsertionDone; |
+} |
+ |
+ |
+static inline bool isValueSeparator(UChar c, bool* ok) |
+{ |
+ if (ok && c == ';') |
+ *ok = false; |
+ // Though isspace() considers \t and \v to be whitespace, Win IE doesn't. |
+ return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '=' || c == ',' || c == '\0'; |
+} |
+ |
+static inline bool isPropertySeparator(UChar c, bool* ok) |
+{ |
+ if (ok && c == ';') |
+ *ok = false; |
+ return c == ','; |
+} |
+ |
+float HTMLMetaElement::parsePositiveNumber(const String& property, const String& value, float minValue, float maxValue) |
+{ |
+ size_t parsedLength; |
+ float rawValue; |
+ float fValue; |
+ if (value.is8Bit()) |
+ rawValue = charactersToFloat(value.characters8(), value.length(), parsedLength); |
+ else |
+ rawValue = charactersToFloat(value.characters16(), value.length(), parsedLength); |
+ |
+ if (!parsedLength || rawValue < 0) { |
+ reportError(InvalidValueError, property, value); |
+ return -1; |
+ } |
+ |
+ if (parsedLength < value.length()) |
+ reportError(TruncatedValueError, property, value); |
+ |
+ fValue = min(maxValue, max(rawValue, minValue)); |
+ if (fValue != rawValue) |
+ reportError(OutOfBoundsValueError, property, value); |
+ |
+ return fValue; |
+} |
+ |
+void HTMLMetaElement::reportError(ErrorType error, const String& property, const String& value) |
+{ |
+ StringBuilder builder; |
+ |
+ switch (error) { |
+ case NoError: |
+ return; |
+ |
+ case InvalidPropertySeparatorError: |
+ builder.appendLiteral("Note that ';' is not a property separator. The list should be comma-separated."); |
+ break; |
+ |
+ case InvalidPropertyError: |
+ builder.appendLiteral("Property \""); |
+ builder.append(property); |
+ builder.appendLiteral("\" not recognized and ignored."); |
+ break; |
+ |
+ case InvalidValueError: |
+ case OutOfBoundsValueError: |
+ case TruncatedValueError: |
+ if (!value.isEmpty()) { |
+ builder.appendLiteral("The value \""); |
+ builder.append(value); |
+ builder.append('\"'); |
+ } else { |
+ builder.appendLiteral("The empty value"); |
+ } |
+ |
+ if (!property.isNull()) { |
+ builder.appendLiteral(" for property \""); |
+ builder.append(property); |
+ builder.append('\"'); |
+ } |
+ |
+ if (error == InvalidValueError) |
+ builder.appendLiteral(" is invalid and the property has been ignored."); |
+ else if (error == OutOfBoundsValueError) |
+ builder.appendLiteral(" is out of bounds and the value has been clamped."); |
+ else |
+ builder.appendLiteral(" was truncated to its numeric prefix."); |
+ break; |
+ |
+ default: |
+ ASSERT_NOT_REACHED(); |
+ } |
+ |
+ document()->addConsoleMessage(MetaContentMessageSource, WarningMessageLevel, builder.toString()); |
+} |
+ |
+void HTMLMetaElement::parseContentAttribute(MutableStylePropertySet* properties, const String& content, PropertyCollector callback) |
+{ |
+ bool ok = true; |
+ |
+ // Tread lightly in this code -- it was specifically designed to mimic Win IE's parsing behavior. |
+ int keyBegin, keyEnd; |
+ int valueBegin, valueEnd; |
+ |
+ int i = 0; |
+ int length = content.length(); |
+ String buffer = content.lower(); |
+ while (i < length) { |
+ // Skip to first non-separator, but don't skip past the end of the string. |
+ while (isValueSeparator(buffer[i], &ok)) { |
+ if (i >= length) |
+ break; |
+ i++; |
+ } |
+ keyBegin = i; |
+ |
+ // skip to first separator |
+ while (!isValueSeparator(buffer[i], &ok)) |
+ i++; |
+ keyEnd = i; |
+ |
+ // Skip to first '=', but don't skip past a ',' or the end of the string. |
+ while (buffer[i] != '=') { |
+ if (isPropertySeparator(buffer[i], &ok) || i >= length) |
+ break; |
+ i++; |
+ } |
+ |
+ // Skip to first non-separator, but don't skip past a ',' or the end of the string. |
+ while (isValueSeparator(buffer[i], &ok)) { |
+ if (isPropertySeparator(buffer[i], &ok) || i >= length) |
+ break; |
+ i++; |
+ } |
+ valueBegin = i; |
+ |
+ // Skip to first separator. |
+ while (!isValueSeparator(buffer[i], &ok)) |
+ i++; |
+ valueEnd = i; |
+ |
+ ASSERT_WITH_SECURITY_IMPLICATION(i <= length); |
+ |
+ String propertyString = buffer.substring(keyBegin, keyEnd - keyBegin); |
+ String valueString = buffer.substring(valueBegin, valueEnd - valueBegin); |
+ |
+ callback(this, properties, propertyString, valueString); |
+ } |
+ if (!ok) |
+ reportError(InvalidPropertySeparatorError, String(), String()); |
+} |
+ |
+PassRefPtr<CSSValue> HTMLMetaElement::parseViewportValueAsLength(const String& propertyString, const String& valueString) |
+{ |
+ const UChar* characters; |
+ unsigned valueLength = valueString.length(); |
+ |
+ const unsigned longestValueLength = 13; |
+ UChar characterBuffer[longestValueLength]; |
+ if (valueString.is8Bit()) { |
+ unsigned length = std::min(longestValueLength, valueLength); |
+ const LChar* characters8 = valueString.characters8(); |
+ for (unsigned i = 0; i < length; ++i) |
+ characterBuffer[i] = characters8[i]; |
+ characters = characterBuffer; |
+ } else { |
+ characters = valueString.characters16(); |
+ } |
+ |
+ SWITCH(characters, valueLength) { |
+ CASE("device-width") { |
+ return cssValuePool().createValue(100, CSSPrimitiveValue::CSS_PERCENTAGE); |
+ } |
+ CASE("device-height") { |
+ return cssValuePool().createValue(100, CSSPrimitiveValue::CSS_PERCENTAGE); |
+ } |
+ } |
+ |
+ // Other keywords and unknown values translate to 1px. |
+ float value = parsePositiveNumber(propertyString, valueString, float(1), float(10000)); |
+ if (value >= 0) |
+ return cssValuePool().createValue(value, CSSPrimitiveValue::CSS_PX); |
+ |
+ return 0; |
+} |
+ |
+PassRefPtr<CSSValue> HTMLMetaElement::parseViewportValueAsZoom(const String& propertyString, const String& valueString) |
+{ |
+ const UChar* characters; |
+ unsigned valueLength = valueString.length(); |
+ |
+ const unsigned longestValueLength = 13; |
+ UChar characterBuffer[longestValueLength]; |
+ if (valueString.is8Bit()) { |
+ unsigned length = std::min(longestValueLength, valueLength); |
+ const LChar* characters8 = valueString.characters8(); |
+ for (unsigned i = 0; i < length; ++i) |
+ characterBuffer[i] = characters8[i]; |
+ characters = characterBuffer; |
+ } else { |
+ characters = valueString.characters16(); |
+ } |
+ |
+ float value = -1; |
+ SWITCH(characters, valueLength) { |
+ CASE("yes") { |
+ value = 1; |
+ } |
+ CASE("no") { |
+ value = 0.1; |
+ } |
+ CASE("device-width") { |
+ value = 10; |
+ } |
+ CASE("device-height") { |
+ value = 10; |
+ } |
+ } |
+ |
+ if (value < 0) |
+ value = parsePositiveNumber(propertyString, valueString, float(0.1), float(10)); |
+ if (value >= 0) |
+ return cssValuePool().createValue(value, CSSPrimitiveValue::CSS_NUMBER); |
+ |
+ return 0; |
+} |
+ |
+CSSValueID HTMLMetaElement::parseViewportValueAsUserZoom(const String& propertyString, const String& valueString) |
+{ |
+ const UChar* characters; |
+ unsigned valueLength = valueString.length(); |
+ |
+ const unsigned longestValueLength = 13; |
+ UChar characterBuffer[longestValueLength]; |
+ if (valueString.is8Bit()) { |
+ unsigned length = std::min(longestValueLength, valueLength); |
+ const LChar* characters8 = valueString.characters8(); |
+ for (unsigned i = 0; i < length; ++i) |
+ characterBuffer[i] = characters8[i]; |
+ characters = characterBuffer; |
+ } else { |
+ characters = valueString.characters16(); |
+ } |
+ |
+ SWITCH(characters, valueLength) { |
+ CASE("yes") { |
+ return CSSValueZoom; |
+ } |
+ CASE("no") { |
+ return CSSValueFixed; |
+ } |
+ CASE("device-width") { |
+ return CSSValueZoom; |
+ } |
+ CASE("device-height") { |
+ return CSSValueZoom; |
+ } |
+ } |
+ |
+ float value = parsePositiveNumber(propertyString, valueString, float(0), float(1)); |
+ // Numbers in the range <-1, 1>, and unknown values (represented as 0), are mapped to "fixed". |
+ // Numbers >= 1, numbers <= -1 are mapped to "zoom" |
+ return (value > -1 && value < 1) ? CSSValueFixed : CSSValueZoom; |
+} |
+ |
+PassRefPtr<CSSValue> HTMLMetaElement::parseViewportValueAsDPI(const String& propertyString, const String& valueString) |
+{ |
+ const UChar* characters; |
+ unsigned valueLength = valueString.length(); |
+ |
+ const unsigned longestValueLength = 10; |
+ UChar characterBuffer[longestValueLength]; |
+ if (valueString.is8Bit()) { |
+ unsigned length = std::min(longestValueLength, valueLength); |
+ const LChar* characters8 = valueString.characters8(); |
+ for (unsigned i = 0; i < length; ++i) |
+ characterBuffer[i] = characters8[i]; |
+ characters = characterBuffer; |
+ } else { |
+ characters = valueString.characters16(); |
+ } |
+ |
+ CSSValueID id = CSSValueAuto; // Fallback for invalid. |
+ SWITCH(characters, valueLength) { |
+ CASE("device-dpi") { |
+ id = CSSValueDevice; |
+ } |
+ CASE("low-dpi") { |
+ id = CSSValueSmall; |
+ } |
+ CASE("medium-dpi") { |
+ id = CSSValueMedium; |
+ } |
+ CASE("high-dpi") { |
+ id = CSSValueLarge; |
+ } |
+ } |
+ |
+ if (id == CSSValueAuto) { |
+ float value = parsePositiveNumber(propertyString, valueString, float(70), float(400)); |
+ |
+ if (value >= 70 && value <= 400) |
+ return cssValuePool().createValue(value, CSSPrimitiveValue::CSS_NUMBER); |
+ } |
+ |
+ return cssValuePool().createIdentifierValue(id); |
+} |
+ |
+void HTMLMetaElement::viewportPropertyCollector(HTMLMetaElement* self, MutableStylePropertySet* properties, const String& propertyString, const String& valueString) |
+{ |
+ const UChar* characters; |
+ unsigned propertyLength = propertyString.length(); |
+ |
+ const unsigned longestPropertyLength = 17; |
+ UChar characterBuffer[longestPropertyLength]; |
+ if (propertyString.is8Bit()) { |
+ unsigned length = std::min(longestPropertyLength, propertyLength); |
+ const LChar* characters8 = propertyString.characters8(); |
+ for (unsigned i = 0; i < length; ++i) |
+ characterBuffer[i] = characters8[i]; |
+ characters = characterBuffer; |
+ } else { |
+ characters = propertyString.characters16(); |
+ } |
+ |
+ SWITCH(characters, propertyLength) { |
+ CASE("width") { |
+ RefPtr<CSSValue> value = self->parseViewportValueAsLength(propertyString, valueString); |
+ if (!value) |
+ return; |
+ properties->setProperty(CSSPropertyMinWidth, CSSValueInternalExtendToZoom); |
+ properties->setProperty(CSSPropertyMaxWidth, value); |
+ return; |
+ } |
+ CASE("height") { |
+ RefPtr<CSSValue> value = self->parseViewportValueAsLength(propertyString, valueString); |
+ if (!value) |
+ return; |
+ properties->setProperty(CSSPropertyMinHeight, CSSValueInternalExtendToZoom); |
+ properties->setProperty(CSSPropertyMaxHeight, value); |
+ return; |
+ } |
+ CASE("initial-scale") { |
+ if (RefPtr<CSSValue> value = self->parseViewportValueAsZoom(propertyString, valueString)) |
+ properties->setProperty(CSSPropertyZoom, value); |
+ return; |
+ } |
+ CASE("minimum-scale") { |
+ if (RefPtr<CSSValue> value = self->parseViewportValueAsZoom(propertyString, valueString)) |
+ properties->setProperty(CSSPropertyMinZoom, value); |
+ return; |
+ } |
+ CASE("maximum-scale") { |
+ if (RefPtr<CSSValue> value = self->parseViewportValueAsZoom(propertyString, valueString)) |
+ properties->setProperty(CSSPropertyMaxZoom, value); |
+ return; |
+ } |
+ CASE("user-scalable") { |
+ CSSValueID value = self->parseViewportValueAsUserZoom(propertyString, valueString); |
+ properties->setProperty(CSSPropertyUserZoom, value); |
+ return; |
+ } |
+ CASE("target-densitydpi") { |
+ RefPtr<CSSValue> value = self->parseViewportValueAsDPI(propertyString, valueString); |
+ properties->setProperty(CSSPropertyInternalTargetDensity, value); |
+ return; |
+ } |
+ } |
+ self->reportError(InvalidPropertyError, propertyString, String()); |
+} |
+ |
+PassRefPtr<StyleRuleBase> HTMLMetaElement::parseViewportContent(const String& contentValue) |
+{ |
+ RefPtr<StyleRuleViewport> rule = StyleRuleViewport::create(); |
+ RefPtr<MutableStylePropertySet> properties = MutableStylePropertySet::create(CSSStrictMode); |
+ parseContentAttribute(properties.get(), contentValue, &HTMLMetaElement::viewportPropertyCollector); |
+ |
+ // For a viewport META element that translates into an @viewport rule with a |
+ // non-"auto" "zoom" declaration and no "width" declaration: |
+ // If it adds a "height" descriptor, add: width: auto; Otherwise, add: width: extend-to-zoom; |
+ RefPtr<CSSValue> widthValue = properties->getPropertyCSSValue(CSSPropertyMinWidth); |
+ RefPtr<CSSValue> zoomValue = properties->getPropertyCSSValue(CSSPropertyZoom); |
+ |
+ if (!widthValue && zoomValue) { |
+ RefPtr<CSSValue> heightValue = properties->getPropertyCSSValue(CSSPropertyMinHeight); |
+ CSSValueID commonHeightValue = heightValue ? CSSValueAuto : CSSValueInternalExtendToZoom; |
+ properties->setProperty(CSSPropertyMinWidth, commonHeightValue); |
+ properties->setProperty(CSSPropertyMaxWidth, commonHeightValue); |
+ } |
+ |
+ rule->setProperties(properties); |
+ |
+ return rule.release(); |
+} |
+ |
+PassRefPtr<StyleRuleBase> HTMLMetaElement::parseHandheldFriendlyContent(const String& contentValue) |
+{ |
+ RefPtr<StyleRuleViewport> rule = StyleRuleViewport::create(); |
+ RefPtr<MutableStylePropertySet> properties = MutableStylePropertySet::create(CSSStrictMode); |
+ properties->setProperty(CSSPropertyInternalPriority, cssValuePool().createValue(2, CSSPrimitiveValue::CSS_NUMBER)); |
+ |
+ if (equalIgnoringCase(contentValue, "true")) { |
+ properties->setProperty(CSSPropertyMinWidth, cssValuePool().createValue(100, CSSPrimitiveValue::CSS_PERCENTAGE)); |
+ properties->setProperty(CSSPropertyMaxWidth, cssValuePool().createValue(100, CSSPrimitiveValue::CSS_PERCENTAGE)); |
+ } |
+ |
+ rule->setProperties(properties); |
+ return rule.release(); |
+} |
+ |
+PassRefPtr<StyleRuleBase> HTMLMetaElement::parseMobileOptimizedContent(const String& contentValue) |
+{ |
+ RefPtr<StyleRuleViewport> rule = StyleRuleViewport::create(); |
+ RefPtr<MutableStylePropertySet> properties = MutableStylePropertySet::create(CSSStrictMode); |
+ properties->setProperty(CSSPropertyInternalPriority, cssValuePool().createValue(3, CSSPrimitiveValue::CSS_NUMBER)); |
+ |
+ float value = parsePositiveNumber(String(), contentValue, float(0), float(10000)); |
+ RefPtr<CSSValue> widthValue; |
+ if (value > 0) |
+ widthValue = cssValuePool().createValue(value, CSSPrimitiveValue::CSS_PX); |
+ else |
+ widthValue = cssValuePool().createValue(100, CSSPrimitiveValue::CSS_PERCENTAGE); |
+ |
+ properties->setProperty(CSSPropertyMinWidth, CSSValueAuto); |
+ properties->setProperty(CSSPropertyMaxWidth, widthValue); |
+ |
+ rule->setProperties(properties); |
+ return rule.release(); |
+} |
+ |
+void HTMLMetaElement::parseAttribute(const QualifiedName& name, const AtomicString& value) |
+{ |
+ if (name == http_equivAttr || name == contentAttr) { |
+ process(); |
+ } else if (name == nameAttr) { |
+ // Do nothing. |
+ } else { |
+ HTMLElement::parseAttribute(name, value); |
+ } |
+} |
+ |
+void HTMLMetaElement::process() |
+{ |
+ // We always need to clear rules as the meta tag could have been modified. |
+ removeStyleSheet(); |
+ |
+ if (!inDocument()) |
+ return; |
+ |
+ // All below situations requires a content attribute (which can be the empty string). |
+ const AtomicString& contentValue = fastGetAttribute(contentAttr); |
+ if (contentValue.isNull()) |
+ return; |
+ |
+ const AtomicString& nameValue = fastGetAttribute(nameAttr); |
+ |
+ if (nameValue.isNull()) { |
+ // Get the document to process the tag, but only if we're actually part of DOM tree (changing a meta tag while |
+ // it's not in the tree shouldn't have any effect on the document) |
+ const AtomicString& httpEquivValue = fastGetAttribute(http_equivAttr); |
+ if (!httpEquivValue.isNull()) |
+ document()->processHttpEquiv(httpEquivValue, contentValue); |
+ return; |
+ } |
+ |
+ if (equalIgnoringCase(nameValue, "viewport")) { |
+ addStyleSheetForRule(parseViewportContent(contentValue)); |
+ } else if (equalIgnoringCase(name(), "handheldfriendly")) { |
+ addStyleSheetForRule(parseHandheldFriendlyContent(contentValue)); |
+ } else if (equalIgnoringCase(name(), "mobileoptimized")) { |
+ addStyleSheetForRule(parseMobileOptimizedContent(contentValue)); |
+ } else if (equalIgnoringCase(name(), "referrer")) { |
+ document()->processReferrerPolicy(contentValue); |
+ } |
+} |
+ |
+void HTMLMetaElement::addStyleSheetForRule(PassRefPtr<StyleRuleBase> rule) |
+{ |
+ ASSERT(inDocument()); |
+ ASSERT(!m_sheet); |
+ |
+ document()->styleSheetCollection()->addStyleSheetCandidateNode(this, false); |
+ |
+ document()->styleSheetCollection()->addPendingSheet(); |
+ |
+ // As the stylesheet has no source URL, the position makes no difference. |
+ TextPosition startPosition = TextPosition::minimumPosition(); |
+ |
+ m_sheet = CSSStyleSheet::createInline(this, KURL(), startPosition, document()->inputEncoding()); |
+ |
+ m_sheet->setMediaQueries(MediaQuerySet::create("screen")); |
+ m_sheet->setTitle(title()); |
+ |
+ m_sheet->contents()->parserAppendRule(rule); |
+ |
+ document()->styleSheetCollection()->removePendingSheet(m_sheet->ownerNode()); |
+} |
+ |
+void HTMLMetaElement::removeStyleSheet() |
+{ |
+ if (m_sheet) { |
+ document()->styleSheetCollection()->removeStyleSheetCandidateNode(this); |
+ m_sheet.release()->clearOwnerNode(); |
+ } |
+ |
+ // No need to resolve style during teardown. |
+ if (document()->renderer()) |
+ document()->styleResolverChanged(DeferRecalcStyle); |
+} |
+ |
+String HTMLMetaElement::content() const |
+{ |
+ return getAttribute(contentAttr); |
+} |
+ |
+String HTMLMetaElement::httpEquiv() const |
+{ |
+ return getAttribute(http_equivAttr); |
+} |
+ |
+String HTMLMetaElement::name() const |
+{ |
+ return getNameAttribute(); |
+} |
+ |
+} |