8
0

getsubnodes.js 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. /**
  2. * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  4. */
  5. /**
  6. * @module watchdog/utils/getsubnodes
  7. */
  8. /* globals EventTarget, Event */
  9. export default function getSubNodes( head, excludedProperties = new Set() ) {
  10. const nodes = [ head ];
  11. // @if CK_DEBUG_WATCHDOG // const prevNodeMap = new Map();
  12. // Nodes are stored to prevent infinite looping.
  13. const subNodes = new Set();
  14. while ( nodes.length > 0 ) {
  15. const node = nodes.shift();
  16. if ( subNodes.has( node ) || shouldNodeBeSkipped( node ) || excludedProperties.has( node ) ) {
  17. continue;
  18. }
  19. subNodes.add( node );
  20. // Handle arrays, maps, sets, custom collections that implements `[ Symbol.iterator ]()`, etc.
  21. if ( node[ Symbol.iterator ] ) {
  22. // The custom editor iterators might cause some problems if the editor is crashed.
  23. try {
  24. for ( const n of node ) {
  25. nodes.push( n );
  26. // @if CK_DEBUG_WATCHDOG // if ( !prevNodeMap.has( n ) ) {
  27. // @if CK_DEBUG_WATCHDOG // prevNodeMap.set( n, node );
  28. // @if CK_DEBUG_WATCHDOG // }
  29. }
  30. } catch ( err ) {
  31. // Do not log errors for broken structures
  32. // since we are in the error handling process already.
  33. // eslint-disable-line no-empty
  34. }
  35. } else {
  36. for ( const key in node ) {
  37. // We share a reference via the protobuf library within the editors,
  38. // hence the shared value should be skipped. Although, it's not a perfect
  39. // solution since new places like that might occur in the future.
  40. if ( key === 'defaultValue' ) {
  41. continue;
  42. }
  43. nodes.push( node[ key ] );
  44. // @if CK_DEBUG_WATCHDOG // if ( !prevNodeMap.has( node[ key ] ) ) {
  45. // @if CK_DEBUG_WATCHDOG // prevNodeMap.set( node[ key ], node );
  46. // @if CK_DEBUG_WATCHDOG // }
  47. }
  48. }
  49. }
  50. // @if CK_DEBUG_WATCHDOG // return { subNodes, prevNodeMap };
  51. return subNodes;
  52. }
  53. function shouldNodeBeSkipped( node ) {
  54. const type = Object.prototype.toString.call( node );
  55. const typeOfNode = typeof node;
  56. return (
  57. typeOfNode === 'number' ||
  58. typeOfNode === 'boolean' ||
  59. typeOfNode === 'string' ||
  60. typeOfNode === 'symbol' ||
  61. typeOfNode === 'function' ||
  62. type === '[object Date]' ||
  63. type === '[object RegExp]' ||
  64. type === '[object Module]' ||
  65. node === undefined ||
  66. node === null ||
  67. // Skip native DOM objects, e.g. Window, nodes, events, etc.
  68. node instanceof EventTarget ||
  69. node instanceof Event
  70. );
  71. }