const fs = require('fs'); const path = require('path'); const assert = require('assert'); const contentPath = path.join(__dirname, '..', 'extension', 'content.js'); const source = fs.readFileSync(contentPath, 'utf8'); function extractFunction(name, text) { const start = text.indexOf(`function ${name}`); assert.notStrictEqual(start, -1, `${name} not found`); const bodyStart = text.indexOf('{', start); let depth = 0; for (let i = bodyStart; i < text.length; i++) { if (text[i] === '{') depth++; if (text[i] === '}') depth--; if (depth === 0) return text.slice(start, i + 1); } throw new Error(`${name} body did not terminate`); } function makeSeekable(ranges = []) { return { length: ranges.length, start(i) { return ranges[i][0]; }, end(i) { return ranges[i][1]; } }; } function makeVideo(name, width, height, options = {}) { return { name, tagName: 'VIDEO', videoWidth: width, videoHeight: height, offsetWidth: width, offsetHeight: height, muted: options.muted ?? true, controls: options.controls ?? true, paused: options.paused ?? true, ended: options.ended ?? false, currentSrc: options.currentSrc ?? 'fixture.mp4', duration: options.duration ?? 0, currentTime: options.currentTime ?? 0, seekable: options.seekable ?? makeSeekable() }; } const lightPreview = makeVideo('light-preview', 160, 90, { muted: false, duration: 30 }); const shadowPlayer = makeVideo('shadow-player', 1920, 1080, { muted: false, duration: 3600 }); const shadowRoot = { querySelectorAll(selector) { if (selector === 'video') return [shadowPlayer]; return []; } }; const shadowHost = { shadowRoot }; const fakeDocument = { querySelectorAll(selector) { if (selector === 'video') return [lightPreview]; return [shadowHost]; } }; // findVideo delegates to a small set of ranking helpers; they have to be lifted // together or this would silently test a stale shape of the finder. const VIDEO_FINDER_PARTS = [ 'findVideo', 'collectVideoCandidates', 'getElementRenderBox', 'elementStylesAllowRendering', 'isElementRendered', 'getRenderedVideoArea', 'getVideoSizeBucket', 'isVideoRendered', 'hasPlayableVideoSource', 'isBackgroundVideo', 'isVideoPlaying', 'isShortUncontrolledVideo', 'compareVideoRanks', 'pickBestVideo' ]; function extractRankingTable(text) { const start = text.indexOf('const VIDEO_RANKING_SIGNALS'); assert.notStrictEqual(start, -1, 'VIDEO_RANKING_SIGNALS not found'); const end = text.indexOf('];', start); assert.notStrictEqual(end, -1, 'VIDEO_RANKING_SIGNALS did not terminate'); return text.slice(start, end + 2); } const fnSource = [ ...VIDEO_FINDER_PARTS.map(name => extractFunction(name, source)), extractRankingTable(source) ].join('\n'); const findVideo = Function('document', `${fnSource}; return findVideo;`)(fakeDocument); const selected = findVideo(fakeDocument); assert.strictEqual( selected, shadowPlayer, 'findVideo should score Shadow DOM videos together with light DOM videos' ); // A hidden preload must not become the active controller merely because it is // the only video currently present. The media-frame monitor re-runs discovery // if its geometry becomes visible later. const lonelyBadCandidate = makeVideo('lonely', 0, 0, { muted: true, duration: 0 }); lonelyBadCandidate.loop = true; lonelyBadCandidate.controls = false; lonelyBadCandidate.offsetWidth = 0; lonelyBadCandidate.offsetHeight = 0; const lonelyDocument = { querySelectorAll(selector) { if (selector === 'video') return [lonelyBadCandidate]; return []; } }; assert.strictEqual( findVideo(lonelyDocument), null, 'a hidden single candidate is not returned as an active player' ); function attachRenderEnvironment(documentNode, elements, { frameElement = null, scrollWidth = 1000, scrollHeight = 700, scrollX = 0, scrollY = 0 } = {}) { const view = { innerWidth: 1000, innerHeight: 700, scrollX, scrollY, frameElement, getComputedStyle(element) { return element._style || { display: 'block', visibility: 'visible', opacity: '1' }; } }; documentNode.defaultView = view; documentNode.documentElement = { scrollWidth, scrollHeight }; for (const element of elements) { element.ownerDocument = documentNode; element.getBoundingClientRect = () => element._rect || { width: element.offsetWidth, height: element.offsetHeight, top: 0, left: 0, right: element.offsetWidth, bottom: element.offsetHeight }; } return view; } const hiddenPlayingVideo = makeVideo('hidden-playing', 900, 506, { controls: true, paused: false, duration: 1200 }); hiddenPlayingVideo._style = { display: 'block', visibility: 'hidden', opacity: '1' }; const visiblePausedVideo = makeVideo('visible-paused', 800, 450, { controls: true, paused: true, duration: 1200 }); const visibilityDocument = { querySelectorAll(selector) { if (selector === 'video') return [hiddenPlayingVideo, visiblePausedVideo]; return []; } }; attachRenderEnvironment(visibilityDocument, [hiddenPlayingVideo, visiblePausedVideo]); assert.strictEqual( findVideo(visibilityDocument), visiblePausedVideo, 'a hidden playing preload must not outrank the visible paused player' ); const belowFoldPlayer = makeVideo('below-fold-player', 800, 450, { controls: true, duration: 1200 }); belowFoldPlayer._rect = { width: 800, height: 450, top: 900, left: 0, right: 800, bottom: 1350 }; const offscreenDecoy = makeVideo('offscreen-decoy', 900, 506, { controls: true, paused: false, duration: 1200 }); offscreenDecoy._rect = { width: 900, height: 506, top: 0, left: -2000, right: -1100, bottom: 506 }; const scrollableDocument = { querySelectorAll(selector) { if (selector === 'video') return [offscreenDecoy, belowFoldPlayer]; return []; } }; attachRenderEnvironment(scrollableDocument, [offscreenDecoy, belowFoldPlayer], { scrollHeight: 1500 }); assert.strictEqual( findVideo(scrollableDocument), belowFoldPlayer, 'a player below the fold remains eligible while a positioned offscreen decoy does not' ); const scrolledPastPlayer = makeVideo('scrolled-past-player', 800, 450, { controls: true, duration: 1200 }); scrolledPastPlayer._rect = { width: 800, height: 450, top: -800, left: 0, right: 800, bottom: -350 }; const scrolledDocument = { querySelectorAll(selector) { if (selector === 'video') return [scrolledPastPlayer]; return []; } }; attachRenderEnvironment(scrolledDocument, [scrolledPastPlayer], { scrollHeight: 1500, scrollY: 900 }); assert.strictEqual( findVideo(scrolledDocument), scrolledPastPlayer, 'a player above the current viewport remains eligible when it is inside the document layout' ); const framedHiddenVideo = makeVideo('framed-hidden', 900, 506, { controls: true, paused: false, duration: 1200 }); const hiddenFrameDocument = { querySelectorAll(selector) { if (selector === 'video') return [framedHiddenVideo]; return []; } }; const hiddenAncestorFrame = { offsetWidth: 900, offsetHeight: 506, _style: { display: 'block', visibility: 'hidden', opacity: '1' }, contentDocument: hiddenFrameDocument }; const hiddenFrameTopDocument = { querySelectorAll(selector) { if (selector === 'video') return []; if (selector === 'iframe, frame') return [hiddenAncestorFrame]; return []; } }; attachRenderEnvironment(hiddenFrameTopDocument, [hiddenAncestorFrame]); attachRenderEnvironment(hiddenFrameDocument, [framedHiddenVideo], { frameElement: hiddenAncestorFrame }); assert.strictEqual( findVideo(hiddenFrameTopDocument), null, 'a video inside a hidden same-origin ancestor frame must not be selected' ); // Same-origin player iframe (jkanime.net): the top document has no