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 +}