+2007-03-28 Alexey Proskuryakov <ap@webkit.org>
+
+ Reviewed by Darin.
+
+ http://bugs.webkit.org/show_bug.cgi?id=13190
+ XPath incorrectly handles namespaces on attributes
+
+ * fast/xpath/attr-namespace-expected.txt: Added.
+ * fast/xpath/attr-namespace.html: Added.
+
+ * fast/xpath/xpath-namespaces-expected.txt:
+ * fast/xpath/xpath-namespaces.html:
+ Cleaned up; added a couple more cases (which passed anyway, but weren't tested for).
+
2007-03-27 Darin Adler <darin@apple.com>
* svg/hixie/text/003-expected.txt: Updated results for this one test that now has
--- /dev/null
+PASS doc.evaluate("//@attr1", doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null).snapshotLength is 1
+PASS doc.evaluate("//@attr2", doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null).snapshotLength is 0
+PASS doc.evaluate("//@ns:attr2", doc, nsResolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null).snapshotLength is 1
+PASS doc.evaluate("//@ns:xmlns", doc, nsResolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null).snapshotLength is 1
+PASS doc.evaluate("//@xml:id", doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null).snapshotLength threw exception Error: NAMESPACE_ERR: DOM Exception 14.
+PASS doc.evaluate("//@xml:id", doc, nsResolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null).snapshotLength is 1
+PASS doc.evaluate("//@*", doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null).snapshotLength is 4
+PASS doc.evaluate("//@ns:*", doc, nsResolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null).snapshotLength is 2
+PASS doc.evaluate("//@xml:*", doc, nsResolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null).snapshotLength is 1
+PASS doc.evaluate("//@xmlns", doc, nsResolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null).snapshotLength is 0
+PASS doc.evaluate("//@xmlns:*", doc, nsResolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null).snapshotLength is 0
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
--- /dev/null
+<html>
+<head>
+<link rel="stylesheet" href="../js/resources/js-test-style.css">
+<script src="../js/resources/js-test-pre.js"></script>
+<script src="xpath-test-pre.js"></script>
+</head>
+<body>
+<div id="console"></div>
+
+<script>
+function nsResolver(prefix) {
+ if (prefix == "ns")
+ return "foobarns";
+ if (prefix == "xml")
+ return "http://www.w3.org/XML/1998/namespace";
+ if (prefix == "xmlns")
+ return "http://www.w3.org/2000/xmlns/";
+ alert("Unexpected prefix " + prefix);
+}
+
+doc = (new DOMParser).parseFromString(
+ '<doc>' +
+ ' <elem attr1="1" ns:attr2="2" xml:id="3" ns:xmlns="4" xmlns:ns="foobarns" xmlns="barfoons"/>' +
+ '</doc>',
+ 'application/xml');
+
+shouldBe('doc.evaluate("//@attr1", doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null).snapshotLength', '1');
+shouldBe('doc.evaluate("//@attr2", doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null).snapshotLength', '0');
+shouldBe('doc.evaluate("//@ns:attr2", doc, nsResolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null).snapshotLength', '1');
+shouldBe('doc.evaluate("//@ns:xmlns", doc, nsResolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null).snapshotLength', '1');
+shouldThrow('doc.evaluate("//@xml:id", doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null).snapshotLength');
+shouldBe('doc.evaluate("//@xml:id", doc, nsResolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null).snapshotLength', '1');
+shouldBe('doc.evaluate("//@*", doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null).snapshotLength', '4');
+shouldBe('doc.evaluate("//@ns:*", doc, nsResolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null).snapshotLength', '2');
+shouldBe('doc.evaluate("//@xml:*", doc, nsResolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null).snapshotLength', '1');
+shouldBe('doc.evaluate("//@xmlns", doc, nsResolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null).snapshotLength', '0');
+shouldBe('doc.evaluate("//@xmlns:*", doc, nsResolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null).snapshotLength', '0');
+
+var successfullyParsed = true;
+
+</script>
+<script src="../js/resources/js-test-post.js"></script>
+</body>
+</html>
This tests that XPath expressions with prefixes work correctly.
-SUCCESS: test completed
+
+PASS /ns:foo
+PASS /ns:*
+PASS /foo:*
+PASS successfullyParsed is true
+
+TEST COMPLETE
<html>
- <head>
- <script>
-function debug(str) {
- var d = document.getElementById('console');
- d.appendChild(document.createTextNode(str + "\n"));
-}
-
-function runTests () {
- if (window.layoutTestController)
- layoutTestController.dumpAsText();
-
- var xmlString = '<ns:foo xmlns:ns="http://www.example.org"/>';
+<head>
+<link rel="stylesheet" href="../js/resources/js-test-style.css">
+<script src="../js/resources/js-test-pre.js"></script>
+<script src="xpath-test-pre.js"></script>
+</head>
+<body>
+<p>This tests that XPath expressions with prefixes work correctly.</p>
+<div id="console"></div>
+<script>
+ var xmlString = '<ns:foo xmlns:ns="http://www.example.org" xmlns:foo="urn:foobar"/>';
var doc = (new DOMParser()).parseFromString(xmlString, "text/xml");
var contextNode = doc.documentElement;
var nsResolver = document.createNSResolver(contextNode);
var expr = doc.createExpression("/ns:foo", nsResolver);
- var result = expr.evaluate(contextNode, XPathResult.ANY_TYPE, null)
+ var result = expr.evaluate(contextNode, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
+ checkSnapshot("/ns:foo", result, [doc.documentElement]);
- var element = result.iterateNext();
+ var expr = doc.createExpression("/ns:*", nsResolver);
+ var result = expr.evaluate(contextNode, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
+ checkSnapshot("/ns:*", result, [doc.documentElement]);
- if (element == 0) {
- debug('FAILURE: no result node was found');
- return;
- }
-
- if (element.nodeName != 'ns:foo') {
- debug('FAILURE: did not find the correct node');
- return;
- }
-
- debug('SUCCESS: test completed')
-}
- </script>
- </head>
-<body onload="runTests()">
-This tests that XPath expressions with prefixes work correctly.
-<pre id="console">
-</pre>
+ var expr = doc.createExpression("/foo:*", nsResolver);
+ var result = expr.evaluate(contextNode, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
+ checkSnapshot("/foo:*", result, []);
+
+ var successfullyParsed = true;
+
+</script>
+<script src="../js/resources/js-test-post.js"></script>
</body>
</html>
+2007-03-28 Alexey Proskuryakov <ap@webkit.org>
+
+ Reviewed by Darin.
+
+ http://bugs.webkit.org/show_bug.cgi?id=13190
+ XPath incorrectly handles namespaces on attributes
+
+ * xml/XPathStep.cpp:
+ (WebCore::XPath::Step::nodesInAxis): Added a special case for faster attribute lookup; gives a slight but
+ measurable performance improvement for bug 13021.
+ (WebCore::XPath::Step::nodeMatches): Fixed NameTest for attribute nodes.
+
+ * xml/XPathStep.h:
+ (WebCore::XPath::Step::NodeTest::NodeTest):
+ (WebCore::XPath::Step::NodeTest::namespaceURI):
+ (WebCore::XPath::Step::nodeTest):
+ (WebCore::XPath::Step::setNodeTest):
+ Move m_namespaceURI to NodeTest, where it belongs. Removed unused m_nodeTestData (oops!).
+
+ * xml/XPathGrammar.y:
+ * xml/XPathPath.cpp:
+ (WebCore::XPath::LocationPath::optimizeStepPair):
+ Accounted for the above change.
+
2007-03-28 Oliver Hunt <oliver@apple.com>
rs=Hyatt.
}
if ($2) {
- $$ = new Step(Step::ChildAxis, Step::NodeTest(Step::NodeTest::NameTest, localName), namespaceURI, *$2);
+ $$ = new Step(Step::ChildAxis, Step::NodeTest(Step::NodeTest::NameTest, localName, namespaceURI), *$2);
PARSER->deletePredicateVector($2);
} else
- $$ = new Step(Step::ChildAxis, Step::NodeTest(Step::NodeTest::NameTest, localName), namespaceURI);
+ $$ = new Step(Step::ChildAxis, Step::NodeTest(Step::NodeTest::NameTest, localName, namespaceURI));
PARSER->deleteString($1);
PARSER->registerParseNode($$);
}
}
if ($3) {
- $$ = new Step($1, Step::NodeTest(Step::NodeTest::NameTest, localName), namespaceURI, *$3);
+ $$ = new Step($1, Step::NodeTest(Step::NodeTest::NameTest, localName, namespaceURI), *$3);
PARSER->deletePredicateVector($3);
} else
- $$ = new Step($1, Step::NodeTest(Step::NodeTest::NameTest, localName), namespaceURI);
+ $$ = new Step($1, Step::NodeTest(Step::NodeTest::NameTest, localName, namespaceURI));
PARSER->deleteString($2);
PARSER->registerParseNode($$);
}
Step* second = m_steps[index + 1];
if (second->axis() == Step::ChildAxis
- && second->namespaceURI().isEmpty()
+ && second->nodeTest().namespaceURI().isEmpty()
&& second->nodeTest().kind() == Step::NodeTest::NameTest
&& second->nodeTest().data() == "*") {
{
}
-Step::Step(Axis axis, const NodeTest& nodeTest, const String& namespaceURI, const Vector<Predicate*>& predicates)
- : m_axis(axis)
- , m_nodeTest(nodeTest)
- , m_namespaceURI(namespaceURI)
- , m_predicates(predicates)
-{
-}
-
Step::~Step()
{
deleteAllValues(m_predicates);
if (context->nodeType() != Node::ELEMENT_NODE)
return nodes;
+ // Avoid lazily creating attribute nodes for attributes that we do not need anyway.
+ if (m_nodeTest.kind() == NodeTest::NameTest && m_nodeTest.data() != "*") {
+ RefPtr<Node> n = static_cast<Element*>(context)->getAttributeNodeNS(m_nodeTest.namespaceURI(), m_nodeTest.data());
+ if (n && n->namespaceURI() != "http://www.w3.org/2000/xmlns/") // In XPath land, namespace nodes are not accessible on the attribute axis.
+ nodes.append(n.release());
+ return nodes;
+ }
+
NamedAttrMap* attrs = context->attributes();
if (!attrs)
return nodes;
return true;
case NodeTest::NameTest: {
const String& name = m_nodeTest.data();
- if (name == "*")
- return node->nodeType() == primaryNodeType(m_axis) && (m_namespaceURI.isEmpty() || m_namespaceURI == node->namespaceURI());
+ const String& namespaceURI = m_nodeTest.namespaceURI();
if (m_axis == AttributeAxis) {
+ ASSERT(node->isAttributeNode());
+
// In XPath land, namespace nodes are not accessible on the attribute axis.
- if (name == "xmlns")
+ if (node->namespaceURI() == "http://www.w3.org/2000/xmlns/")
return false;
- // FIXME: check the namespace!
- return node->nodeName() == name;
- } else if (m_axis == NamespaceAxis) {
+ if (name == "*")
+ return namespaceURI.isEmpty() || node->namespaceURI() == namespaceURI;
+
+ return node->localName() == name && node->namespaceURI() == namespaceURI;
+ }
+
+ if (m_axis == NamespaceAxis) {
// Node test on the namespace axis is not implemented yet
- } else {
- // We use tagQName here because we don't want the element name in uppercase
- // like we get with HTML elements.
- // Paths without namespaces should match HTML elements in HTML documents despite those having an XHTML namespace.
- return node->nodeType() == Node::ELEMENT_NODE
- && static_cast<Element*>(node)->tagQName().localName() == name
- && ((node->isHTMLElement() && node->document()->isHTMLDocument() && m_namespaceURI.isNull()) || m_namespaceURI == node->namespaceURI());
+ return false;
}
+
+ if (name == "*")
+ return node->nodeType() == primaryNodeType(m_axis) && (namespaceURI.isEmpty() || namespaceURI == node->namespaceURI());
+
+ // We use tagQName here because we don't want the element name in uppercase
+ // like we get with HTML elements.
+ // Paths without namespaces should match HTML elements in HTML documents despite those having an XHTML namespace.
+ return node->nodeType() == Node::ELEMENT_NODE
+ && static_cast<Element*>(node)->tagQName().localName() == name
+ && ((node->isHTMLElement() && node->document()->isHTMLDocument() && namespaceURI.isNull()) || namespaceURI == node->namespaceURI());
}
}
ASSERT_NOT_REACHED();
ElementNodeTest // XPath 2.0
};
- NodeTest(Kind kind, const String& data = String()) : m_kind(kind), m_data(data) {}
+ NodeTest(Kind kind) : m_kind(kind) {}
+ NodeTest(Kind kind, const String& data) : m_kind(kind), m_data(data) {}
+ NodeTest(Kind kind, const String& data, const String& namespaceURI) : m_kind(kind), m_data(data), m_namespaceURI(namespaceURI) {}
Kind kind() const { return m_kind; }
const String data() const { return m_data; }
+ const String namespaceURI() const { return m_namespaceURI; }
private:
Kind m_kind;
String m_data;
+ String m_namespaceURI;
};
Step(Axis, const NodeTest& nodeTest, const Vector<Predicate*>& predicates = Vector<Predicate*>());
- Step(Axis, const NodeTest& nodeTest, const String& namespaceURI, const Vector<Predicate*>& predicates = Vector<Predicate*>());
~Step();
NodeSet evaluate(Node* context) const;
Axis axis() const { return m_axis; }
NodeTest nodeTest() const { return m_nodeTest; }
- const String& nodeTestData() const { return m_nodeTestData; }
- const String& namespaceURI() const { return m_namespaceURI; }
const Vector<Predicate*>& predicates() const { return m_predicates; }
void setAxis(Axis axis) { m_axis = axis; }
void setNodeTest(NodeTest nodeTest) { m_nodeTest = nodeTest; }
- void setNodeTestData(const String& nodeTestData) { m_nodeTestData = nodeTestData; }
- void setNamespaceURI(const String& namespaceURI) { m_namespaceURI = namespaceURI; }
void setPredicates(const Vector<Predicate*>& predicates) { m_predicates = predicates; }
private:
Axis m_axis;
NodeTest m_nodeTest;
- String m_nodeTestData;
- String m_namespaceURI;
Vector<Predicate*> m_predicates;
};