From 38cad9451dbde52fbab0835e3c1673825c57e5b7 Mon Sep 17 00:00:00 2001 From: Wilco Fiers Date: Thu, 1 Apr 2021 10:41:15 +0200 Subject: [PATCH] fix(respondable): work on iframes in shadow DOM (#2857) --- lib/core/utils/respondable/assert-window.js | 8 ++-- test/core/utils/respondable.js | 47 ++++++++++++++++++++- 2 files changed, 50 insertions(+), 5 deletions(-) diff --git a/lib/core/utils/respondable/assert-window.js b/lib/core/utils/respondable/assert-window.js index 8e767d0d51..46f37d9659 100644 --- a/lib/core/utils/respondable/assert-window.js +++ b/lib/core/utils/respondable/assert-window.js @@ -10,10 +10,10 @@ export function assertIsParentWindow(win) { export function assertIsFrameWindow(win) { assetNotGlobalWindow(win); - const frames = Array.from(window.frames); - if (!frames.some(frame => frame === win)) { - throw new Error('Respondable target must be a frame in the current window'); - } + assert( + win.parent === window, + 'Respondable target must be a frame in the current window' + ); } export function assetNotGlobalWindow(win) { diff --git a/test/core/utils/respondable.js b/test/core/utils/respondable.js index 12fecdf41e..a016a6bc8c 100644 --- a/test/core/utils/respondable.js +++ b/test/core/utils/respondable.js @@ -31,6 +31,7 @@ describe('axe.utils.respondable', function() { var postMessage = window.postMessage; var captureError = axe.testUtils.captureError; var isIE11 = axe.testUtils.isIE11; + var shadowSupported = axe.testUtils.shadowSupport.v1; this.timeout(1000); beforeEach(function(done) { @@ -151,6 +152,37 @@ describe('axe.utils.respondable', function() { ); }); + (shadowSupported ? it : xit)('works with frames in shadow DOM', function( + done + ) { + fixture.innerHTML = '
'; + var shadowRoot = fixture + .querySelector('#shadow-root') + .attachShadow({ mode: 'open' }); + frame = document.createElement('iframe'); + frame.src = '../mock/frames/test.html'; + + frame.addEventListener('load', function() { + var called = false; + frameWin = frame.contentWindow; + frameSubscribe = frameWin.axe.utils.respondable.subscribe; + + frameSubscribe('greeting', function(msg) { + assert.equal(msg, 'hello'); + called = true; + }); + respondable(frameWin, 'greeting', 'hello'); + afterMessage( + frameWin, + captureError(function() { + assert.isTrue(called); + done(); + }, done) + ); + }); + shadowRoot.appendChild(frame); + }); + it('is not called on a different topic', function(done) { var called = false; frameSubscribe('otherTopic', function() { @@ -226,6 +258,17 @@ describe('axe.utils.respondable', function() { respondable(frameWin, 'greeting', new Error('expected message')); }); + (isIE11 ? it.skip : it)( + // In IE win.parent is read-only + 'throws if frame.parent is not the window', + function() { + frameWin.parent = frameWin; + assert.throws(function() { + respondable(frameWin, 'greeting'); + }); + } + ); + (isIE11 ? it.skip : it)( // In IE win.parent is read-only 'is not called when the source is not a frame in the page', @@ -239,8 +282,10 @@ describe('axe.utils.respondable', function() { frameSubscribe('greeting', function() { doneOnce(new Error('subscribe should not be called')); }); - frameWin.parent = frameWin; respondable(frameWin, 'greeting'); + // Swap parent after the message is sent, but before it is received: + frameWin.parent = frameWin; + setTimeout( captureError(function() { assert.isTrue(called);