From bec2fac049a1fe2ca3c845e2af7d17da5017eaa4 Mon Sep 17 00:00:00 2001 From: Aleksander Nowodzinski Date: Fri, 24 Nov 2017 10:34:31 +0100 Subject: [PATCH] Bulletproofed isDomNode helper when used in iframes. Used isDomNode along with isWindow in DOMEmitterMixin. --- src/dom/emittermixin.js | 5 +++-- src/dom/isdomnode.js | 12 +++++++++--- tests/dom/isdomnode.js | 28 ++++++++++++++++++++++++---- 3 files changed, 36 insertions(+), 9 deletions(-) diff --git a/src/dom/emittermixin.js b/src/dom/emittermixin.js index 4e72602..7da5529 100644 --- a/src/dom/emittermixin.js +++ b/src/dom/emittermixin.js @@ -11,6 +11,7 @@ import { default as EmitterMixin, _getEmitterListenedTo, _setEmitterId } from '. import uid from '../uid'; import extend from '../lib/lodash/extend'; import isDomNode from './isdomnode'; +import isWindow from './iswindow'; /** * Mixin that injects the DOM events API into its host. It provides the API @@ -56,7 +57,7 @@ const DomEmitterMixin = extend( {}, EmitterMixin, { // Check if emitter is an instance of DOM Node. If so, replace the argument with // corresponding ProxyEmitter (or create one if not existing). - if ( isDomNode( emitter ) ) { + if ( isDomNode( emitter ) || isWindow( emitter ) ) { args[ 0 ] = this._getProxyEmitter( emitter ) || new ProxyEmitter( emitter ); } @@ -85,7 +86,7 @@ const DomEmitterMixin = extend( {}, EmitterMixin, { const emitter = args[ 0 ]; // Check if emitter is an instance of DOM Node. If so, replace the argument with corresponding ProxyEmitter. - if ( isDomNode( emitter ) ) { + if ( isDomNode( emitter ) || isWindow( emitter ) ) { const proxy = this._getProxyEmitter( emitter ); // Element has no listeners. diff --git a/src/dom/isdomnode.js b/src/dom/isdomnode.js index 3fe7ec1..dca4258 100644 --- a/src/dom/isdomnode.js +++ b/src/dom/isdomnode.js @@ -7,8 +7,6 @@ * @module utils/dom/isdomnode */ -import isNative from '../lib/lodash/isNative'; - /** * Checks if the object is a native DOM Node. * @@ -16,5 +14,13 @@ import isNative from '../lib/lodash/isNative'; * @returns {Boolean} */ export default function isDomNode( obj ) { - return !!( obj && isNative( obj.addEventListener ) ); + if ( obj ) { + if ( obj.defaultView ) { + return obj instanceof obj.defaultView.Document; + } else if ( obj.ownerDocument && obj.ownerDocument.defaultView ) { + return obj instanceof obj.ownerDocument.defaultView.Node; + } + } + + return false; } diff --git a/tests/dom/isdomnode.js b/tests/dom/isdomnode.js index a78a3d7..80f5f85 100644 --- a/tests/dom/isdomnode.js +++ b/tests/dom/isdomnode.js @@ -9,14 +9,34 @@ import isDomNode from '../../src/dom/isdomnode'; describe( 'isDomNode()', () => { it( 'detects native DOM nodes', () => { + expect( isDomNode( document ) ).to.be.true; + expect( isDomNode( document.createElement( 'div' ) ) ).to.be.true; + expect( isDomNode( document.createTextNode( 'Foo' ) ) ).to.be.true; + expect( isDomNode( {} ) ).to.be.false; expect( isDomNode( null ) ).to.be.false; expect( isDomNode( undefined ) ).to.be.false; expect( isDomNode( new Date() ) ).to.be.false; expect( isDomNode( 42 ) ).to.be.false; - expect( isDomNode( window ) ).to.be.true; - expect( isDomNode( document ) ).to.be.true; - expect( isDomNode( document.createElement( 'div' ) ) ).to.be.true; - expect( isDomNode( document.createTextNode( 'Foo' ) ) ).to.be.true; + expect( isDomNode( window ) ).to.be.false; + } ); + + it( 'works for nodes in an iframe', done => { + const iframe = document.createElement( 'iframe' ); + + iframe.addEventListener( 'load', () => { + const iframeDocument = iframe.contentWindow.document; + + expect( isDomNode( iframeDocument ) ).to.be.true; + expect( isDomNode( iframeDocument.createElement( 'div' ) ) ).to.be.true; + expect( isDomNode( iframeDocument.createTextNode( 'Foo' ) ) ).to.be.true; + + expect( isDomNode( iframe.contentWindow ) ).to.be.false; + + iframe.remove(); + done(); + } ); + + document.body.appendChild( iframe ); } ); } );