Bug 1032979 part 2. Getting outerHTML on a node from an XML document should not use the self-closing form of empty container tags from the HTML namespace. r=smaug

--HG--
rename : testing/web-platform/tests/html/syntax/serializing-html-fragments/outerHTML.html => testing/web-platform/tests/html/syntax/serializing-xml-fragments/outerHTML.html
This commit is contained in:
Boris Zbarsky 2016-01-13 21:36:25 -05:00
Родитель 34ae6e1bac
Коммит 6efc9783c7
6 изменённых файлов: 121 добавлений и 108 удалений

Просмотреть файл

@ -412,47 +412,6 @@ nsXHTMLContentSerializer::SerializeAttributes(nsIContent* aContent,
return true;
}
bool
nsXHTMLContentSerializer::AppendEndOfElementStart(nsIContent *aOriginalElement,
nsIAtom * aName,
int32_t aNamespaceID,
nsAString& aStr)
{
// this method is not called by nsHTMLContentSerializer
// so we don't have to check HTML element, just XHTML
NS_ASSERTION(!mIsHTMLSerializer, "nsHTMLContentSerializer shouldn't call this method !");
if (kNameSpaceID_XHTML != aNamespaceID) {
return nsXMLContentSerializer::AppendEndOfElementStart(aOriginalElement, aName,
aNamespaceID, aStr);
}
nsIContent* content = aOriginalElement;
// for non empty elements, even if they are not a container, we always
// serialize their content, because the XHTML element could contain non XHTML
// nodes useful in some context, like in an XSLT stylesheet
if (HasNoChildren(content)) {
nsIParserService* parserService = nsContentUtils::GetParserService();
if (parserService) {
bool isContainer;
parserService->
IsContainer(parserService->HTMLCaseSensitiveAtomTagToId(aName),
isContainer);
if (!isContainer) {
// for backward compatibility with HTML 4 user agents
// only non-container HTML elements can be closed immediatly,
// and a space is added before />
return AppendToString(NS_LITERAL_STRING(" />"), aStr);
}
}
}
return AppendToString(kGreaterThan, aStr);
}
bool
nsXHTMLContentSerializer::AfterElementStart(nsIContent* aContent,
nsIContent* aOriginalElement,
@ -552,52 +511,26 @@ nsXHTMLContentSerializer::CheckElementStart(nsIContent * aContent,
}
bool
nsXHTMLContentSerializer::CheckElementEnd(nsIContent * aContent,
bool & aForceFormat,
nsXHTMLContentSerializer::CheckElementEnd(Element* aElement,
bool& aForceFormat,
nsAString& aStr)
{
NS_ASSERTION(!mIsHTMLSerializer, "nsHTMLContentSerializer shouldn't call this method !");
aForceFormat = !(mFlags & nsIDocumentEncoder::OutputIgnoreMozDirty) &&
aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::mozdirty);
aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::mozdirty);
// this method is not called by nsHTMLContentSerializer
// so we don't have to check HTML element, just XHTML
if (aContent->IsHTMLElement()) {
if (mIsCopying && aContent->IsHTMLElement(nsGkAtoms::ol)) {
NS_ASSERTION((!mOLStateStack.IsEmpty()), "Cannot have an empty OL Stack");
/* Though at this point we must always have an state to be deleted as all
the OL opening tags are supposed to push an olState object to the stack*/
if (!mOLStateStack.IsEmpty()) {
if (mIsCopying && aElement->IsHTMLElement(nsGkAtoms::ol)) {
NS_ASSERTION((!mOLStateStack.IsEmpty()), "Cannot have an empty OL Stack");
/* Though at this point we must always have an state to be deleted as all
the OL opening tags are supposed to push an olState object to the stack*/
if (!mOLStateStack.IsEmpty()) {
mOLStateStack.RemoveElementAt(mOLStateStack.Length() -1);
}
}
if (HasNoChildren(aContent)) {
nsIParserService* parserService = nsContentUtils::GetParserService();
if (parserService) {
bool isContainer;
parserService->
IsContainer(parserService->HTMLCaseSensitiveAtomTagToId(
aContent->NodeInfo()->NameAtom()),
isContainer);
if (!isContainer) {
// non-container HTML elements are already closed,
// see AppendEndOfElementStart
return false;
}
}
}
// for backward compatibility with old HTML user agents,
// empty elements should have an ending tag, so we mustn't call
// nsXMLContentSerializer::CheckElementEnd
return true;
}
bool dummyFormat;
return nsXMLContentSerializer::CheckElementEnd(aContent, dummyFormat, aStr);
return nsXMLContentSerializer::CheckElementEnd(aElement, dummyFormat, aStr);
}
bool

Просмотреть файл

@ -47,20 +47,14 @@ class nsXHTMLContentSerializer : public nsXMLContentSerializer {
nsAString& aStr,
nsresult& aResult) override;
MOZ_WARN_UNUSED_RESULT
virtual bool AppendEndOfElementStart(nsIContent *aOriginalElement,
nsIAtom * aName,
int32_t aNamespaceID,
nsAString& aStr) override;
MOZ_WARN_UNUSED_RESULT
virtual bool AfterElementStart(nsIContent* aContent,
nsIContent* aOriginalElement,
nsAString& aStr) override;
virtual bool CheckElementEnd(nsIContent * aContent,
bool & aForceFormat,
nsAString& aStr) override;
virtual bool CheckElementEnd(mozilla::dom::Element* aContent,
bool& aForceFormat,
nsAString& aStr) override;
virtual void AfterElementEnd(nsIContent * aContent,
nsAString& aStr) override;

Просмотреть файл

@ -960,8 +960,7 @@ nsXMLContentSerializer::AppendElementStart(Element* aElement,
name, aStr, skipAttr, addNSAttr),
NS_ERROR_OUT_OF_MEMORY);
NS_ENSURE_TRUE(AppendEndOfElementStart(aOriginalElement, name,
content->GetNameSpaceID(), aStr),
NS_ENSURE_TRUE(AppendEndOfElementStart(aElement, aOriginalElement, aStr),
NS_ERROR_OUT_OF_MEMORY);
if ((mDoFormat || forceFormat) && !mDoRaw && !PreLevel()
@ -974,19 +973,56 @@ nsXMLContentSerializer::AppendElementStart(Element* aElement,
return NS_OK;
}
// aElement is the actual element we're outputting. aOriginalElement is the one
// in the original DOM, which is the one we have to test for kids.
static bool
ElementNeedsSeparateEndTag(Element* aElement, Element* aOriginalElement)
{
if (aOriginalElement->GetChildCount()) {
// We have kids, so we need a separate end tag. This needs to be checked on
// aOriginalElement because that's the one that's actually in the DOM and
// might have kids.
return true;
}
if (!aElement->IsHTMLElement()) {
// Empty non-HTML elements can just skip a separate end tag.
return false;
}
// HTML container tags should have a separate end tag even if empty, per spec.
// See
// https://w3c.github.io/DOM-Parsing/#dfn-concept-xml-serialization-algorithm
bool isHTMLContainer = true; // Default in case we get no parser service.
nsIParserService* parserService = nsContentUtils::GetParserService();
if (parserService) {
nsIAtom* localName = aElement->NodeInfo()->NameAtom();
parserService->IsContainer(
parserService->HTMLCaseSensitiveAtomTagToId(localName),
isHTMLContainer);
}
return isHTMLContainer;
}
bool
nsXMLContentSerializer::AppendEndOfElementStart(nsIContent *aOriginalElement,
nsIAtom * aName,
int32_t aNamespaceID,
nsXMLContentSerializer::AppendEndOfElementStart(Element* aElement,
Element* aOriginalElement,
nsAString& aStr)
{
// We don't output a separate end tag for empty elements
if (!aOriginalElement->GetChildCount()) {
return AppendToString(NS_LITERAL_STRING("/>"), aStr);
}
else {
if (ElementNeedsSeparateEndTag(aElement, aOriginalElement)) {
return AppendToString(kGreaterThan, aStr);
}
// We don't need a separate end tag. For HTML elements (which at this point
// must be non-containers), append a space before the '/', per spec. See
// https://w3c.github.io/DOM-Parsing/#dfn-concept-xml-serialization-algorithm
if (aOriginalElement->IsHTMLElement()) {
if (!AppendToString(kSpace, aStr)) {
return false;
}
}
return AppendToString(NS_LITERAL_STRING("/>"), aStr);
}
NS_IMETHODIMP
@ -998,7 +1034,7 @@ nsXMLContentSerializer::AppendElementEnd(Element* aElement,
nsIContent* content = aElement;
bool forceFormat = false, outputElementEnd;
outputElementEnd = CheckElementEnd(content, forceFormat, aStr);
outputElementEnd = CheckElementEnd(aElement, forceFormat, aStr);
nsIAtom *name = content->NodeInfo()->NameAtom();
@ -1119,13 +1155,17 @@ nsXMLContentSerializer::CheckElementStart(nsIContent * aContent,
}
bool
nsXMLContentSerializer::CheckElementEnd(nsIContent * aContent,
bool & aForceFormat,
nsXMLContentSerializer::CheckElementEnd(Element* aElement,
bool& aForceFormat,
nsAString& aStr)
{
// We don't output a separate end tag for empty element
aForceFormat = false;
return aContent->GetChildCount() > 0;
// XXXbz this is a bit messed up, but by now we don't have our fixed-up
// version of aElement anymore. Let's hope fixup never changes the localName
// or namespace...
return ElementNeedsSeparateEndTag(aElement, aElement);
}
bool

Просмотреть файл

@ -233,14 +233,16 @@ class nsXMLContentSerializer : public nsIContentSerializer {
nsresult& aResult);
/**
* this method is responsible to finish the start tag,
* in particulary to append the "greater than" sign
* This method is responsible for appending the '>' at the end of the start
* tag, possibly preceded by '/' and maybe a ' ' before that too.
*
* aElement and aOriginalElement are the same as the corresponding arguments
* to AppendElementStart.
*/
MOZ_WARN_UNUSED_RESULT
virtual bool AppendEndOfElementStart(nsIContent *aOriginalElement,
nsIAtom * aName,
int32_t aNamespaceID,
nsAString& aStr);
bool AppendEndOfElementStart(mozilla::dom::Element* aEleemnt,
mozilla::dom::Element* aOriginalElement,
nsAString& aStr);
/**
* This method can be redefine to serialize additional things just after
@ -260,9 +262,9 @@ class nsXMLContentSerializer : public nsIContentSerializer {
* by setting aForceFormat to true.
* @return boolean true if the element can be output
*/
virtual bool CheckElementEnd(nsIContent * aContent,
bool & aForceFormat,
nsAString& aStr);
virtual bool CheckElementEnd(mozilla::dom::Element* aElement,
bool& aForceFormat,
nsAString& aStr);
/**
* This method can be redefine to serialize additional things just after

Просмотреть файл

@ -30174,6 +30174,12 @@
"deleted": [],
"items": {
"testharness": {
"html/syntax/serializing-xml-fragments/outerHTML.html": [
{
"path": "html/syntax/serializing-xml-fragments/outerHTML.html",
"url": "/html/syntax/serializing-xml-fragments/outerHTML.html"
}
],
"streams/byte-length-queuing-strategy.https.html": [
{
"path": "streams/byte-length-queuing-strategy.https.html",

Просмотреть файл

@ -0,0 +1,38 @@
<!DOCTYPE html>
<html>
<head>
<title>HTML Test: element.outerHTML to verify XML fragment serialization algorithm</title>
<link rel="author" title="Intel" href="http://www.intel.com/">
<link rel="help" href="https://w3c.github.io/DOM-Parsing/#dfn-concept-serialize-xml">
<link rel="help" href="https://w3c.github.io/DOM-Parsing/#widl-Element-outerHTML">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../html-element-list.js"></script>
</head>
<body>
<div id="log"></div>
<script>
test(function() {
var doc = document.implementation.createDocument(null, "");
assert_equals(doc.contentType, "application/xml");
var html_ns = "http://www.w3.org/1999/xhtml";
elements_with_end_tag.forEach(function(ele) {
test(function() {
var e = doc.createElementNS(html_ns, ele);
assert_equals(e.outerHTML,
`<${ele} xmlns="${html_ns}"></${ele}>`,
ele + " node created." );
}, "Node for " + ele);
});
elements_without_end_tag.forEach(function(ele) {
test(function() {
var e = doc.createElementNS(html_ns, ele);
assert_equals(e.outerHTML,
`<${ele} xmlns="${html_ns}" />`,
ele + " node created." );
}, "Node for " + ele);
});
}, document.title);
</script>
</body>
</html>