chromium/chrome/test/data/extensions/api_test/webnavigation/prerendering/test_prerendering.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.

const inServiceWorker = 'ServiceWorkerGlobalScope' in self;
const scriptUrl = '_test_resources/api_test/webnavigation/framework.js';
let ready;
const onScriptLoad = chrome.test.loadScript(scriptUrl);

if (inServiceWorker) {
  ready = onScriptLoad;
} else {
  let onWindowLoad = new Promise((resolve) => {
    window.onload = resolve;
  });
  ready = Promise.all([onWindowLoad, onScriptLoad]);
}

ready.then(async function() {
  const config = await promise(chrome.test.getConfig);
  const port = config.testServer.port;

  // This test verifies the order of events of the first prerendering frame
  // and the prerender activation. The order of the first prerendering
  // navigation is onBeforeNavigate => onCommitted, and the one of the
  // prerendering activation is onBeforeNavigate => onCommitted =>
  // onDOMContentLoaded => onCompleted. DOMContentLoaded is dipatched on
  // activation, because this is to avoid notifying observers about a load event
  // triggered from an inactive RenderFrameHost.
  chrome.test.runTests([
    async function testVerifyPrerenderingFramesCallbackOrder() {
      const urlPrefix =
          `http://a.test:${port}/extensions/api_test/webnavigation/prerendering/`;
      const prerenderTargetUrl = urlPrefix + 'a.html';
      const initiatorUrl = urlPrefix + 'prerender.html';
      let expectedEvents = [
        // events
        {
          label: 'onBeforeNavigate-1',
          event: 'onBeforeNavigate',
          details: {
            documentLifecycle: 'active',
            frameId: 0,
            frameType: 'outermost_frame',
            parentFrameId: -1,
            processId: -1,
            tabId: 0,
            timeStamp: 0,
            url: initiatorUrl
          }
        },
        {
          label: 'onCommitted-1',
          event: 'onCommitted',
          details: {
            documentId: 1,
            documentLifecycle: 'active',
            frameId: 0,
            frameType: 'outermost_frame',
            parentFrameId: -1,
            processId: 0,
            tabId: 0,
            timeStamp: 0,
            transitionQualifiers:[],
            transitionType:"link",
            url: initiatorUrl
          }
        },
        {
          label: 'onDOMContentLoaded-1',
          event: 'onDOMContentLoaded',
          details: {
            documentId: 1,
            documentLifecycle: 'active',
            frameId: 0,
            frameType: 'outermost_frame',
            parentFrameId: -1,
            processId: 0,
            tabId: 0,
            timeStamp: 0,
            url: initiatorUrl
          }
        },
        {
          label: 'onCompleted-1',
          event: 'onCompleted',
          details: {
            documentId: 1,
            documentLifecycle: 'active',
            frameId: 0,
            frameType: 'outermost_frame',
            parentFrameId: -1,
            processId: 0,
            tabId: 0,
            timeStamp: 0,
            url: initiatorUrl
          }
        },
        {
          label: 'onBeforeNavigate-2',
          event: 'onBeforeNavigate',
          details: {
            documentLifecycle: 'prerender',
            frameId: 1,
            frameType: 'outermost_frame',
            parentFrameId: -1,
            processId: -1,
            tabId: 0,
            timeStamp: 0,
            url: prerenderTargetUrl
          }
        },
        {
          label: 'onCommitted-2',
          event: 'onCommitted',
          details: {
            documentId: 2,
            documentLifecycle: 'prerender',
            frameId: 1,
            frameType: 'outermost_frame',
            parentFrameId: -1,
            processId: 1,
            tabId: 0,
            timeStamp: 0,
            transitionQualifiers:[],
            transitionType:"link",
            url: prerenderTargetUrl
          }
        },
        {
          label: 'onBeforeNavigate-3',
          event: 'onBeforeNavigate',
          details: {
            documentLifecycle: 'active',
            frameId: 0,
            frameType: 'outermost_frame',
            parentFrameId: -1,
            processId: -1,
            tabId: 0,
            timeStamp: 0,
            url: prerenderTargetUrl
          }
        },
        {
          label: 'onCommitted-3',
          event: 'onCommitted',
          details: {
            documentId: 2,
            documentLifecycle: 'active',
            frameId: 0,
            frameType: 'outermost_frame',
            parentFrameId: -1,
            processId: 1,
            tabId: 0,
            timeStamp: 0,
            transitionQualifiers:[],
            transitionType:"link",
            url: prerenderTargetUrl
          }
        },
        {
          label: 'onDOMContentLoaded-3',
          event: 'onDOMContentLoaded',
          details: {
            documentId: 2,
            documentLifecycle: 'active',
            frameId: 0,
            frameType: 'outermost_frame',
            parentFrameId: -1,
            processId: 1,
            tabId: 0,
            timeStamp: 0,
            url: prerenderTargetUrl
          }
        },
        {
          label: 'onCompleted-3',
          event: 'onCompleted',
          details: {
            documentId: 2,
            documentLifecycle: 'active',
            frameId: 0,
            frameType: 'outermost_frame',
            parentFrameId: -1,
            processId: 1,
            tabId: 0,
            timeStamp: 0,
            url: prerenderTargetUrl
          }
        },
      ];

      let expectedPrerenderedOrder = ['onBeforeNavigate-2', 'onCommitted-2'];

      if (!inServiceWorker) {
        expectedEvents.push(
          // TODO(crbug.com/40365717): Remove this expectation when the crbug
          // is fixed.
          {
            label: 'onCommitted-2-activation-callback',
            event: 'onCommitted',
            details: {
              documentId: 2,
              documentLifecycle: 'prerender',
              frameId: 1,
              frameType: 'outermost_frame',
              parentFrameId: -1,
              processId: 1,
              tabId: 0,
              timeStamp: 0,
              transitionQualifiers:[],
              transitionType:"link",
              url: prerenderTargetUrl
            }
          });
          expectedPrerenderedOrder.push('onCommitted-2-activation-callback');
      }

      expect(
          expectedEvents,
          [
            // Events
            // *-1: for navigate to the initiator page.
            // *-2: for prerendering.
            // *-3: for prerendering activation.
            ['onBeforeNavigate-1', 'onCommitted-1',
            'onDOMContentLoaded-1', 'onCompleted-1'],
            expectedPrerenderedOrder,
            ['onBeforeNavigate-3', 'onCommitted-3',
            'onDOMContentLoaded-3', 'onCompleted-3'],
            ['onBeforeNavigate-1', 'onBeforeNavigate-2', 'onBeforeNavigate-3']
          ],
          { urls: ['<all_urls>'] },  // filter
          []);

      const activationCallback = details => {
        chrome.test.assertEq('prerender', details.documentLifecycle);
        chrome.tabs.executeScript({
          code: `location.href = '${prerenderTargetUrl}';`,
          runAt: 'document_idle'
        });
        chrome.webNavigation.onCommitted.removeListener(activationCallback);
      };

      chrome.webNavigation.onCommitted.addListener(
        activationCallback, {url: [{pathContains: '/a.html'}]});

      // Navigate to a page that initiates prerendering "a.html".
      let tab = await promise(chrome.tabs.create, {"url": initiatorUrl});
    },
  ]);
});