2 <!-- This page can create whatever iframe structure you want, across whatever
3 sites you want. This is useful for testing site isolation.
5 Example usage in a browsertest, explained:
8 test_server()->GetURL("a.com", "/cross_site_iframe_factory.html?a(b(c,d))");
10 When you navigate to the above URL, the outer document (on a.com) will create a
13 <iframe src="http://b.com:1234/cross_site_iframe_factory.html?b(c(),d())">
15 Inside of which, then, are created the two leaf iframes:
17 <iframe src="http://c.com:1234/cross_site_iframe_factory.html?c()">
18 <iframe src="http://d.com:1234/cross_site_iframe_factory.html?d()">
20 To make this page work, your browsertest needs a MockHostResolver, like:
22 void SetUpOnMainThread() override {
23 host_resolver()->AddRule("*", "127.0.0.1");
24 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
27 You can play around with the arguments by loading this page via file://, but
28 you probably won't get the same process behavior as if you loaded via http. -->
30 <title>Cross-site iframe factory
</title>
33 font-family: Sans-Serif;
41 box-shadow:
2px
2px
2px #
888888;
46 <h2 id='siteNameHeading'
></h2>
49 <script src='tree_parser_util.js'
></script>
50 <script type='text/javascript'
>
53 * Determines a random pastel-ish color from the first character of a string.
55 function pastelColorForFirstCharacter(seedString
, lightness
) {
56 // Map the first character to an index. This could be negative, we don't
58 var index
= seedString
.charCodeAt(0) - 'a'.charCodeAt(0);
60 // If the first character is 'a', this will the the starting color.
61 var hueOfA
= 200; // Spoiler alert: it's blue.
63 // Color palette generation articles suggest that spinning the hue wheel by
64 // the golden ratio yields a magically nice color distribution. Something
65 // about sunflower seeds. I am skeptical of the rigor of that claim (probably
66 // any irrational number at a slight offset from 2/3 would do) but it does
68 var phi
= 2 / (1 + Math
.pow(5, .5));
69 var hue
= Math
.round((360 * index
* phi
+ hueOfA
) % 360);
70 return 'hsl(' + hue
+ ', 60%, ' + Math
.round(100 * lightness
) + '%)';
73 function backgroundColorForSite(site
) {
75 return pastelColorForFirstCharacter(site
, .75);
78 function borderColorForSite(site
) {
79 // Darker color in the same hue has the background.
80 return pastelColorForFirstCharacter(site
, .32);
84 * Adds ".com" to an argument if it doesn't already have a top level domain.
85 * This cuts down on noise in the query string, letting you use single-letter
88 function canonicalizeSite(siteString
) {
89 if (siteString
.indexOf('.') == -1)
90 return siteString
+ '.com';
95 * Simple recursive layout heuristic, since frames can't size themselves.
96 * This scribbles .layoutX and .layoutY properties into |tree|.
98 function layout(tree
) {
99 // Step 1: layout children.
100 var numFrames
= tree
.children
.length
;
101 for (var i
= 0; i
< numFrames
; i
++) {
102 layout(tree
.children
[i
]);
105 // Step 2: find largest child.
106 var largestChildX
= 0;
107 var largestChildY
= 0;
108 for (var i
= 0; i
< numFrames
; i
++) {
109 largestChildX
= Math
.max(largestChildX
, tree
.children
[i
].layoutX
);
110 largestChildY
= Math
.max(largestChildY
, tree
.children
[i
].layoutY
);
113 // Step 3: Tweakable control parameters.
114 var minX
= 110; // Should be wide enough to fit a decent sized domain.
115 var minY
= 110; // Could be less, but squares look nice.
116 var extraYPerLevel
= 50; // Needs to be tall enough to fit a line of text.
117 var extraXPerLevel
= 50; // Could be less, but squares look nice.
119 // Account for padding around each <iframe>.
123 // Step 4: Assume a gridSizeX-by-gridSizeY layout that's big enough to fit if
124 // all children were the size of the largest one.
125 var gridSizeX
= Math
.ceil(Math
.sqrt(numFrames
));
126 var gridSizeY
= Math
.round(Math
.sqrt(numFrames
));
127 tree
.layoutX
= Math
.max(gridSizeX
* largestChildX
+ extraXPerLevel
, minX
);
128 tree
.layoutY
= Math
.max(gridSizeY
* largestChildY
+ extraYPerLevel
, minY
);
132 var goCrossSite
= !window
.location
.protocol
.startsWith('file');
133 var queryString
= decodeURIComponent(window
.location
.search
.substring(1));
134 var frameTree
= TreeParserUtil
.parse(queryString
);
135 var currentSite
= canonicalizeSite(frameTree
.value
);
137 // Apply style to the current document.
138 document
.getElementById('siteNameHeading').appendChild(
139 document
.createTextNode(currentSite
));
140 document
.body
.style
.backgroundColor
= backgroundColorForSite(currentSite
);
142 // Determine how big the children should be (using a very rough heuristic).
145 for (var i
= 0; i
< frameTree
.children
.length
; i
++) {
146 // Compute the URL for this iframe .
147 var site
= canonicalizeSite(frameTree
.children
[i
].value
);
148 var subtreeString
= TreeParserUtil
.flatten(frameTree
.children
[i
]);
150 url
+= window
.location
.protocol
+ '//'; // scheme (preserved)
151 url
+= goCrossSite
? site
: window
.location
.host
; // host
152 if (window
.location
.port
)
153 url
+= ':' + window
.location
.port
; // port (preserved)
154 url
+= window
.location
.pathname
; // path (preserved)
155 url
+= '?' + encodeURIComponent(subtreeString
); // query
157 // Add the iframe to the document.
158 var iframe
= document
.createElement('iframe');
160 iframe
.id
= "child-" + i
;
161 iframe
.style
.borderColor
= borderColorForSite(site
);
162 iframe
.width
= frameTree
.children
[i
].layoutX
;
163 iframe
.height
= frameTree
.children
[i
].layoutY
;
164 document
.body
.appendChild(iframe
);