mirror of
https://github.com/Shik3i/KoalaSync.git
synced 2026-08-18 23:26:17 +00:00
558 lines
20 KiB
JavaScript
558 lines
20 KiB
JavaScript
import { describe, expect, it, vi } from 'vitest';
|
|
import {
|
|
MEDIA_FRAME_ACCESS_REQUIRED,
|
|
inspectMediaFrame,
|
|
resolveMediaContentTarget,
|
|
selectMediaFrame
|
|
} from './media-frame-target.js';
|
|
|
|
function video(overrides = {}) {
|
|
const candidate = {
|
|
hasSource: true,
|
|
rendered: true,
|
|
background: false,
|
|
shortUncontrolled: false,
|
|
sizeBucket: 18,
|
|
playing: false,
|
|
controls: true,
|
|
readyState: 4,
|
|
duration: 1200,
|
|
renderedArea: 830 * 498,
|
|
...overrides
|
|
};
|
|
candidate.shortUncontrolled = overrides.shortUncontrolled ?? (
|
|
!candidate.controls && candidate.duration > 0 && candidate.duration < 300
|
|
);
|
|
return candidate;
|
|
}
|
|
|
|
function frame(frameId, overrides = {}) {
|
|
return {
|
|
frameId,
|
|
documentId: `document-${frameId}`,
|
|
result: {
|
|
href: `https://player-${frameId}.example/embed`,
|
|
origin: `https://player-${frameId}.example`,
|
|
isTop: frameId === 0,
|
|
videoCount: 1,
|
|
bestVideo: video(),
|
|
frameArea: 830 * 498,
|
|
parentFrameVisible: true,
|
|
parentFrameArea: 830 * 498,
|
|
embeddedFrames: [],
|
|
...overrides
|
|
}
|
|
};
|
|
}
|
|
|
|
describe('cross-origin media-frame targeting', () => {
|
|
it('selects a visible cross-origin player over a hidden loaded copy', () => {
|
|
const selected = selectMediaFrame([
|
|
frame(4),
|
|
frame(5, { parentFrameVisible: false, bestVideo: video({ playing: true }) })
|
|
]);
|
|
expect(selected.frameId).toBe(4);
|
|
});
|
|
|
|
it('does not select a video hidden inside a same-origin descendant', () => {
|
|
expect(selectMediaFrame([
|
|
frame(0, { bestVideo: video({ rendered: false }) }),
|
|
frame(6, {
|
|
parentFrameVisible: false,
|
|
bestVideo: video({ rendered: true, playing: true })
|
|
})
|
|
])).toBeNull();
|
|
});
|
|
|
|
it('keeps a real player ahead of a larger muted looping background video', () => {
|
|
const selected = selectMediaFrame([
|
|
frame(2, { bestVideo: video({ sizeBucket: 24, background: true, controls: false }) }),
|
|
frame(7, { bestVideo: video({ sizeBucket: 18 }) })
|
|
]);
|
|
expect(selected.frameId).toBe(7);
|
|
});
|
|
|
|
it('keeps an active long player ahead of a larger ordinary ad video', () => {
|
|
const selected = selectMediaFrame([
|
|
frame(2, { bestVideo: video({ sizeBucket: 24, duration: 30, playing: false, controls: false }) }),
|
|
frame(7, { bestVideo: video({ sizeBucket: 18, duration: 1200, playing: true, controls: true }) })
|
|
]);
|
|
expect(selected.frameId).toBe(7);
|
|
});
|
|
|
|
it('keeps a paused long player ahead of a playing short uncontrolled ad', () => {
|
|
const selected = selectMediaFrame([
|
|
frame(2, { bestVideo: video({
|
|
sizeBucket: 24,
|
|
duration: 30,
|
|
playing: true,
|
|
controls: false,
|
|
shortUncontrolled: true
|
|
}) }),
|
|
frame(7, { bestVideo: video({
|
|
sizeBucket: 18,
|
|
duration: 1200,
|
|
playing: false,
|
|
controls: true
|
|
}) })
|
|
]);
|
|
expect(selected.frameId).toBe(7);
|
|
});
|
|
|
|
it('keeps same-origin reachable media under the top-frame controller', () => {
|
|
const sharedVideo = video();
|
|
const selected = selectMediaFrame([
|
|
frame(0, { bestVideo: sharedVideo, videoCount: 1 }),
|
|
frame(6, { bestVideo: sharedVideo, videoCount: 1 })
|
|
]);
|
|
expect(selected.frameId).toBe(0);
|
|
});
|
|
|
|
it('refuses to guess between equally-ranked frames without visibility evidence', () => {
|
|
expect(selectMediaFrame([
|
|
frame(3, { parentFrameVisible: null }),
|
|
frame(4, { parentFrameVisible: null })
|
|
])).toBeNull();
|
|
});
|
|
|
|
it('returns the exact selected frame and document after probing', async () => {
|
|
const results = [frame(0, { bestVideo: null, videoCount: 0 }), frame(8)];
|
|
const executeScript = vi.fn().mockResolvedValue(results);
|
|
await expect(resolveMediaContentTarget(
|
|
{ scripting: { executeScript } },
|
|
42,
|
|
{ attempts: 1, probeDelayMs: 0 }
|
|
)).resolves.toEqual({
|
|
frameId: 8,
|
|
documentId: 'document-8',
|
|
frameUrl: 'https://player-8.example/embed',
|
|
hasVideo: true,
|
|
scriptTarget: { tabId: 42, documentIds: ['document-8'] },
|
|
monitorTargets: [
|
|
{ tabId: 42, documentIds: ['document-0'] },
|
|
{ tabId: 42, documentIds: ['document-8'] }
|
|
]
|
|
});
|
|
const visibilityDispatches = executeScript.mock.calls.filter(([options]) => (
|
|
options.func?.name === 'dispatchParentFrameVisibilityProbe'
|
|
));
|
|
expect(visibilityDispatches.length).toBeGreaterThanOrEqual(4);
|
|
});
|
|
|
|
it('keeps the top target inactive when the only discovered video is hidden', async () => {
|
|
const results = [
|
|
frame(0, { bestVideo: video({ rendered: false }) }),
|
|
frame(6, { parentFrameVisible: false })
|
|
];
|
|
const executeScript = vi.fn().mockResolvedValue(results);
|
|
await expect(resolveMediaContentTarget(
|
|
{ scripting: { executeScript } },
|
|
42,
|
|
{ attempts: 1, probeDelayMs: 0 }
|
|
)).resolves.toEqual({
|
|
frameId: 0,
|
|
documentId: null,
|
|
frameUrl: null,
|
|
hasVideo: false,
|
|
scriptTarget: { tabId: 42 },
|
|
monitorTargets: [
|
|
{ tabId: 42, documentIds: ['document-0'] },
|
|
{ tabId: 42, documentIds: ['document-6'] }
|
|
]
|
|
});
|
|
});
|
|
|
|
it('uses all-frame probing without a navigation permission', async () => {
|
|
const executeScript = vi.fn().mockResolvedValue([frame(8)]);
|
|
|
|
await expect(resolveMediaContentTarget(
|
|
{ scripting: { executeScript } },
|
|
42,
|
|
{ attempts: 1, probeDelayMs: 0 }
|
|
)).resolves.toMatchObject({
|
|
frameId: 8,
|
|
hasVideo: true,
|
|
scriptTarget: { tabId: 42, documentIds: ['document-8'] },
|
|
monitorTargets: expect.arrayContaining([
|
|
{ tabId: 42, documentIds: ['document-8'] }
|
|
])
|
|
});
|
|
expect(executeScript.mock.calls.some(([options]) => (
|
|
options.target?.tabId === 42 && options.target?.allFrames === true
|
|
))).toBe(true);
|
|
});
|
|
|
|
it('isolates a rejected all-frame sweep and still finds an embedded player', async () => {
|
|
const top = frame(0, {
|
|
href: 'https://anime.example/watch',
|
|
origin: 'https://anime.example',
|
|
bestVideo: null,
|
|
videoCount: 0,
|
|
embeddedFrames: [{
|
|
href: 'https://player.example/embed',
|
|
origin: 'https://player.example',
|
|
area: 860 * 490,
|
|
width: 860,
|
|
height: 490,
|
|
visible: true,
|
|
mediaHint: true
|
|
}]
|
|
});
|
|
const player = frame(1, {
|
|
href: 'https://player.example/embed'
|
|
});
|
|
const executeScript = vi.fn(async options => {
|
|
if (options.target?.allFrames === true) throw new Error('one frame rejected');
|
|
const frameId = options.target?.frameIds?.[0];
|
|
if (options.func?.name === 'inspectMediaFrame') {
|
|
if (frameId === 0) return [top];
|
|
if (frameId === 1) return [player];
|
|
return [];
|
|
}
|
|
return [];
|
|
});
|
|
|
|
await expect(resolveMediaContentTarget(
|
|
{ scripting: { executeScript } },
|
|
47,
|
|
{ attempts: 1, probeDelayMs: 0 }
|
|
)).resolves.toMatchObject({
|
|
frameId: 1,
|
|
documentId: 'document-1',
|
|
hasVideo: true,
|
|
scriptTarget: { tabId: 47, documentIds: ['document-1'] },
|
|
monitorTargets: expect.arrayContaining([
|
|
{ tabId: 47, documentIds: ['document-0'] },
|
|
{ tabId: 47, documentIds: ['document-1'] }
|
|
])
|
|
});
|
|
});
|
|
|
|
it('sweeps individual frame IDs when an all-frame result is partial', async () => {
|
|
const top = frame(0, {
|
|
href: 'https://anime.example/watch',
|
|
origin: 'https://anime.example',
|
|
bestVideo: null,
|
|
videoCount: 0,
|
|
embeddedFrames: [{
|
|
href: 'https://player.example/embed',
|
|
origin: 'https://player.example',
|
|
area: 860 * 490,
|
|
width: 860,
|
|
height: 490,
|
|
visible: true,
|
|
mediaHint: true
|
|
}]
|
|
});
|
|
const partialFrame = frame(2, { bestVideo: null, videoCount: 0 });
|
|
const player = frame(3, { href: 'https://player.example/embed' });
|
|
const executeScript = vi.fn(async options => {
|
|
if (options.target?.allFrames === true) {
|
|
return options.func?.name === 'inspectMediaFrame' ? [top, partialFrame] : [];
|
|
}
|
|
const frameId = options.target?.frameIds?.[0];
|
|
if (options.func?.name === 'inspectMediaFrame') {
|
|
if (frameId === 0) return [top];
|
|
if (frameId === 3) return [player];
|
|
return [];
|
|
}
|
|
return [];
|
|
});
|
|
|
|
await expect(resolveMediaContentTarget(
|
|
{ scripting: { executeScript } },
|
|
48,
|
|
{ attempts: 1, probeDelayMs: 0 }
|
|
)).resolves.toMatchObject({
|
|
frameId: 3,
|
|
documentId: 'document-3',
|
|
hasVideo: true,
|
|
scriptTarget: { tabId: 48, documentIds: ['document-3'] },
|
|
monitorTargets: expect.arrayContaining([
|
|
{ tabId: 48, documentIds: ['document-0'] },
|
|
{ tabId: 48, documentIds: ['document-2'] },
|
|
{ tabId: 48, documentIds: ['document-3'] }
|
|
])
|
|
});
|
|
});
|
|
|
|
it('does not trust parent visibility from an older probe token', () => {
|
|
const originalWindow = globalThis.window;
|
|
const originalDocument = globalThis.document;
|
|
const fakeWindow = {
|
|
top: {},
|
|
location: { href: 'https://player.example/embed', origin: 'https://player.example' },
|
|
innerWidth: 800,
|
|
innerHeight: 450,
|
|
__koalaParentFrameVisibility: { token: 'old-token', visible: true, area: 360000 }
|
|
};
|
|
const fakeDocument = {
|
|
location: fakeWindow.location,
|
|
defaultView: fakeWindow,
|
|
querySelectorAll: () => []
|
|
};
|
|
globalThis.window = fakeWindow;
|
|
globalThis.document = fakeDocument;
|
|
try {
|
|
expect(inspectMediaFrame('new-token')).toMatchObject({
|
|
parentFrameVisible: null,
|
|
parentFrameArea: null
|
|
});
|
|
} finally {
|
|
if (originalWindow === undefined) delete globalThis.window;
|
|
else globalThis.window = originalWindow;
|
|
if (originalDocument === undefined) delete globalThis.document;
|
|
else globalThis.document = originalDocument;
|
|
}
|
|
});
|
|
|
|
it('recognizes the current Google Drive youtube.googleapis.com player', async () => {
|
|
const top = frame(0, {
|
|
href: 'https://drive.google.com/drive/u/0/search?q=video',
|
|
origin: 'https://drive.google.com',
|
|
bestVideo: null,
|
|
videoCount: 0,
|
|
embeddedFrames: [{
|
|
href: 'https://youtube.googleapis.com/embed/drive-file-id?origin=https%3A%2F%2Fdrive.google.com',
|
|
origin: 'https://youtube.googleapis.com',
|
|
area: 280 * 157,
|
|
width: 280,
|
|
height: 157,
|
|
visible: true,
|
|
depth: 1,
|
|
mediaHint: false
|
|
}]
|
|
});
|
|
const executeScript = vi.fn()
|
|
.mockResolvedValueOnce([top])
|
|
.mockResolvedValueOnce([top]);
|
|
|
|
await expect(resolveMediaContentTarget(
|
|
{ scripting: { executeScript } },
|
|
42,
|
|
{ attempts: 1, probeDelayMs: 0 }
|
|
)).rejects.toMatchObject({
|
|
code: MEDIA_FRAME_ACCESS_REQUIRED,
|
|
host: 'youtube.googleapis.com',
|
|
originPattern: 'https://youtube.googleapis.com/*'
|
|
});
|
|
});
|
|
|
|
it('recognizes a YummyAnime-style nested inaccessible player origin', async () => {
|
|
const top = frame(0, {
|
|
href: 'https://yummyanime.tv/show.html',
|
|
origin: 'https://yummyanime.tv',
|
|
bestVideo: null,
|
|
videoCount: 0,
|
|
embeddedFrames: [{
|
|
href: 'https://absciss.thealloha.club/?token=redacted',
|
|
origin: 'https://absciss.thealloha.club',
|
|
area: 830 * 498,
|
|
width: 830,
|
|
height: 498,
|
|
visible: true,
|
|
depth: 2,
|
|
mediaHint: true
|
|
}]
|
|
});
|
|
const executeScript = vi.fn()
|
|
.mockResolvedValueOnce([top])
|
|
.mockResolvedValueOnce([top]);
|
|
|
|
await expect(resolveMediaContentTarget(
|
|
{ scripting: { executeScript } },
|
|
43,
|
|
{ attempts: 1, probeDelayMs: 0 }
|
|
)).rejects.toMatchObject({
|
|
code: MEDIA_FRAME_ACCESS_REQUIRED,
|
|
host: 'absciss.thealloha.club',
|
|
originPattern: 'https://absciss.thealloha.club/*'
|
|
});
|
|
});
|
|
|
|
it('requests the inaccessible player instead of selecting an accessible background video', async () => {
|
|
const top = frame(0, {
|
|
bestVideo: video({ background: true, controls: false, renderedArea: 900 * 506 }),
|
|
embeddedFrames: [{
|
|
href: 'https://player.external.example/watch',
|
|
origin: 'https://player.external.example',
|
|
area: 900 * 506,
|
|
width: 900,
|
|
height: 506,
|
|
visible: true,
|
|
mediaHint: true
|
|
}]
|
|
});
|
|
const executeScript = vi.fn().mockResolvedValue([top]);
|
|
await expect(resolveMediaContentTarget(
|
|
{ scripting: { executeScript } },
|
|
44,
|
|
{ attempts: 1, probeDelayMs: 0 }
|
|
)).rejects.toMatchObject({
|
|
code: MEDIA_FRAME_ACCESS_REQUIRED,
|
|
originPattern: 'https://player.external.example/*'
|
|
});
|
|
});
|
|
|
|
it('requests the inaccessible main player instead of selecting a larger short ad', async () => {
|
|
const top = frame(0, {
|
|
bestVideo: video({
|
|
playing: true,
|
|
controls: false,
|
|
duration: 30,
|
|
renderedArea: 500 * 281,
|
|
sizeBucket: 24
|
|
}),
|
|
embeddedFrames: [{
|
|
href: 'https://player.external.example/watch',
|
|
origin: 'https://player.external.example',
|
|
area: 900 * 506,
|
|
width: 900,
|
|
height: 506,
|
|
visible: true,
|
|
mediaHint: true
|
|
}]
|
|
});
|
|
const executeScript = vi.fn().mockResolvedValue([top]);
|
|
await expect(resolveMediaContentTarget(
|
|
{ scripting: { executeScript } },
|
|
44,
|
|
{ attempts: 1, probeDelayMs: 0 }
|
|
)).rejects.toMatchObject({
|
|
code: MEDIA_FRAME_ACCESS_REQUIRED,
|
|
originPattern: 'https://player.external.example/*'
|
|
});
|
|
});
|
|
|
|
it('keeps a paused long custom player over a larger inaccessible heuristic frame', async () => {
|
|
const top = frame(0, {
|
|
bestVideo: video({
|
|
playing: false,
|
|
controls: false,
|
|
duration: 7200,
|
|
renderedArea: 600 * 338
|
|
}),
|
|
embeddedFrames: [{
|
|
href: 'https://widget.external.example/watch',
|
|
origin: 'https://widget.external.example',
|
|
area: 900 * 506,
|
|
width: 900,
|
|
height: 506,
|
|
visible: true,
|
|
mediaHint: true
|
|
}]
|
|
});
|
|
const executeScript = vi.fn().mockResolvedValue([top]);
|
|
await expect(resolveMediaContentTarget(
|
|
{ scripting: { executeScript } },
|
|
44,
|
|
{ attempts: 1, probeDelayMs: 0 }
|
|
)).resolves.toMatchObject({ frameId: 0, hasVideo: true });
|
|
});
|
|
|
|
it('uses Firefox-compatible portless match patterns for embedded origins', async () => {
|
|
const top = frame(0, {
|
|
bestVideo: null,
|
|
videoCount: 0,
|
|
embeddedFrames: [{
|
|
href: 'http://127.0.0.1:4173/player',
|
|
origin: 'http://127.0.0.1:4173',
|
|
area: 900 * 506,
|
|
width: 900,
|
|
height: 506,
|
|
visible: true,
|
|
mediaHint: true
|
|
}]
|
|
});
|
|
const executeScript = vi.fn().mockResolvedValue([top]);
|
|
await expect(resolveMediaContentTarget(
|
|
{ scripting: { executeScript } },
|
|
45,
|
|
{ attempts: 1, probeDelayMs: 0 }
|
|
)).rejects.toMatchObject({ originPattern: 'http://127.0.0.1/*' });
|
|
});
|
|
|
|
it('does not request access for one large non-media iframe', async () => {
|
|
const top = frame(0, {
|
|
bestVideo: null,
|
|
videoCount: 0,
|
|
embeddedFrames: [{
|
|
href: 'https://maps.example/view',
|
|
origin: 'https://maps.example',
|
|
area: 900 * 506,
|
|
width: 900,
|
|
height: 506,
|
|
visible: true,
|
|
mediaHint: false
|
|
}]
|
|
});
|
|
const executeScript = vi.fn().mockResolvedValue([top]);
|
|
await expect(resolveMediaContentTarget(
|
|
{ scripting: { executeScript } },
|
|
46,
|
|
{ attempts: 1, probeDelayMs: 0 }
|
|
)).resolves.toMatchObject({ frameId: 0, hasVideo: false });
|
|
});
|
|
|
|
it('does not retain a permission prompt for a player frame that disappeared', async () => {
|
|
const withPlayer = frame(0, {
|
|
bestVideo: null,
|
|
videoCount: 0,
|
|
embeddedFrames: [{
|
|
href: 'https://player.external.example/watch',
|
|
origin: 'https://player.external.example',
|
|
area: 900 * 506,
|
|
width: 900,
|
|
height: 506,
|
|
visible: true,
|
|
mediaHint: true
|
|
}]
|
|
});
|
|
const withoutPlayer = frame(0, { bestVideo: null, videoCount: 0, embeddedFrames: [] });
|
|
const executeScript = vi.fn()
|
|
.mockResolvedValueOnce([withPlayer])
|
|
.mockResolvedValueOnce([withoutPlayer]);
|
|
await expect(resolveMediaContentTarget(
|
|
{ scripting: { executeScript } },
|
|
46,
|
|
{ attempts: 2, retryDelayMs: 0, probeDelayMs: 0 }
|
|
)).resolves.toMatchObject({ frameId: 0, hasVideo: false });
|
|
});
|
|
|
|
it('does not request access for small or ambiguously-sized embedded frames', async () => {
|
|
const top = frame(0, {
|
|
bestVideo: null,
|
|
videoCount: 0,
|
|
embeddedFrames: [
|
|
{ href: 'https://ad-one.example', origin: 'https://ad-one.example', area: 300 * 250, width: 300, height: 250, visible: true },
|
|
{ href: 'https://ad-two.example', origin: 'https://ad-two.example', area: 300 * 250, width: 300, height: 250, visible: true }
|
|
]
|
|
});
|
|
const executeScript = vi.fn()
|
|
.mockResolvedValueOnce([top])
|
|
.mockResolvedValueOnce([top]);
|
|
await expect(resolveMediaContentTarget(
|
|
{ scripting: { executeScript } },
|
|
44,
|
|
{ attempts: 1, probeDelayMs: 0 }
|
|
)).resolves.toMatchObject({ frameId: 0, scriptTarget: { tabId: 44 } });
|
|
});
|
|
|
|
it('keeps the tab target active when equal player frames are ambiguous', async () => {
|
|
const results = [
|
|
frame(3, { parentFrameVisible: null }),
|
|
frame(4, { parentFrameVisible: null })
|
|
];
|
|
const executeScript = vi.fn().mockResolvedValue(results);
|
|
await expect(resolveMediaContentTarget(
|
|
{ scripting: { executeScript } },
|
|
45,
|
|
{ attempts: 1, probeDelayMs: 0 }
|
|
)).resolves.toMatchObject({
|
|
frameId: 0,
|
|
hasVideo: false,
|
|
scriptTarget: { tabId: 45 }
|
|
});
|
|
});
|
|
});
|