dom/xslt/xpath/nsXPathResult.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial