Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/xslt/xpath/XPathResult.cpp
Line
Count
Source (jump to first uncovered line)
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/. */
5
6
#include "XPathResult.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 "nsDOMString.h"
13
#include "txXPathTreeWalker.h"
14
#include "nsCycleCollectionParticipant.h"
15
#include "mozilla/dom/XPathResultBinding.h"
16
17
namespace mozilla {
18
namespace dom {
19
20
XPathResult::XPathResult(nsINode* aParent)
21
    : mParent(aParent),
22
      mDocument(nullptr),
23
      mCurrentPos(0),
24
      mResultType(ANY_TYPE),
25
      mInvalidIteratorState(true),
26
      mBooleanResult(false),
27
      mNumberResult(0)
28
0
{
29
0
}
30
31
XPathResult::XPathResult(const XPathResult &aResult)
32
    : mParent(aResult.mParent),
33
      mResult(aResult.mResult),
34
      mResultNodes(aResult.mResultNodes),
35
      mDocument(aResult.mDocument),
36
      mContextNode(aResult.mContextNode),
37
      mCurrentPos(0),
38
      mResultType(aResult.mResultType),
39
      mInvalidIteratorState(aResult.mInvalidIteratorState)
40
0
{
41
0
    if (mDocument) {
42
0
        mDocument->AddMutationObserver(this);
43
0
    }
44
0
}
45
46
XPathResult::~XPathResult()
47
0
{
48
0
    RemoveObserver();
49
0
}
50
51
NS_IMPL_CYCLE_COLLECTION_CLASS(XPathResult)
52
53
NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(XPathResult)
54
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(XPathResult)
55
0
    NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
56
0
    NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
57
0
    {
58
0
        tmp->RemoveObserver();
59
0
    }
60
0
    NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument)
61
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
62
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(XPathResult)
63
0
    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
64
0
    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
65
0
    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResultNodes)
66
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
67
68
NS_IMPL_CYCLE_COLLECTING_ADDREF(XPathResult)
69
NS_IMPL_CYCLE_COLLECTING_RELEASE(XPathResult)
70
71
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(XPathResult)
72
0
    NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
73
0
    NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
74
0
    NS_INTERFACE_MAP_ENTRY(nsIXPathResult)
75
0
    NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXPathResult)
76
0
NS_INTERFACE_MAP_END
77
78
JSObject*
79
XPathResult::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
80
0
{
81
0
    return XPathResult_Binding::Wrap(aCx, this, aGivenProto);
82
0
}
83
84
void
85
XPathResult::RemoveObserver()
86
0
{
87
0
    if (mDocument) {
88
0
        mDocument->RemoveMutationObserver(this);
89
0
    }
90
0
}
91
92
nsINode*
93
XPathResult::IterateNext(ErrorResult& aRv)
94
0
{
95
0
    if (!isIterator()) {
96
0
        aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
97
0
        return nullptr;
98
0
    }
99
0
100
0
    if (mDocument) {
101
0
        mDocument->FlushPendingNotifications(FlushType::Content);
102
0
    }
103
0
104
0
    if (mInvalidIteratorState) {
105
0
        aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
106
0
        return nullptr;
107
0
    }
108
0
109
0
    return mResultNodes.SafeObjectAt(mCurrentPos++);
110
0
}
111
112
void
113
XPathResult::NodeWillBeDestroyed(const nsINode* aNode)
114
0
{
115
0
    nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
116
0
    // Set to null to avoid unregistring unnecessarily
117
0
    mDocument = nullptr;
118
0
    Invalidate(aNode->IsContent() ? aNode->AsContent() : nullptr);
119
0
}
120
121
void
122
XPathResult::CharacterDataChanged(nsIContent* aContent,
123
                                  const CharacterDataChangeInfo&)
124
0
{
125
0
    Invalidate(aContent);
126
0
}
127
128
void
129
XPathResult::AttributeChanged(Element* aElement,
130
                              int32_t aNameSpaceID,
131
                              nsAtom* aAttribute,
132
                              int32_t aModType,
133
                              const nsAttrValue* aOldValue)
134
0
{
135
0
    Invalidate(aElement);
136
0
}
137
138
void
139
XPathResult::ContentAppended(nsIContent* aFirstNewContent)
140
0
{
141
0
    Invalidate(aFirstNewContent->GetParent());
142
0
}
143
144
void
145
XPathResult::ContentInserted(nsIContent* aChild)
146
0
{
147
0
    Invalidate(aChild->GetParent());
148
0
}
149
150
void
151
XPathResult::ContentRemoved(nsIContent* aChild, nsIContent* aPreviousSibling)
152
0
{
153
0
    Invalidate(aChild->GetParent());
154
0
}
155
156
nsresult
157
XPathResult::SetExprResult(txAExprResult* aExprResult, uint16_t aResultType,
158
                           nsINode* aContextNode)
159
0
{
160
0
    MOZ_ASSERT(aExprResult);
161
0
162
0
    if ((isSnapshot(aResultType) || isIterator(aResultType) ||
163
0
         isNode(aResultType)) &&
164
0
        aExprResult->getResultType() != txAExprResult::NODESET) {
165
0
        // The DOM spec doesn't really say what should happen when reusing an
166
0
        // XPathResult and an error is thrown. Let's not touch the XPathResult
167
0
        // in that case.
168
0
        return NS_ERROR_DOM_TYPE_ERR;
169
0
    }
170
0
171
0
    mResultType = aResultType;
172
0
    mContextNode = do_GetWeakReference(aContextNode);
173
0
174
0
    if (mDocument) {
175
0
        mDocument->RemoveMutationObserver(this);
176
0
        mDocument = nullptr;
177
0
    }
178
0
179
0
    mResultNodes.Clear();
180
0
181
0
    // XXX This will keep the recycler alive, should we clear it?
182
0
    mResult = aExprResult;
183
0
    switch (mResultType) {
184
0
        case BOOLEAN_TYPE:
185
0
        {
186
0
            mBooleanResult = mResult->booleanValue();
187
0
            break;
188
0
        }
189
0
        case NUMBER_TYPE:
190
0
        {
191
0
            mNumberResult = mResult->numberValue();
192
0
            break;
193
0
        }
194
0
        case STRING_TYPE:
195
0
        {
196
0
            mResult->stringValue(mStringResult);
197
0
            break;
198
0
        }
199
0
        default:
200
0
        {
201
0
            MOZ_ASSERT(isNode() || isIterator() || isSnapshot());
202
0
        }
203
0
    }
204
0
205
0
    if (aExprResult->getResultType() == txAExprResult::NODESET) {
206
0
        txNodeSet *nodeSet = static_cast<txNodeSet*>(aExprResult);
207
0
        int32_t i, count = nodeSet->size();
208
0
        for (i = 0; i < count; ++i) {
209
0
            nsINode* node = txXPathNativeNode::getNode(nodeSet->get(i));
210
0
            mResultNodes.AppendObject(node);
211
0
        }
212
0
213
0
        if (count > 0) {
214
0
            mResult = nullptr;
215
0
        }
216
0
    }
217
0
218
0
    if (!isIterator()) {
219
0
        return NS_OK;
220
0
    }
221
0
222
0
    mInvalidIteratorState = false;
223
0
224
0
    if (mResultNodes.Count() > 0) {
225
0
        // If we support the document() function in DOM-XPath we need to
226
0
        // observe all documents that we have resultnodes in.
227
0
        mDocument = mResultNodes[0]->OwnerDoc();
228
0
        NS_ASSERTION(mDocument, "We need a document!");
229
0
        if (mDocument) {
230
0
            mDocument->AddMutationObserver(this);
231
0
        }
232
0
    }
233
0
234
0
    return NS_OK;
235
0
}
236
237
void
238
XPathResult::Invalidate(const nsIContent* aChangeRoot)
239
0
{
240
0
    nsCOMPtr<nsINode> contextNode = do_QueryReferent(mContextNode);
241
0
    if (contextNode && aChangeRoot && aChangeRoot->GetBindingParent()) {
242
0
        // If context node is in anonymous content, changes to
243
0
        // non-anonymous content need to invalidate the XPathResult. If
244
0
        // the changes are happening in a different anonymous trees, no
245
0
        // invalidation should happen.
246
0
        nsIContent* ctxBindingParent = nullptr;
247
0
        if (contextNode->IsContent()) {
248
0
            ctxBindingParent =
249
0
              contextNode->AsContent()->GetBindingParent();
250
0
        } else if (auto* attr = Attr::FromNode(contextNode)) {
251
0
            if (Element* parent = attr->GetElement()) {
252
0
                ctxBindingParent = parent->GetBindingParent();
253
0
            }
254
0
        }
255
0
        if (ctxBindingParent != aChangeRoot->GetBindingParent()) {
256
0
          return;
257
0
        }
258
0
    }
259
0
260
0
    mInvalidIteratorState = true;
261
0
    // Make sure nulling out mDocument is the last thing we do.
262
0
    if (mDocument) {
263
0
        mDocument->RemoveMutationObserver(this);
264
0
        mDocument = nullptr;
265
0
    }
266
0
}
267
268
nsresult
269
XPathResult::GetExprResult(txAExprResult** aExprResult)
270
0
{
271
0
    if (isIterator() && mInvalidIteratorState) {
272
0
        return NS_ERROR_DOM_INVALID_STATE_ERR;
273
0
    }
274
0
275
0
    if (mResult) {
276
0
        NS_ADDREF(*aExprResult = mResult);
277
0
278
0
        return NS_OK;
279
0
    }
280
0
281
0
    if (mResultNodes.Count() == 0) {
282
0
        return NS_ERROR_DOM_INVALID_STATE_ERR;
283
0
    }
284
0
285
0
    RefPtr<txNodeSet> nodeSet = new txNodeSet(nullptr);
286
0
    if (!nodeSet) {
287
0
        return NS_ERROR_OUT_OF_MEMORY;
288
0
    }
289
0
290
0
    uint32_t i, count = mResultNodes.Count();
291
0
    for (i = 0; i < count; ++i) {
292
0
        nsAutoPtr<txXPathNode> node(txXPathNativeNode::createXPathNode(mResultNodes[i]));
293
0
        if (!node) {
294
0
            return NS_ERROR_OUT_OF_MEMORY;
295
0
        }
296
0
297
0
        nodeSet->append(*node);
298
0
    }
299
0
300
0
    NS_ADDREF(*aExprResult = nodeSet);
301
0
302
0
    return NS_OK;
303
0
}
304
305
nsresult
306
XPathResult::Clone(nsIXPathResult **aResult)
307
0
{
308
0
    *aResult = nullptr;
309
0
310
0
    if (isIterator() && mInvalidIteratorState) {
311
0
        return NS_ERROR_DOM_INVALID_STATE_ERR;
312
0
    }
313
0
314
0
    NS_ADDREF(*aResult = new XPathResult(*this));
315
0
316
0
    return NS_OK;
317
0
}
318
319
} // namespace dom
320
} // namespace mozilla