diff options
author | Feng Qian <fqian@google.com> | 2009-06-17 12:12:20 -0700 |
---|---|---|
committer | Feng Qian <fqian@google.com> | 2009-06-17 12:12:20 -0700 |
commit | 5f1ab04193ad0130ca8204aadaceae083aca9881 (patch) | |
tree | 5a92cd389e2cfe7fb67197ce14b38469462379f8 /WebCore/xml | |
parent | 194315e5a908cc8ed67d597010544803eef1ac59 (diff) | |
download | external_webkit-5f1ab04193ad0130ca8204aadaceae083aca9881.zip external_webkit-5f1ab04193ad0130ca8204aadaceae083aca9881.tar.gz external_webkit-5f1ab04193ad0130ca8204aadaceae083aca9881.tar.bz2 |
Get WebKit r44544.
Diffstat (limited to 'WebCore/xml')
-rw-r--r-- | WebCore/xml/XMLHttpRequest.cpp | 43 | ||||
-rw-r--r-- | WebCore/xml/XMLHttpRequest.h | 4 | ||||
-rw-r--r-- | WebCore/xml/XMLHttpRequest.idl | 2 | ||||
-rw-r--r-- | WebCore/xml/XMLHttpRequestException.idl | 2 | ||||
-rw-r--r-- | WebCore/xml/XPathException.idl | 2 | ||||
-rw-r--r-- | WebCore/xml/XPathExpression.cpp | 19 | ||||
-rw-r--r-- | WebCore/xml/XPathExpressionNode.cpp | 4 | ||||
-rw-r--r-- | WebCore/xml/XPathExpressionNode.h | 32 | ||||
-rw-r--r-- | WebCore/xml/XPathFunctions.cpp | 115 | ||||
-rw-r--r-- | WebCore/xml/XPathFunctions.h | 12 | ||||
-rw-r--r-- | WebCore/xml/XPathNodeSet.h | 16 | ||||
-rw-r--r-- | WebCore/xml/XPathPath.cpp | 92 | ||||
-rw-r--r-- | WebCore/xml/XPathPath.h | 12 | ||||
-rw-r--r-- | WebCore/xml/XPathPredicate.cpp | 2 | ||||
-rw-r--r-- | WebCore/xml/XPathPredicate.h | 16 | ||||
-rw-r--r-- | WebCore/xml/XPathResult.cpp | 46 | ||||
-rw-r--r-- | WebCore/xml/XPathResult.h | 22 | ||||
-rw-r--r-- | WebCore/xml/XPathStep.cpp | 277 | ||||
-rw-r--r-- | WebCore/xml/XPathStep.h | 41 | ||||
-rw-r--r-- | WebCore/xml/XPathUtil.cpp | 20 | ||||
-rw-r--r-- | WebCore/xml/XPathValue.cpp | 22 | ||||
-rw-r--r-- | WebCore/xml/XPathVariableReference.h | 4 | ||||
-rw-r--r-- | WebCore/xml/XSLStyleSheet.cpp | 14 | ||||
-rw-r--r-- | WebCore/xml/XSLTProcessor.cpp | 5 |
24 files changed, 510 insertions, 314 deletions
diff --git a/WebCore/xml/XMLHttpRequest.cpp b/WebCore/xml/XMLHttpRequest.cpp index dd3f361..76ee048 100644 --- a/WebCore/xml/XMLHttpRequest.cpp +++ b/WebCore/xml/XMLHttpRequest.cpp @@ -66,7 +66,6 @@ XMLHttpRequestStaticData::XMLHttpRequestStaticData() m_forbiddenRequestHeaders.add("accept-encoding"); m_forbiddenRequestHeaders.add("access-control-request-headers"); m_forbiddenRequestHeaders.add("access-control-request-method"); - m_forbiddenRequestHeaders.add("authorization"); m_forbiddenRequestHeaders.add("connection"); m_forbiddenRequestHeaders.add("content-length"); m_forbiddenRequestHeaders.add("content-transfer-encoding"); @@ -284,10 +283,20 @@ void XMLHttpRequest::callReadyStateChangeListener() dispatchReadyStateChangeEvent(); - if (m_state == DONE) + if (m_state == DONE && !m_error) dispatchLoadEvent(); } +void XMLHttpRequest::setWithCredentials(bool value, ExceptionCode& ec) +{ + if (m_state != OPENED || m_loader) { + ec = INVALID_STATE_ERR; + return; + } + + m_includeCredentials = value; +} + void XMLHttpRequest::open(const String& method, const KURL& url, bool async, ExceptionCode& ec) { internalAbort(); @@ -381,7 +390,7 @@ void XMLHttpRequest::send(Document* document, ExceptionCode& ec) if (!initSend(ec)) return; - if (m_method != "GET" && m_method != "HEAD" && (m_url.protocolIs("http") || m_url.protocolIs("https"))) { + if (m_method != "GET" && m_method != "HEAD" && m_url.protocolInHTTPFamily()) { String contentType = getRequestHeader("Content-Type"); if (contentType.isEmpty()) { #if ENABLE(DASHBOARD_SUPPORT) @@ -412,7 +421,7 @@ void XMLHttpRequest::send(const String& body, ExceptionCode& ec) if (!initSend(ec)) return; - if (!body.isNull() && m_method != "GET" && m_method != "HEAD" && (m_url.protocolIs("http") || m_url.protocolIs("https"))) { + if (!body.isNull() && m_method != "GET" && m_method != "HEAD" && m_url.protocolInHTTPFamily()) { String contentType = getRequestHeader("Content-Type"); if (contentType.isEmpty()) { #if ENABLE(DASHBOARD_SUPPORT) @@ -436,7 +445,7 @@ void XMLHttpRequest::send(File* body, ExceptionCode& ec) if (!initSend(ec)) return; - if (m_method != "GET" && m_method != "HEAD" && (m_url.protocolIs("http") || m_url.protocolIs("https"))) { + if (m_method != "GET" && m_method != "HEAD" && m_url.protocolInHTTPFamily()) { // FIXME: Should we set a Content-Type if one is not set. // FIXME: add support for uploading bundles. m_requestEntityBody = FormData::create(); @@ -509,10 +518,17 @@ void XMLHttpRequest::makeSimpleCrossOriginAccessRequest(ExceptionCode& ec) { ASSERT(isSimpleCrossOriginAccessRequest(m_method, m_requestHeaders)); + // Cross-origin requests are only defined for HTTP. We would catch this when checking response headers later, but there is no reason to send a request that's guaranteed to be denied. + if (!m_url.protocolInHTTPFamily()) { + ec = XMLHttpRequestException::NETWORK_ERR; + networkError(); + return; + } + KURL url = m_url; url.setUser(String()); url.setPass(String()); - + ResourceRequest request(url); request.setHTTPMethod(m_method); request.setAllowHTTPCookies(m_includeCredentials); @@ -636,7 +652,9 @@ void XMLHttpRequest::loadRequestSynchronously(ResourceRequest& request, Exceptio m_loader = 0; m_exceptionCode = 0; - ThreadableLoader::loadResourceSynchronously(scriptExecutionContext(), request, *this); + StoredCredentials storedCredentials = (m_sameOriginRequest || m_includeCredentials) ? AllowStoredCredentials : DoNotAllowStoredCredentials; + + ThreadableLoader::loadResourceSynchronously(scriptExecutionContext(), request, *this, storedCredentials); if (!m_exceptionCode && m_error) m_exceptionCode = XMLHttpRequestException::NETWORK_ERR; ec = m_exceptionCode; @@ -650,15 +668,13 @@ void XMLHttpRequest::loadRequestAsynchronously(ResourceRequest& request) // This is true while running onunload handlers. // FIXME: We need to be able to send XMLHttpRequests from onunload, <http://bugs.webkit.org/show_bug.cgi?id=10904>. // FIXME: Maybe create can return null for other reasons too? - // We need to keep content sniffing enabled for local files due to CFNetwork not providing a MIME type - // for local files otherwise, <rdar://problem/5671813>. LoadCallbacks callbacks = m_inPreflight ? DoNotSendLoadCallbacks : SendLoadCallbacks; - ContentSniff contentSniff = request.url().isLocalFile() ? SniffContent : DoNotSniffContent; + StoredCredentials storedCredentials = (m_sameOriginRequest || m_includeCredentials) ? AllowStoredCredentials : DoNotAllowStoredCredentials; if (m_upload) request.setReportUploadProgress(true); - m_loader = ThreadableLoader::create(scriptExecutionContext(), this, request, callbacks, contentSniff); + m_loader = ThreadableLoader::create(scriptExecutionContext(), this, request, callbacks, DoNotSniffContent, storedCredentials, RequireSameRedirectOrigin); if (m_loader) { // Neither this object nor the JavaScript wrapper should be deleted while @@ -682,7 +698,7 @@ void XMLHttpRequest::abort() if ((m_state <= OPENED && !sendFlag) || m_state == DONE) m_state = UNSENT; - else { + else { ASSERT(!m_loader); changeState(DONE); m_state = UNSENT; @@ -736,8 +752,7 @@ void XMLHttpRequest::genericError() clearRequest(); m_error = true; - // The spec says we should "Synchronously switch the state to DONE." and then "Synchronously dispatch a readystatechange event on the object" - // but this does not match Firefox. + changeState(DONE); } void XMLHttpRequest::networkError() diff --git a/WebCore/xml/XMLHttpRequest.h b/WebCore/xml/XMLHttpRequest.h index 544866a..6955c11 100644 --- a/WebCore/xml/XMLHttpRequest.h +++ b/WebCore/xml/XMLHttpRequest.h @@ -63,6 +63,8 @@ public: String statusText(ExceptionCode&) const; int status(ExceptionCode&) const; State readyState() const; + bool withCredentials() const { return m_includeCredentials; } + void setWithCredentials(bool, ExceptionCode&); void open(const String& method, const KURL&, bool async, ExceptionCode&); void open(const String& method, const KURL&, bool async, const String& user, ExceptionCode&); void open(const String& method, const KURL&, bool async, const String& user, const String& password, ExceptionCode&); @@ -195,7 +197,7 @@ private: RefPtr<FormData> m_requestEntityBody; String m_mimeTypeOverride; bool m_async; - bool m_includeCredentials; // FIXME: Currently, setting this flag is not implemented, so it is always false. + bool m_includeCredentials; RefPtr<ThreadableLoader> m_loader; State m_state; diff --git a/WebCore/xml/XMLHttpRequest.idl b/WebCore/xml/XMLHttpRequest.idl index 3187160..79005e2 100644 --- a/WebCore/xml/XMLHttpRequest.idl +++ b/WebCore/xml/XMLHttpRequest.idl @@ -53,6 +53,8 @@ module xml { readonly attribute unsigned short readyState; // request + attribute boolean withCredentials + setter raises(DOMException); // void open(in DOMString method, in DOMString url); // void open(in DOMString method, in DOMString url, in boolean async); // void open(in DOMString method, in DOMString url, in boolean async, in DOMString user); diff --git a/WebCore/xml/XMLHttpRequestException.idl b/WebCore/xml/XMLHttpRequestException.idl index 706beb2..380e426 100644 --- a/WebCore/xml/XMLHttpRequestException.idl +++ b/WebCore/xml/XMLHttpRequestException.idl @@ -37,7 +37,7 @@ module xml { readonly attribute DOMString name; readonly attribute DOMString message; -#if defined(LANGUAGE_JAVASCRIPT) +#if defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT // Override in a Mozilla compatible format [DontEnum] DOMString toString(); #endif diff --git a/WebCore/xml/XPathException.idl b/WebCore/xml/XPathException.idl index 6e25514..c3c95e3 100644 --- a/WebCore/xml/XPathException.idl +++ b/WebCore/xml/XPathException.idl @@ -37,7 +37,7 @@ module xpath { readonly attribute DOMString name; readonly attribute DOMString message; -#if defined(LANGUAGE_JAVASCRIPT) +#if defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT // Override in a Mozilla compatible format [DontEnum] DOMString toString(); #endif diff --git a/WebCore/xml/XPathExpression.cpp b/WebCore/xml/XPathExpression.cpp index ecec79e..6188426 100644 --- a/WebCore/xml/XPathExpression.cpp +++ b/WebCore/xml/XPathExpression.cpp @@ -1,6 +1,6 @@ /* - * Copyright 2005 Frerich Raabe <raabe@kde.org> - * Copyright (C) 2006 Apple Computer, Inc. + * Copyright (C) 2005 Frerich Raabe <raabe@kde.org> + * Copyright (C) 2006, 2009 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -30,9 +30,8 @@ #if ENABLE(XPATH) #include "Document.h" -#include "ExceptionCode.h" -#include "Node.h" #include "PlatformString.h" +#include "XPathException.h" #include "XPathExpressionNode.h" #include "XPathNSResolver.h" #include "XPathParser.h" @@ -67,15 +66,21 @@ PassRefPtr<XPathResult> XPathExpression::evaluate(Node* contextNode, unsigned sh return 0; } - Node* eventTarget = contextNode->ownerDocument() ? contextNode->ownerDocument() : contextNode; - EvaluationContext& evaluationContext = Expression::evaluationContext(); evaluationContext.node = contextNode; evaluationContext.size = 1; evaluationContext.position = 1; - RefPtr<XPathResult> result = XPathResult::create(eventTarget, m_topExpression->evaluate()); + evaluationContext.hadTypeConversionError = false; + RefPtr<XPathResult> result = XPathResult::create(contextNode->document(), m_topExpression->evaluate()); evaluationContext.node = 0; // Do not hold a reference to the context node, as this may prevent the whole document from being destroyed in time. + if (evaluationContext.hadTypeConversionError) { + // It is not specified what to do if type conversion fails while evaluating an expression, and INVALID_EXPRESSION_ERR is not exactly right + // when the failure happens in an otherwise valid expression because of a variable. But XPathEvaluator does not support variables, so it's close enough. + ec = XPathException::INVALID_EXPRESSION_ERR; + return 0; + } + if (type != XPathResult::ANY_TYPE) { ec = 0; result->convertTo(type, ec); diff --git a/WebCore/xml/XPathExpressionNode.cpp b/WebCore/xml/XPathExpressionNode.cpp index 647c6af..4656f8d 100644 --- a/WebCore/xml/XPathExpressionNode.cpp +++ b/WebCore/xml/XPathExpressionNode.cpp @@ -30,7 +30,6 @@ #if ENABLE(XPATH) #include "Node.h" -#include "XPathValue.h" #include <wtf/StdLibExtras.h> namespace WebCore { @@ -43,6 +42,9 @@ EvaluationContext& Expression::evaluationContext() } Expression::Expression() + : m_isContextNodeSensitive(false) + , m_isContextPositionSensitive(false) + , m_isContextSizeSensitive(false) { } diff --git a/WebCore/xml/XPathExpressionNode.h b/WebCore/xml/XPathExpressionNode.h index 9c5f79b..d12b451 100644 --- a/WebCore/xml/XPathExpressionNode.h +++ b/WebCore/xml/XPathExpressionNode.h @@ -1,6 +1,6 @@ /* - * Copyright 2005 Frerich Raabe <raabe@kde.org> - * Copyright (C) 2006 Apple Computer, Inc. + * Copyright (C) 2005 Frerich Raabe <raabe@kde.org> + * Copyright (C) 2006, 2009 Apple Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -31,23 +31,21 @@ #include "StringHash.h" #include "Node.h" +#include "XPathValue.h" #include <wtf/HashMap.h> #include <wtf/Vector.h> namespace WebCore { namespace XPath { - - class Value; struct EvaluationContext { - EvaluationContext() : node(0), size(0), position(0) { } - RefPtr<Node> node; unsigned long size; unsigned long position; HashMap<String, String> variableBindings; + bool hadTypeConversionError; }; class ParseNode { @@ -64,7 +62,22 @@ namespace WebCore { virtual Value evaluate() const = 0; - void addSubExpression(Expression* expr) { m_subExpressions.append(expr); } + void addSubExpression(Expression* expr) + { + m_subExpressions.append(expr); + m_isContextNodeSensitive |= expr->m_isContextNodeSensitive; + m_isContextPositionSensitive |= expr->m_isContextPositionSensitive; + m_isContextSizeSensitive |= expr->m_isContextSizeSensitive; + } + + bool isContextNodeSensitive() const { return m_isContextNodeSensitive; } + bool isContextPositionSensitive() const { return m_isContextPositionSensitive; } + bool isContextSizeSensitive() const { return m_isContextSizeSensitive; } + void setIsContextNodeSensitive(bool value) { m_isContextNodeSensitive = value; } + void setIsContextPositionSensitive(bool value) { m_isContextPositionSensitive = value; } + void setIsContextSizeSensitive(bool value) { m_isContextSizeSensitive = value; } + + virtual Value::Type resultType() const = 0; protected: unsigned subExprCount() const { return m_subExpressions.size(); } @@ -73,6 +86,11 @@ namespace WebCore { private: Vector<Expression*> m_subExpressions; + + // Evaluation details that can be used for optimization. + bool m_isContextNodeSensitive; + bool m_isContextPositionSensitive; + bool m_isContextSizeSensitive; }; } diff --git a/WebCore/xml/XPathFunctions.cpp b/WebCore/xml/XPathFunctions.cpp index 3abd603..da39443 100644 --- a/WebCore/xml/XPathFunctions.cpp +++ b/WebCore/xml/XPathFunctions.cpp @@ -1,6 +1,6 @@ /* - * Copyright 2005 Frerich Raabe <raabe@kde.org> - * Copyright (C) 2006 Apple Computer, Inc. + * Copyright (C) 2005 Frerich Raabe <raabe@kde.org> + * Copyright (C) 2006, 2009 Apple Inc. * Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org> * * Redistribution and use in source and binary forms, with or without @@ -32,7 +32,8 @@ #include "Document.h" #include "Element.h" -#include "NamedAttrMap.h" +#include "NamedNodeMap.h" +#include "ProcessingInstruction.h" #include "XMLNames.h" #include "XPathUtil.h" #include "XPathValue.h" @@ -74,110 +75,157 @@ static HashMap<String, FunctionRec>* functionMap; class FunLast : public Function { virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::NumberValue; } +public: + FunLast() { setIsContextSizeSensitive(true); } }; class FunPosition : public Function { virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::NumberValue; } +public: + FunPosition() { setIsContextPositionSensitive(true); } }; class FunCount : public Function { virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::NumberValue; } }; class FunId : public Function { virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::NodeSetValue; } }; class FunLocalName : public Function { virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::StringValue; } +public: + FunLocalName() { setIsContextNodeSensitive(true); } // local-name() with no arguments uses context node. }; class FunNamespaceURI : public Function { virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::StringValue; } +public: + FunNamespaceURI() { setIsContextNodeSensitive(true); } // namespace-uri() with no arguments uses context node. }; class FunName : public Function { virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::StringValue; } +public: + FunName() { setIsContextNodeSensitive(true); } // name() with no arguments uses context node. }; class FunString : public Function { virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::StringValue; } +public: + FunString() { setIsContextNodeSensitive(true); } // string() with no arguments uses context node. }; class FunConcat : public Function { virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::StringValue; } }; class FunStartsWith : public Function { virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::BooleanValue; } }; class FunContains : public Function { virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::BooleanValue; } }; class FunSubstringBefore : public Function { virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::StringValue; } }; class FunSubstringAfter : public Function { virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::StringValue; } }; class FunSubstring : public Function { virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::StringValue; } }; class FunStringLength : public Function { virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::NumberValue; } +public: + FunStringLength() { setIsContextNodeSensitive(true); } // string-length() with no arguments uses context node. }; class FunNormalizeSpace : public Function { virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::StringValue; } +public: + FunNormalizeSpace() { setIsContextNodeSensitive(true); } // normalize-space() with no arguments uses context node. }; class FunTranslate : public Function { virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::StringValue; } }; class FunBoolean : public Function { virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::BooleanValue; } }; class FunNot : public Function { virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::BooleanValue; } }; class FunTrue : public Function { virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::BooleanValue; } }; class FunFalse : public Function { virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::BooleanValue; } }; class FunLang : public Function { virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::BooleanValue; } +public: + FunLang() { setIsContextNodeSensitive(true); } // lang() always works on context node. }; class FunNumber : public Function { virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::NumberValue; } +public: + FunNumber() { setIsContextNodeSensitive(true); } // number() with no arguments uses context node. }; class FunSum : public Function { virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::NumberValue; } }; class FunFloor : public Function { virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::NumberValue; } }; class FunCeiling : public Function { virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::NumberValue; } }; class FunRound : public Function { virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::NumberValue; } public: static double round(double); }; @@ -246,8 +294,13 @@ inline bool Interval::contains(int value) const void Function::setArguments(const Vector<Expression*>& args) { - Vector<Expression*>::const_iterator end = args.end(); + ASSERT(!subExprCount()); + + // Some functions use context node as implicit argument, so when explicit arguments are added, they may no longer be context node sensitive. + if (m_name != "lang" && !args.isEmpty()) + setIsContextNodeSensitive(false); + Vector<Expression*>::const_iterator end = args.end(); for (Vector<Expression*>::const_iterator it = args.begin(); it != end; it++) addSubExpression(*it); } @@ -310,71 +363,67 @@ Value FunId::evaluate() const return Value(result, Value::adopt); } +static inline String expandedNameLocalPart(Node* node) +{ + // The local part of an XPath expanded-name matches DOM local name for most node types, except for namespace nodes and processing instruction nodes. + ASSERT(node->nodeType() != Node::XPATH_NAMESPACE_NODE); // Not supported yet. + if (node->nodeType() == Node::PROCESSING_INSTRUCTION_NODE) + return static_cast<ProcessingInstruction*>(node)->target(); + return node->localName().string(); +} + +static inline String expandedName(Node* node) +{ + const AtomicString& prefix = node->prefix(); + return prefix.isEmpty() ? expandedNameLocalPart(node) : prefix + ":" + expandedNameLocalPart(node); +} + Value FunLocalName::evaluate() const { - Node* node = 0; if (argCount() > 0) { Value a = arg(0)->evaluate(); if (!a.isNodeSet()) return ""; - node = a.toNodeSet().firstNode(); - if (!node) - return ""; + Node* node = a.toNodeSet().firstNode(); + return node ? expandedNameLocalPart(node) : ""; } - if (!node) - node = evaluationContext().node.get(); - - return node->localName().string(); + return expandedNameLocalPart(evaluationContext().node.get()); } Value FunNamespaceURI::evaluate() const { - Node* node = 0; if (argCount() > 0) { Value a = arg(0)->evaluate(); if (!a.isNodeSet()) return ""; - node = a.toNodeSet().firstNode(); - if (!node) - return ""; + Node* node = a.toNodeSet().firstNode(); + return node ? node->namespaceURI().string() : ""; } - if (!node) - node = evaluationContext().node.get(); - - return node->namespaceURI().string(); + return evaluationContext().node->namespaceURI().string(); } Value FunName::evaluate() const { - Node* node = 0; if (argCount() > 0) { Value a = arg(0)->evaluate(); if (!a.isNodeSet()) return ""; - node = a.toNodeSet().firstNode(); - if (!node) - return ""; + Node* node = a.toNodeSet().firstNode(); + return node ? expandedName(node) : ""; } - if (!node) - node = evaluationContext().node.get(); - - const AtomicString& prefix = node->prefix(); - return prefix.isEmpty() ? node->localName().string() : prefix + ":" + node->localName(); + return expandedName(evaluationContext().node.get()); } Value FunCount::evaluate() const { Value a = arg(0)->evaluate(); - if (!a.isNodeSet()) - return 0.0; - return double(a.toNodeSet().size()); } @@ -537,7 +586,7 @@ Value FunLang::evaluate() const Attribute* languageAttribute = 0; Node* node = evaluationContext().node.get(); while (node) { - NamedAttrMap* attrs = node->attributes(); + NamedNodeMap* attrs = node->attributes(); if (attrs) languageAttribute = attrs->getAttributeItem(XMLNames::langAttr); if (languageAttribute) diff --git a/WebCore/xml/XPathFunctions.h b/WebCore/xml/XPathFunctions.h index f22d3ef..62d687f 100644 --- a/WebCore/xml/XPathFunctions.h +++ b/WebCore/xml/XPathFunctions.h @@ -1,6 +1,6 @@ /* - * functions.h - Copyright 2005 Frerich Raabe <raabe@kde.org> - * Copyright (C) 2006 Apple Computer, Inc. + * Copyright (C) 2005 Frerich Raabe <raabe@kde.org> + * Copyright (C) 2006, 2009 Apple Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -39,7 +39,6 @@ namespace WebCore { public: void setArguments(const Vector<Expression*>&); void setName(const String& name) { m_name = name; } - protected: Expression* arg(int pos) { return subExpr(pos); } const Expression* arg(int pos) const { return subExpr(pos); } @@ -52,11 +51,10 @@ namespace WebCore { Function* createFunction(const String& name, const Vector<Expression*>& args = Vector<Expression*>()); - } + } // namespace XPath -} +} // namespace WebCore #endif // ENABLE(XPATH) -#endif // XPath_Functions_H - +#endif // XPathFunctions_h diff --git a/WebCore/xml/XPathNodeSet.h b/WebCore/xml/XPathNodeSet.h index 2ab6f1f..1130488 100644 --- a/WebCore/xml/XPathNodeSet.h +++ b/WebCore/xml/XPathNodeSet.h @@ -39,17 +39,14 @@ namespace WebCore { class NodeSet { public: - - NodeSet() : m_isSorted(true) {} - NodeSet(const NodeSet& other) : m_isSorted(other.m_isSorted), m_nodes(other.m_nodes) {} - NodeSet& operator=(const NodeSet& other) { m_isSorted = other.m_isSorted; m_nodes = other.m_nodes; return *this; } + NodeSet() : m_isSorted(true), m_subtreesAreDisjoint(false) { } size_t size() const { return m_nodes.size(); } bool isEmpty() const { return !m_nodes.size(); } Node* operator[](unsigned i) const { return m_nodes.at(i).get(); } void reserveCapacity(size_t newCapacity) { m_nodes.reserveCapacity(newCapacity); } void clear() { m_nodes.clear(); } - void swap(NodeSet& other) { std::swap(m_isSorted, other.m_isSorted); m_nodes.swap(other.m_nodes); } + void swap(NodeSet& other) { std::swap(m_isSorted, other.m_isSorted); std::swap(m_subtreesAreDisjoint, other.m_subtreesAreDisjoint); m_nodes.swap(other.m_nodes); } // NodeSet itself does not verify that nodes in it are unique. void append(Node* node) { m_nodes.append(node); } @@ -62,16 +59,21 @@ namespace WebCore { // Returns 0 if the set is empty. Node* anyNode() const; - // NodeSet itself doesn't check if it is contains sorted data - the caller should tell it if it does not. + // NodeSet itself doesn't check if it contains nodes in document order - the caller should tell it if it does not. void markSorted(bool isSorted) { m_isSorted = isSorted; } - bool isSorted() const { return m_isSorted; } + bool isSorted() const { return m_isSorted || m_nodes.size() < 2; } void sort() const; + // No node in the set is ancestor of another. Unlike m_isSorted, this is assumed to be false, unless the caller sets it to true. + void markSubtreesDisjoint(bool disjoint) { m_subtreesAreDisjoint = disjoint; } + bool subtreesAreDisjoint() const { return m_subtreesAreDisjoint || m_nodes.size() < 2; } + void reverse(); private: bool m_isSorted; + bool m_subtreesAreDisjoint; Vector<RefPtr<Node> > m_nodes; }; diff --git a/WebCore/xml/XPathPath.cpp b/WebCore/xml/XPathPath.cpp index bc7b153..1a7ed3f 100644 --- a/WebCore/xml/XPathPath.cpp +++ b/WebCore/xml/XPathPath.cpp @@ -1,6 +1,6 @@ /* - * Copyright 2005 Frerich Raabe <raabe@kde.org> - * Copyright (C) 2006 Apple Computer, Inc. + * Copyright (C) 2005 Frerich Raabe <raabe@kde.org> + * Copyright (C) 2006, 2009 Apple Inc. * Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org> * * Redistribution and use in source and binary forms, with or without @@ -41,6 +41,9 @@ namespace XPath { Filter::Filter(Expression* expr, const Vector<Predicate*>& predicates) : m_expr(expr), m_predicates(predicates) { + setIsContextNodeSensitive(m_expr->isContextNodeSensitive()); + setIsContextPositionSensitive(m_expr->isContextPositionSensitive()); + setIsContextSizeSensitive(m_expr->isContextSizeSensitive()); } Filter::~Filter() @@ -53,9 +56,6 @@ Value Filter::evaluate() const { Value v = m_expr->evaluate(); - if (!v.isNodeSet()) - return v; - NodeSet& nodes = v.modifiableNodeSet(); nodes.sort(); @@ -83,6 +83,7 @@ Value Filter::evaluate() const LocationPath::LocationPath() : m_absolute(false) { + setIsContextNodeSensitive(true); } LocationPath::~LocationPath() @@ -94,9 +95,8 @@ Value LocationPath::evaluate() const { EvaluationContext& evaluationContext = Expression::evaluationContext(); EvaluationContext backupContext = evaluationContext; - /* For absolute location paths, the context node is ignored - the - * document's root node is used instead. - */ + // For absolute location paths, the context node is ignored - the + // document's root node is used instead. Node* context = evaluationContext.node.get(); if (m_absolute && context->nodeType() != Node::DOCUMENT_NODE) context = context->ownerDocument(); @@ -111,18 +111,33 @@ Value LocationPath::evaluate() const void LocationPath::evaluate(NodeSet& nodes) const { + bool resultIsSorted = nodes.isSorted(); + for (unsigned i = 0; i < m_steps.size(); i++) { Step* step = m_steps[i]; NodeSet newNodes; HashSet<Node*> newNodesSet; + bool needToCheckForDuplicateNodes = !nodes.subtreesAreDisjoint() || (step->axis() != Step::ChildAxis && step->axis() != Step::SelfAxis + && step->axis() != Step::DescendantAxis && step->axis() != Step::DescendantOrSelfAxis && step->axis() != Step::AttributeAxis); + + if (needToCheckForDuplicateNodes) + resultIsSorted = false; + + // This is a simplified check that can be improved to handle more cases. + if (nodes.subtreesAreDisjoint() && (step->axis() == Step::ChildAxis || step->axis() == Step::SelfAxis)) + newNodes.markSubtreesDisjoint(true); + for (unsigned j = 0; j < nodes.size(); j++) { NodeSet matches; step->evaluate(nodes[j], matches); - + + if (!matches.isSorted()) + resultIsSorted = false; + for (size_t nodeIndex = 0; nodeIndex < matches.size(); ++nodeIndex) { Node* node = matches[nodeIndex]; - if (newNodesSet.add(node).second) + if (!needToCheckForDuplicateNodes || newNodesSet.add(node).second) newNodes.append(node); } } @@ -130,53 +145,46 @@ void LocationPath::evaluate(NodeSet& nodes) const nodes.swap(newNodes); } - nodes.markSorted(false); + nodes.markSorted(resultIsSorted); } -void LocationPath::optimizeStepPair(unsigned index) +void LocationPath::appendStep(Step* step) { - 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->nodeTest().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()); + unsigned stepCount = m_steps.size(); + if (stepCount) { + bool dropSecondStep; + optimizeStepPair(m_steps[stepCount - 1], step, dropSecondStep); + if (dropSecondStep) { + delete step; + return; } } -} - -void LocationPath::appendStep(Step* step) -{ + step->optimize(); m_steps.append(step); - - unsigned stepCount = m_steps.size(); - if (stepCount > 1) - optimizeStepPair(stepCount - 2); } void LocationPath::insertFirstStep(Step* step) { + if (m_steps.size()) { + bool dropSecondStep; + optimizeStepPair(step, m_steps[0], dropSecondStep); + if (dropSecondStep) { + delete m_steps[0]; + m_steps[0] = step; + return; + } + } + step->optimize(); m_steps.insert(0, step); - - if (m_steps.size() > 1) - optimizeStepPair(0); } Path::Path(Filter* filter, LocationPath* path) - : m_filter(filter), - m_path(path) + : m_filter(filter) + , m_path(path) { + setIsContextNodeSensitive(filter->isContextNodeSensitive()); + setIsContextPositionSensitive(filter->isContextPositionSensitive()); + setIsContextSizeSensitive(filter->isContextSizeSensitive()); } Path::~Path() diff --git a/WebCore/xml/XPathPath.h b/WebCore/xml/XPathPath.h index 46e57ff..dc77971 100644 --- a/WebCore/xml/XPathPath.h +++ b/WebCore/xml/XPathPath.h @@ -1,6 +1,6 @@ /* - * path.h - Copyright 2005 Frerich Raabe <raabe@kde.org> - * Copyright (C) 2006 Apple Computer, Inc. + * Copyright (C) 2005 Frerich Raabe <raabe@kde.org> + * Copyright (C) 2006, 2009 Apple Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -47,6 +47,8 @@ namespace WebCore { virtual Value evaluate() const; private: + virtual Value::Type resultType() const { return Value::NodeSetValue; } + Expression* m_expr; Vector<Predicate*> m_predicates; }; @@ -55,7 +57,7 @@ namespace WebCore { public: LocationPath(); virtual ~LocationPath(); - void setAbsolute(bool value) { m_absolute = value; } + void setAbsolute(bool value) { m_absolute = value; setIsContextNodeSensitive(!m_absolute); } virtual Value evaluate() const; void evaluate(NodeSet& nodes) const; // nodes is an input/output parameter @@ -64,7 +66,7 @@ namespace WebCore { void insertFirstStep(Step* step); private: - void optimizeStepPair(unsigned index); + virtual Value::Type resultType() const { return Value::NodeSetValue; } Vector<Step*> m_steps; bool m_absolute; @@ -79,6 +81,8 @@ namespace WebCore { virtual Value evaluate() const; private: + virtual Value::Type resultType() const { return Value::NodeSetValue; } + Filter* m_filter; LocationPath* m_path; }; diff --git a/WebCore/xml/XPathPredicate.cpp b/WebCore/xml/XPathPredicate.cpp index 7b3e4d8..2a6482f 100644 --- a/WebCore/xml/XPathPredicate.cpp +++ b/WebCore/xml/XPathPredicate.cpp @@ -233,8 +233,6 @@ Value Union::evaluate() const { Value lhsResult = subExpr(0)->evaluate(); Value rhs = subExpr(1)->evaluate(); - if (!lhsResult.isNodeSet() || !rhs.isNodeSet()) - return NodeSet(); NodeSet& resultSet = lhsResult.modifiableNodeSet(); const NodeSet& rhsNodes = rhs.toNodeSet(); diff --git a/WebCore/xml/XPathPredicate.h b/WebCore/xml/XPathPredicate.h index 8d1b0d8..6c9d413 100644 --- a/WebCore/xml/XPathPredicate.h +++ b/WebCore/xml/XPathPredicate.h @@ -41,6 +41,8 @@ namespace WebCore { Number(double); private: virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::NumberValue; } + Value m_value; }; @@ -49,12 +51,15 @@ namespace WebCore { StringExpression(const String&); private: virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::StringValue; } + Value m_value; }; class Negative : public Expression { private: virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::NumberValue; } }; class NumericOp : public Expression { @@ -65,6 +70,8 @@ namespace WebCore { NumericOp(Opcode, Expression* lhs, Expression* rhs); private: virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::NumberValue; } + Opcode m_opcode; }; @@ -74,7 +81,9 @@ namespace WebCore { EqTestOp(Opcode, Expression* lhs, Expression* rhs); virtual Value evaluate() const; private: + virtual Value::Type resultType() const { return Value::BooleanValue; } bool compare(const Value&, const Value&) const; + Opcode m_opcode; }; @@ -83,14 +92,17 @@ namespace WebCore { enum Opcode { OP_And, OP_Or }; LogicalOp(Opcode, Expression* lhs, Expression* rhs); private: + virtual Value::Type resultType() const { return Value::BooleanValue; } bool shortCircuitOn() const; virtual Value evaluate() const; + Opcode m_opcode; }; class Union : public Expression { private: virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::NodeSetValue; } }; class Predicate : Noncopyable { @@ -98,6 +110,10 @@ namespace WebCore { Predicate(Expression*); ~Predicate(); bool evaluate() const; + + bool isContextPositionSensitive() const { return m_expr->isContextPositionSensitive() || m_expr->resultType() == Value::NumberValue; } + bool isContextSizeSensitive() const { return m_expr->isContextSizeSensitive(); } + private: Expression* m_expr; }; diff --git a/WebCore/xml/XPathResult.cpp b/WebCore/xml/XPathResult.cpp index a1c8a08..b608280 100644 --- a/WebCore/xml/XPathResult.cpp +++ b/WebCore/xml/XPathResult.cpp @@ -1,6 +1,6 @@ /* - * Copyright 2005 Frerich Raabe <raabe@kde.org> - * Copyright (C) 2006 Apple Computer, Inc. + * Copyright (C) 2005 Frerich Raabe <raabe@kde.org> + * Copyright (C) 2006, 2009 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -29,8 +29,7 @@ #if ENABLE(XPATH) -#include "EventListener.h" -#include "EventNames.h" +#include "Document.h" #include "Node.h" #include "ExceptionCode.h" #include "XPathEvaluator.h" @@ -40,22 +39,9 @@ namespace WebCore { using namespace XPath; -class InvalidatingEventListener : public EventListener { -public: - static PassRefPtr<InvalidatingEventListener> create(XPathResult* result) { return adoptRef(new InvalidatingEventListener(result)); } - virtual void handleEvent(Event*, bool) { m_result->invalidateIteratorState(); } - -private: - InvalidatingEventListener(XPathResult* result) : m_result(result) { } - XPathResult* m_result; -}; - -XPathResult::XPathResult(Node* eventTarget, const Value& value) +XPathResult::XPathResult(Document* document, const Value& value) : m_value(value) - , m_eventTarget(eventTarget) { - m_eventListener = InvalidatingEventListener::create(this); - m_eventTarget->addEventListener(eventNames().DOMSubtreeModifiedEvent, m_eventListener, false); switch (m_value.type()) { case Value::BooleanValue: m_resultType = BOOLEAN_TYPE; @@ -70,7 +56,8 @@ XPathResult::XPathResult(Node* eventTarget, const Value& value) m_resultType = UNORDERED_NODE_ITERATOR_TYPE; m_nodeSetPosition = 0; m_nodeSet = m_value.toNodeSet(); - m_invalidIteratorState = false; + m_document = document; + m_domTreeVersion = document->domTreeVersion(); return; } ASSERT_NOT_REACHED(); @@ -78,8 +65,6 @@ XPathResult::XPathResult(Node* eventTarget, const Value& value) XPathResult::~XPathResult() { - if (m_eventTarget) - m_eventTarget->removeEventListener(eventNames().DOMSubtreeModifiedEvent, m_eventListener.get(), false); } void XPathResult::convertTo(unsigned short type, ExceptionCode& ec) @@ -174,24 +159,13 @@ Node* XPathResult::singleNodeValue(ExceptionCode& ec) const return nodes.anyNode(); } -void XPathResult::invalidateIteratorState() -{ - m_invalidIteratorState = true; - - ASSERT(m_eventTarget); - ASSERT(m_eventListener); - - m_eventTarget->removeEventListener(eventNames().DOMSubtreeModifiedEvent, m_eventListener.get(), false); - - m_eventTarget = 0; -} - bool XPathResult::invalidIteratorState() const { if (resultType() != UNORDERED_NODE_ITERATOR_TYPE && resultType() != ORDERED_NODE_ITERATOR_TYPE) return false; - - return m_invalidIteratorState; + + ASSERT(m_document); + return m_document->domTreeVersion() != m_domTreeVersion; } unsigned long XPathResult::snapshotLength(ExceptionCode& ec) const @@ -211,7 +185,7 @@ Node* XPathResult::iterateNext(ExceptionCode& ec) return 0; } - if (m_invalidIteratorState) { + if (invalidIteratorState()) { ec = INVALID_STATE_ERR; return 0; } diff --git a/WebCore/xml/XPathResult.h b/WebCore/xml/XPathResult.h index 03accc6..3b91d66 100644 --- a/WebCore/xml/XPathResult.h +++ b/WebCore/xml/XPathResult.h @@ -1,6 +1,6 @@ /* - * Copyright 2005 Frerich Raabe <raabe@kde.org> - * Copyright (C) 2006 Apple Computer, Inc. + * Copyright (C) 2005 Frerich Raabe <raabe@kde.org> + * Copyright (C) 2006, 2009 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -29,15 +29,14 @@ #if ENABLE(XPATH) -#include <wtf/RefCounted.h> #include "XPathValue.h" +#include <wtf/RefCounted.h> namespace WebCore { typedef int ExceptionCode; - class EventListener; - class Node; + class Document; class Node; class String; @@ -56,7 +55,7 @@ namespace WebCore { FIRST_ORDERED_NODE_TYPE = 9 }; - static PassRefPtr<XPathResult> create(Node* eventTarget, const XPath::Value& value) { return adoptRef(new XPathResult(eventTarget, value)); } + static PassRefPtr<XPathResult> create(Document* document, const XPath::Value& value) { return adoptRef(new XPathResult(document, value)); } ~XPathResult(); void convertTo(unsigned short type, ExceptionCode&); @@ -73,21 +72,18 @@ namespace WebCore { Node* iterateNext(ExceptionCode&); Node* snapshotItem(unsigned long index, ExceptionCode&); - void invalidateIteratorState(); - private: - XPathResult(Node*, const XPath::Value&); + XPathResult(Document*, const XPath::Value&); XPath::Value m_value; unsigned m_nodeSetPosition; XPath::NodeSet m_nodeSet; // FIXME: why duplicate the node set stored in m_value? unsigned short m_resultType; - bool m_invalidIteratorState; - RefPtr<Node> m_eventTarget; - RefPtr<EventListener> m_eventListener; + RefPtr<Document> m_document; + unsigned m_domTreeVersion; }; -} +} // namespace WebCore #endif // ENABLE(XPATH) diff --git a/WebCore/xml/XPathStep.cpp b/WebCore/xml/XPathStep.cpp index ae8d9c4..411b616 100644 --- a/WebCore/xml/XPathStep.cpp +++ b/WebCore/xml/XPathStep.cpp @@ -1,6 +1,6 @@ /* - * Copyright 2005 Frerich Raabe <raabe@kde.org> - * Copyright (C) 2006 Apple Computer, Inc. + * Copyright (C) 2005 Frerich Raabe <raabe@kde.org> + * Copyright (C) 2006, 2009 Apple Inc. All rights reserved. * Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org> * * Redistribution and use in source and binary forms, with or without @@ -30,10 +30,10 @@ #if ENABLE(XPATH) +#include "Attr.h" #include "Document.h" #include "Element.h" -#include "NamedAttrMap.h" -#include "XPathNSResolver.h" +#include "NamedNodeMap.h" #include "XPathParser.h" #include "XPathUtil.h" @@ -50,14 +50,74 @@ Step::Step(Axis axis, const NodeTest& nodeTest, const Vector<Predicate*>& predic Step::~Step() { deleteAllValues(m_predicates); + deleteAllValues(m_nodeTest.mergedPredicates()); +} + +void Step::optimize() +{ + // Evaluate predicates as part of node test if possible to avoid building unnecessary NodeSets. + // E.g., there is no need to build a set of all "foo" nodes to evaluate "foo[@bar]", we can check the predicate while enumerating. + // This optimization can be applied to predicates that are not context node list sensitive, or to first predicate that is only context position sensitive, e.g. foo[position() mod 2 = 0]. + Vector<Predicate*> remainingPredicates; + for (size_t i = 0; i < m_predicates.size(); ++i) { + Predicate* predicate = m_predicates[i]; + if ((!predicate->isContextPositionSensitive() || m_nodeTest.mergedPredicates().isEmpty()) && !predicate->isContextSizeSensitive() && remainingPredicates.isEmpty()) { + m_nodeTest.mergedPredicates().append(predicate); + } else + remainingPredicates.append(predicate); + } + swap(remainingPredicates, m_predicates); +} + +void optimizeStepPair(Step* first, Step* second, bool& dropSecondStep) +{ + dropSecondStep = false; + + if (first->m_axis == Step::DescendantOrSelfAxis + && first->m_nodeTest.kind() == Step::NodeTest::AnyNodeTest + && !first->m_predicates.size() + && !first->m_nodeTest.mergedPredicates().size()) { + + ASSERT(first->m_nodeTest.data().isEmpty()); + ASSERT(first->m_nodeTest.namespaceURI().isEmpty()); + + // Optimize the common case of "//" AKA /descendant-or-self::node()/child::NodeTest to /descendant::NodeTest. + if (second->m_axis == Step::ChildAxis && second->predicatesAreContextListInsensitive()) { + first->m_axis = Step::DescendantAxis; + first->m_nodeTest = Step::NodeTest(second->m_nodeTest.kind(), second->m_nodeTest.data(), second->m_nodeTest.namespaceURI()); + swap(second->m_nodeTest.mergedPredicates(), first->m_nodeTest.mergedPredicates()); + swap(second->m_predicates, first->m_predicates); + first->optimize(); + dropSecondStep = true; + } + } +} + +bool Step::predicatesAreContextListInsensitive() const +{ + for (size_t i = 0; i < m_predicates.size(); ++i) { + Predicate* predicate = m_predicates[i]; + if (predicate->isContextPositionSensitive() || predicate->isContextSizeSensitive()) + return false; + } + + for (size_t i = 0; i < m_nodeTest.mergedPredicates().size(); ++i) { + Predicate* predicate = m_nodeTest.mergedPredicates()[i]; + if (predicate->isContextPositionSensitive() || predicate->isContextSizeSensitive()) + return false; + } + + return true; } void Step::evaluate(Node* context, NodeSet& nodes) const { - nodesInAxis(context, nodes); - EvaluationContext& evaluationContext = Expression::evaluationContext(); - + evaluationContext.position = 0; + + nodesInAxis(context, nodes); + + // Check predicates that couldn't be merged into node test. for (unsigned i = 0; i < m_predicates.size(); i++) { Predicate* predicate = m_predicates[i]; @@ -68,7 +128,7 @@ void Step::evaluate(Node* context, NodeSet& nodes) const for (unsigned j = 0; j < nodes.size(); j++) { Node* node = nodes[j]; - Expression::evaluationContext().node = node; + evaluationContext.node = node; evaluationContext.size = nodes.size(); evaluationContext.position = j + 1; if (predicate->evaluate()) @@ -79,6 +139,95 @@ void Step::evaluate(Node* context, NodeSet& nodes) const } } +static inline Node::NodeType primaryNodeType(Step::Axis axis) +{ + switch (axis) { + case Step::AttributeAxis: + return Node::ATTRIBUTE_NODE; + case Step::NamespaceAxis: + return Node::XPATH_NAMESPACE_NODE; + default: + return Node::ELEMENT_NODE; + } +} + +// Evaluate NodeTest without considering merged predicates. +static inline bool nodeMatchesBasicTest(Node* node, Step::Axis axis, const Step::NodeTest& nodeTest) +{ + switch (nodeTest.kind()) { + case Step::NodeTest::TextNodeTest: + return node->nodeType() == Node::TEXT_NODE || node->nodeType() == Node::CDATA_SECTION_NODE; + case Step::NodeTest::CommentNodeTest: + return node->nodeType() == Node::COMMENT_NODE; + case Step::NodeTest::ProcessingInstructionNodeTest: { + const AtomicString& name = nodeTest.data(); + return node->nodeType() == Node::PROCESSING_INSTRUCTION_NODE && (name.isEmpty() || node->nodeName() == name); + } + case Step::NodeTest::AnyNodeTest: + return true; + case Step::NodeTest::NameTest: { + const AtomicString& name = nodeTest.data(); + const AtomicString& namespaceURI = nodeTest.namespaceURI(); + + if (axis == Step::AttributeAxis) { + ASSERT(node->isAttributeNode()); + + // In XPath land, namespace nodes are not accessible on the attribute axis. + if (node->namespaceURI() == "http://www.w3.org/2000/xmlns/") + return false; + + if (name == starAtom) + return namespaceURI.isEmpty() || node->namespaceURI() == namespaceURI; + + return node->localName() == name && node->namespaceURI() == namespaceURI; + } + + // Node test on the namespace axis is not implemented yet, the caller has a check for it. + ASSERT(axis != Step::NamespaceAxis); + + // For other axes, the principal node type is element. + ASSERT(primaryNodeType(axis) == Node::ELEMENT_NODE); + if (node->nodeType() != Node::ELEMENT_NODE) + return false; + + if (name == starAtom) + return namespaceURI.isEmpty() || namespaceURI == node->namespaceURI(); + + if (node->isHTMLElement() && node->document()->isHTMLDocument()) { + // Paths without namespaces should match HTML elements in HTML documents despite those having an XHTML namespace. Names are compared case-insensitively. + return equalIgnoringCase(static_cast<Element*>(node)->localName(), name) && (namespaceURI.isNull() || namespaceURI == node->namespaceURI()); + } + return static_cast<Element*>(node)->hasLocalName(name) && namespaceURI == node->namespaceURI(); + } + } + ASSERT_NOT_REACHED(); + return false; +} + +static inline bool nodeMatches(Node* node, Step::Axis axis, const Step::NodeTest& nodeTest) +{ + if (!nodeMatchesBasicTest(node, axis, nodeTest)) + return false; + + EvaluationContext& evaluationContext = Expression::evaluationContext(); + + // Only the first merged predicate may depend on position. + ++evaluationContext.position; + + const Vector<Predicate*>& mergedPredicates = nodeTest.mergedPredicates(); + for (unsigned i = 0; i < mergedPredicates.size(); i++) { + Predicate* predicate = mergedPredicates[i]; + + evaluationContext.node = node; + // No need to set context size - we only get here when evaluating predicates that do not depend on it. + if (!predicate->evaluate()) + return false; + } + + return true; +} + +// Result nodes are ordered in axis order. Node test (including merged predicates) is applied. void Step::nodesInAxis(Node* context, NodeSet& nodes) const { ASSERT(nodes.isEmpty()); @@ -88,7 +237,7 @@ void Step::nodesInAxis(Node* context, NodeSet& nodes) const return; for (Node* n = context->firstChild(); n; n = n->nextSibling()) - if (nodeMatches(n)) + if (nodeMatches(n, ChildAxis, m_nodeTest)) nodes.append(n); return; case DescendantAxis: @@ -96,17 +245,17 @@ void Step::nodesInAxis(Node* context, NodeSet& nodes) const return; for (Node* n = context->firstChild(); n; n = n->traverseNextNode(context)) - if (nodeMatches(n)) + if (nodeMatches(n, DescendantAxis, m_nodeTest)) nodes.append(n); return; case ParentAxis: if (context->isAttributeNode()) { Node* n = static_cast<Attr*>(context)->ownerElement(); - if (nodeMatches(n)) + if (nodeMatches(n, ParentAxis, m_nodeTest)) nodes.append(n); } else { Node* n = context->parentNode(); - if (n && nodeMatches(n)) + if (n && nodeMatches(n, ParentAxis, m_nodeTest)) nodes.append(n); } return; @@ -114,11 +263,11 @@ void Step::nodesInAxis(Node* context, NodeSet& nodes) const Node* n = context; if (context->isAttributeNode()) { n = static_cast<Attr*>(context)->ownerElement(); - if (nodeMatches(n)) + if (nodeMatches(n, AncestorAxis, m_nodeTest)) nodes.append(n); } for (n = n->parentNode(); n; n = n->parentNode()) - if (nodeMatches(n)) + if (nodeMatches(n, AncestorAxis, m_nodeTest)) nodes.append(n); nodes.markSorted(false); return; @@ -129,7 +278,7 @@ void Step::nodesInAxis(Node* context, NodeSet& nodes) const return; for (Node* n = context->nextSibling(); n; n = n->nextSibling()) - if (nodeMatches(n)) + if (nodeMatches(n, FollowingSiblingAxis, m_nodeTest)) nodes.append(n); return; case PrecedingSiblingAxis: @@ -138,7 +287,7 @@ void Step::nodesInAxis(Node* context, NodeSet& nodes) const return; for (Node* n = context->previousSibling(); n; n = n->previousSibling()) - if (nodeMatches(n)) + if (nodeMatches(n, PrecedingSiblingAxis, m_nodeTest)) nodes.append(n); nodes.markSorted(false); @@ -147,15 +296,15 @@ void Step::nodesInAxis(Node* context, NodeSet& nodes) const if (context->isAttributeNode()) { Node* p = static_cast<Attr*>(context)->ownerElement(); while ((p = p->traverseNextNode())) - if (nodeMatches(p)) + if (nodeMatches(p, FollowingAxis, m_nodeTest)) nodes.append(p); } else { for (Node* p = context; !isRootDomNode(p); p = p->parentNode()) { for (Node* n = p->nextSibling(); n; n = n->nextSibling()) { - if (nodeMatches(n)) + if (nodeMatches(n, FollowingAxis, m_nodeTest)) nodes.append(n); for (Node* c = n->firstChild(); c; c = c->traverseNextNode(n)) - if (nodeMatches(c)) + if (nodeMatches(c, FollowingAxis, m_nodeTest)) nodes.append(c); } } @@ -168,7 +317,7 @@ void Step::nodesInAxis(Node* context, NodeSet& nodes) const Node* n = context; while (Node* parent = n->parent()) { for (n = n->traversePreviousNode(); n != parent; n = n->traversePreviousNode()) - if (nodeMatches(n)) + if (nodeMatches(n, PrecedingAxis, m_nodeTest)) nodes.append(n); n = parent; } @@ -180,20 +329,22 @@ void Step::nodesInAxis(Node* context, NodeSet& nodes) const return; // Avoid lazily creating attribute nodes for attributes that we do not need anyway. - if (m_nodeTest.kind() == NodeTest::NameTest && m_nodeTest.data() != "*") { + if (m_nodeTest.kind() == NodeTest::NameTest && m_nodeTest.data() != starAtom) { 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()); + if (n && n->namespaceURI() != "http://www.w3.org/2000/xmlns/") { // In XPath land, namespace nodes are not accessible on the attribute axis. + if (nodeMatches(n.get(), AttributeAxis, m_nodeTest)) // Still need to check merged predicates. + nodes.append(n.release()); + } return; } - NamedAttrMap* attrs = context->attributes(); + NamedNodeMap* attrs = context->attributes(); if (!attrs) return; for (unsigned i = 0; i < attrs->length(); ++i) { RefPtr<Attr> attr = attrs->attributeItem(i)->createAttrIfNeeded(static_cast<Element*>(context)); - if (nodeMatches(attr.get())) + if (nodeMatches(attr.get(), AttributeAxis, m_nodeTest)) nodes.append(attr.release()); } return; @@ -202,30 +353,30 @@ void Step::nodesInAxis(Node* context, NodeSet& nodes) const // XPath namespace nodes are not implemented yet. return; case SelfAxis: - if (nodeMatches(context)) + if (nodeMatches(context, SelfAxis, m_nodeTest)) nodes.append(context); return; case DescendantOrSelfAxis: - if (nodeMatches(context)) + if (nodeMatches(context, DescendantOrSelfAxis, m_nodeTest)) nodes.append(context); if (context->isAttributeNode()) // In XPath model, attribute nodes do not have children. return; for (Node* n = context->firstChild(); n; n = n->traverseNextNode(context)) - if (nodeMatches(n)) + if (nodeMatches(n, DescendantOrSelfAxis, m_nodeTest)) nodes.append(n); return; case AncestorOrSelfAxis: { - if (nodeMatches(context)) + if (nodeMatches(context, AncestorOrSelfAxis, m_nodeTest)) nodes.append(context); Node* n = context; if (context->isAttributeNode()) { n = static_cast<Attr*>(context)->ownerElement(); - if (nodeMatches(n)) + if (nodeMatches(n, AncestorOrSelfAxis, m_nodeTest)) nodes.append(n); } for (n = n->parentNode(); n; n = n->parentNode()) - if (nodeMatches(n)) + if (nodeMatches(n, AncestorOrSelfAxis, m_nodeTest)) nodes.append(n); nodes.markSorted(false); @@ -236,70 +387,6 @@ void Step::nodesInAxis(Node* context, NodeSet& nodes) const } -bool Step::nodeMatches(Node* node) const -{ - switch (m_nodeTest.kind()) { - case NodeTest::TextNodeTest: - return node->nodeType() == Node::TEXT_NODE || node->nodeType() == Node::CDATA_SECTION_NODE; - case NodeTest::CommentNodeTest: - return node->nodeType() == Node::COMMENT_NODE; - case NodeTest::ProcessingInstructionNodeTest: { - const String& name = m_nodeTest.data(); - return node->nodeType() == Node::PROCESSING_INSTRUCTION_NODE && (name.isEmpty() || node->nodeName() == name); - } - case NodeTest::ElementNodeTest: - return node->isElementNode(); - case NodeTest::AnyNodeTest: - return true; - case NodeTest::NameTest: { - const String& name = m_nodeTest.data(); - 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 (node->namespaceURI() == "http://www.w3.org/2000/xmlns/") - return false; - - 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 - 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(); - return false; -} - -Node::NodeType Step::primaryNodeType(Axis axis) const -{ - switch (axis) { - case AttributeAxis: - return Node::ATTRIBUTE_NODE; - case NamespaceAxis: - return Node::XPATH_NAMESPACE_NODE; - default: - return Node::ELEMENT_NODE; - } -} - } } diff --git a/WebCore/xml/XPathStep.h b/WebCore/xml/XPathStep.h index f1420d0..1c26327 100644 --- a/WebCore/xml/XPathStep.h +++ b/WebCore/xml/XPathStep.h @@ -1,6 +1,6 @@ /* - * Copyright 2005 Frerich Raabe <raabe@kde.org> - * Copyright (C) 2006 Apple Computer, Inc. + * Copyright (C) 2005 Frerich Raabe <raabe@kde.org> + * Copyright (C) 2006, 2009 Apple Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -52,8 +52,7 @@ namespace WebCore { class NodeTest { public: enum Kind { - TextNodeTest, CommentNodeTest, ProcessingInstructionNodeTest, AnyNodeTest, NameTest, - ElementNodeTest // XPath 2.0 + TextNodeTest, CommentNodeTest, ProcessingInstructionNodeTest, AnyNodeTest, NameTest }; NodeTest(Kind kind) : m_kind(kind) {} @@ -61,44 +60,48 @@ namespace WebCore { 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; } + const AtomicString& data() const { return m_data; } + const AtomicString& namespaceURI() const { return m_namespaceURI; } + Vector<Predicate*>& mergedPredicates() { return m_mergedPredicates; } + const Vector<Predicate*>& mergedPredicates() const { return m_mergedPredicates; } private: Kind m_kind; - String m_data; - String m_namespaceURI; + AtomicString m_data; + AtomicString m_namespaceURI; + + // When possible, we merge some or all predicates with node test for better performance. + Vector<Predicate*> m_mergedPredicates; }; Step(Axis, const NodeTest& nodeTest, const Vector<Predicate*>& predicates = Vector<Predicate*>()); ~Step(); + void optimize(); + void evaluate(Node* context, NodeSet&) const; - + Axis axis() const { return m_axis; } - NodeTest nodeTest() const { return m_nodeTest; } - const Vector<Predicate*>& predicates() const { return m_predicates; } - - void setAxis(Axis axis) { m_axis = axis; } - void setNodeTest(NodeTest nodeTest) { m_nodeTest = nodeTest; } - void setPredicates(const Vector<Predicate*>& predicates) { m_predicates = predicates; } - + const NodeTest& nodeTest() const { return m_nodeTest; } + private: + friend void optimizeStepPair(Step*, Step*, bool&); + bool predicatesAreContextListInsensitive() const; + void parseNodeTest(const String&); void nodesInAxis(Node* context, NodeSet&) const; - bool nodeMatches(Node*) const; String namespaceFromNodetest(const String& nodeTest) const; - Node::NodeType primaryNodeType(Axis) const; Axis m_axis; NodeTest m_nodeTest; Vector<Predicate*> m_predicates; }; + void optimizeStepPair(Step*, Step*, bool& dropSecondStep); } } #endif // ENABLE(XPATH) -#endif // XPath_Step_H +#endif // XPathStep_h diff --git a/WebCore/xml/XPathUtil.cpp b/WebCore/xml/XPathUtil.cpp index ab4b1d4..0100bea 100644 --- a/WebCore/xml/XPathUtil.cpp +++ b/WebCore/xml/XPathUtil.cpp @@ -1,6 +1,6 @@ /* - * Copyright 2005 Frerich Raabe <raabe@kde.org> - * Copyright (C) 2006 Apple Computer, Inc. + * Copyright (C) 2005 Frerich Raabe <raabe@kde.org> + * Copyright (C) 2006, 2009 Apple Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -51,13 +51,17 @@ String stringValue(Node* node) return node->nodeValue(); default: if (isRootDomNode(node) || node->nodeType() == Node::ELEMENT_NODE) { - String str; - - for (Node* n = node->firstChild(); n; n = n->traverseNextNode(node)) - if (n->isTextNode()) - str += n->nodeValue(); + Vector<UChar> result; + result.reserveCapacity(1024); - return str; + for (Node* n = node->firstChild(); n; n = n->traverseNextNode(node)) { + if (n->isTextNode()) { + const String& nodeValue = n->nodeValue(); + result.append(nodeValue.characters(), nodeValue.length()); + } + } + + return String::adopt(result); } } diff --git a/WebCore/xml/XPathValue.cpp b/WebCore/xml/XPathValue.cpp index bac0e13..29e211e 100644 --- a/WebCore/xml/XPathValue.cpp +++ b/WebCore/xml/XPathValue.cpp @@ -30,11 +30,11 @@ #if ENABLE(XPATH) #include "Node.h" +#include "XPathExpressionNode.h" #include "XPathUtil.h" - +#include <limits> #include <wtf/MathExtras.h> #include <wtf/StdLibExtras.h> -#include <limits> using std::numeric_limits; @@ -45,6 +45,9 @@ const Value::AdoptTag Value::adopt = {}; const NodeSet& Value::toNodeSet() const { + if (!isNodeSet()) + Expression::evaluationContext().hadTypeConversionError = true; + if (!m_data) { DEFINE_STATIC_LOCAL(NodeSet, emptyNodeSet, ()); return emptyNodeSet; @@ -55,6 +58,9 @@ const NodeSet& Value::toNodeSet() const NodeSet& Value::modifiableNodeSet() { + if (!isNodeSet()) + Expression::evaluationContext().hadTypeConversionError = true; + if (!m_data) m_data = ValueData::create(); @@ -86,8 +92,18 @@ double Value::toNumber() const case NumberValue: return m_number; case StringValue: { + const String& str = m_data->m_string.simplifyWhiteSpace(); + + // String::toDouble() supports exponential notation, which is not allowed in XPath. + unsigned len = str.length(); + for (unsigned i = 0; i < len; ++i) { + UChar c = str[i]; + if (!isASCIIDigit(c) && c != '.' && c != '-') + return numeric_limits<double>::quiet_NaN(); + } + bool canConvert; - double value = m_data->m_string.simplifyWhiteSpace().toDouble(&canConvert); + double value = str.toDouble(&canConvert); if (canConvert) return value; return numeric_limits<double>::quiet_NaN(); diff --git a/WebCore/xml/XPathVariableReference.h b/WebCore/xml/XPathVariableReference.h index 811176d..5e5a59a 100644 --- a/WebCore/xml/XPathVariableReference.h +++ b/WebCore/xml/XPathVariableReference.h @@ -33,12 +33,14 @@ namespace WebCore { namespace XPath { - + + // Variable references are not used with XPathEvaluator. class VariableReference : public Expression { public: VariableReference(const String& name); private: virtual Value evaluate() const; + virtual Value::Type resultType() const { ASSERT_NOT_REACHED(); return Value::NumberValue; } String m_name; }; diff --git a/WebCore/xml/XSLStyleSheet.cpp b/WebCore/xml/XSLStyleSheet.cpp index 0d112a5..b7d52f8 100644 --- a/WebCore/xml/XSLStyleSheet.cpp +++ b/WebCore/xml/XSLStyleSheet.cpp @@ -30,11 +30,12 @@ #include "DocLoader.h" #include "Document.h" #include "Frame.h" -#include "loader.h" #include "Node.h" #include "XMLTokenizer.h" +#include "XMLTokenizerScope.h" #include "XSLImportRule.h" #include "XSLTProcessor.h" +#include "loader.h" #include <libxml/uri.h> #include <libxslt/xsltutils.h> @@ -138,7 +139,6 @@ bool XSLStyleSheet::parseString(const String& string, bool) // Parse in a single chunk into an xmlDocPtr const UChar BOM = 0xFEFF; const unsigned char BOMHighByte = *reinterpret_cast<const unsigned char*>(&BOM); - setLoaderForLibXMLCallbacks(docLoader()); if (!m_stylesheetDocTaken) xmlFreeDoc(m_stylesheetDoc); m_stylesheetDocTaken = false; @@ -146,8 +146,8 @@ bool XSLStyleSheet::parseString(const String& string, bool) Console* console = 0; if (Frame* frame = ownerDocument()->frame()) console = frame->domWindow()->console(); - xmlSetStructuredErrorFunc(console, XSLTProcessor::parseErrorFunc); - xmlSetGenericErrorFunc(console, XSLTProcessor::genericErrorFunc); + + XMLTokenizerScope scope(docLoader(), XSLTProcessor::genericErrorFunc, XSLTProcessor::parseErrorFunc, console); const char* buffer = reinterpret_cast<const char*>(string.characters()); int size = string.length() * sizeof(UChar); @@ -171,13 +171,9 @@ bool XSLStyleSheet::parseString(const String& string, bool) BOMHighByte == 0xFF ? "UTF-16LE" : "UTF-16BE", XML_PARSE_NOENT | XML_PARSE_DTDATTR | XML_PARSE_NOWARNING | XML_PARSE_NOCDATA); xmlFreeParserCtxt(ctxt); - - loadChildSheets(); - xmlSetStructuredErrorFunc(0, 0); - xmlSetGenericErrorFunc(0, 0); + loadChildSheets(); - setLoaderForLibXMLCallbacks(0); return m_stylesheetDoc; } diff --git a/WebCore/xml/XSLTProcessor.cpp b/WebCore/xml/XSLTProcessor.cpp index 198c90c..3865124 100644 --- a/WebCore/xml/XSLTProcessor.cpp +++ b/WebCore/xml/XSLTProcessor.cpp @@ -55,11 +55,10 @@ #include <wtf/Assertions.h> #include <wtf/Platform.h> #include <wtf/Vector.h> + #if PLATFORM(MAC) #include "SoftLinking.h" -#endif -#if PLATFORM(MAC) SOFT_LINK_LIBRARY(libxslt); SOFT_LINK(libxslt, xsltFreeStylesheet, void, (xsltStylesheetPtr sheet), (sheet)) SOFT_LINK(libxslt, xsltFreeTransformContext, void, (xsltTransformContextPtr ctxt), (ctxt)) @@ -128,7 +127,7 @@ static xmlDocPtr docLoaderFunc(const xmlChar* uri, bool requestAllowed = globalDocLoader->frame() && globalDocLoader->doc()->securityOrigin()->canRequest(url); if (requestAllowed) { - globalDocLoader->frame()->loader()->loadResourceSynchronously(url, error, response, data); + globalDocLoader->frame()->loader()->loadResourceSynchronously(url, AllowStoredCredentials, error, response, data); requestAllowed = globalDocLoader->doc()->securityOrigin()->canRequest(response.url()); } if (!requestAllowed) { |