inDeepTreeWalker.cpp 11 KB

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