dom/xslt/xpath/nsXPathResult.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/dom/xslt/xpath/nsXPathResult.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,437 @@
     1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include "nsXPathResult.h"
    1.10 +#include "txExprResult.h"
    1.11 +#include "txNodeSet.h"
    1.12 +#include "nsError.h"
    1.13 +#include "mozilla/dom/Attr.h"
    1.14 +#include "mozilla/dom/Element.h"
    1.15 +#include "nsDOMClassInfoID.h"
    1.16 +#include "nsIDOMNode.h"
    1.17 +#include "nsIDOMDocument.h"
    1.18 +#include "nsDOMString.h"
    1.19 +#include "txXPathTreeWalker.h"
    1.20 +#include "nsCycleCollectionParticipant.h"
    1.21 +
    1.22 +using namespace mozilla::dom;
    1.23 +
    1.24 +nsXPathResult::nsXPathResult() : mDocument(nullptr),
    1.25 +                                 mCurrentPos(0),
    1.26 +                                 mResultType(ANY_TYPE),
    1.27 +                                 mInvalidIteratorState(true),
    1.28 +                                 mBooleanResult(false),
    1.29 +                                 mNumberResult(0)
    1.30 +{
    1.31 +}
    1.32 +
    1.33 +nsXPathResult::nsXPathResult(const nsXPathResult &aResult)
    1.34 +    : mResult(aResult.mResult),
    1.35 +      mResultNodes(aResult.mResultNodes),
    1.36 +      mDocument(aResult.mDocument),
    1.37 +      mCurrentPos(0),
    1.38 +      mResultType(aResult.mResultType),
    1.39 +      mContextNode(aResult.mContextNode),
    1.40 +      mInvalidIteratorState(aResult.mInvalidIteratorState)
    1.41 +{
    1.42 +    if (mDocument) {
    1.43 +        mDocument->AddMutationObserver(this);
    1.44 +    }
    1.45 +}
    1.46 +
    1.47 +nsXPathResult::~nsXPathResult()
    1.48 +{
    1.49 +    RemoveObserver();
    1.50 +}
    1.51 +
    1.52 +NS_IMPL_CYCLE_COLLECTION_CLASS(nsXPathResult)
    1.53 +
    1.54 +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXPathResult)
    1.55 +    {
    1.56 +        tmp->RemoveObserver();
    1.57 +    }
    1.58 +    NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument)
    1.59 +NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    1.60 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXPathResult)
    1.61 +    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
    1.62 +    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResultNodes)
    1.63 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    1.64 +
    1.65 +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXPathResult)
    1.66 +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXPathResult)
    1.67 +
    1.68 +DOMCI_DATA(XPathResult, nsXPathResult)
    1.69 +
    1.70 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXPathResult)
    1.71 +    NS_INTERFACE_MAP_ENTRY(nsIDOMXPathResult)
    1.72 +    NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
    1.73 +    NS_INTERFACE_MAP_ENTRY(nsIXPathResult)
    1.74 +    NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMXPathResult)
    1.75 +    NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(XPathResult)
    1.76 +NS_INTERFACE_MAP_END
    1.77 +
    1.78 +void
    1.79 +nsXPathResult::RemoveObserver()
    1.80 +{
    1.81 +    if (mDocument) {
    1.82 +        mDocument->RemoveMutationObserver(this);
    1.83 +    }
    1.84 +}
    1.85 +
    1.86 +NS_IMETHODIMP
    1.87 +nsXPathResult::GetResultType(uint16_t *aResultType)
    1.88 +{
    1.89 +    *aResultType = mResultType;
    1.90 +
    1.91 +    return NS_OK;
    1.92 +}
    1.93 +
    1.94 +NS_IMETHODIMP
    1.95 +nsXPathResult::GetNumberValue(double *aNumberValue)
    1.96 +{
    1.97 +    if (mResultType != NUMBER_TYPE) {
    1.98 +        return NS_ERROR_DOM_TYPE_ERR;
    1.99 +    }
   1.100 +
   1.101 +    *aNumberValue = mNumberResult;
   1.102 +
   1.103 +    return NS_OK;
   1.104 +}
   1.105 +
   1.106 +NS_IMETHODIMP
   1.107 +nsXPathResult::GetStringValue(nsAString &aStringValue)
   1.108 +{
   1.109 +    if (mResultType != STRING_TYPE) {
   1.110 +        return NS_ERROR_DOM_TYPE_ERR;
   1.111 +    }
   1.112 +
   1.113 +    aStringValue = mStringResult;
   1.114 +
   1.115 +    return NS_OK;
   1.116 +}
   1.117 +
   1.118 +NS_IMETHODIMP
   1.119 +nsXPathResult::GetBooleanValue(bool *aBooleanValue)
   1.120 +{
   1.121 +    if (mResultType != BOOLEAN_TYPE) {
   1.122 +        return NS_ERROR_DOM_TYPE_ERR;
   1.123 +    }
   1.124 +
   1.125 +    *aBooleanValue = mBooleanResult;
   1.126 +
   1.127 +    return NS_OK;
   1.128 +}
   1.129 +
   1.130 +NS_IMETHODIMP
   1.131 +nsXPathResult::GetSingleNodeValue(nsIDOMNode **aSingleNodeValue)
   1.132 +{
   1.133 +    if (!isNode()) {
   1.134 +        return NS_ERROR_DOM_TYPE_ERR;
   1.135 +    }
   1.136 +
   1.137 +    if (mResultNodes.Count() > 0) {
   1.138 +        NS_ADDREF(*aSingleNodeValue = mResultNodes[0]);
   1.139 +    }
   1.140 +    else {
   1.141 +        *aSingleNodeValue = nullptr;
   1.142 +    }
   1.143 +
   1.144 +    return NS_OK;
   1.145 +}
   1.146 +
   1.147 +NS_IMETHODIMP
   1.148 +nsXPathResult::GetInvalidIteratorState(bool *aInvalidIteratorState)
   1.149 +{
   1.150 +    *aInvalidIteratorState = isIterator() && mInvalidIteratorState;
   1.151 +
   1.152 +    return NS_OK;
   1.153 +}
   1.154 +
   1.155 +NS_IMETHODIMP
   1.156 +nsXPathResult::GetSnapshotLength(uint32_t *aSnapshotLength)
   1.157 +{
   1.158 +    if (!isSnapshot()) {
   1.159 +        return NS_ERROR_DOM_TYPE_ERR;
   1.160 +    }
   1.161 +
   1.162 +    *aSnapshotLength = (uint32_t)mResultNodes.Count();
   1.163 +
   1.164 +    return NS_OK;
   1.165 +}
   1.166 +
   1.167 +NS_IMETHODIMP
   1.168 +nsXPathResult::IterateNext(nsIDOMNode **aResult)
   1.169 +{
   1.170 +    if (!isIterator()) {
   1.171 +        return NS_ERROR_DOM_TYPE_ERR;
   1.172 +    }
   1.173 +
   1.174 +    if (mDocument) {
   1.175 +        mDocument->FlushPendingNotifications(Flush_Content);
   1.176 +    }
   1.177 +
   1.178 +    if (mInvalidIteratorState) {
   1.179 +        return NS_ERROR_DOM_INVALID_STATE_ERR;
   1.180 +    }
   1.181 +
   1.182 +    if (mCurrentPos < (uint32_t)mResultNodes.Count()) {
   1.183 +        NS_ADDREF(*aResult = mResultNodes[mCurrentPos++]);
   1.184 +    }
   1.185 +    else {
   1.186 +        *aResult = nullptr;
   1.187 +    }
   1.188 +
   1.189 +    return NS_OK;
   1.190 +}
   1.191 +
   1.192 +NS_IMETHODIMP
   1.193 +nsXPathResult::SnapshotItem(uint32_t aIndex, nsIDOMNode **aResult)
   1.194 +{
   1.195 +    if (!isSnapshot()) {
   1.196 +        return NS_ERROR_DOM_TYPE_ERR;
   1.197 +    }
   1.198 +
   1.199 +    NS_IF_ADDREF(*aResult = mResultNodes.SafeObjectAt(aIndex));
   1.200 +
   1.201 +    return NS_OK;
   1.202 +}
   1.203 +
   1.204 +void
   1.205 +nsXPathResult::NodeWillBeDestroyed(const nsINode* aNode)
   1.206 +{
   1.207 +    nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
   1.208 +    // Set to null to avoid unregistring unnecessarily
   1.209 +    mDocument = nullptr;
   1.210 +    Invalidate(aNode->IsNodeOfType(nsINode::eCONTENT) ?
   1.211 +               static_cast<const nsIContent*>(aNode) : nullptr);
   1.212 +}
   1.213 +
   1.214 +void
   1.215 +nsXPathResult::CharacterDataChanged(nsIDocument* aDocument,
   1.216 +                                    nsIContent *aContent,
   1.217 +                                    CharacterDataChangeInfo* aInfo)
   1.218 +{
   1.219 +    Invalidate(aContent);
   1.220 +}
   1.221 +
   1.222 +void
   1.223 +nsXPathResult::AttributeChanged(nsIDocument* aDocument,
   1.224 +                                Element* aElement,
   1.225 +                                int32_t aNameSpaceID,
   1.226 +                                nsIAtom* aAttribute,
   1.227 +                                int32_t aModType)
   1.228 +{
   1.229 +    Invalidate(aElement);
   1.230 +}
   1.231 +
   1.232 +void
   1.233 +nsXPathResult::ContentAppended(nsIDocument* aDocument,
   1.234 +                               nsIContent* aContainer,
   1.235 +                               nsIContent* aFirstNewContent,
   1.236 +                               int32_t aNewIndexInContainer)
   1.237 +{
   1.238 +    Invalidate(aContainer);
   1.239 +}
   1.240 +
   1.241 +void
   1.242 +nsXPathResult::ContentInserted(nsIDocument* aDocument,
   1.243 +                               nsIContent* aContainer,
   1.244 +                               nsIContent* aChild,
   1.245 +                               int32_t aIndexInContainer)
   1.246 +{
   1.247 +    Invalidate(aContainer);
   1.248 +}
   1.249 +
   1.250 +void
   1.251 +nsXPathResult::ContentRemoved(nsIDocument* aDocument,
   1.252 +                              nsIContent* aContainer,
   1.253 +                              nsIContent* aChild,
   1.254 +                              int32_t aIndexInContainer,
   1.255 +                              nsIContent* aPreviousSibling)
   1.256 +{
   1.257 +    Invalidate(aContainer);
   1.258 +}
   1.259 +
   1.260 +nsresult
   1.261 +nsXPathResult::SetExprResult(txAExprResult* aExprResult, uint16_t aResultType,
   1.262 +                             nsINode* aContextNode)
   1.263 +{
   1.264 +    MOZ_ASSERT(aExprResult);
   1.265 +
   1.266 +    if ((isSnapshot(aResultType) || isIterator(aResultType) ||
   1.267 +         isNode(aResultType)) &&
   1.268 +        aExprResult->getResultType() != txAExprResult::NODESET) {
   1.269 +        // The DOM spec doesn't really say what should happen when reusing an
   1.270 +        // XPathResult and an error is thrown. Let's not touch the XPathResult
   1.271 +        // in that case.
   1.272 +        return NS_ERROR_DOM_TYPE_ERR;
   1.273 +    }
   1.274 +
   1.275 +    mResultType = aResultType;
   1.276 +    mContextNode = do_GetWeakReference(aContextNode);
   1.277 +
   1.278 +    if (mDocument) {
   1.279 +        mDocument->RemoveMutationObserver(this);
   1.280 +        mDocument = nullptr;
   1.281 +    }
   1.282 + 
   1.283 +    mResultNodes.Clear();
   1.284 +
   1.285 +    // XXX This will keep the recycler alive, should we clear it?
   1.286 +    mResult = aExprResult;
   1.287 +    switch (mResultType) {
   1.288 +        case BOOLEAN_TYPE:
   1.289 +        {
   1.290 +            mBooleanResult = mResult->booleanValue();
   1.291 +            break;
   1.292 +        }
   1.293 +        case NUMBER_TYPE:
   1.294 +        {
   1.295 +            mNumberResult = mResult->numberValue();
   1.296 +            break;
   1.297 +        }
   1.298 +        case STRING_TYPE:
   1.299 +        {
   1.300 +            mResult->stringValue(mStringResult);
   1.301 +            break;
   1.302 +        }
   1.303 +        default:
   1.304 +        {
   1.305 +            MOZ_ASSERT(isNode() || isIterator() || isSnapshot());
   1.306 +        }
   1.307 +    }
   1.308 +
   1.309 +    if (aExprResult->getResultType() == txAExprResult::NODESET) {
   1.310 +        txNodeSet *nodeSet = static_cast<txNodeSet*>(aExprResult);
   1.311 +        nsCOMPtr<nsIDOMNode> node;
   1.312 +        int32_t i, count = nodeSet->size();
   1.313 +        for (i = 0; i < count; ++i) {
   1.314 +            txXPathNativeNode::getNode(nodeSet->get(i), getter_AddRefs(node));
   1.315 +            if (node) {
   1.316 +                mResultNodes.AppendObject(node);
   1.317 +            }
   1.318 +        }
   1.319 +
   1.320 +        if (count > 0) {
   1.321 +            mResult = nullptr;
   1.322 +        }
   1.323 +    }
   1.324 +
   1.325 +    if (!isIterator()) {
   1.326 +        return NS_OK;
   1.327 +    }
   1.328 +
   1.329 +    mInvalidIteratorState = false;
   1.330 +
   1.331 +    if (mResultNodes.Count() > 0) {
   1.332 +        // If we support the document() function in DOM-XPath we need to
   1.333 +        // observe all documents that we have resultnodes in.
   1.334 +        nsCOMPtr<nsIDOMDocument> document;
   1.335 +        mResultNodes[0]->GetOwnerDocument(getter_AddRefs(document));
   1.336 +        if (document) {
   1.337 +            mDocument = do_QueryInterface(document);
   1.338 +        }
   1.339 +        else {
   1.340 +            mDocument = do_QueryInterface(mResultNodes[0]);
   1.341 +        }
   1.342 +
   1.343 +        NS_ASSERTION(mDocument, "We need a document!");
   1.344 +        if (mDocument) {
   1.345 +            mDocument->AddMutationObserver(this);
   1.346 +        }
   1.347 +    }
   1.348 +
   1.349 +    return NS_OK;
   1.350 +}
   1.351 +
   1.352 +void
   1.353 +nsXPathResult::Invalidate(const nsIContent* aChangeRoot)
   1.354 +{
   1.355 +    nsCOMPtr<nsINode> contextNode = do_QueryReferent(mContextNode);
   1.356 +    if (contextNode && aChangeRoot && aChangeRoot->GetBindingParent()) {
   1.357 +        // If context node is in anonymous content, changes to
   1.358 +        // non-anonymous content need to invalidate the XPathResult. If
   1.359 +        // the changes are happening in a different anonymous trees, no
   1.360 +        // invalidation should happen.
   1.361 +        nsIContent* ctxBindingParent = nullptr;
   1.362 +        if (contextNode->IsNodeOfType(nsINode::eCONTENT)) {
   1.363 +            ctxBindingParent =
   1.364 +                static_cast<nsIContent*>(contextNode.get())
   1.365 +                    ->GetBindingParent();
   1.366 +        } else if (contextNode->IsNodeOfType(nsINode::eATTRIBUTE)) {
   1.367 +            Element* parent =
   1.368 +              static_cast<Attr*>(contextNode.get())->GetElement();
   1.369 +            if (parent) {
   1.370 +                ctxBindingParent = parent->GetBindingParent();
   1.371 +            }
   1.372 +        }
   1.373 +        if (ctxBindingParent != aChangeRoot->GetBindingParent()) {
   1.374 +          return;
   1.375 +        }
   1.376 +    }
   1.377 +
   1.378 +    mInvalidIteratorState = true;
   1.379 +    // Make sure nulling out mDocument is the last thing we do.
   1.380 +    if (mDocument) {
   1.381 +        mDocument->RemoveMutationObserver(this);
   1.382 +        mDocument = nullptr;
   1.383 +    }
   1.384 +}
   1.385 +
   1.386 +nsresult
   1.387 +nsXPathResult::GetExprResult(txAExprResult** aExprResult)
   1.388 +{
   1.389 +    if (isIterator() && mInvalidIteratorState) {
   1.390 +        return NS_ERROR_DOM_INVALID_STATE_ERR;
   1.391 +    }
   1.392 +
   1.393 +    if (mResult) {
   1.394 +        NS_ADDREF(*aExprResult = mResult);
   1.395 +
   1.396 +        return NS_OK;
   1.397 +    }
   1.398 +
   1.399 +    if (mResultNodes.Count() == 0) {
   1.400 +        return NS_ERROR_DOM_INVALID_STATE_ERR;
   1.401 +    }
   1.402 +
   1.403 +    nsRefPtr<txNodeSet> nodeSet = new txNodeSet(nullptr);
   1.404 +    if (!nodeSet) {
   1.405 +        return NS_ERROR_OUT_OF_MEMORY;
   1.406 +    }
   1.407 +
   1.408 +    uint32_t i, count = mResultNodes.Count();
   1.409 +    for (i = 0; i < count; ++i) {
   1.410 +        nsAutoPtr<txXPathNode> node(txXPathNativeNode::createXPathNode(mResultNodes[i]));
   1.411 +        if (!node) {
   1.412 +            return NS_ERROR_OUT_OF_MEMORY;
   1.413 +        }
   1.414 +
   1.415 +        nodeSet->append(*node);
   1.416 +    }
   1.417 +
   1.418 +    NS_ADDREF(*aExprResult = nodeSet);
   1.419 +
   1.420 +    return NS_OK;
   1.421 +}
   1.422 +
   1.423 +nsresult
   1.424 +nsXPathResult::Clone(nsIXPathResult **aResult)
   1.425 +{
   1.426 +    *aResult = nullptr;
   1.427 +
   1.428 +    if (isIterator() && mInvalidIteratorState) {
   1.429 +        return NS_ERROR_DOM_INVALID_STATE_ERR;
   1.430 +    }
   1.431 +
   1.432 +    nsCOMPtr<nsIXPathResult> result = new nsXPathResult(*this);
   1.433 +    if (!result) {
   1.434 +        return NS_ERROR_OUT_OF_MEMORY;
   1.435 +    }
   1.436 +
   1.437 +    result.swap(*aResult);
   1.438 +
   1.439 +    return NS_OK;
   1.440 +}

mercurial