chromium/chrome/test/data/extensions/api_test/executescript/removed_frames/test.js

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

// A simple helper to keep track of how many responses are received, and fail
// if it receives a "fail" message. It's unfortunate that these are never
// cleaned up, but, realistically, doesn't matter.
function ResponseCounter() {
  this.responsesReceived = 0;
  this.expectedResponses = -1;
  var listenerFunction = function(request, sender, sendResponse) {
    if (request == 'fail') {
      chrome.test.fail('Received bad message');
    } else {
      chrome.test.assertEq('complete', request);
      ++this.responsesReceived;
      chrome.test.assertTrue(this.responsesReceived <= this.expectedResponses);
      if (this.responsesReceived == this.expectedResponses &&
          this.doneCallback) {
        this.removeListener();
        this.doneCallback();
      }
    }
  }.bind(this);
  this.removeListener = function() {
    chrome.runtime.onMessage.removeListener(listenerFunction);
  }
  chrome.runtime.onMessage.addListener(listenerFunction);
}

var waitForCommittedAndRun = function(functionToRun, numCommits, url) {
  var committedCount = 0;
  var counter = new ResponseCounter();  // Every test gets a counter.
  var onCommitted = function(details) {
    if (++committedCount == numCommits) {
      functionToRun(counter, details.tabId);
      chrome.webNavigation.onCommitted.removeListener(onCommitted);
    }
  };
  chrome.webNavigation.onCommitted.addListener(onCommitted);
  chrome.tabs.create({url: url});
};

chrome.test.getConfig(function(config) {
  var url = 'http://a.com:' + config.testServer.port +
            '/extensions/api_test/executescript/removed_frames/outer.html';
  // Regression tests for crbug.com/500574.
  chrome.test.runTests([
   function testInjectAndDeleteIframeFromMainFrame() {
      waitForCommittedAndRun(injectAndDeleteIframeFromMainFrame, 2, url);
    },
    function testInjectAndDeleteIframeFromIframe() {
      waitForCommittedAndRun(injectAndDeleteIframeFromIframe, 2, url);
    }
  ]);
});

function injectAndDeleteIframeFromMainFrame(counter, tabId) {
  // Inject code into each frame. If it's the parent frame, it removes the child
  // frame from the DOM (invalidating it). The child frame's code shouldn't
  // finish executing, since it's been removed.
  counter.expectedResponses = 1;
  counter.doneCallback = function() {
    chrome.test.assertEq(1, counter.responsesReceived);
    chrome.test.succeed();
  };
  var injectFrameCode = [
      'if (window === window.top) {',
      '  iframe = document.getElementsByTagName("iframe")[0];',
      '  iframe.parentElement.removeChild(iframe);',
      '  chrome.runtime.sendMessage("complete");',
      '}'
  ].join('\n');
  chrome.tabs.executeScript(
      tabId,
      {code: injectFrameCode, allFrames: true, runAt: 'document_idle'});
};

function injectAndDeleteIframeFromIframe(counter, tabId) {
  // Inject code into each frame. Have the child frame remove itself, deleting
  // the frame while it's still executing.
  counter.expectedResponses = 2;
  counter.doneCallback = function() {
    chrome.test.assertEq(2, counter.responsesReceived);
    chrome.test.succeed();
  };
  var injectFrameCode = [
      'if (window.self !== window.top) {',
      '  var iframe = window.top.document.getElementsByTagName("iframe")[0];',
      '  if (!iframe || iframe.contentWindow !== window)',
      '    chrome.runtime.sendMessage("fail");',
      '  else',
      '    window.top.document.body.removeChild(iframe);',
      '} else {',
      '  chrome.runtime.sendMessage("complete");',
      '}'
  ].join('\n');
  // We also use two "executeScript" calls here so that we have a pending script
  // execution on a frame that gets deleted.
  chrome.tabs.executeScript(
      tabId,
      {code: injectFrameCode, allFrames: true, runAt: 'document_idle'});
  chrome.tabs.executeScript(
      tabId,
      {code: injectFrameCode, allFrames: true, runAt: 'document_idle'});
}