Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/layout/inspector/inDeepTreeWalker.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "inDeepTreeWalker.h"
8
#include "inLayoutUtils.h"
9
10
#include "nsString.h"
11
#include "nsIDocument.h"
12
#include "nsServiceManagerUtils.h"
13
#include "nsIContent.h"
14
#include "ChildIterator.h"
15
#include "mozilla/dom/Element.h"
16
#include "mozilla/dom/InspectorUtils.h"
17
#include "mozilla/dom/NodeFilterBinding.h"
18
19
/*****************************************************************************
20
 * This implementation does not currently operaate according to the W3C spec.
21
 * In particular it does NOT handle DOM mutations during the walk.  It also
22
 * ignores whatToShow and the filter.
23
 *****************************************************************************/
24
25
////////////////////////////////////////////////////
26
27
inDeepTreeWalker::inDeepTreeWalker()
28
  : mShowAnonymousContent(false),
29
    mShowSubDocuments(false),
30
    mShowDocumentsAsNodes(false),
31
    mCurrentIndex(-1),
32
    mWhatToShow(mozilla::dom::NodeFilter_Binding::SHOW_ALL)
33
0
{
34
0
}
35
36
inDeepTreeWalker::~inDeepTreeWalker()
37
0
{
38
0
}
39
40
NS_IMPL_ISUPPORTS(inDeepTreeWalker,
41
                  inIDeepTreeWalker)
42
43
////////////////////////////////////////////////////
44
// inIDeepTreeWalker
45
46
NS_IMETHODIMP
47
inDeepTreeWalker::GetShowAnonymousContent(bool *aShowAnonymousContent)
48
0
{
49
0
  *aShowAnonymousContent = mShowAnonymousContent;
50
0
  return NS_OK;
51
0
}
52
53
NS_IMETHODIMP
54
inDeepTreeWalker::SetShowAnonymousContent(bool aShowAnonymousContent)
55
0
{
56
0
  mShowAnonymousContent = aShowAnonymousContent;
57
0
  return NS_OK;
58
0
}
59
60
NS_IMETHODIMP
61
inDeepTreeWalker::GetShowSubDocuments(bool *aShowSubDocuments)
62
0
{
63
0
  *aShowSubDocuments = mShowSubDocuments;
64
0
  return NS_OK;
65
0
}
66
67
NS_IMETHODIMP
68
inDeepTreeWalker::SetShowSubDocuments(bool aShowSubDocuments)
69
0
{
70
0
  mShowSubDocuments = aShowSubDocuments;
71
0
  return NS_OK;
72
0
}
73
74
NS_IMETHODIMP
75
inDeepTreeWalker::GetShowDocumentsAsNodes(bool *aShowDocumentsAsNodes)
76
0
{
77
0
  *aShowDocumentsAsNodes = mShowDocumentsAsNodes;
78
0
  return NS_OK;
79
0
}
80
81
NS_IMETHODIMP
82
inDeepTreeWalker::SetShowDocumentsAsNodes(bool aShowDocumentsAsNodes)
83
0
{
84
0
  mShowDocumentsAsNodes = aShowDocumentsAsNodes;
85
0
  return NS_OK;
86
0
}
87
88
NS_IMETHODIMP
89
inDeepTreeWalker::Init(nsINode* aRoot, uint32_t aWhatToShow)
90
0
{
91
0
  if (!aRoot) {
92
0
    return NS_ERROR_INVALID_ARG;
93
0
  }
94
0
95
0
  mRoot = aRoot;
96
0
  mCurrentNode = aRoot;
97
0
  mWhatToShow = aWhatToShow;
98
0
99
0
  return NS_OK;
100
0
}
101
102
////////////////////////////////////////////////////
103
104
NS_IMETHODIMP
105
inDeepTreeWalker::GetRoot(nsINode** aRoot)
106
0
{
107
0
  *aRoot = mRoot;
108
0
  NS_IF_ADDREF(*aRoot);
109
0
  return NS_OK;
110
0
}
111
112
NS_IMETHODIMP
113
inDeepTreeWalker::GetWhatToShow(uint32_t* aWhatToShow)
114
0
{
115
0
  *aWhatToShow = mWhatToShow;
116
0
  return NS_OK;
117
0
}
118
119
NS_IMETHODIMP
120
inDeepTreeWalker::GetCurrentNode(nsINode** aCurrentNode)
121
0
{
122
0
  *aCurrentNode = mCurrentNode;
123
0
  NS_IF_ADDREF(*aCurrentNode);
124
0
  return NS_OK;
125
0
}
126
127
already_AddRefed<nsINode>
128
inDeepTreeWalker::GetParent()
129
0
{
130
0
  MOZ_ASSERT(mCurrentNode);
131
0
132
0
  if (mCurrentNode == mRoot) {
133
0
    return nullptr;
134
0
  }
135
0
136
0
  nsINode* parentNode =
137
0
    InspectorUtils::GetParentForNode(*mCurrentNode, mShowAnonymousContent);
138
0
139
0
  uint16_t nodeType = 0;
140
0
  if (parentNode) {
141
0
    nodeType = parentNode->NodeType();
142
0
  }
143
0
  // For compatibility reasons by default we skip the document nodes
144
0
  // from the walk.
145
0
  if (!mShowDocumentsAsNodes &&
146
0
      nodeType == nsINode::DOCUMENT_NODE &&
147
0
      parentNode != mRoot) {
148
0
    parentNode =
149
0
      InspectorUtils::GetParentForNode(*parentNode, mShowAnonymousContent);
150
0
  }
151
0
152
0
  return do_AddRef(parentNode);
153
0
}
154
155
static already_AddRefed<nsINodeList>
156
GetChildren(nsINode* aParent,
157
            bool aShowAnonymousContent,
158
            bool aShowSubDocuments)
159
0
{
160
0
  MOZ_ASSERT(aParent);
161
0
162
0
  nsCOMPtr<nsINodeList> ret;
163
0
  if (aShowSubDocuments) {
164
0
    nsIDocument* domdoc = inLayoutUtils::GetSubDocumentFor(aParent);
165
0
    if (domdoc) {
166
0
      aParent = domdoc;
167
0
    }
168
0
  }
169
0
170
0
  nsCOMPtr<nsIContent> parentAsContent = do_QueryInterface(aParent);
171
0
  if (parentAsContent && aShowAnonymousContent) {
172
0
      ret = parentAsContent->GetChildren(nsIContent::eAllChildren);
173
0
  } else {
174
0
    // If it's not a content, then it's a document (or an attribute but we can ignore that
175
0
    // case here). If aShowAnonymousContent is false we also want to fall back to ChildNodes
176
0
    // so we can skip any native anon content that GetChildren would return.
177
0
    ret = aParent->ChildNodes();
178
0
  }
179
0
  return ret.forget();
180
0
}
181
182
NS_IMETHODIMP
183
inDeepTreeWalker::SetCurrentNode(nsINode* aCurrentNode)
184
0
{
185
0
  // mCurrentNode can only be null if init either failed, or has not been
186
0
  // called yet.
187
0
  if (!mCurrentNode || !aCurrentNode) {
188
0
    return NS_ERROR_FAILURE;
189
0
  }
190
0
191
0
  // If Document nodes are skipped by the walk, we should not allow
192
0
  // one to set one as the current node either.
193
0
  if (!mShowDocumentsAsNodes) {
194
0
    if (aCurrentNode->NodeType() == nsINode::DOCUMENT_NODE) {
195
0
      return NS_ERROR_FAILURE;
196
0
    }
197
0
  }
198
0
199
0
  return SetCurrentNode(aCurrentNode, nullptr);
200
0
}
201
202
203
nsresult
204
inDeepTreeWalker::SetCurrentNode(nsINode* aCurrentNode,
205
                                 nsINodeList* aSiblings)
206
0
{
207
0
  MOZ_ASSERT(aCurrentNode);
208
0
209
0
  // We want to store the original state so in case of error
210
0
  // we can restore that.
211
0
  nsCOMPtr<nsINodeList> tmpSiblings = mSiblings;
212
0
  nsCOMPtr<nsINode> tmpCurrent = mCurrentNode;
213
0
  mSiblings = aSiblings;
214
0
  mCurrentNode = aCurrentNode;
215
0
216
0
  // If siblings were not passed in as argument we have to
217
0
  // get them from the parent node of aCurrentNode.
218
0
  // Note: in the mShowDoucmentsAsNodes case when a sub document
219
0
  // is set as the current, we don't want to get the children
220
0
  // from the iframe accidentally here, so let's just skip this
221
0
  // part for document nodes, they should never have siblings.
222
0
  if (!mSiblings) {
223
0
    if (aCurrentNode->NodeType() != nsINode::DOCUMENT_NODE) {
224
0
      nsCOMPtr<nsINode> parent = GetParent();
225
0
      if (parent) {
226
0
        mSiblings = GetChildren(parent,
227
0
                                mShowAnonymousContent,
228
0
                                mShowSubDocuments);
229
0
      }
230
0
    }
231
0
  }
232
0
233
0
  if (mSiblings && mSiblings->Length()) {
234
0
    // We cached all the siblings (if there are any) of the current node, but we
235
0
    // still have to set the index too, to be able to iterate over them.
236
0
    nsCOMPtr<nsIContent> currentAsContent = do_QueryInterface(mCurrentNode);
237
0
    MOZ_ASSERT(currentAsContent);
238
0
    int32_t index = mSiblings->IndexOf(currentAsContent);
239
0
    if (index < 0) {
240
0
      // If someone tries to set current node to some value that is not reachable
241
0
      // otherwise, let's throw. (For example mShowAnonymousContent is false and some
242
0
      // XBL anon content was passed in)
243
0
244
0
      // Restore state first.
245
0
      mCurrentNode = tmpCurrent;
246
0
      mSiblings = tmpSiblings;
247
0
      return NS_ERROR_INVALID_ARG;
248
0
    }
249
0
    mCurrentIndex = index;
250
0
  } else {
251
0
    mCurrentIndex = -1;
252
0
  }
253
0
  return NS_OK;
254
0
}
255
256
NS_IMETHODIMP
257
inDeepTreeWalker::ParentNode(nsINode** _retval)
258
0
{
259
0
  *_retval = nullptr;
260
0
  if (!mCurrentNode || mCurrentNode == mRoot) {
261
0
    return NS_OK;
262
0
  }
263
0
264
0
  nsCOMPtr<nsINode> parent = GetParent();
265
0
266
0
  if (!parent) {
267
0
    return NS_OK;
268
0
  }
269
0
270
0
  nsresult rv = SetCurrentNode(parent);
271
0
  NS_ENSURE_SUCCESS(rv,rv);
272
0
273
0
  parent.forget(_retval);
274
0
  return NS_OK;
275
0
}
276
277
// FirstChild and LastChild are very similar methods, this is the generic
278
// version for internal use. With aReverse = true it returns the LastChild.
279
nsresult
280
inDeepTreeWalker::EdgeChild(nsINode** _retval, bool aFront)
281
0
{
282
0
  if (!mCurrentNode) {
283
0
    return NS_ERROR_FAILURE;
284
0
  }
285
0
286
0
  *_retval = nullptr;
287
0
288
0
  nsCOMPtr<nsINode> echild;
289
0
  if (mShowSubDocuments && mShowDocumentsAsNodes) {
290
0
    // GetChildren below, will skip the document node from
291
0
    // the walk. But if mShowDocumentsAsNodes is set to true
292
0
    // we want to include the (sub)document itself too.
293
0
    echild = inLayoutUtils::GetSubDocumentFor(mCurrentNode);
294
0
  }
295
0
296
0
  nsCOMPtr<nsINodeList> children;
297
0
  if (!echild) {
298
0
    children = GetChildren(mCurrentNode,
299
0
                           mShowAnonymousContent,
300
0
                           mShowSubDocuments);
301
0
    if (children && children->Length() > 0) {
302
0
      echild = children->Item(aFront ? 0 : children->Length() - 1);
303
0
    }
304
0
  }
305
0
306
0
  if (echild) {
307
0
    nsresult rv = SetCurrentNode(echild, children);
308
0
    NS_ENSURE_SUCCESS(rv, rv);
309
0
    NS_ADDREF(*_retval = mCurrentNode);
310
0
  }
311
0
312
0
  return NS_OK;
313
0
}
314
315
NS_IMETHODIMP
316
inDeepTreeWalker::FirstChild(nsINode** _retval)
317
0
{
318
0
  return EdgeChild(_retval, /* aFront = */ true);
319
0
}
320
321
NS_IMETHODIMP
322
inDeepTreeWalker::LastChild(nsINode **_retval)
323
0
{
324
0
  return EdgeChild(_retval, /* aFront = */ false);
325
0
}
326
327
NS_IMETHODIMP
328
inDeepTreeWalker::PreviousSibling(nsINode **_retval)
329
0
{
330
0
  *_retval = nullptr;
331
0
  if (!mCurrentNode || !mSiblings || mCurrentIndex < 1) {
332
0
    return NS_OK;
333
0
  }
334
0
335
0
  nsIContent* prev = mSiblings->Item(--mCurrentIndex);
336
0
  mCurrentNode = prev;
337
0
  NS_ADDREF(*_retval = mCurrentNode);
338
0
  return NS_OK;
339
0
}
340
341
NS_IMETHODIMP
342
inDeepTreeWalker::NextSibling(nsINode **_retval)
343
0
{
344
0
  *_retval = nullptr;
345
0
  if (!mCurrentNode || !mSiblings ||
346
0
      mCurrentIndex + 1 >= (int32_t) mSiblings->Length()) {
347
0
    return NS_OK;
348
0
  }
349
0
350
0
  nsIContent* next = mSiblings->Item(++mCurrentIndex);
351
0
  mCurrentNode = next;
352
0
  NS_ADDREF(*_retval = mCurrentNode);
353
0
  return NS_OK;
354
0
}
355
356
NS_IMETHODIMP
357
inDeepTreeWalker::PreviousNode(nsINode **_retval)
358
0
{
359
0
  if (!mCurrentNode || mCurrentNode == mRoot) {
360
0
    // Nowhere to go from here
361
0
    *_retval = nullptr;
362
0
    return NS_OK;
363
0
  }
364
0
365
0
  nsCOMPtr<nsINode> node;
366
0
  PreviousSibling(getter_AddRefs(node));
367
0
368
0
  if (!node) {
369
0
    return ParentNode(_retval);
370
0
  }
371
0
372
0
  // Now we're positioned at our previous sibling.  But since the DOM tree
373
0
  // traversal is depth-first, the previous node is its most deeply nested last
374
0
  // child.  Just loop until LastChild() returns null; since the LastChild()
375
0
  // call that returns null won't affect our position, we will then be
376
0
  // positioned at the correct node.
377
0
  while (node) {
378
0
    LastChild(getter_AddRefs(node));
379
0
  }
380
0
381
0
  NS_ADDREF(*_retval = mCurrentNode);
382
0
  return NS_OK;
383
0
}
384
385
NS_IMETHODIMP
386
inDeepTreeWalker::NextNode(nsINode **_retval)
387
0
{
388
0
  if (!mCurrentNode) {
389
0
    return NS_OK;
390
0
  }
391
0
392
0
  // First try our kids
393
0
  FirstChild(_retval);
394
0
395
0
  if (*_retval) {
396
0
    return NS_OK;
397
0
  }
398
0
399
0
  // Now keep trying next siblings up the parent chain, but if we
400
0
  // discover there's nothing else restore our state.
401
#ifdef DEBUG
402
  nsINode* origCurrentNode = mCurrentNode;
403
#endif
404
0
  uint32_t lastChildCallsToMake = 0;
405
0
  while (1) {
406
0
    NextSibling(_retval);
407
0
408
0
    if (*_retval) {
409
0
      return NS_OK;
410
0
    }
411
0
412
0
    nsCOMPtr<nsINode> parent;
413
0
    ParentNode(getter_AddRefs(parent));
414
0
    if (!parent) {
415
0
      // Nowhere else to go; we're done.  Restore our state.
416
0
      while (lastChildCallsToMake--) {
417
0
        nsCOMPtr<nsINode> dummy;
418
0
        LastChild(getter_AddRefs(dummy));
419
0
      }
420
0
      NS_ASSERTION(mCurrentNode == origCurrentNode,
421
0
                   "Didn't go back to the right node?");
422
0
      *_retval = nullptr;
423
0
      return NS_OK;
424
0
    }
425
0
    ++lastChildCallsToMake;
426
0
  }
427
0
428
0
  MOZ_ASSERT_UNREACHABLE("how did we get here?");
429
0
  return NS_OK;
430
0
}