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, 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', 'getRenderedVideoArea', 'getVideoSizeBucket', 'isVideoRendered', 'hasPlayableVideoSource', 'isBackgroundVideo', 'isVideoPlaying', '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' ); // Invariant that protects every already-working single-player site: with one // candidate the ranking is never consulted, so no signal can turn a page that // used to sync into "no video found". 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), lonelyBadCandidate, 'a single candidate is returned even when every ranking signal is against it' ); // Same-origin player iframe (jkanime.net): the top document has no