blob: bf90db45734fc3329e2e97d8378cd71236fe10cd [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
if (top === window) {
testTop();
} else if (!parent.location.search.includes('end') ||
window.didRunAtDocumentEnd) {
testChild();
}
function testTop() {
var testMessage = {};
function reportFrames() {
testMessage.frameCount = frames.length;
testMessage.frameHTML = window.frameHTML;
chrome.runtime.sendMessage(testMessage);
}
if (window.frameHTML) { // Set by child frame...
// about:blank frames are synchronously parsed, so their document_end script
// injection happens before the main frame's injection.
var expectChildBeforeMain = location.search.includes('?blankend');
if (!expectChildBeforeMain) {
// Add a message to the test notification to cause the test to fail,
// with some useful information for diagnostics.
testMessage.warning = 'Content script in child frame was executed ' +
'before the main frame\'s content script!';
}
reportFrames();
} else {
window.frameHTML = '(not set)';
window.onmessage = function(event) {
chrome.test.assertEq('toBeRemoved', event.data);
reportFrames();
};
}
}
function testChild() {
var TEST_HOST = parent.location.hostname;
if (TEST_HOST === 'synchronous') {
doRemove();
} else if (TEST_HOST === 'microtask') {
Promise.resolve().then(doRemove);
} else if (TEST_HOST === 'macrotask') {
setTimeout(doRemove, 0);
} else if (TEST_HOST.startsWith('domnodeinserted')) {
removeOnEvent('DOMNodeInserted');
} else if (TEST_HOST.startsWith('domsubtreemodified')) {
removeOnEvent('DOMSubtreeModified');
} else {
console.error('Unexpected test: ' + TEST_HOST);
chrome.test.fail();
}
function doRemove() {
parent.frameHTML = document.documentElement.outerHTML || '(no outerHTML)';
parent.postMessage('toBeRemoved', '*');
// frameElement = <iframe> element in parent document.
frameElement.remove();
}
function removeOnEvent(eventName) {
var expected = parseInt(TEST_HOST.match(/\d+/)[0]);
document.addEventListener(eventName, function() {
// Synchronously remove the frame in the mutation event.
if (--expected === 0)
doRemove();
});
// Fallback in case the mutation events are not triggered.
new Promise(function(resolve) {
// The window.onload event signals that the document and its resources
// have finished loading, so we don't expect any other parser-initiated
// DOM mutations after that point.
if (document.readyState === 'complete')
resolve();
else
window.addEventListener('load', resolve);
}).then(function() {
if (expected > 0) {
expected = 0;
// Print this message to make it clear that the expected condition
// (mutation event |eventName| triggered XXX times) did not happen.
console.log('Mutation condition not triggered: ' + TEST_HOST);
doRemove();
}
});
}
}