瀏覽代碼

Moved watchdog utils to the Watchdog package.

Maciej Bukowski 5 年之前
父節點
當前提交
c26527ab2b

+ 37 - 0
packages/ckeditor5-watchdog/src/utils/areconnectedThroughProperties.js

@@ -0,0 +1,37 @@
+/**
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+/**
+ * @module watchdog/utils/areconnectedthroughproperties
+ */
+
+import getSubNodes from './getsubnodes';
+
+/**
+ * Traverses both structures to find out whether there is a reference that is shared between both structures.
+ *
+ * @param {Object|Array} obj1
+ * @param {Object|Array} obj2
+ */
+export default function areConnectedThroughProperties( obj1, obj2 ) {
+	if ( obj1 === obj2 && isObject( obj1 ) ) {
+		return true;
+	}
+
+	const subNodes1 = getSubNodes( obj1 );
+	const subNodes2 = getSubNodes( obj2 );
+
+	for ( const node of subNodes1 ) {
+		if ( subNodes2.has( node ) ) {
+			return true;
+		}
+	}
+
+	return false;
+}
+
+function isObject( structure ) {
+	return typeof structure === 'object' && structure !== null;
+}

+ 62 - 0
packages/ckeditor5-watchdog/src/utils/getsubnodes.js

@@ -0,0 +1,62 @@
+/**
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+/**
+ * @module watchdog/utils/getsubnodes
+ */
+
+/* globals EventTarget, Event */
+
+export default function getSubNodes( head ) {
+	const nodes = [ head ];
+
+	// Nodes are stored to prevent infinite looping.
+	const subNodes = new Set();
+
+	while ( nodes.length > 0 ) {
+		const node = nodes.shift();
+
+		if ( subNodes.has( node ) || shouldNodeBeSkipped( node ) ) {
+			continue;
+		}
+
+		subNodes.add( node );
+
+		// Handle arrays, maps, sets, custom collections that implements `[ Symbol.iterator ]()`, etc.
+		if ( node[ Symbol.iterator ] ) {
+			// The custom editor iterators might cause some problems if the editor is crashed.
+			try {
+				nodes.push( ...node );
+			} catch ( err ) {
+				// eslint-disable-line no-empty
+			}
+		} else {
+			nodes.push( ...Object.values( node ) );
+		}
+	}
+
+	return subNodes;
+}
+
+function shouldNodeBeSkipped( node ) {
+	const type = Object.prototype.toString.call( node );
+
+	return (
+		type === '[object Number]' ||
+		type === '[object Boolean]' ||
+		type === '[object String]' ||
+		type === '[object Symbol]' ||
+		type === '[object Function]' ||
+		type === '[object Date]' ||
+		type === '[object RegExp]' ||
+
+		node === undefined ||
+		node === null ||
+
+		// Skip native DOM objects, e.g. Window, nodes, events, etc.
+		node instanceof EventTarget ||
+		node instanceof Event
+	);
+}