void MarkupAccumulator::appendQuotedURLAttributeValue(StringBuilder& result, const Element* element, const Attribute& attribute) { ASSERT(element->isURLAttribute(const_cast<Attribute*>(&attribute))); const String resolvedURLString = resolveURLIfNeeded(element, attribute.value()); UChar quoteChar = '\"'; String strippedURLString = resolvedURLString.stripWhiteSpace(); if (protocolIsJavaScript(strippedURLString)) { // minimal escaping for javascript urls if (strippedURLString.contains('"')) { if (strippedURLString.contains('\'')) strippedURLString.replace('\"', """); else quoteChar = '\''; } result.append(quoteChar); result.append(strippedURLString); result.append(quoteChar); return; } // FIXME: This does not fully match other browsers. Firefox percent-escapes non-ASCII characters for innerHTML. result.append(quoteChar); appendAttributeValue(result, resolvedURLString, false); result.append(quoteChar); }
void MarkupAccumulator::appendNamespace(StringBuilder& result, const AtomicString& prefix, const AtomicString& namespaceURI, Namespaces& namespaces, bool allowEmptyDefaultNS) { namespaces.checkConsistency(); if (namespaceURI.isEmpty()) { // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-xhtml-syntax.html#xml-fragment-serialization-algorithm if (allowEmptyDefaultNS && namespaces.get(emptyAtom.impl())) { result.append(' '); result.append(xmlnsAtom.string()); result.appendLiteral("=\"\""); } return; } // Use emptyAtoms's impl() for both null and empty strings since the HashMap can't handle 0 as a key AtomicStringImpl* pre = prefix.isEmpty() ? emptyAtom.impl() : prefix.impl(); AtomicStringImpl* foundNS = namespaces.get(pre); if (foundNS != namespaceURI.impl() && !namespaces.get(namespaceURI.impl())) { namespaces.set(pre, namespaceURI.impl()); result.append(' '); result.append(xmlnsAtom.string()); if (!prefix.isEmpty()) { result.append(':'); result.append(prefix); } result.append('='); result.append('"'); appendAttributeValue(result, namespaceURI, false); result.append('"'); } }
void MarkupFormatter::appendAttribute(StringBuilder& result, const Element& element, const Attribute& attribute, Namespaces* namespaces) { bool documentIsHTML = serializeAsHTMLDocument(element); QualifiedName prefixedName = attribute.name(); if (documentIsHTML && !attributeIsInSerializedNamespace(attribute)) { result.append(' '); result.append(attribute.name().localName()); } else { if (attribute.namespaceURI() == XMLNSNames::xmlnsNamespaceURI) { if (!attribute.prefix() && attribute.localName() != xmlnsAtom) prefixedName.setPrefix(xmlnsAtom); if (namespaces) { // Account for the namespace attribute we're about to append. const AtomicString& lookupKey = (!attribute.prefix()) ? emptyAtom : attribute.localName(); namespaces->set(lookupKey, attribute.value()); } } else if (attribute.namespaceURI() == XMLNames::xmlNamespaceURI) { if (!attribute.prefix()) prefixedName.setPrefix(xmlAtom); } else { if (attribute.namespaceURI() == XLinkNames::xlinkNamespaceURI) { if (!attribute.prefix()) prefixedName.setPrefix(xlinkAtom); } if (namespaces && shouldAddNamespaceAttribute(attribute, element)) { if (!prefixedName.prefix()) { // This behavior is in process of being standardized. See crbug.com/248044 and https://www.w3.org/Bugs/Public/show_bug.cgi?id=24208 String prefixPrefix("ns", 2); for (unsigned i = attribute.namespaceURI().impl()->existingHash(); ; ++i) { AtomicString newPrefix(String(prefixPrefix + String::number(i))); AtomicString foundURI = namespaces->get(newPrefix); if (foundURI == attribute.namespaceURI() || foundURI == nullAtom) { // We already generated a prefix for this namespace. prefixedName.setPrefix(newPrefix); break; } } } ASSERT(prefixedName.prefix()); appendNamespace(result, prefixedName.prefix(), attribute.namespaceURI(), *namespaces); } } result.append(' '); result.append(prefixedName.toString()); } result.append('='); if (element.isURLAttribute(attribute)) { appendQuotedURLAttributeValue(result, element, attribute); } else { result.append('"'); appendAttributeValue(result, attribute.value(), documentIsHTML); result.append('"'); } }
void StyledMarkupAccumulator::appendStyleNodeOpenTag(StringBuilder& out, StylePropertySet* style, const Document& document, bool isBlock) { // wrappingStyleForSerialization should have removed -webkit-text-decorations-in-effect ASSERT(propertyMissingOrEqualToNone(style, CSSPropertyWebkitTextDecorationsInEffect)); if (isBlock) out.appendLiteral("<div style=\""); else out.appendLiteral("<span style=\""); appendAttributeValue(out, style->asText(), document.isHTMLDocument()); out.appendLiteral("\">"); }
void StyledMarkupAccumulator::appendElement(StringBuilder& out, Element& element, bool addDisplayInline) { const bool documentIsHTML = element.document().isHTMLDocument(); appendOpenTag(out, element, 0); const bool shouldAnnotateOrForceInline = element.isHTMLElement() && (shouldAnnotate() || addDisplayInline); const bool shouldOverrideStyleAttr = shouldAnnotateOrForceInline || shouldApplyWrappingStyle(element); AttributeCollection attributes = element.attributes(); AttributeCollection::iterator end = attributes.end(); for (AttributeCollection::iterator it = attributes.begin(); it != end; ++it) { // We'll handle the style attribute separately, below. if (it->name() == HTMLNames::styleAttr && shouldOverrideStyleAttr) continue; appendAttribute(out, element, *it, 0); } if (shouldOverrideStyleAttr) { RefPtr<EditingStyle> newInlineStyle = nullptr; if (shouldApplyWrappingStyle(element)) { newInlineStyle = m_wrappingStyle->copy(); newInlineStyle->removePropertiesInElementDefaultStyle(&element); newInlineStyle->removeStyleConflictingWithStyleOfElement(&element); } else newInlineStyle = EditingStyle::create(); if (element.isStyledElement() && element.inlineStyle()) newInlineStyle->overrideWithStyle(element.inlineStyle()); if (shouldAnnotateOrForceInline) { if (shouldAnnotate()) newInlineStyle->mergeStyleFromRulesForSerialization(&toHTMLElement(element)); if (&element == m_highestNodeToBeSerialized && m_shouldAnnotate == AnnotateForNavigationTransition) newInlineStyle->addAbsolutePositioningFromElement(element); if (addDisplayInline) newInlineStyle->forceInline(); } if (!newInlineStyle->isEmpty()) { out.appendLiteral(" style=\""); appendAttributeValue(out, newInlineStyle->style()->asText(), documentIsHTML); out.append('\"'); } } appendCloseTag(out, element); }
void MarkupAccumulator::appendNamespace(StringBuilder& result, const AtomicString& prefix, const AtomicString& namespaceURI, Namespaces& namespaces) { if (namespaceURI.isEmpty()) return; const AtomicString& lookupKey = (!prefix) ? emptyAtom : prefix; AtomicString foundURI = namespaces.get(lookupKey); if (foundURI != namespaceURI) { namespaces.set(lookupKey, namespaceURI); result.append(' '); result.append(xmlnsAtom.string()); if (!prefix.isEmpty()) { result.append(':'); result.append(prefix); } result.appendLiteral("=\""); appendAttributeValue(result, namespaceURI, false); result.append('"'); } }
void MarkupAccumulator::appendNamespace(StringBuilder& result, const AtomicString& prefix, const AtomicString& namespaceURI, Namespaces& namespaces) { if (namespaceURI.isEmpty()) return; // Use emptyAtoms's impl() for both null and empty strings since the HashMap can't handle 0 as a key StringImpl* pre = prefix.isEmpty() ? emptyAtom.impl() : prefix.impl(); StringImpl* foundNS = namespaces.get(pre); if (foundNS != namespaceURI.impl()) { namespaces.set(pre, namespaceURI.impl()); result.append(' '); result.append(xmlnsAtom.string()); if (!prefix.isEmpty()) { result.append(':'); result.append(prefix); } result.appendLiteral("=\""); appendAttributeValue(result, namespaceURI, false); result.append('"'); } }
void MarkupAccumulator::appendAttribute(StringBuilder& result, const Element& element, const Attribute& attribute, Namespaces* namespaces) { bool documentIsHTML = element.document().isHTMLDocument(); result.append(' '); QualifiedName prefixedName = attribute.name(); if (documentIsHTML && !attributeIsInSerializedNamespace(attribute)) result.append(attribute.name().localName()); else { if (!attribute.namespaceURI().isEmpty()) { AtomicStringImpl* foundNS = namespaces && attribute.prefix().impl() ? namespaces->get(attribute.prefix().impl()) : 0; bool prefixIsAlreadyMappedToOtherNS = foundNS && foundNS != attribute.namespaceURI().impl(); if (attribute.prefix().isEmpty() || !foundNS || prefixIsAlreadyMappedToOtherNS) { if (AtomicStringImpl* prefix = namespaces ? namespaces->get(attribute.namespaceURI().impl()) : 0) prefixedName.setPrefix(AtomicString(prefix)); else { bool shouldBeDeclaredUsingAppendNamespace = !attribute.prefix().isEmpty() && !foundNS; if (!shouldBeDeclaredUsingAppendNamespace && attribute.localName() != xmlnsAtom && namespaces) generateUniquePrefix(prefixedName, *namespaces); } } } result.append(prefixedName.toString()); } result.append('='); if (element.isURLAttribute(attribute)) appendQuotedURLAttributeValue(result, element, attribute); else { result.append('"'); appendAttributeValue(result, attribute.value(), documentIsHTML); result.append('"'); } if ((inXMLFragmentSerialization() || !documentIsHTML) && namespaces && shouldAddNamespaceAttribute(attribute, *namespaces)) appendNamespace(result, prefixedName.prefix(), prefixedName.namespaceURI(), *namespaces); }
void MarkupAccumulator::appendNamespace(StringBuilder& result, const AtomicString& prefix, const AtomicString& namespaceURI, Namespaces& namespaces, bool allowEmptyDefaultNS) { namespaces.checkConsistency(); if (namespaceURI.isEmpty()) { // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-xhtml-syntax.html#xml-fragment-serialization-algorithm if (allowEmptyDefaultNS && namespaces.get(emptyAtom.impl())) { result.append(' '); result.append(xmlnsAtom.string()); result.appendLiteral("=\"\""); } return; } // Use emptyAtoms's impl() for both null and empty strings since the HashMap can't handle 0 as a key AtomicStringImpl* pre = prefix.isEmpty() ? emptyAtom.impl() : prefix.impl(); AtomicStringImpl* foundNS = namespaces.get(pre); if (foundNS != namespaceURI.impl()) { namespaces.set(pre, namespaceURI.impl()); // Add namespace to prefix pair so we can do constraint checking later. if (inXMLFragmentSerialization() && !prefix.isEmpty()) namespaces.set(namespaceURI.impl(), pre); // Make sure xml prefix and namespace are always known to uphold the constraints listed at http://www.w3.org/TR/xml-names11/#xmlReserved. if (namespaceURI.impl() == XMLNames::xmlNamespaceURI.impl()) return; result.append(' '); result.append(xmlnsAtom.string()); if (!prefix.isEmpty()) { result.append(':'); result.append(prefix); } result.append('='); result.append('"'); appendAttributeValue(result, namespaceURI, false); result.append('"'); } }
void MarkupAccumulator::appendAttribute(StringBuilder& out, Element* element, const Attribute& attribute, Namespaces* namespaces) { bool documentIsHTML = element->document()->isHTMLDocument(); out.append(' '); if (documentIsHTML) out.append(attribute.name().localName()); else out.append(attribute.name().toString()); out.append('='); if (element->isURLAttribute(const_cast<Attribute*>(&attribute))) appendQuotedURLAttributeValue(out, element, attribute); else { out.append('\"'); appendAttributeValue(out, attribute.value(), documentIsHTML); out.append('\"'); } if (!documentIsHTML && namespaces && shouldAddNamespaceAttribute(attribute, *namespaces)) appendNamespace(out, attribute.prefix(), attribute.namespaceURI(), *namespaces); }
void MarkupAccumulator::appendAttribute(StringBuilder& result, const Element& element, const Attribute& attribute, Namespaces* namespaces) { bool documentIsHTML = serializeAsHTMLDocument(element); result.append(' '); QualifiedName prefixedName = attribute.name(); if (documentIsHTML && !attributeIsInSerializedNamespace(attribute)) { result.append(attribute.name().localName()); } else { if (attribute.namespaceURI() == XLinkNames::xlinkNamespaceURI) { if (!attribute.prefix()) prefixedName.setPrefix(xlinkAtom); } else if (attribute.namespaceURI() == XMLNames::xmlNamespaceURI) { if (!attribute.prefix()) prefixedName.setPrefix(xmlAtom); } else if (attribute.namespaceURI() == XMLNSNames::xmlnsNamespaceURI) { if (attribute.name() != XMLNSNames::xmlnsAttr && !attribute.prefix()) prefixedName.setPrefix(xmlnsAtom); } result.append(prefixedName.toString()); } result.append('='); if (element.isURLAttribute(attribute)) { appendQuotedURLAttributeValue(result, element, attribute); } else { result.append('"'); appendAttributeValue(result, attribute.value(), documentIsHTML); result.append('"'); } if (!documentIsHTML && namespaces && shouldAddNamespaceAttribute(attribute, *namespaces)) appendNamespace(result, prefixedName.prefix(), prefixedName.namespaceURI(), *namespaces); }