chromium/chrome/test/data/extensions/api_test/declarative_net_request/fenced_frames/background.js

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

var expectedCallback;
var tab;

// Navigates to |url| and invokes |callback| when a rule has been queued.
function navigateTab(url, callback) {
  expectedCallback = callback;
  chrome.tabs.update(tab.id, {url: url});
}

var matchedRules = [];
var documentIds = [];
var nextDocumentId = 1;

var onRuleMatchedDebugCallback = (rule) => {
  matchedRules.push(rule);
  expectedCallback(tab);
};

var testServerPort;
function getServerURL(host) {
  if (!testServerPort)
    throw new Error('Called getServerURL outside of runTests.');
  return `https://${host}:${testServerPort}`;
}

function resetMatchedRules() {
  matchedRules = [];
}

function verifyExpectedRuleInfo(expectedRuleInfo) {
  const matchedRule = matchedRules[0];

  // The request ID may not be known but should be populated.
  chrome.test.assertTrue(matchedRule.request.hasOwnProperty('requestId'));
  delete matchedRule.request.requestId;

  // Since the documentId/parentDocumentId is a unique random identifier it is
  // not useful to tests. Normalize them so that test cases can assert
  // against a fixed number.
  if ('parentDocumentId' in matchedRule.request) {
    if (documentIds[matchedRule.request.parentDocumentId] === undefined) {
      documentIds[matchedRule.request.parentDocumentId] = nextDocumentId++;
    }
    matchedRule.request.parentDocumentId =
      documentIds[matchedRule.request.parentDocumentId];
  }
  if ('documentId' in matchedRule.request) {
    if (documentIds[matchedRule.request.documentId] === undefined) {
      documentIds[matchedRule.request.documentId] = nextDocumentId++;
    }
    matchedRule.request.documentId =
      documentIds[matchedRule.request.documentId];
  }

  chrome.test.assertEq(expectedRuleInfo, matchedRule);
}

// Opaque initiators serialize to "null".
const kOpaqueInitiator = "null";

var tests = [
  function setup() {
    chrome.declarativeNetRequest.onRuleMatchedDebug.addListener(
        onRuleMatchedDebugCallback);

    // Wait for a round trip to ensure the listener is properly added in the
    // browser process before initiating any requests.
    chrome.test.waitForRoundTrip('msg', chrome.test.succeed);
  },

  // Makes sure block rules apply for fenced frames.
  function testBlockRule() {
    resetMatchedRules();

    const baseUrl = getServerURL('a.test') +
          '/extensions/api_test/declarative_net_request/fenced_frames/';
    const url = baseUrl + 'blocked.html';
    const fencedFrameUrl = baseUrl + 'blocked_fenced_frame.html';
    navigateTab(url, (tab) => {
      const expectedRuleInfo = {
        request: {
          initiator: kOpaqueInitiator,
          method: 'GET',
          frameId: 5,
          documentLifecycle: 'active',
          frameType: 'fenced_frame',
          parentDocumentId: 1,
          parentFrameId: 0,
          type: 'sub_frame',
          tabId: tab.id,
          url: fencedFrameUrl
        },
        rule: {ruleId: 1, rulesetId: 'rules'}
      };
      verifyExpectedRuleInfo(expectedRuleInfo);

      const getFencedFrameWidth =
        '(async function() {' +
        '  while(true) { ' +
        '    await new Promise(requestAnimationFrame);' +
        '    let width =' +
        '      document.getElementsByTagName("fencedframe")[0].clientWidth;' +
        '    if (width == 0) {' +
        '      chrome.runtime.sendMessage({width: width});' +
        '      break;' +
        '    }' +
        '  }' +
        '})()';

      chrome.runtime.onMessage.addListener(results => {
        // Ensure the clientWidth is 0 indicating
        // the frame has no layout size and was
        // collapsed correctly.
        chrome.test.assertEq(0, results.width);
        chrome.test.succeed();
      });
      chrome.tabs.executeScript(tab.id, {frameId: 0,
                                         code: getFencedFrameWidth});
    });
  },

  // Makes sure rule 4 for subframes applies and not rule 2 for main frames
  // or rule 3 for thirdParty domains.
  function testAllowRule() {
    resetMatchedRules();

    const baseUrl = getServerURL('a.test') +
          '/extensions/api_test/declarative_net_request/fenced_frames/';
    const url = baseUrl + 'allow.html';
    const fencedFrameUrl = baseUrl + 'allowed_fenced_frame.html';
    navigateTab(url, (tab) => {
      const expectedRuleInfo = {
        request: {
          initiator: kOpaqueInitiator,
          method: 'GET',
          frameId: 7,
          documentLifecycle: 'active',
          frameType: 'fenced_frame',
          parentDocumentId: 2,
          parentFrameId: 0,
          type: 'sub_frame',
          tabId: tab.id,
          url: fencedFrameUrl
        },
        rule: {ruleId: 4, rulesetId: 'rules'}
      };
      verifyExpectedRuleInfo(expectedRuleInfo);
      chrome.test.succeed();
    });
  },

  // Uses rule 5 to allow a subresource inside the fenced frame.
  function testAllowResourceRule() {
    resetMatchedRules();

    const baseUrl = getServerURL('a.test') +
      '/extensions/api_test/declarative_net_request/fenced_frames/';
    const url = baseUrl + 'resource1.html';
    const matchedImageUrl = baseUrl + 'icon1.png';
    navigateTab(url, (tab) => {
      const expectedRuleInfo = {
        request: {
          initiator: getServerURL('a.test'),
          method: 'GET',
          documentId: 4,
          frameId: 9,
          documentLifecycle: 'active',
          frameType: 'fenced_frame',
          parentDocumentId: 3,
          parentFrameId: 0,
          type: 'image',
          tabId: tab.id,
          url: matchedImageUrl
        },
        rule: { ruleId: 5, rulesetId: 'rules' }
      };
      verifyExpectedRuleInfo(expectedRuleInfo);
      chrome.test.succeed();
    });
  },

  // Uses rule 6 to block a subresource inside the fenced frame.
  function testBlockResourceRule() {
    resetMatchedRules();

    const baseUrl = getServerURL('a.test') +
      '/extensions/api_test/declarative_net_request/fenced_frames/';
    const url = baseUrl + 'resource2.html';
    const matchedImageUrl = baseUrl + 'icon2.png';
    navigateTab(url, (tab) => {
      const expectedRuleInfo = {
        request: {
          initiator: getServerURL('a.test'),
          method: 'GET',
          documentId: 6,
          frameId: 11,
          documentLifecycle: 'active',
          frameType: 'fenced_frame',
          parentDocumentId: 5,
          parentFrameId: 0,
          type: 'image',
          tabId: tab.id,
          url: matchedImageUrl
        },
        rule: { ruleId: 6, rulesetId: 'rules' }
      };
      verifyExpectedRuleInfo(expectedRuleInfo);
      chrome.test.succeed();
    });
  }
];

chrome.test.getConfig(async (config) => {
  testServerPort = config.testServer.port;
  tab = await new Promise(function(resolve, reject) {
    chrome.tabs.create({"url": "about:blank"}, (value) => {
      resolve(value);
    });
  });

  chrome.test.runTests(tests);
});