blob: e4e808b041461eda169f7f0d7944ba1233d43be1 [file] [log] [blame]
<html>
<!-- This page can create whatever iframe structure you want, across whatever
sites you want. This is useful for testing site isolation.
Example usage in a browsertest, explained:
GURL url = embedded_test_server()->
GetURL("a.com", "/cross_site_iframe_factory.html?a(b(c,d))");
When you navigate to the above URL, the outer document (on a.com) will create a
single iframe:
<iframe src="http://b.com:1234/cross_site_iframe_factory.html?b(c(),d())">
Inside of which, then, are created the two leaf iframes:
<iframe src="http://c.com:1234/cross_site_iframe_factory.html?c()">
<iframe src="http://d.com:1234/cross_site_iframe_factory.html?d()">
To make this page work, your browsertest needs a MockHostResolver, like:
void SetUpOnMainThread() override {
host_resolver()->AddRule("*", "127.0.0.1");
ASSERT_TRUE(embedded_test_server()->Start());
}
You can play around with the arguments by loading this page via file://, but
you probably won't get the same process behavior as if you loaded via http. -->
<head>
<title>Cross-site iframe factory</title>
<style>
body {
font-family: Sans-Serif;
text-align: center;
}
iframe {
border-radius: 7px;
border-style: solid;
vertical-align: top;
margin: 2px;
box-shadow: 2px 2px 2px #888888;
}
</style>
</head>
<body>
<h2 id='siteNameHeading'></h2>
<script src='tree_parser_util.js'></script>
<script type='text/javascript'>
/**
* Determines a random pastel-ish color from the first character of a string.
*/
function pastelColorForFirstCharacter(seedString, lightness) {
// Map the first character to an index. This could be negative, we don't
// really care.
var index = seedString.charCodeAt(0) - 'a'.charCodeAt(0);
// If the first character is 'a', this will the the starting color.
var hueOfA = 200; // Spoiler alert: it's blue.
// Color palette generation articles suggest that spinning the hue wheel by
// the golden ratio yields a magically nice color distribution. Something
// about sunflower seeds. I am skeptical of the rigor of that claim (probably
// any irrational number at a slight offset from 2/3 would do) but it does
// look pretty.
var phi = 2 / (1 + Math.pow(5, .5));
var hue = Math.round((360 * index * phi + hueOfA) % 360);
return 'hsl(' + hue + ', 60%, ' + Math.round(100 * lightness) + '%)';
}
function backgroundColorForSite(site) {
// Light pastel.
return pastelColorForFirstCharacter(site, .75);
}
function borderColorForSite(site) {
// Darker color in the same hue has the background.
return pastelColorForFirstCharacter(site, .32);
}
/**
* Adds ".com" to an argument if it doesn't already have a top level domain.
* This cuts down on noise in the query string, letting you use single-letter
* names.
*/
function canonicalizeSite(siteString) {
if (siteString.indexOf('.') == -1)
return siteString + '.com';
return siteString;
}
/**
* Simple recursive layout heuristic, since frames can't size themselves.
* This scribbles .layoutX and .layoutY properties into |tree|.
*/
function layout(tree) {
// Step 1: layout children.
var numFrames = tree.children.length;
for (var i = 0; i < numFrames; i++) {
layout(tree.children[i]);
}
// Step 2: find largest child.
var largestChildX = 0;
var largestChildY = 0;
for (var i = 0; i < numFrames; i++) {
largestChildX = Math.max(largestChildX, tree.children[i].layoutX);
largestChildY = Math.max(largestChildY, tree.children[i].layoutY);
}
// Step 3: Tweakable control parameters.
var minX = 110; // Should be wide enough to fit a decent sized domain.
var minY = 110; // Could be less, but squares look nice.
var extraYPerLevel = 50; // Needs to be tall enough to fit a line of text.
var extraXPerLevel = 50; // Could be less, but squares look nice.
// Account for padding around each <iframe>.
largestChildX += 30;
largestChildY += 30;
// Step 4: Assume a gridSizeX-by-gridSizeY layout that's big enough to fit if
// all children were the size of the largest one.
var gridSizeX = Math.ceil(Math.sqrt(numFrames));
var gridSizeY = Math.round(Math.sqrt(numFrames));
tree.layoutX = Math.max(gridSizeX * largestChildX + extraXPerLevel, minX);
tree.layoutY = Math.max(gridSizeY * largestChildY + extraYPerLevel, minY);
}
function main() {
var goCrossSite = !window.location.protocol.startsWith('file');
var queryString = decodeURIComponent(window.location.search.substring(1));
var frameTree = TreeParserUtil.parse(queryString);
var currentSite = canonicalizeSite(frameTree.value);
// Apply style to the current document.
document.getElementById('siteNameHeading').appendChild(
document.createTextNode(currentSite));
document.body.style.backgroundColor = backgroundColorForSite(currentSite);
// Determine how big the children should be (using a very rough heuristic).
layout(frameTree);
for (var i = 0; i < frameTree.children.length; i++) {
// Compute the URL for this iframe .
var site = canonicalizeSite(frameTree.children[i].value);
var subtreeString = TreeParserUtil.flatten(frameTree.children[i]);
var url = '';
url += window.location.protocol + '//'; // scheme (preserved)
url += goCrossSite ? site : window.location.host; // host
if (window.location.port)
url += ':' + window.location.port; // port (preserved)
url += window.location.pathname; // path (preserved)
url += '?' + encodeURIComponent(subtreeString); // query
// Add the iframe to the document.
var iframe = document.createElement('iframe');
iframe.src = url;
iframe.id = "child-" + i;
iframe.style.borderColor = borderColorForSite(site);
iframe.width = frameTree.children[i].layoutX;
iframe.height = frameTree.children[i].layoutY;
document.body.appendChild(iframe);
}
}
main();
</script>
</body></html>