Procházet zdrojové kódy

Merge pull request #179 from ckeditor/t/178

Internal: A warning should show up when a Rect instance is for an HTML element or a DOM range not belonging to a rendered DOM tree. Closes #178.
Piotrek Koszuliński před 8 roky
rodič
revize
6060425510

+ 30 - 4
packages/ckeditor5-utils/src/dom/rect.js

@@ -11,6 +11,7 @@ import global from './global';
 import isRange from './isrange';
 import isElement from '../lib/lodash/isElement';
 import getBorderWidths from './getborderwidths';
+import log from '../log';
 
 /**
  * A helper class representing a `ClientRect` object, e.g. value returned by
@@ -46,6 +47,8 @@ export default class Rect {
 	 * @param {HTMLElement|Range|Window|ClientRect|module:utils/dom/rect~Rect|Object} source A source object to create the rect.
 	 */
 	constructor( source ) {
+		const isSourceRange = isRange( source );
+
 		/**
 		 * The object this rect is for.
 		 *
@@ -60,10 +63,33 @@ export default class Rect {
 			enumerable: false
 		} );
 
-		if ( isElement( source ) ) {
-			copyRectProperties( this, source.getBoundingClientRect() );
-		} else if ( isRange( source ) ) {
-			copyRectProperties( this, Rect.getDomRangeRects( source )[ 0 ] );
+		if ( isElement( source ) || isSourceRange ) {
+			const sourceNode = isSourceRange ? source.startContainer : source;
+
+			if ( !sourceNode.ownerDocument || !sourceNode.ownerDocument.body.contains( sourceNode ) ) {
+				/**
+				 * The `Rect` class depends on `getBoundingClientRect` and `getClientRects` DOM methods.
+				 * If the {@link #constructor source} of a rect in an HTML element or a DOM range but it does
+				 * not belong to any rendered DOM tree, these methods will fail to obtain the geometry and
+				 * the rect instance makes little sense to the features using it.
+				 *
+				 * To get rid of this warning make sure the source passed to the constructor
+				 * is a descendant of `window.document.body`.
+				 *
+				 * @error rect-source-not-in-dom
+				 * @param {String} source The source of the Rect instance.
+				 */
+				log.warn(
+					'rect-source-not-in-dom: The source of this rect does not belong to any rendered DOM tree.',
+					{ source }
+				);
+			}
+
+			if ( isSourceRange ) {
+				copyRectProperties( this, Rect.getDomRangeRects( source )[ 0 ] );
+			} else {
+				copyRectProperties( this, source.getBoundingClientRect() );
+			}
 		} else if ( source === global.window ) {
 			const { innerWidth, innerHeight } = global.window;
 

+ 26 - 0
packages/ckeditor5-utils/tests/dom/rect.js

@@ -7,6 +7,7 @@
 
 import global from '../../src/dom/global';
 import Rect from '../../src/dom/rect';
+import log from '../../src/log';
 import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
 
 testUtils.createSinonSandbox();
@@ -23,6 +24,8 @@ describe( 'Rect', () => {
 			width: 20,
 			height: 20
 		};
+
+		testUtils.sinon.stub( log, 'warn' );
 	} );
 
 	describe( 'constructor()', () => {
@@ -139,6 +142,29 @@ describe( 'Rect', () => {
 
 			assertRect( geometry, sourceGeometry );
 		} );
+
+		it( 'should warn if the source does not belong to rendered DOM tree (HTML element)', () => {
+			const element = document.createElement( 'div' );
+
+			testUtils.sinon.stub( element, 'getBoundingClientRect' ).returns( geometry );
+
+			const rect = new Rect( element );
+			sinon.assert.calledOnce( log.warn );
+			sinon.assert.calledWithExactly( log.warn, sinon.match( /^rect-source-not-in-dom/ ), { source: element } );
+			assertRect( rect, geometry );
+		} );
+
+		it( 'should warn if the source does not belong to rendered DOM tree (DOM Range)', () => {
+			const range = document.createRange();
+
+			range.collapse();
+			testUtils.sinon.stub( range, 'getClientRects' ).returns( [ geometry ] );
+
+			const rect = new Rect( range );
+			sinon.assert.calledOnce( log.warn );
+			sinon.assert.calledWithExactly( log.warn, sinon.match( /^rect-source-not-in-dom/ ), { source: range } );
+			assertRect( rect, geometry );
+		} );
 	} );
 
 	describe( 'clone()', () => {