diff --git a/js/content-script.js b/js/content-script.js index d287825..33971df 100644 --- a/js/content-script.js +++ b/js/content-script.js @@ -554,8 +554,7 @@ function restoreVideo() { let mvClassList = [mvImpl.mvClass, 'core']; for(let cn of mvClassList) { - let nodes = document.querySelectorAll('[mvclass='+cn+']'); - for(let node of nodes) { + for(let { elem: node } of deepQueryAll('[mvclass='+cn+']')) { node.removeAttribute('mvclass'); } } @@ -608,19 +607,40 @@ function maximizeVideo(selectedNode, chain = []) { maximizeMainNode(); } +// Collect every element matching `selector`, including elements inside open +// shadow roots (e.g. the web component that learn.udacity.com +// uses to host its YouTube iframe). Each result carries the chain of shadow +// hosts (outermost first) so callers can walk back out of the shadow tree, +// matching the `chain` convention used by findVideoElement()/maximizeVideo(). +function deepQueryAll(selector, root = document, chain = []) { + const results = []; + root.querySelectorAll(selector).forEach(elem => { + results.push({ elem: elem, chain: [...chain] }); + }); + root.querySelectorAll('*').forEach(elem => { + if (elem.shadowRoot) { + results.push(...deepQueryAll(selector, elem.shadowRoot, [...chain, elem])); + } + }); + return results; +} + +function getAllIFrames() { + return deepQueryAll('iframe'); +} + function getChildIFrameById(id) { - let iframes = document.getElementsByTagName('IFRAME'); - for(let iframe of iframes) { - if(iframe.getAttribute('mv_iframe') === id) { - return iframe; + for(let info of getAllIFrames()) { + if(info.elem.getAttribute('mv_iframe') === id) { + return info; } } + return { elem: null, chain: [] }; } window.addEventListener('message', e => { if (e.data.action === 'getId') { //message from child - let iframes = document.getElementsByTagName('IFRAME'); - for(let iframe of iframes) { + for(let { elem: iframe } of getAllIFrames()) { let vmi = iframe.getAttribute('mv_iframe'); if(!vmi) { iframe.setAttribute('mv_iframe', idCount); @@ -647,20 +667,22 @@ window.addEventListener('message', e => { } } else if(e.data.action === 'setVideoNode'){ //message from child - let iframe = getChildIFrameById(e.data.id); + let { elem: iframe, chain } = getChildIFrameById(e.data.id); + if(!iframe) return; let hashCode = iframe.getAttribute('mvHashCode'); if(!hashCode) { hashCode = getHashCode(HASHCODE_LENGTH); iframe.setAttribute('mvHashCode', hashCode); } mvImpl.currentHashCode = hashCode; - maximizeVideo(iframe); + maximizeVideo(iframe, chain); // if(window !== window.top) { // window.parent.postMessage({action: 'getId', senderId: selfId, nextAction: 'setVideoNode'},'*'); // } } else if(e.data.action === 'addVideoElements'){ //message from child - let iframe = getChildIFrameById(e.data.id); + let { elem: iframe } = getChildIFrameById(e.data.id); + if(!iframe) return; let iframeRect = iframe.getBoundingClientRect(); if(window !== window.top) { for(let elemInfo of e.data.elemInfos) {