File size: 3,976 Bytes
97ee7cb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
import { expect, test, type Frame, type Page } from '@playwright/test';

type SandboxProbeWindow = Window & typeof globalThis & {
  __bodyBeforeUnloadCalls: number;
  __clickCalls: number;
  __framesetBeforeUnloadCalls: number;
};

async function mountSandboxWidget(page: Page, id: string, html: string): Promise<Frame> {
  const token = `token-${id}`;

  await page.evaluate(({ html: widgetHtml, id: widgetId, token: widgetToken }) => {
    const iframe = document.createElement('iframe');
    iframe.dataset.testid = widgetId;
    iframe.setAttribute('sandbox', 'allow-scripts');
    iframe.src = `/wm-widget-sandbox.html#id=${encodeURIComponent(widgetId)}&token=${encodeURIComponent(widgetToken)}`;

    function handleReady(event: MessageEvent): void {
      if (
        event.source !== iframe.contentWindow
        || !event.data
        || event.data.type !== 'wm-widget-ready'
        || event.data.id !== widgetId
        || event.data.token !== widgetToken
      ) {
        return;
      }

      document.documentElement.dataset.widgetReady = widgetId;
      iframe.contentWindow?.postMessage(
        { type: 'wm-html', id: widgetId, token: widgetToken, html: widgetHtml },
        '*',
      );
      window.removeEventListener('message', handleReady);
    }

    window.addEventListener('message', handleReady);
    document.body.appendChild(iframe);
  }, { html, id, token });

  const iframe = page.locator(`iframe[data-testid="${id}"]`);
  await expect(iframe).toHaveAttribute('sandbox', 'allow-scripts');
  await expect.poll(
    () => page.evaluate(() => document.documentElement.dataset.widgetReady),
  ).toBe(id);

  const handle = await iframe.elementHandle();
  const frame = await handle?.contentFrame();
  expect(frame).not.toBeNull();
  return frame!;
}

test.describe('PRO widget sandbox', () => {
  test.beforeEach(async ({ page }) => {
    await page.route('**/widget-sandbox-parent', (route) => route.fulfill({
      status: 200,
      contentType: 'text/html',
      body: '<!doctype html><html><body></body></html>',
    }));
    await page.goto('/widget-sandbox-parent');
  });

  test('stops reflected body and frameset beforeunload handlers after document.open', async ({ page }) => {
    const bodyFrame = await mountSandboxWidget(page, 'body-widget', `<!doctype html>
<html>
<body onbeforeunload="window.__bodyBeforeUnloadCalls += 1">
  <script>
    window.__bodyBeforeUnloadCalls = 0;
    window.__clickCalls = 0;

    window.addEventListener('click', function () { window.__clickCalls += 1; });
    window.dispatchEvent(new Event('beforeunload'));
    window.dispatchEvent(new Event('click'));
    document.body.dataset.widgetReady = 'true';
  </script>
</body>
</html>`);

    await expect.poll(
      () => bodyFrame.evaluate(() => document.body?.dataset.widgetReady),
    ).toBe('true');

    expect(await bodyFrame.evaluate(() => {
      const probe = window as SandboxProbeWindow;
      return {
        body: probe.__bodyBeforeUnloadCalls,
        click: probe.__clickCalls,
      };
    })).toEqual({ body: 0, click: 1 });

    await bodyFrame.evaluate(() => window.dispatchEvent(new Event('beforeunload')));
    expect(await bodyFrame.evaluate(() => {
      const probe = window as SandboxProbeWindow;
      return probe.__bodyBeforeUnloadCalls;
    })).toBe(0);

    const framesetFrame = await mountSandboxWidget(page, 'frameset-widget', `<!doctype html>
<html>
<head><script>window.__framesetBeforeUnloadCalls = 0;</script></head>
<frameset onbeforeunload="window.__framesetBeforeUnloadCalls += 1">
  <frame src="about:blank">
</frameset>
</html>`);

    await expect.poll(
      () => framesetFrame.evaluate(() => Boolean(document.querySelector('frameset'))),
    ).toBe(true);
    await framesetFrame.evaluate(() => window.dispatchEvent(new Event('beforeunload')));
    expect(
      await framesetFrame.evaluate(
        () => (window as SandboxProbeWindow).__framesetBeforeUnloadCalls,
      ),
    ).toBe(0);
  });
});