chromium/chrome/test/data/extensions/api_test/content_scripts/prerendering/test.js

// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

let testServerPort = 0;

// Obtains a full URL that match a configuration that meets the expectations for
// options described in `details`.
function getUrl(path, details) {
  let host = 'default.test';
  if (details && details.matchAboutBlank)
    host = 'match_about_blank.test';
  const url = `http://${host}:${testServerPort}/extensions/${path}`;
  return url;
}

// Tests receiving messages from a content script to ensure pre-rendered frames
// are correctly handled with and without `all_frames`.
// For the initiator page, `test_file_with_prerendering.html`, we expect
// receiving a `top_frame_only` and an `all_frames` messages. Prerendered page,
// `test_wile_with_iframe.html` has an iframe. So, we expect receiving a
// `top_frame_only` and two `all_frames` messages. In total, we expect two
// `top_frame_only` and three `all_frames` messages. After the final page
// activation, we receive `activated`.
async function testWithIframe() {
  let numAllFramesMessages = 0;
  let numTopFrameOnlyMessages = 0;
  const testCallback = (message, sender, sendResponse) => {
    if (message == 'all_frames') {
      numAllFramesMessages++;
      chrome.test.assertTrue(numAllFramesMessages <= 3);
    } else if (message == 'top_frame_only') {
      numTopFrameOnlyMessages++;
      chrome.test.assertTrue(
          numTopFrameOnlyMessages <= 2,
          'Unexpected: maybe wrong injection on the activation');
    } else if (message == 'activated') {
      chrome.runtime.onMessage.removeListener(testCallback);
      // Inject a second script into the now-activated frame, but run it at
      // document_idle. This ensures that any content scripts that will run on
      // the frame have already done so, since they run at document_start.
      chrome.tabs.executeScript(
          {
            code: '// Empty',
            runAt: 'document_idle',
          },
          () => {
            chrome.test.assertEq(3, numAllFramesMessages);
            chrome.test.assertEq(2, numTopFrameOnlyMessages);
            chrome.test.succeed();
          });
    } else {
      chrome.runtime.onMessage.removeListener(testCallback);
      chrome.test.fail('Unexpected message: ' + JSON.stringify(message));
    }
    if (numAllFramesMessages == 3 && numTopFrameOnlyMessages == 2) {
      // Navigate to the pre-rendered page.
      // TODO(crbug.com/40208062): `chrome.tabs.update` can not activate
      // the pre-rendered page, but takes a new navigation instead.
      const url = getUrl('test_file_with_iframe.html');
      chrome.tabs.executeScript({code: `location.href = '${url}';`});
    }
  };
  chrome.runtime.onMessage.addListener(testCallback);
  chrome.tabs.update({url: getUrl('test_file_with_prerendering.html')});
}

// Tests receiving messages from a content script to ensure pre-rendered frames
// including `about:blank` are correctly handled with or without
// `match_about_blank`. For the initiator page,
// `test_file_with_prerendering_page_with_about_blank_iframe.html`, we expect
// receiving a `top_frame_only` and an `all_frames` messages. Prerendered page,
// `test_wile_with_about_blank_iframe.html` has an `about:blank` iframe. So, we
// expect receiving a `top_frame_only` and a `all_frames` from the main page,
// and iff `match_about_blank` specified, it receives another `all_frames` from
// `about:blank` page. In total, we expect two `top_frame_only` and two or three
// `all_frames_ messages respectively for the cases with and without
// `match_about_blank`. After the final page activation, we receive `activated`.
async function testWithAboutBlankIframe(details) {
  const matchAboutBlank = details && details.matchAboutBlank;
  const expectedNumAllFramesMessages = 2 + (matchAboutBlank ? 1 : 0);
  const expectedNumTopFrameOnlyMessages = 2;
  let numAllFramesMessages = 0;
  let numTopFrameOnlyMessages = 0;
  const testCallback = (message, sender, sendResponse) => {
    if (message == 'all_frames') {
      numAllFramesMessages++;
      chrome.test.assertTrue(
          numAllFramesMessages <= expectedNumAllFramesMessages,
          'Unexpected: maybe running on about:blank');
    } else if (message == 'top_frame_only') {
      numTopFrameOnlyMessages++;
      chrome.test.assertTrue(
          numAllFramesMessages <= expectedNumTopFrameOnlyMessages,
          'Unexpected: maybe wrong injection on the activation');
    } else if (message == 'activated') {
      chrome.runtime.onMessage.removeListener(testCallback);
      // Inject a second script into the now-activated frame, but run it at
      // document_idle. This ensures that any content scripts that will run on
      // the frame have already done so, since they run at document_start.
      chrome.tabs.executeScript(
          {
            code: '// Empty',
            runAt: 'document_idle',
          },
          () => {
            chrome.test.assertEq(
                expectedNumAllFramesMessages, numAllFramesMessages);
            chrome.test.assertEq(
                expectedNumTopFrameOnlyMessages, numTopFrameOnlyMessages);
            chrome.test.succeed();
          });
    } else {
      chrome.runtime.onMessage.removeListener(testCallback);
      chrome.test.fail('Unexpected message: ' + JSON.stringify(message));
    }
    if (numAllFramesMessages == expectedNumAllFramesMessages &&
        numTopFrameOnlyMessages == expectedNumTopFrameOnlyMessages) {
      // Navigate to the pre-rendered page.
      // TODO(crbug.com/40208062): `chrome.tabs.update` can not activate
      // the pre-rendered page, but takes a new navigation instead.
      const url = getUrl('test_file_with_about_blank_iframe.html', details);
      chrome.tabs.executeScript({code: `location.href = '${url}';`});
    }
  };
  chrome.runtime.onMessage.addListener(testCallback);
  const url = getUrl(
      'test_file_with_prerendering_page_with_about_blank_iframe.html', details);
  chrome.tabs.update({url: url});
}

chrome.test.getConfig(async config => {
  testServerPort = config.testServer.port;

  // TODO(https://crbug.com/3731231): Add more tests for
  // `match_origin_as_fallback` and manifest v3.
  chrome.test.runTests([
    testWithIframe,
    // TODO(crbug.com/40853029): These two tests are flaky and time out.
    // testWithAboutBlankIframe,
    // testWithAboutBlankIframe.bind(this, {matchAboutBlank: true}),
  ]);
});