Reviewed by Darin.

        A partial fix for http://bugs.webkit.org/show_bug.cgi?id=13021
        XPath can be very slow

        * xml/XPathExpression.cpp:
        (WebCore::XPathExpression::evaluate): Cache evaluationContext in a local variable.

        * xml/XPathExpressionNode.cpp:
        (WebCore::XPath::Expression::evaluationContext):
        * xml/XPathExpressionNode.h:
        (WebCore::XPath::Expression::addSubExpression):
        (WebCore::XPath::Expression::subExprCount):
        (WebCore::XPath::Expression::subExpr):
        * xml/XPathFunctions.cpp:
        * xml/XPathFunctions.h:
        (WebCore::XPath::Function::setName):
        (WebCore::XPath::Function::arg):
        (WebCore::XPath::Function::argCount):
        (WebCore::XPath::Function::name):
        Made one-liners critical for performance inline.

        * xml/XPathGrammar.y: Fully parse NodeTests, so that strings are no longer passed for what is
        essentially an enum. Use LocationPath accessors to add steps, instead of directly manipulating
        internal data members.

        * xml/XPathParser.cpp:
        (WebCore::XPath::Parser::parseStatement):
        (WebCore::XPath::Parser::registerNodeTest):
        (WebCore::XPath::Parser::deleteNodeTest):
        * xml/XPathParser.h:
        Added support methods for changes in XPathGrammar.y.

        * xml/XPathPath.cpp:
        (WebCore::XPath::Filter::evaluate): Cache evaluationContext in a local variable. Use swap() to avoid
        performing vector assignments.
        (WebCore::XPath::LocationPath::evaluate): Use swap() to avoid performing vector assignments.
        (WebCore::XPath::LocationPath::optimizeStepPair): This new method is called during LocationPath construction, 
        to simplify the path as it's being built. Currently, the only optimized case is "//*" - it is a basis for
        important operations that cannot be efficiently written in XPath 1.0, but can be optimized with a little bit
        of XPath 2.0.
        (WebCore::XPath::LocationPath::appendStep): A new accessor that modifies m_steps and calls optimizeStepPair().
        (WebCore::XPath::LocationPath::insertFirstStep): Ditto.
        * xml/XPathPath.h:
        (WebCore::XPath::LocationPath::setAbsolute): A new accessor.

        * xml/XPathStep.h:
        (WebCore::XPath::Step::NodeTest::):
        (WebCore::XPath::Step::NodeTest::NodeTest):
        (WebCore::XPath::Step::NodeTest::kind):
        (WebCore::XPath::Step::NodeTest::data):
        Step::NodeTest is a new sub-class that represents a fully parsed NodeTest.
        (WebCore::XPath::Step::axis):
        (WebCore::XPath::Step::nodeTest):
        (WebCore::XPath::Step::nodeTestData):
        (WebCore::XPath::Step::namespaceURI):
        (WebCore::XPath::Step::predicates):
        (WebCore::XPath::Step::setAxis):
        (WebCore::XPath::Step::setNodeTest):
        (WebCore::XPath::Step::setNodeTestData):
        (WebCore::XPath::Step::setNamespaceURI):
        (WebCore::XPath::Step::setPredicates):
        New accessors that let optimizeStepPair() manipulate Step data.

        * xml/XPathStep.cpp:
        (WebCore::XPath::Step::Step): Use the new NodeTest class.
        (WebCore::XPath::Step::evaluate): Cache evaluationContext in a local variable. Use swap() to avoid
        performing unneeded vector assignments.
        (WebCore::XPath::Step::nodesInAxis): Cosmetic changes.
        (WebCore::XPath::Step::nodeTestMatches): Use NodeTest instead of parsing the test from string each time.
        Added a partial implementation of XPath 2.0 element() node test.



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@20102 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog
index 862676b..6f83107 100644
--- a/LayoutTests/ChangeLog
+++ b/LayoutTests/ChangeLog
@@ -1,3 +1,14 @@
+2007-03-11  Alexey Proskuryakov  <ap@webkit.org>
+
+        Reviewed by Darin.
+
+        http://bugs.webkit.org/show_bug.cgi?id=13021
+        XPath can be very slow
+
+        * fast/xpath/4XPath/Core/test_location_path-expected.txt:
+        * fast/xpath/4XPath/Core/test_parser-expected.txt:
+        These tests now pass, as "//*" now happens to produce correct node order.
+
 2007-03-10  Alexey Proskuryakov  <ap@webkit.org>
 
         Reviewed by Darin.
diff --git a/LayoutTests/fast/xpath/4XPath/Core/test_location_path-expected.txt b/LayoutTests/fast/xpath/4XPath/Core/test_location_path-expected.txt
index 441a645..288bb03 100644
--- a/LayoutTests/fast/xpath/4XPath/Core/test_location_path-expected.txt
+++ b/LayoutTests/fast/xpath/4XPath/Core/test_location_path-expected.txt
@@ -1,4 +1,4 @@
-FAIL //* item 2 incorrect (expected GCHILD, actual CHILD2)
+PASS //*
 PASS */*
 PASS /
 PASS /child::*
diff --git a/LayoutTests/fast/xpath/4XPath/Core/test_parser-expected.txt b/LayoutTests/fast/xpath/4XPath/Core/test_parser-expected.txt
index 8aa7b8b..41075c5 100644
--- a/LayoutTests/fast/xpath/4XPath/Core/test_parser-expected.txt
+++ b/LayoutTests/fast/xpath/4XPath/Core/test_parser-expected.txt
@@ -2,7 +2,7 @@
 PASS /child::*
 PASS /*/*
 PASS /child::*/*/child::GCHILD
-FAIL //* item 2 incorrect (expected GCHILD, actual CHILD2)
+PASS //*
 PASS //GCHILD
 PASS //@attr1
 PASS x:GCHILD
diff --git a/WebCore/ChangeLog b/WebCore/ChangeLog
index 081ceb5..e279f49 100644
--- a/WebCore/ChangeLog
+++ b/WebCore/ChangeLog
@@ -1,3 +1,77 @@
+2007-03-11  Alexey Proskuryakov  <ap@webkit.org>
+
+        Reviewed by Darin.
+
+        A partial fix for http://bugs.webkit.org/show_bug.cgi?id=13021
+        XPath can be very slow
+
+        * xml/XPathExpression.cpp:
+        (WebCore::XPathExpression::evaluate): Cache evaluationContext in a local variable.
+
+        * xml/XPathExpressionNode.cpp:
+        (WebCore::XPath::Expression::evaluationContext):
+        * xml/XPathExpressionNode.h:
+        (WebCore::XPath::Expression::addSubExpression):
+        (WebCore::XPath::Expression::subExprCount):
+        (WebCore::XPath::Expression::subExpr):
+        * xml/XPathFunctions.cpp:
+        * xml/XPathFunctions.h:
+        (WebCore::XPath::Function::setName):
+        (WebCore::XPath::Function::arg):
+        (WebCore::XPath::Function::argCount):
+        (WebCore::XPath::Function::name):
+        Made one-liners critical for performance inline.
+
+        * xml/XPathGrammar.y: Fully parse NodeTests, so that strings are no longer passed for what is
+        essentially an enum. Use LocationPath accessors to add steps, instead of directly manipulating
+        internal data members.
+
+        * xml/XPathParser.cpp:
+        (WebCore::XPath::Parser::parseStatement):
+        (WebCore::XPath::Parser::registerNodeTest):
+        (WebCore::XPath::Parser::deleteNodeTest):
+        * xml/XPathParser.h:
+        Added support methods for changes in XPathGrammar.y.
+
+        * xml/XPathPath.cpp:
+        (WebCore::XPath::Filter::evaluate): Cache evaluationContext in a local variable. Use swap() to avoid
+        performing vector assignments.
+        (WebCore::XPath::LocationPath::evaluate): Use swap() to avoid performing vector assignments.
+        (WebCore::XPath::LocationPath::optimizeStepPair): This new method is called during LocationPath construction, 
+        to simplify the path as it's being built. Currently, the only optimized case is "//*" - it is a basis for
+        important operations that cannot be efficiently written in XPath 1.0, but can be optimized with a little bit
+        of XPath 2.0.
+        (WebCore::XPath::LocationPath::appendStep): A new accessor that modifies m_steps and calls optimizeStepPair().
+        (WebCore::XPath::LocationPath::insertFirstStep): Ditto.
+        * xml/XPathPath.h:
+        (WebCore::XPath::LocationPath::setAbsolute): A new accessor.
+
+        * xml/XPathStep.h:
+        (WebCore::XPath::Step::NodeTest::):
+        (WebCore::XPath::Step::NodeTest::NodeTest):
+        (WebCore::XPath::Step::NodeTest::kind):
+        (WebCore::XPath::Step::NodeTest::data):
+        Step::NodeTest is a new sub-class that represents a fully parsed NodeTest.
+        (WebCore::XPath::Step::axis):
+        (WebCore::XPath::Step::nodeTest):
+        (WebCore::XPath::Step::nodeTestData):
+        (WebCore::XPath::Step::namespaceURI):
+        (WebCore::XPath::Step::predicates):
+        (WebCore::XPath::Step::setAxis):
+        (WebCore::XPath::Step::setNodeTest):
+        (WebCore::XPath::Step::setNodeTestData):
+        (WebCore::XPath::Step::setNamespaceURI):
+        (WebCore::XPath::Step::setPredicates):
+        New accessors that let optimizeStepPair() manipulate Step data.
+
+        * xml/XPathStep.cpp:
+        (WebCore::XPath::Step::Step): Use the new NodeTest class.
+        (WebCore::XPath::Step::evaluate): Cache evaluationContext in a local variable. Use swap() to avoid
+        performing unneeded vector assignments.
+        (WebCore::XPath::Step::nodesInAxis): Cosmetic changes.
+        (WebCore::XPath::Step::nodeTestMatches): Use NodeTest instead of parsing the test from string each time.
+        Added a partial implementation of XPath 2.0 element() node test.
+
 2007-03-10  Alexey Proskuryakov  <ap@webkit.org>
 
         Reviewed by Darin.
diff --git a/WebCore/xml/XPathExpression.cpp b/WebCore/xml/XPathExpression.cpp
index a17324f..9231001 100644
--- a/WebCore/xml/XPathExpression.cpp
+++ b/WebCore/xml/XPathExpression.cpp
@@ -71,9 +71,10 @@
         ? contextNode->ownerDocument()
         : static_cast<EventTargetNode*>(contextNode);
 
-    Expression::evaluationContext().node = contextNode;
-    Expression::evaluationContext().size = 1;
-    Expression::evaluationContext().position = 1;
+    EvaluationContext& evaluationContext = Expression::evaluationContext();
+    evaluationContext.node = contextNode;
+    evaluationContext.size = 1;
+    evaluationContext.position = 1;
     RefPtr<XPathResult> result = new XPathResult(eventTarget, m_topExpression->evaluate());
 
     if (type != XPathResult::ANY_TYPE) {
diff --git a/WebCore/xml/XPathExpressionNode.cpp b/WebCore/xml/XPathExpressionNode.cpp
index c37aa6b..88e349e 100644
--- a/WebCore/xml/XPathExpressionNode.cpp
+++ b/WebCore/xml/XPathExpressionNode.cpp
@@ -35,7 +35,7 @@
 namespace WebCore {
 namespace XPath {
     
-EvaluationContext &Expression::evaluationContext()
+EvaluationContext& Expression::evaluationContext()
 {
     static EvaluationContext evaluationContext;
     return evaluationContext;
@@ -50,28 +50,6 @@
     deleteAllValues(m_subExpressions);
 }
 
-void Expression::addSubExpression(Expression* expr)
-{
-    m_subExpressions.append(expr);
-}
-
-unsigned Expression::subExprCount() const
-{
-    return m_subExpressions.size();
-}
-
-Expression* Expression::subExpr(unsigned i)
-{
-    ASSERT(i < subExprCount());
-    return m_subExpressions[i];
-}
-
-const Expression* Expression::subExpr(unsigned i) const
-{
-    ASSERT(i < subExprCount());
-    return m_subExpressions[i];
-}
-
 }
 }
 
diff --git a/WebCore/xml/XPathExpressionNode.h b/WebCore/xml/XPathExpressionNode.h
index c9ac08c..6a483f1 100644
--- a/WebCore/xml/XPathExpressionNode.h
+++ b/WebCore/xml/XPathExpressionNode.h
@@ -65,12 +65,12 @@
 
             virtual Value evaluate() const = 0;
 
-            void addSubExpression(Expression*);
+            void addSubExpression(Expression* expr) { m_subExpressions.append(expr); }
 
         protected:
-            unsigned subExprCount() const;
-            Expression* subExpr(unsigned);
-            const Expression* subExpr(unsigned) const;
+            unsigned subExprCount() const { return m_subExpressions.size(); }
+            Expression* subExpr(unsigned i) { return m_subExpressions[i]; }
+            const Expression* subExpr(unsigned i) const { return m_subExpressions[i]; }
 
         private:
             Vector<Expression*> m_subExpressions;
diff --git a/WebCore/xml/XPathFunctions.cpp b/WebCore/xml/XPathFunctions.cpp
index 259699b..a4d5bff 100644
--- a/WebCore/xml/XPathFunctions.cpp
+++ b/WebCore/xml/XPathFunctions.cpp
@@ -250,31 +250,6 @@
         addSubExpression(*it);
 }
 
-void Function::setName(const String& name)
-{
-    m_name = name;
-}
-
-Expression* Function::arg(int i)
-{
-    return subExpr(i);
-}
-
-const Expression* Function::arg(int i) const
-{
-    return subExpr(i);
-}
-
-unsigned int Function::argCount() const
-{
-    return subExprCount();
-}
-
-String Function::name() const
-{
-    return m_name;
-}
-
 Value FunLast::evaluate() const
 {
     return Expression::evaluationContext().size;
diff --git a/WebCore/xml/XPathFunctions.h b/WebCore/xml/XPathFunctions.h
index 819c5a8..f22d3ef 100644
--- a/WebCore/xml/XPathFunctions.h
+++ b/WebCore/xml/XPathFunctions.h
@@ -38,13 +38,13 @@
         class Function : public Expression {
         public:
             void setArguments(const Vector<Expression*>&);
-            void setName(const String&);
+            void setName(const String& name) { m_name = name; }
 
         protected:
-            Expression* arg(int pos);
-            const Expression* arg(int pos) const;
-            unsigned int argCount() const;
-            String name() const;
+            Expression* arg(int pos) { return subExpr(pos); }
+            const Expression* arg(int pos) const { return subExpr(pos); }
+            unsigned int argCount() const { return subExprCount(); }
+            String name() const { return m_name; }
 
         private:
             String m_name;
diff --git a/WebCore/xml/XPathGrammar.y b/WebCore/xml/XPathGrammar.y
index 38678d3..bcaa0ab 100644
--- a/WebCore/xml/XPathGrammar.y
+++ b/WebCore/xml/XPathGrammar.y
@@ -55,6 +55,7 @@
 %union
 {
     Step::Axis axis;
+    Step::NodeTest* nodeTest;
     NumericOp::Opcode numop;
     EqTestOp::Opcode eqop;
     String* str;
@@ -89,7 +90,7 @@
 %type <step> Step
 %type <axis> AxisSpecifier
 %type <step> DescendantOrSelf
-%type <str> NodeTest
+%type <nodeTest> NodeTest
 %type <expr> Predicate
 %type <predList> OptionalPredicateList
 %type <predList> PredicateList
@@ -122,12 +123,12 @@
 LocationPath:
     RelativeLocationPath
     {
-        $$->m_absolute = false;
+        $$->setAbsolute(false);
     }
     |
     AbsoluteLocationPath
     {
-        $$->m_absolute = true;
+        $$->setAbsolute(true);
     }
     ;
 
@@ -146,7 +147,7 @@
     DescendantOrSelf RelativeLocationPath
     {
         $$ = $2;
-        $$->m_steps.insert(0, $1);
+        $$->insertFirstStep($1);
         PARSER->unregisterParseNode($1);
     }
     ;
@@ -155,21 +156,21 @@
     Step
     {
         $$ = new LocationPath;
-        $$->m_steps.append($1);
+        $$->appendStep($1);
         PARSER->unregisterParseNode($1);
         PARSER->registerParseNode($$);
     }
     |
     RelativeLocationPath '/' Step
     {
-        $$->m_steps.append($3);
+        $$->appendStep($3);
         PARSER->unregisterParseNode($3);
     }
     |
     RelativeLocationPath DescendantOrSelf Step
     {
-        $$->m_steps.append($2);
-        $$->m_steps.append($3);
+        $$->appendStep($2);
+        $$->appendStep($3);
         PARSER->unregisterParseNode($2);
         PARSER->unregisterParseNode($3);
     }
@@ -183,7 +184,7 @@
             PARSER->deletePredicateVector($2);
         } else
             $$ = new Step(Step::ChildAxis, *$1);
-        PARSER->deleteString($1);
+        PARSER->deleteNodeTest($1);
         PARSER->registerParseNode($$);
     }
     |
@@ -197,10 +198,10 @@
         }
         
         if ($2) {
-            $$ = new Step(Step::ChildAxis, localName, namespaceURI, *$2);
+            $$ = new Step(Step::ChildAxis, Step::NodeTest(Step::NodeTest::NameTest, localName), namespaceURI, *$2);
             PARSER->deletePredicateVector($2);
         } else
-            $$ = new Step(Step::ChildAxis, localName, namespaceURI);
+            $$ = new Step(Step::ChildAxis, Step::NodeTest(Step::NodeTest::NameTest, localName), namespaceURI);
         PARSER->deleteString($1);
         PARSER->registerParseNode($$);
     }
@@ -212,7 +213,7 @@
             PARSER->deletePredicateVector($3);
         } else
             $$ = new Step($1, *$2);
-        PARSER->deleteString($2);
+        PARSER->deleteNodeTest($2);
         PARSER->registerParseNode($$);
     }
     |
@@ -226,10 +227,10 @@
         }
 
         if ($3) {
-            $$ = new Step($1, localName, namespaceURI, *$3);
+            $$ = new Step($1, Step::NodeTest(Step::NodeTest::NameTest, localName), namespaceURI, *$3);
             PARSER->deletePredicateVector($3);
         } else
-            $$ = new Step($1, localName, namespaceURI);
+            $$ = new Step($1, Step::NodeTest(Step::NodeTest::NameTest, localName), namespaceURI);
         PARSER->deleteString($2);
         PARSER->registerParseNode($$);
     }
@@ -249,20 +250,30 @@
 NodeTest:
     NODETYPE '(' ')'
     {
-        $$ = new String(*$1 + "()");
+        if (*$1 == "node")
+            $$ = new Step::NodeTest(Step::NodeTest::AnyNodeTest);
+        else if (*$1 == "text")
+            $$ = new Step::NodeTest(Step::NodeTest::TextNodeTest);
+        else if (*$1 == "comment")
+            $$ = new Step::NodeTest(Step::NodeTest::CommentNodeTest);
+
         PARSER->deleteString($1);
-        PARSER->registerString($$);
+        PARSER->registerNodeTest($$);
     }
     |
     PI '(' ')'
+    {
+        $$ = new Step::NodeTest(Step::NodeTest::ProcessingInstructionNodeTest);
+        PARSER->deleteString($1);        
+        PARSER->registerNodeTest($$);
+    }
     |
     PI '(' LITERAL ')'
     {
-        String s = *$1 + " " + *$3;
-        $$ = new String(s.stripWhiteSpace());
+        $$ = new Step::NodeTest(Step::NodeTest::ProcessingInstructionNodeTest, $3->stripWhiteSpace());
         PARSER->deleteString($1);        
         PARSER->deleteString($3);
-        PARSER->registerString($$);
+        PARSER->registerNodeTest($$);
     }
     ;
 
@@ -301,7 +312,7 @@
 DescendantOrSelf:
     SLASHSLASH
     {
-        $$ = new Step(Step::DescendantOrSelfAxis, "node()");
+        $$ = new Step(Step::DescendantOrSelfAxis, Step::NodeTest(Step::NodeTest::AnyNodeTest));
         PARSER->registerParseNode($$);
     }
     ;
@@ -309,13 +320,13 @@
 AbbreviatedStep:
     '.'
     {
-        $$ = new Step(Step::SelfAxis, "node()");
+        $$ = new Step(Step::SelfAxis, Step::NodeTest(Step::NodeTest::AnyNodeTest));
         PARSER->registerParseNode($$);
     }
     |
     DOTDOT
     {
-        $$ = new Step(Step::ParentAxis, "node()");
+        $$ = new Step(Step::ParentAxis, Step::NodeTest(Step::NodeTest::AnyNodeTest));
         PARSER->registerParseNode($$);
     }
     ;
@@ -411,7 +422,7 @@
     |
     FilterExpr '/' RelativeLocationPath
     {
-        $3->m_absolute = true;
+        $3->setAbsolute(true);
         $$ = new Path(static_cast<Filter*>($1), $3);
         PARSER->unregisterParseNode($1);
         PARSER->unregisterParseNode($3);
@@ -420,8 +431,8 @@
     |
     FilterExpr DescendantOrSelf RelativeLocationPath
     {
-        $3->m_steps.insert(0, $2);
-        $3->m_absolute = true;
+        $3->insertFirstStep($2);
+        $3->setAbsolute(true);
         $$ = new Path(static_cast<Filter*>($1), $3);
         PARSER->unregisterParseNode($1);
         PARSER->unregisterParseNode($2);
diff --git a/WebCore/xml/XPathParser.cpp b/WebCore/xml/XPathParser.cpp
index 1abb45c..96e1b8b 100644
--- a/WebCore/xml/XPathParser.cpp
+++ b/WebCore/xml/XPathParser.cpp
@@ -494,6 +494,9 @@
         deleteAllValues(m_strings);
         m_strings.clear();
 
+        deleteAllValues(m_nodeTests);
+        m_nodeTests.clear();
+
         m_topExpr = 0;
 
         if (m_gotNamespaceError)
@@ -508,6 +511,7 @@
     ASSERT(m_expressionVectors.size() == 0);
     ASSERT(m_predicateVectors.size() == 0);
     ASSERT(m_strings.size() == 0);
+    ASSERT(m_nodeTests.size() == 0);
 
     m_parseNodes.clear();
     Expression* result = m_topExpr;
@@ -600,6 +604,27 @@
     delete s;
 }
 
+void Parser::registerNodeTest(Step::NodeTest* t)
+{
+    if (t == 0)
+        return;
+    
+    ASSERT(!m_nodeTests.contains(t));
+    
+    m_nodeTests.add(t);        
+}
+
+void Parser::deleteNodeTest(Step::NodeTest* t)
+{
+    if (t == 0)
+        return;
+    
+    ASSERT(m_nodeTests.contains(t));
+    
+    m_nodeTests.remove(t);
+    delete t;
+}
+
 }
 }
 
diff --git a/WebCore/xml/XPathParser.h b/WebCore/xml/XPathParser.h
index e13874f..8d6da3f 100644
--- a/WebCore/xml/XPathParser.h
+++ b/WebCore/xml/XPathParser.h
@@ -86,6 +86,9 @@
             void registerString(String*);
             void deleteString(String*);
 
+            void registerNodeTest(Step::NodeTest*);
+            void deleteNodeTest(Step::NodeTest*);
+
         private:
             bool isOperatorContext() const;
 
@@ -117,6 +120,7 @@
             HashSet<Vector<Predicate*>*> m_predicateVectors;
             HashSet<Vector<Expression*>*> m_expressionVectors;
             HashSet<String*> m_strings;
+            HashSet<Step::NodeTest*> m_nodeTests;
         };
 
     }
diff --git a/WebCore/xml/XPathPath.cpp b/WebCore/xml/XPathPath.cpp
index ff2859b..162e676 100644
--- a/WebCore/xml/XPathPath.cpp
+++ b/WebCore/xml/XPathPath.cpp
@@ -55,25 +55,27 @@
     if (!v.isNodeVector()) 
         return v;
 
-    NodeVector inNodes = v.toNodeVector(), outNodes;
+    NodeVector nodes = v.toNodeVector();
+
+    EvaluationContext& evaluationContext = Expression::evaluationContext();
     for (unsigned i = 0; i < m_predicates.size(); i++) {
-        outNodes.clear();
-        Expression::evaluationContext().size = inNodes.size();
-        Expression::evaluationContext().position = 0;
+        NodeVector newNodes;
+        evaluationContext.size = nodes.size();
+        evaluationContext.position = 0;
         
-        for (unsigned j = 0; j < inNodes.size(); j++) {
-            Node* node = inNodes[j].get();
+        for (unsigned j = 0; j < nodes.size(); j++) {
+            Node* node = nodes[j].get();
             
-            Expression::evaluationContext().node = node;
-            ++Expression::evaluationContext().position;
+            evaluationContext.node = node;
+            ++evaluationContext.position;
             
             if (m_predicates[i]->evaluate())
-                outNodes.append(node);
+                newNodes.append(node);
         }
-        inNodes = outNodes;
+        nodes.swap(newNodes);
     }
 
-    return outNodes;
+    return nodes;
 }
 
 LocationPath::LocationPath()
@@ -120,12 +122,52 @@
             }
         }
         
-        inDOMNodes = outDOMNodes;
+        inDOMNodes.swap(outDOMNodes);
     }
 
     return inDOMNodes;
 }
 
+void LocationPath::optimizeStepPair(unsigned index)
+{
+    Step* first = m_steps[index];
+    
+    if (first->axis() == Step::DescendantOrSelfAxis
+        && first->nodeTest().kind() == Step::NodeTest::AnyNodeTest
+        && first->predicates().size() == 0) {
+
+        Step* second = m_steps[index + 1];
+        if (second->axis() == Step::ChildAxis
+            && second->namespaceURI().isEmpty()
+            && second->nodeTest().kind() == Step::NodeTest::NameTest
+            && second->nodeTest().data() == "*") {
+
+            // Optimize the common case of "//*" AKA descendant-or-self::node()/child::*.
+            first->setAxis(Step::DescendantAxis);
+            second->setAxis(Step::SelfAxis);
+            second->setNodeTest(Step::NodeTest::ElementNodeTest);
+            ASSERT(second->nodeTest().data().isEmpty());
+        }
+    }
+}
+
+void LocationPath::appendStep(Step* step)
+{
+    m_steps.append(step);
+    
+    unsigned stepCount = m_steps.size();
+    if (stepCount > 1)
+        optimizeStepPair(stepCount - 2);
+}
+
+void LocationPath::insertFirstStep(Step* step)
+{
+    m_steps.insert(0, step);
+
+    if (m_steps.size() > 1)
+        optimizeStepPair(0);
+}
+
 Path::Path(Filter* filter, LocationPath* path)
     : m_filter(filter),
     m_path(path)
diff --git a/WebCore/xml/XPathPath.h b/WebCore/xml/XPathPath.h
index e4f8906..b19130a 100644
--- a/WebCore/xml/XPathPath.h
+++ b/WebCore/xml/XPathPath.h
@@ -57,15 +57,19 @@
         public:
             LocationPath();
             virtual ~LocationPath();
+            void setAbsolute(bool value) { m_absolute = value; }
 
             virtual Value evaluate() const;
             Value evaluate(const NodeVector& startNodes) const;
 
+            void appendStep(Step* step);
+            void insertFirstStep(Step* step);
+
         private:
+            void optimizeStepPair(unsigned index);
+
             Vector<Step*> m_steps;
             bool m_absolute;
-
-            friend int ::xpathyyparse(void*);
         };
 
         class Path : public Expression
diff --git a/WebCore/xml/XPathStep.cpp b/WebCore/xml/XPathStep.cpp
index 81d9bb2..b33972a 100644
--- a/WebCore/xml/XPathStep.cpp
+++ b/WebCore/xml/XPathStep.cpp
@@ -38,14 +38,14 @@
 namespace WebCore {
 namespace XPath {
 
-Step::Step(Axis axis, const String& nodeTest, const Vector<Predicate*>& predicates)
+Step::Step(Axis axis, const NodeTest& nodeTest, const Vector<Predicate*>& predicates)
     : m_axis(axis)
     , m_nodeTest(nodeTest)
     , m_predicates(predicates)
 {
 }
 
-Step::Step(Axis axis, const String& nodeTest, const String& namespaceURI, const Vector<Predicate*>& predicates)
+Step::Step(Axis axis, const NodeTest& nodeTest, const String& namespaceURI, const Vector<Predicate*>& predicates)
     : m_axis(axis)
     , m_nodeTest(nodeTest)
     , m_namespaceURI(namespaceURI)
@@ -60,31 +60,32 @@
 
 NodeVector Step::evaluate(Node* context) const
 {
-    NodeVector inNodes = nodesInAxis(context), outNodes;
-    inNodes = nodeTestMatches(inNodes);
+    NodeVector nodes = nodesInAxis(context);
+    nodes = nodeTestMatches(nodes);
     
-    outNodes = inNodes;
+    EvaluationContext& evaluationContext = Expression::evaluationContext();
+    
     for (unsigned i = 0; i < m_predicates.size(); i++) {
         Predicate* predicate = m_predicates[i];
 
-        outNodes.clear();
-        Expression::evaluationContext().size = inNodes.size();
-        Expression::evaluationContext().position = 1;
-        for (unsigned j = 0; j < inNodes.size(); j++) {
-            Node* node = inNodes[j].get();
+        NodeVector newNodes;
+        evaluationContext.size = nodes.size();
+        evaluationContext.position = 1;
+        for (unsigned j = 0; j < nodes.size(); j++) {
+            Node* node = nodes[j].get();
 
             Expression::evaluationContext().node = node;
-            EvaluationContext backupCtx = Expression::evaluationContext();
+            EvaluationContext backupCtx = evaluationContext;
             if (predicate->evaluate())
-                outNodes.append(node);
+                newNodes.append(node);
 
-            Expression::evaluationContext() = backupCtx;
-            ++Expression::evaluationContext().position;
+            evaluationContext = backupCtx;
+            ++evaluationContext.position;
         }
 
-        inNodes = outNodes;
+        nodes.swap(newNodes);
     }
-    return outNodes;
+    return nodes;
 }
 
 NodeVector Step::nodesInAxis(Node* context) const
@@ -155,10 +156,9 @@
                 nodes.append (attrs->item(i));
             return nodes;
         }
-        case NamespaceAxis: {
+        case NamespaceAxis:
             // XPath namespace nodes are not implemented yet.
             return NodeVector();
-        }
         case SelfAxis:
             nodes.append(context);
             return nodes;
@@ -173,7 +173,7 @@
                 nodes.append(n);
             return nodes;
     }
-
+    ASSERT_NOT_REACHED();
     return NodeVector();
 }
 
@@ -182,86 +182,91 @@
 {
     NodeVector matches;
 
-    if (m_nodeTest == "*") {
-        for (unsigned i = 0; i < nodes.size(); i++) {
-            Node* node = nodes[i].get();
-            if (node->nodeType() == primaryNodeType(m_axis) &&
-                (m_namespaceURI.isEmpty() || m_namespaceURI == node->namespaceURI()))
-                matches.append(node);
-        }
-        return matches;
-    } else if (m_nodeTest == "text()") {
-        HashSet<Node*> nodeSet;
-        for (unsigned i = 0; i < nodes.size(); i++) {
-            Node* node = nodes[i].get();
-            if ((node->nodeType() == Node::TEXT_NODE || node->nodeType() == Node::CDATA_SECTION_NODE)) {
-                nodeSet.add(node);
-                if (!nodeSet.contains(node->previousSibling())) // See <http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#TextNodes>
-                    matches.append(node);
-            }
-        }
-        return matches;
-    } else if (m_nodeTest == "comment()") {
-        for (unsigned i = 0; i < nodes.size(); i++) {
-            Node* node = nodes[i].get();
-            if (node->nodeType() == Node::COMMENT_NODE)
-                matches.append(node);
-        }
-        return matches;
-    } else if (m_nodeTest.startsWith("processing-instruction")) {
-        String param;
-
-        const int space = m_nodeTest.find(' ');
-        if (space > -1)
-            param = m_nodeTest.substring(space + 1);
-
-        for (unsigned i = 0; i < nodes.size(); i++) {
-            Node* node = nodes[i].get();
-
-            if (node->nodeType() == Node::PROCESSING_INSTRUCTION_NODE &&
-                (param.isEmpty() || node->nodeName() == param))
-                    matches.append(node);
-        }    
-        return matches;
-    } else if (m_nodeTest == "node()")
-        return nodes;
-    else {
-        if (m_axis == AttributeAxis) {
-            // In XPath land, namespace nodes are not accessible
-            // on the attribute axis.
-            if (m_nodeTest == "xmlns")
-                return matches;
-
+    switch (m_nodeTest.kind()) {
+        case NodeTest::TextNodeTest: {
+            HashSet<Node*> nodeSet;
             for (unsigned i = 0; i < nodes.size(); i++) {
                 Node* node = nodes[i].get();
-                
-                if (node->nodeName() == m_nodeTest) {
-                    matches.append(node);
-                    break; // There can only be one.
+                if ((node->nodeType() == Node::TEXT_NODE || node->nodeType() == Node::CDATA_SECTION_NODE)) {
+                    nodeSet.add(node);
+                    if (!nodeSet.contains(node->previousSibling())) // See <http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#TextNodes>
+                        matches.append(node);
                 }
             }
-
-            return matches;
-        } else if (m_axis == NamespaceAxis) {
-            // Node test on the namespace axis is not implemented yet
-        } else {
-            for (unsigned i = 0; i < nodes.size(); i++) {
-                Node* node = nodes[i].get();
-
-                // 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.
-                if (node->nodeType() == Node::ELEMENT_NODE
-                    && static_cast<Element*>(node)->tagQName().localName() == m_nodeTest
-                    && ((node->isHTMLElement() && node->document()->isHTMLDocument() && m_namespaceURI.isNull()) || m_namespaceURI == node->namespaceURI()))
-                    matches.append(node);
-            }
-
             return matches;
         }
-    }
+        case NodeTest::CommentNodeTest:
+            for (unsigned i = 0; i < nodes.size(); i++) {
+                Node* node = nodes[i].get();
+                if (node->nodeType() == Node::COMMENT_NODE)
+                    matches.append(node);
+            }
+            return matches;
+        case NodeTest::ProcessingInstructionNodeTest:
+            for (unsigned i = 0; i < nodes.size(); i++) {
+                Node* node = nodes[i].get();
+                const String& name = m_nodeTest.data();
+                if (node->nodeType() == Node::PROCESSING_INSTRUCTION_NODE && (name.isEmpty() || node->nodeName() == name))
+                        matches.append(node);
+            }    
+            return matches;
+        case NodeTest::ElementNodeTest:
+            for (unsigned i = 0; i < nodes.size(); i++) {
+                Node* node = nodes[i].get();
+                if (node->isElementNode())
+                    matches.append(node);
+            }
+            return matches;
+        case NodeTest::AnyNodeTest:
+            return nodes;
+        case NodeTest::NameTest: {
+            const String& name = m_nodeTest.data();
+            if (name == "*") {
+                for (unsigned i = 0; i < nodes.size(); i++) {
+                    Node* node = nodes[i].get();
+                    if (node->nodeType() == primaryNodeType(m_axis) &&
+                        (m_namespaceURI.isEmpty() || m_namespaceURI == node->namespaceURI()))
+                        matches.append(node);
+                }
+                return matches;
+            }
+            if (m_axis == AttributeAxis) {
+                // In XPath land, namespace nodes are not accessible
+                // on the attribute axis.
+                if (name == "xmlns")
+                    return matches;
 
-    return matches;
+                for (unsigned i = 0; i < nodes.size(); i++) {
+                    Node* node = nodes[i].get();
+                    
+                    if (node->nodeName() == name) {
+                        matches.append(node);
+                        break; // There can only be one.
+                    }
+                }
+
+                return matches;
+            } else if (m_axis == NamespaceAxis) {
+                // Node test on the namespace axis is not implemented yet
+            } else {
+                for (unsigned i = 0; i < nodes.size(); i++) {
+                    Node* node = nodes[i].get();
+
+                    // 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.
+                    if (node->nodeType() == Node::ELEMENT_NODE
+                        && static_cast<Element*>(node)->tagQName().localName() == name
+                        && ((node->isHTMLElement() && node->document()->isHTMLDocument() && m_namespaceURI.isNull()) || m_namespaceURI == node->namespaceURI()))
+                        matches.append(node);
+                }
+
+                return matches;
+            }
+        }
+    }
+    ASSERT_NOT_REACHED();
+    return NodeVector();
 }
 
 Node::NodeType Step::primaryNodeType(Axis axis) const
diff --git a/WebCore/xml/XPathStep.h b/WebCore/xml/XPathStep.h
index 65a7101..8758867 100644
--- a/WebCore/xml/XPathStep.h
+++ b/WebCore/xml/XPathStep.h
@@ -48,21 +48,52 @@
                 ParentAxis, PrecedingAxis, PrecedingSiblingAxis,
                 SelfAxis
             };
+            
+            class NodeTest {
+            public:
+                enum Kind {
+                    TextNodeTest, CommentNodeTest, ProcessingInstructionNodeTest, AnyNodeTest, NameTest,
+                    ElementNodeTest // XPath 2.0
+                };
+                
+                NodeTest(Kind kind, const String& data = String()) : m_kind(kind), m_data(data) {}
+                
+                Kind kind() const { return m_kind; }
+                const String data() const { return m_data; }
+                
+            private:
+                Kind m_kind;
+                String m_data;
+            };
 
-            Step(Axis, const String& nodeTest, const Vector<Predicate*>& predicates = Vector<Predicate*>());
-            Step(Axis, const String& nodeTest, const String& namespaceURI, const Vector<Predicate*>& predicates = Vector<Predicate*>());
+            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();
 
             NodeVector 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:
+            void parseNodeTest(const String&);
             NodeVector nodesInAxis(Node* context) const;
             NodeVector nodeTestMatches(const NodeVector& nodes) const;
             String namespaceFromNodetest(const String& nodeTest) const;
             Node::NodeType primaryNodeType(Axis) const;
 
             Axis m_axis;
-            String m_nodeTest;
+            NodeTest m_nodeTest;
+            String m_nodeTestData;
             String m_namespaceURI;
             Vector<Predicate*> m_predicates;
         };