dom/xslt/xpath/nsXPathResult.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "nsXPathResult.h"
michael@0 7 #include "txExprResult.h"
michael@0 8 #include "txNodeSet.h"
michael@0 9 #include "nsError.h"
michael@0 10 #include "mozilla/dom/Attr.h"
michael@0 11 #include "mozilla/dom/Element.h"
michael@0 12 #include "nsDOMClassInfoID.h"
michael@0 13 #include "nsIDOMNode.h"
michael@0 14 #include "nsIDOMDocument.h"
michael@0 15 #include "nsDOMString.h"
michael@0 16 #include "txXPathTreeWalker.h"
michael@0 17 #include "nsCycleCollectionParticipant.h"
michael@0 18
michael@0 19 using namespace mozilla::dom;
michael@0 20
michael@0 21 nsXPathResult::nsXPathResult() : mDocument(nullptr),
michael@0 22 mCurrentPos(0),
michael@0 23 mResultType(ANY_TYPE),
michael@0 24 mInvalidIteratorState(true),
michael@0 25 mBooleanResult(false),
michael@0 26 mNumberResult(0)
michael@0 27 {
michael@0 28 }
michael@0 29
michael@0 30 nsXPathResult::nsXPathResult(const nsXPathResult &aResult)
michael@0 31 : mResult(aResult.mResult),
michael@0 32 mResultNodes(aResult.mResultNodes),
michael@0 33 mDocument(aResult.mDocument),
michael@0 34 mCurrentPos(0),
michael@0 35 mResultType(aResult.mResultType),
michael@0 36 mContextNode(aResult.mContextNode),
michael@0 37 mInvalidIteratorState(aResult.mInvalidIteratorState)
michael@0 38 {
michael@0 39 if (mDocument) {
michael@0 40 mDocument->AddMutationObserver(this);
michael@0 41 }
michael@0 42 }
michael@0 43
michael@0 44 nsXPathResult::~nsXPathResult()
michael@0 45 {
michael@0 46 RemoveObserver();
michael@0 47 }
michael@0 48
michael@0 49 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXPathResult)
michael@0 50
michael@0 51 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXPathResult)
michael@0 52 {
michael@0 53 tmp->RemoveObserver();
michael@0 54 }
michael@0 55 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument)
michael@0 56 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
michael@0 57 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXPathResult)
michael@0 58 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
michael@0 59 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResultNodes)
michael@0 60 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
michael@0 61
michael@0 62 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXPathResult)
michael@0 63 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXPathResult)
michael@0 64
michael@0 65 DOMCI_DATA(XPathResult, nsXPathResult)
michael@0 66
michael@0 67 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXPathResult)
michael@0 68 NS_INTERFACE_MAP_ENTRY(nsIDOMXPathResult)
michael@0 69 NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
michael@0 70 NS_INTERFACE_MAP_ENTRY(nsIXPathResult)
michael@0 71 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMXPathResult)
michael@0 72 NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(XPathResult)
michael@0 73 NS_INTERFACE_MAP_END
michael@0 74
michael@0 75 void
michael@0 76 nsXPathResult::RemoveObserver()
michael@0 77 {
michael@0 78 if (mDocument) {
michael@0 79 mDocument->RemoveMutationObserver(this);
michael@0 80 }
michael@0 81 }
michael@0 82
michael@0 83 NS_IMETHODIMP
michael@0 84 nsXPathResult::GetResultType(uint16_t *aResultType)
michael@0 85 {
michael@0 86 *aResultType = mResultType;
michael@0 87
michael@0 88 return NS_OK;
michael@0 89 }
michael@0 90
michael@0 91 NS_IMETHODIMP
michael@0 92 nsXPathResult::GetNumberValue(double *aNumberValue)
michael@0 93 {
michael@0 94 if (mResultType != NUMBER_TYPE) {
michael@0 95 return NS_ERROR_DOM_TYPE_ERR;
michael@0 96 }
michael@0 97
michael@0 98 *aNumberValue = mNumberResult;
michael@0 99
michael@0 100 return NS_OK;
michael@0 101 }
michael@0 102
michael@0 103 NS_IMETHODIMP
michael@0 104 nsXPathResult::GetStringValue(nsAString &aStringValue)
michael@0 105 {
michael@0 106 if (mResultType != STRING_TYPE) {
michael@0 107 return NS_ERROR_DOM_TYPE_ERR;
michael@0 108 }
michael@0 109
michael@0 110 aStringValue = mStringResult;
michael@0 111
michael@0 112 return NS_OK;
michael@0 113 }
michael@0 114
michael@0 115 NS_IMETHODIMP
michael@0 116 nsXPathResult::GetBooleanValue(bool *aBooleanValue)
michael@0 117 {
michael@0 118 if (mResultType != BOOLEAN_TYPE) {
michael@0 119 return NS_ERROR_DOM_TYPE_ERR;
michael@0 120 }
michael@0 121
michael@0 122 *aBooleanValue = mBooleanResult;
michael@0 123
michael@0 124 return NS_OK;
michael@0 125 }
michael@0 126
michael@0 127 NS_IMETHODIMP
michael@0 128 nsXPathResult::GetSingleNodeValue(nsIDOMNode **aSingleNodeValue)
michael@0 129 {
michael@0 130 if (!isNode()) {
michael@0 131 return NS_ERROR_DOM_TYPE_ERR;
michael@0 132 }
michael@0 133
michael@0 134 if (mResultNodes.Count() > 0) {
michael@0 135 NS_ADDREF(*aSingleNodeValue = mResultNodes[0]);
michael@0 136 }
michael@0 137 else {
michael@0 138 *aSingleNodeValue = nullptr;
michael@0 139 }
michael@0 140
michael@0 141 return NS_OK;
michael@0 142 }
michael@0 143
michael@0 144 NS_IMETHODIMP
michael@0 145 nsXPathResult::GetInvalidIteratorState(bool *aInvalidIteratorState)
michael@0 146 {
michael@0 147 *aInvalidIteratorState = isIterator() && mInvalidIteratorState;
michael@0 148
michael@0 149 return NS_OK;
michael@0 150 }
michael@0 151
michael@0 152 NS_IMETHODIMP
michael@0 153 nsXPathResult::GetSnapshotLength(uint32_t *aSnapshotLength)
michael@0 154 {
michael@0 155 if (!isSnapshot()) {
michael@0 156 return NS_ERROR_DOM_TYPE_ERR;
michael@0 157 }
michael@0 158
michael@0 159 *aSnapshotLength = (uint32_t)mResultNodes.Count();
michael@0 160
michael@0 161 return NS_OK;
michael@0 162 }
michael@0 163
michael@0 164 NS_IMETHODIMP
michael@0 165 nsXPathResult::IterateNext(nsIDOMNode **aResult)
michael@0 166 {
michael@0 167 if (!isIterator()) {
michael@0 168 return NS_ERROR_DOM_TYPE_ERR;
michael@0 169 }
michael@0 170
michael@0 171 if (mDocument) {
michael@0 172 mDocument->FlushPendingNotifications(Flush_Content);
michael@0 173 }
michael@0 174
michael@0 175 if (mInvalidIteratorState) {
michael@0 176 return NS_ERROR_DOM_INVALID_STATE_ERR;
michael@0 177 }
michael@0 178
michael@0 179 if (mCurrentPos < (uint32_t)mResultNodes.Count()) {
michael@0 180 NS_ADDREF(*aResult = mResultNodes[mCurrentPos++]);
michael@0 181 }
michael@0 182 else {
michael@0 183 *aResult = nullptr;
michael@0 184 }
michael@0 185
michael@0 186 return NS_OK;
michael@0 187 }
michael@0 188
michael@0 189 NS_IMETHODIMP
michael@0 190 nsXPathResult::SnapshotItem(uint32_t aIndex, nsIDOMNode **aResult)
michael@0 191 {
michael@0 192 if (!isSnapshot()) {
michael@0 193 return NS_ERROR_DOM_TYPE_ERR;
michael@0 194 }
michael@0 195
michael@0 196 NS_IF_ADDREF(*aResult = mResultNodes.SafeObjectAt(aIndex));
michael@0 197
michael@0 198 return NS_OK;
michael@0 199 }
michael@0 200
michael@0 201 void
michael@0 202 nsXPathResult::NodeWillBeDestroyed(const nsINode* aNode)
michael@0 203 {
michael@0 204 nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
michael@0 205 // Set to null to avoid unregistring unnecessarily
michael@0 206 mDocument = nullptr;
michael@0 207 Invalidate(aNode->IsNodeOfType(nsINode::eCONTENT) ?
michael@0 208 static_cast<const nsIContent*>(aNode) : nullptr);
michael@0 209 }
michael@0 210
michael@0 211 void
michael@0 212 nsXPathResult::CharacterDataChanged(nsIDocument* aDocument,
michael@0 213 nsIContent *aContent,
michael@0 214 CharacterDataChangeInfo* aInfo)
michael@0 215 {
michael@0 216 Invalidate(aContent);
michael@0 217 }
michael@0 218
michael@0 219 void
michael@0 220 nsXPathResult::AttributeChanged(nsIDocument* aDocument,
michael@0 221 Element* aElement,
michael@0 222 int32_t aNameSpaceID,
michael@0 223 nsIAtom* aAttribute,
michael@0 224 int32_t aModType)
michael@0 225 {
michael@0 226 Invalidate(aElement);
michael@0 227 }
michael@0 228
michael@0 229 void
michael@0 230 nsXPathResult::ContentAppended(nsIDocument* aDocument,
michael@0 231 nsIContent* aContainer,
michael@0 232 nsIContent* aFirstNewContent,
michael@0 233 int32_t aNewIndexInContainer)
michael@0 234 {
michael@0 235 Invalidate(aContainer);
michael@0 236 }
michael@0 237
michael@0 238 void
michael@0 239 nsXPathResult::ContentInserted(nsIDocument* aDocument,
michael@0 240 nsIContent* aContainer,
michael@0 241 nsIContent* aChild,
michael@0 242 int32_t aIndexInContainer)
michael@0 243 {
michael@0 244 Invalidate(aContainer);
michael@0 245 }
michael@0 246
michael@0 247 void
michael@0 248 nsXPathResult::ContentRemoved(nsIDocument* aDocument,
michael@0 249 nsIContent* aContainer,
michael@0 250 nsIContent* aChild,
michael@0 251 int32_t aIndexInContainer,
michael@0 252 nsIContent* aPreviousSibling)
michael@0 253 {
michael@0 254 Invalidate(aContainer);
michael@0 255 }
michael@0 256
michael@0 257 nsresult
michael@0 258 nsXPathResult::SetExprResult(txAExprResult* aExprResult, uint16_t aResultType,
michael@0 259 nsINode* aContextNode)
michael@0 260 {
michael@0 261 MOZ_ASSERT(aExprResult);
michael@0 262
michael@0 263 if ((isSnapshot(aResultType) || isIterator(aResultType) ||
michael@0 264 isNode(aResultType)) &&
michael@0 265 aExprResult->getResultType() != txAExprResult::NODESET) {
michael@0 266 // The DOM spec doesn't really say what should happen when reusing an
michael@0 267 // XPathResult and an error is thrown. Let's not touch the XPathResult
michael@0 268 // in that case.
michael@0 269 return NS_ERROR_DOM_TYPE_ERR;
michael@0 270 }
michael@0 271
michael@0 272 mResultType = aResultType;
michael@0 273 mContextNode = do_GetWeakReference(aContextNode);
michael@0 274
michael@0 275 if (mDocument) {
michael@0 276 mDocument->RemoveMutationObserver(this);
michael@0 277 mDocument = nullptr;
michael@0 278 }
michael@0 279
michael@0 280 mResultNodes.Clear();
michael@0 281
michael@0 282 // XXX This will keep the recycler alive, should we clear it?
michael@0 283 mResult = aExprResult;
michael@0 284 switch (mResultType) {
michael@0 285 case BOOLEAN_TYPE:
michael@0 286 {
michael@0 287 mBooleanResult = mResult->booleanValue();
michael@0 288 break;
michael@0 289 }
michael@0 290 case NUMBER_TYPE:
michael@0 291 {
michael@0 292 mNumberResult = mResult->numberValue();
michael@0 293 break;
michael@0 294 }
michael@0 295 case STRING_TYPE:
michael@0 296 {
michael@0 297 mResult->stringValue(mStringResult);
michael@0 298 break;
michael@0 299 }
michael@0 300 default:
michael@0 301 {
michael@0 302 MOZ_ASSERT(isNode() || isIterator() || isSnapshot());
michael@0 303 }
michael@0 304 }
michael@0 305
michael@0 306 if (aExprResult->getResultType() == txAExprResult::NODESET) {
michael@0 307 txNodeSet *nodeSet = static_cast<txNodeSet*>(aExprResult);
michael@0 308 nsCOMPtr<nsIDOMNode> node;
michael@0 309 int32_t i, count = nodeSet->size();
michael@0 310 for (i = 0; i < count; ++i) {
michael@0 311 txXPathNativeNode::getNode(nodeSet->get(i), getter_AddRefs(node));
michael@0 312 if (node) {
michael@0 313 mResultNodes.AppendObject(node);
michael@0 314 }
michael@0 315 }
michael@0 316
michael@0 317 if (count > 0) {
michael@0 318 mResult = nullptr;
michael@0 319 }
michael@0 320 }
michael@0 321
michael@0 322 if (!isIterator()) {
michael@0 323 return NS_OK;
michael@0 324 }
michael@0 325
michael@0 326 mInvalidIteratorState = false;
michael@0 327
michael@0 328 if (mResultNodes.Count() > 0) {
michael@0 329 // If we support the document() function in DOM-XPath we need to
michael@0 330 // observe all documents that we have resultnodes in.
michael@0 331 nsCOMPtr<nsIDOMDocument> document;
michael@0 332 mResultNodes[0]->GetOwnerDocument(getter_AddRefs(document));
michael@0 333 if (document) {
michael@0 334 mDocument = do_QueryInterface(document);
michael@0 335 }
michael@0 336 else {
michael@0 337 mDocument = do_QueryInterface(mResultNodes[0]);
michael@0 338 }
michael@0 339
michael@0 340 NS_ASSERTION(mDocument, "We need a document!");
michael@0 341 if (mDocument) {
michael@0 342 mDocument->AddMutationObserver(this);
michael@0 343 }
michael@0 344 }
michael@0 345
michael@0 346 return NS_OK;
michael@0 347 }
michael@0 348
michael@0 349 void
michael@0 350 nsXPathResult::Invalidate(const nsIContent* aChangeRoot)
michael@0 351 {
michael@0 352 nsCOMPtr<nsINode> contextNode = do_QueryReferent(mContextNode);
michael@0 353 if (contextNode && aChangeRoot && aChangeRoot->GetBindingParent()) {
michael@0 354 // If context node is in anonymous content, changes to
michael@0 355 // non-anonymous content need to invalidate the XPathResult. If
michael@0 356 // the changes are happening in a different anonymous trees, no
michael@0 357 // invalidation should happen.
michael@0 358 nsIContent* ctxBindingParent = nullptr;
michael@0 359 if (contextNode->IsNodeOfType(nsINode::eCONTENT)) {
michael@0 360 ctxBindingParent =
michael@0 361 static_cast<nsIContent*>(contextNode.get())
michael@0 362 ->GetBindingParent();
michael@0 363 } else if (contextNode->IsNodeOfType(nsINode::eATTRIBUTE)) {
michael@0 364 Element* parent =
michael@0 365 static_cast<Attr*>(contextNode.get())->GetElement();
michael@0 366 if (parent) {
michael@0 367 ctxBindingParent = parent->GetBindingParent();
michael@0 368 }
michael@0 369 }
michael@0 370 if (ctxBindingParent != aChangeRoot->GetBindingParent()) {
michael@0 371 return;
michael@0 372 }
michael@0 373 }
michael@0 374
michael@0 375 mInvalidIteratorState = true;
michael@0 376 // Make sure nulling out mDocument is the last thing we do.
michael@0 377 if (mDocument) {
michael@0 378 mDocument->RemoveMutationObserver(this);
michael@0 379 mDocument = nullptr;
michael@0 380 }
michael@0 381 }
michael@0 382
michael@0 383 nsresult
michael@0 384 nsXPathResult::GetExprResult(txAExprResult** aExprResult)
michael@0 385 {
michael@0 386 if (isIterator() && mInvalidIteratorState) {
michael@0 387 return NS_ERROR_DOM_INVALID_STATE_ERR;
michael@0 388 }
michael@0 389
michael@0 390 if (mResult) {
michael@0 391 NS_ADDREF(*aExprResult = mResult);
michael@0 392
michael@0 393 return NS_OK;
michael@0 394 }
michael@0 395
michael@0 396 if (mResultNodes.Count() == 0) {
michael@0 397 return NS_ERROR_DOM_INVALID_STATE_ERR;
michael@0 398 }
michael@0 399
michael@0 400 nsRefPtr<txNodeSet> nodeSet = new txNodeSet(nullptr);
michael@0 401 if (!nodeSet) {
michael@0 402 return NS_ERROR_OUT_OF_MEMORY;
michael@0 403 }
michael@0 404
michael@0 405 uint32_t i, count = mResultNodes.Count();
michael@0 406 for (i = 0; i < count; ++i) {
michael@0 407 nsAutoPtr<txXPathNode> node(txXPathNativeNode::createXPathNode(mResultNodes[i]));
michael@0 408 if (!node) {
michael@0 409 return NS_ERROR_OUT_OF_MEMORY;
michael@0 410 }
michael@0 411
michael@0 412 nodeSet->append(*node);
michael@0 413 }
michael@0 414
michael@0 415 NS_ADDREF(*aExprResult = nodeSet);
michael@0 416
michael@0 417 return NS_OK;
michael@0 418 }
michael@0 419
michael@0 420 nsresult
michael@0 421 nsXPathResult::Clone(nsIXPathResult **aResult)
michael@0 422 {
michael@0 423 *aResult = nullptr;
michael@0 424
michael@0 425 if (isIterator() && mInvalidIteratorState) {
michael@0 426 return NS_ERROR_DOM_INVALID_STATE_ERR;
michael@0 427 }
michael@0 428
michael@0 429 nsCOMPtr<nsIXPathResult> result = new nsXPathResult(*this);
michael@0 430 if (!result) {
michael@0 431 return NS_ERROR_OUT_OF_MEMORY;
michael@0 432 }
michael@0 433
michael@0 434 result.swap(*aResult);
michael@0 435
michael@0 436 return NS_OK;
michael@0 437 }

mercurial