description: copy of missing files in content/test/data, taken from upstream git

This is just
cp chromium-git/content/test/data/*{html,mojom,js} \
   chromium-debian/content/test/data/

Upstream bug report about the missing files:
https://bugs.chromium.org/p/chromium/issues/detail?id=1293630
I'm not sure how much of them are actually needed, but it's easier to
just copy all of them for now. Hopefully this will be fixed upstream
shortly.

This copy is from chromium 114.0.5735.45.

--- /dev/null
+++ b/content/test/data/web_ui_test.test-mojom
@@ -0,0 +1,13 @@
+// Copyright 2020 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module content.mojom;
+
+import "content/test/data/web_ui_test_types.test-mojom";
+import "url/mojom/url.mojom";
+
+interface WebUIMojoTestCache {
+  Put(url.mojom.Url url, string contents);
+  GetAll() => (array<CacheItem> items);
+};
--- /dev/null
+++ b/content/test/data/web_ui_test_types.test-mojom
@@ -0,0 +1,16 @@
+// Copyright 2020 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module content.mojom;
+
+import "url/mojom/url.mojom";
+
+// This struct is imported by web_ui_test.test-mojom and is used to provide
+// test coverage of non-shared WebUI mojom JS modules importing other
+// non-shared WebUI mojom JS modules.
+struct CacheItem {
+  url.mojom.Url url;
+  string contents;
+};
+
--- /dev/null
+++ b/content/test/data/mojo_bindings_web_test.test-mojom
@@ -0,0 +1,86 @@
+// Copyright 2018 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module mojo_bindings_test.mojom;
+
+// NOTE: This import exists to exercise automatic dependency loading in
+// generated JS modules.
+import "content/test/data/mojo_bindings_web_test_types.test-mojom";
+
+// An interface whose definition covers various types of message signatures in
+// order to exercise the lite JS mojom bindings.
+interface TestMessageTarget {
+  enum NestedEnum {
+    kFoo,
+    kBar,
+  };
+
+  // Zero arguments, no reply.
+  Poke();
+
+  // Zero-argument request, zero-argument reply.
+  Ping() => ();
+
+  // Request and reply both with arguments.
+  Repeat(string? message, array<int32>? numbers)
+      => (string? message, array<int32>? numbers);
+
+  Echo(NestedEnum nested) => (NestedEnum nested);
+
+  Deconstruct(TestStruct test_struct)
+       => (int32 x, int32 y,
+           // Using TestStruct.StructEnum causes a compile failure. Use
+           // int32 instead.
+           // TODO(crbug.com/1011660): Change |z| to TestStruct.StructEnum.
+           int32 z);
+
+  Flatten(array<TestStruct> values) => (array<int32> values);
+  FlattenUnions(array<TestUnion> unions) => (array<int32> x, array<int32> s);
+
+  RequestSubinterface(pending_receiver<Subinterface> receiver,
+                      pending_remote<SubinterfaceClient> client);
+};
+
+interface Subinterface {
+  Push(int32 value);
+  Flush();
+};
+
+interface SubinterfaceClient {
+  DidFlush(array<int32> values);
+};
+
+struct StructVersionTest {
+  string a;
+  string b;
+  [MinVersion=1] string? c;
+  [MinVersion=2] string? d;
+};
+
+interface InterfaceVersionTest {
+  Foo(int32 x, [MinVersion=1] int32 y) => (int32 z, [MinVersion=1] int32 w);
+};
+
+interface Counter {
+  // Two different varieties of observer addition to exercise sending remotes
+  // and receiving receivers.
+  AddObserver(pending_associated_remote<CounterObserver> observer);
+  AddNewObserver() => (pending_associated_receiver<CounterObserver> receiver);
+  RemoveAllObservers();
+
+  // Two different varieties of cloning to exercise sending receivers and
+  // receiving remotes.
+  Clone(pending_associated_receiver<Counter> receiver);
+  CloneToNewRemote() => (pending_associated_remote<Counter> remote);
+
+  // Increments the counter, notifies all observers, then replies. Because
+  // observers are associated with this interface, they are therefore guaranteed
+  // to observe an increment before the caller observes its corresponding reply.
+  Increment() => (int32 count);
+};
+
+interface CounterObserver {
+  OnCountChanged(int32 count);
+  OnCloneDisconnected();
+};
--- /dev/null
+++ b/content/test/data/mojo_bindings_web_test_types.test-mojom
@@ -0,0 +1,25 @@
+// Copyright 2020 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module mojo_bindings_test.mojom;
+
+struct TestStruct {
+  enum StructEnum {
+    kZero = 0,
+    kOne  = 1,
+  };
+
+  const int32 kStructConst = 2;
+
+  int32 x;
+  int32 y = kStructConst;
+  StructEnum z = StructEnum.kOne;
+  const bool isValid = false;
+};
+
+union TestUnion {
+  int32 x;
+  TestStruct s;
+};
+
--- /dev/null
+++ b/content/test/data/lite_js_test.mojom
@@ -0,0 +1,67 @@
+// Copyright 2018 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module lite_js_test.mojom;
+
+struct TestStruct {
+  enum StructEnum {
+    kZero = 0,
+    kOne  = 1,
+  };
+
+  const int32 kStructConst = 2;
+
+  int32 x;
+  int32 y = kStructConst;
+  StructEnum z = StructEnum.kOne;
+  const bool isValid = false;
+};
+
+union TestUnion {
+  int32 x;
+  TestStruct s;
+};
+
+// An interface whose definition covers various types of message signatures in
+// order to exercise the lite JS mojom bindings.
+interface TestMessageTarget {
+  enum NestedEnum {
+    kFoo,
+    kBar,
+  };
+
+  // Zero arguments, no reply.
+  Poke();
+
+  // Zero-argument request, zero-argument reply.
+  Ping() => ();
+
+  // Request and reply both with arguments.
+  Repeat(string? message, array<int32>? numbers)
+      => (string? message, array<int32>? numbers);
+
+  Echo(NestedEnum nested) => (NestedEnum nested);
+
+  Deconstruct(TestStruct test_struct)
+       => (int32 x, int32 y,
+           // Using TestStruct.StructEnum causes a compile failure. Use
+           // int32 instead.
+           // TODO(crbug.com/1011660): Change |z| to TestStruct.StructEnum.
+           int32 z);
+
+  Flatten(array<TestStruct> values) => (array<int32> values);
+  FlattenUnions(array<TestUnion> unions) => (array<int32> x, array<int32> s);
+
+  RequestSubinterface(pending_receiver<Subinterface> receiver,
+                      pending_remote<SubinterfaceClient> client);
+};
+
+interface Subinterface {
+  Push(int32 value);
+  Flush();
+};
+
+interface SubinterfaceClient {
+  DidFlush(array<int32> values);
+};
--- /dev/null
+++ b/content/test/data/mojo_web_test_helper_test.mojom
@@ -0,0 +1,10 @@
+// Copyright 2014 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module content.mojom;
+
+interface MojoWebTestHelper {
+  Reverse(string message) => (string reversed);
+};
+
--- /dev/null
+++ b/content/test/data/accept-header-iframe.html
@@ -0,0 +1,7 @@
+<html>
+<body>
+<script>
+var request = new XMLHttpRequest();
+</script>
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/accept-header.html
@@ -0,0 +1,46 @@
+<html>
+<head>
+<script src="test.js"></script>
+<link rel="stylesheet"  href="/test.css">
+<link rel="icon" href="/test.ico" type="image/x-icon"/>
+<link rel="manifest" href="/manifest">
+<iframe src="/iframe.html"></iframe>
+<iframe src="/accept-header-iframe.html"></iframe>
+<img src="/image.gif">
+</head>
+<style>
+@font-face {
+  font-family: "TestFontFamily";
+  src: url("/font.otf");
+}
+p.customfont {
+    font-family: "TestFontFamily";
+}
+</style>
+<video src="/media.mp4" autoplay controls></video>
+<body>Hello World</body>
+<a ping="/ping" href="javascript:void(0)" id="ping">
+<p class="customfont">font</p>
+<link rel="prefetch" href="/prefetch">
+
+<script>
+var request = new XMLHttpRequest();
+request.open('GET', '/xhr', true);
+request.send(null);
+
+request = new XMLHttpRequest();
+request.open('GET', '/xhr_with_accept_header', true);
+request.setRequestHeader('Accept', 'custom/type');
+request.send(null);
+
+document.getElementById('ping').click();
+
+var worker = new Worker('/worker.js');
+if ("SharedWorker" in window)
+  var shared_worker = new SharedWorker('/shared_worker.js');
+
+navigator.serviceWorker.register('/service_worker.js')
+.catch(function(error) {});
+
+</script>
+</html>
--- /dev/null
+++ b/content/test/data/allowed_frames.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<head>
+</head>
+<body>
+  <!-- These frames should not have any other attributes, other than an id and
+    those attributes required for testing feature policy creation and
+    replication. See https://crbug.com/705658 -->
+  <iframe id="child-0"></iframe>
+  <iframe id="child-1" allow></iframe>
+  <iframe id="child-2" allow="fullscreen; geolocation" src="/cross-site/bar.com/title1.html"></iframe>
+  <iframe id="child-3" allow="fullscreen; geolocation" src="title1.html"></iframe>
+</body>
+</html>
+
--- /dev/null
+++ b/content/test/data/body_overflow_hidden.html
@@ -0,0 +1,17 @@
+<html>
+<head>
+  <meta name='viewport' content='width=device-width, minimum-scale=1'>
+</head>
+<style>
+body {
+  height: 10000px;
+  overflow: hidden;
+}
+</style>
+<div style="width:100px; height:300px; overflow: scroll;">
+  <div style="width:150px; height:250px; overflow: scroll">
+    <div style="width:300px; height:200px; overflow: scroll">
+    </div>
+  </div>
+</div>
+</html>
--- /dev/null
+++ b/content/test/data/click-nocontent-link.html
@@ -0,0 +1,73 @@
+<html>
+
+ <head><title>Click nocontent link</title>
+ <script>
+  function simulateClick(target) {
+    var evt = document.createEvent("MouseEvents");
+    evt.initMouseEvent("click", true, true, window,
+                       0, 0, 0, 0, 0, false, false,
+                       false, false, 0, null);
+
+    return target.dispatchEvent(evt);
+  }
+
+  function clickNoContentTargetedLink() {
+    return simulateClick(document.getElementById("nocontent_targeted_link"));
+  }
+
+  function clickNoContentScriptedTargetedLink() {
+    return simulateClick(document.getElementById(
+        "nocontent_scripted_targeted_link"));
+  }
+
+  function getNewWindowReference() {
+    // Grab a reference to the existing foo window, but don't otherwise access
+    // it in any way.
+    var w = window.open("", "foo");
+    // w is intentionally unused after this point to avoid triggering a call to
+    // DidAccessInitialDocument().
+
+    // Modify the title to give the test a notification to listen for. Use a
+    // timeout so that any DidAccessInitialDocument() notification arrives
+    // first. Note that this intentionally uses |window| instead of |w| to avoid
+    // test boilerplate from triggering calls to DidAccessInitialDocument().
+    setTimeout(function() { window.document.title = "Modified Title"; });
+  }
+
+  function modifyNewWindow() {
+    // Grab a reference to the existing foo window and modify its content.
+    var w = window.open("", "foo");
+    w.document.body.innerHTML += "Modified";
+
+    // Modify the title to give the test a notification to listen for. Use a
+    // timeout so that any DidAccessInitialDocument() notification arrives
+    // first. Note that this intentionally uses |window| instead of |w| to avoid
+    // test boilerplate from triggering calls to DidAccessInitialDocument().
+    setTimeout(function() { window.document.title = "Modified Title"; });
+  }
+
+  function modifyNewWindowWithDocumentOpen() {
+    // Grab a reference to the existing foo window and modify its content.
+    var w = window.open("", "foo");
+    w.document.open();
+    w.document.write("Modified");
+    w.document.close();
+
+    // Modify the title to give the test a notification to listen for. Use a
+    // timeout so that any DidAccessInitialDocument() notification arrives
+    // first. Note that this intentionally uses |window| instead of |w| to avoid
+    // test boilerplate from triggering calls to DidAccessInitialDocument().
+    setTimeout(function() { window.document.title = "Modified Title"; });
+  }
+ </script>
+ </head>
+
+<a href="/nocontent" id="nocontent_targeted_link" target="foo">
+  /nocontent target=foo</a><br>
+<button onclick="modifyNewWindow()">Modify New Window</button><br>
+
+<a href="/nocontent" id="nocontent_scripted_targeted_link" target="foo"
+   onclick="modifyNewWindow()">
+  /nocontent scripted target=foo</a><br>
+
+</html>
--- /dev/null
+++ b/content/test/data/click-noreferrer-links.html
@@ -0,0 +1,163 @@
+<html>
+
+ <head><title>Click noreferrer links</title>
+ <script>
+  function setOriginForLinks(baseUrl) {
+    var links = [
+        'noref_and_tblank_link',
+        'noopener_and_tblank_link',
+        'tblank_link',
+        'noref_link',
+        'noopener_link'
+    ];
+    links.forEach(function(linkId) {
+      link = document.getElementById(linkId);
+      link.href = baseUrl + link.pathname.substr(1);
+    });
+  }
+  function simulateClick(target) {
+    var evt = document.createEvent("MouseEvents");
+    evt.initMouseEvent("click", true, true, window,
+                       0, 0, 0, 0, 0, false, false,
+                       false, false, 0, null);
+
+    return target.dispatchEvent(evt);
+  }
+
+  function clickNoRefTargetBlankLink() {
+    return simulateClick(document.getElementById("noref_and_tblank_link"));
+  }
+
+  function clickNoOpenerTargetBlankLink() {
+    return simulateClick(document.getElementById("noopener_and_tblank_link"));
+  }
+
+  function clickSameSiteNoRefTargetedLink() {
+    return simulateClick(
+        document.getElementById("samesite_noref_and_targeted_link"));
+  }
+
+  function clickSameSiteNoOpenerTargetedLink() {
+    return simulateClick(
+        document.getElementById("samesite_noopener_and_targeted_link"));
+  }
+
+  function clickSameSiteTargetedLink() {
+    return simulateClick(document.getElementById("samesite_targeted_link"));
+  }
+
+  function clickSameSiteTargetBlankLink() {
+    return simulateClick(document.getElementById("samesite_tblank_link"));
+  }
+
+  function clickTargetBlankLink() {
+    return simulateClick(document.getElementById("tblank_link"));
+  }
+
+  function clickNoRefLink() {
+    return simulateClick(document.getElementById("noref_link"));
+  }
+
+  function clickNoOpenerLink() {
+    return simulateClick(document.getElementById("noopener_link"));
+  }
+
+  function clickBlankTargetedLink() {
+    return simulateClick(document.getElementById("blank_targeted_link"));
+  }
+
+  function testScriptAccessToWindow() {
+    // Grab a reference to the existing foo window and access its location.
+    try {
+      var w = window.open("", "foo");
+      var url = w.location.href;
+      return url != undefined;
+    } catch (e) {
+      return false;
+    }
+  }
+
+  function testCloseWindow() {
+    // Grab a reference to the existing foo window and close it.
+    var w = window.open("", "foo");
+    w.close();
+    return true;
+  }
+
+  // Listen to incoming messages and reply to them.
+  var receivedMessages = 0;
+  window.addEventListener("message", messageReceived, false);
+  function messageReceived(event) {
+    receivedMessages++;
+    event.source.postMessage(event.data, "*");
+  }
+
+  // Send a message which contains a message port.
+  var mc;
+  function postWithPortToFoo() {
+    mc = new MessageChannel();
+    mc.port1.onmessage = portMessageReceived;
+    mc.port1.start();
+    var w = window.open("", "foo");
+    w.postMessage({message: "msg-with-port", port: mc.port2}, "*", [mc.port2]);
+    return true;
+  }
+
+  var receivedMessagesViaPort = 0;
+  function portMessageReceived(event) {
+    receivedMessagesViaPort++;
+    // Change the title to generate a notification.
+    document.title = event.data;
+  }
+
+  var last_opened_window = undefined;
+  function saveWindowReference() {
+    // Grab a reference to the existing foo window into a global variable
+    // for later testing.
+    last_opened_window = window.open("", "foo");
+  }
+  function getLastOpenedWindowLocation() {
+    return last_opened_window.location.href;
+  }
+
+  function openWindowWithTargetAndFeatures(path, target, features) {
+    var w = window.open(path, target, features);
+    return w !== null;
+  }
+ </script>
+ </head>
+
+<a href="title2.html" id="samesite_noref_and_targeted_link"
+   rel="noreferrer" target="foo">
+  same-site rel=noreferrer and target=foo</a><br>
+<a href="title2.html" id="samesite_noopener_and_targeted_link"
+   rel="noopener" target="foo">
+  same-site rel=noopener and target=foo</a><br>
+<a href="navigate_opener.html" id="samesite_targeted_link" target="foo">
+  same-site target=foo</a><br>
+<a href="title2.html" id="samesite_tblank_link" rel="opener" target="_blank">
+  same-site rel=opener target=_blank</a><br>
+<a href="about:blank" id="blank_targeted_link" target="foo">
+  blank_targeted_link=foo</a><br>
+
+<!-- The following set of links have to be fixed at runtime with the proper
+  scheme://host:port/ string, since the port is randomly generated. The
+  setOriginForLinks method is provided for facilitating the replacement and
+  should be called by each browser test utilizing these links. -->
+<a href="http://REPLACE/title2.html"
+   id="noref_and_tblank_link" rel="noreferrer" target="_blank">
+  rel=noreferrer and target=_blank</a><br>
+<a href="http://REPLACE/title2.html"
+   id="noopener_and_tblank_link" rel="noopener" target="_blank">
+  rel=noopener and target=_blank</a><br>
+<a href="http://REPLACE/title2.html" id="tblank_link"
+   rel="opener" target="_blank">rel=opener target=_blank</a><br>
+<a href="http://REPLACE/title2.html" id="noref_link"
+   rel="noreferrer">rel=noreferrer</a><br>
+<a href="http://REPLACE/title2.html" id="noopener_link"
+   rel="noopener">rel=opener</a><br>
+
+
+<iframe id="frame1" src="frame_tree/1-1.html"></iframe>
+
+</html>
--- /dev/null
+++ b/content/test/data/client_redirect.html
@@ -0,0 +1,7 @@
+<html>
+<head>
+<title>Client redirect</title>
+<meta http-equiv="refresh" content="0; url=title1.html">
+</head>
+<body>Client redirect</body>
+</html>
--- /dev/null
+++ b/content/test/data/color-scheme-2.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="utf-8">
+  <meta name="viewport" content="width=device-width">
+  <title>color scheme test</title>
+</head>
+
+<script>
+  if (window.matchMedia("(prefers-color-scheme: light)").matches) {
+    document.documentElement.style.backgroundColor = "White";
+    document.documentElement.style.color = "Black";
+  } else if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
+    document.documentElement.style.backgroundColor = "Black";
+    document.documentElement.style.color = "White";
+  }
+</script>
+<body>
+color scheme test 2
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/color-scheme-dark.html
@@ -0,0 +1,3 @@
+<head>
+  <meta name="color-scheme" content="dark">
+</head>
--- /dev/null
+++ b/content/test/data/color-scheme.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="utf-8">
+  <meta name="viewport" content="width=device-width">
+  <title>color scheme test</title>
+</head>
+
+<script>
+  if (window.matchMedia("(prefers-color-scheme: light)").matches) {
+    document.documentElement.style.backgroundColor = "White";
+    document.documentElement.style.color = "Black";
+  } else if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
+    document.documentElement.style.backgroundColor = "Black";
+    document.documentElement.style.color = "White";
+  }
+</script>
+<body>
+color scheme test
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/content-disposition-empty.html
@@ -0,0 +1,4 @@
+<html>
+<head><title>success</title></head>
+<body>this page should be displayed instead of downloaded</body>
+</html>
--- /dev/null
+++ b/content/test/data/content-disposition-inline.html
@@ -0,0 +1,4 @@
+<html>
+<head><title>success</title></head>
+<body>this page should be displayed instead of downloaded</body>
+</html>
--- /dev/null
+++ b/content/test/data/content-sniffer-test0.html
@@ -0,0 +1,4 @@
+<html>
+<head><title>Content Sniffer Test 0</title></head>
+<body>We should sniff this content as HTML.</body>
+</html>
--- /dev/null
+++ b/content/test/data/content-sniffer-test1.html
@@ -0,0 +1,4 @@
+<html>
+<head><title>Content Sniffer Test 1</title></head>
+<body>We should not sniff this content as HTML.</body>
+</html>
--- /dev/null
+++ b/content/test/data/content-sniffer-test2.html
@@ -0,0 +1,4 @@
+<html>
+<head><title>Content Sniffer Test 2</title></head>
+<body>We should not sniff this as HTML as server says its a GIF.</body>
+</html>
--- /dev/null
+++ b/content/test/data/content-sniffer-test3.html
@@ -0,0 +1,16 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+<meta HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
+<title>Content Sniffer Test 3</title>
+</head>
+<body>
+<script id="sc">
+var iframe = document.createElement('iframe');
+iframe.frameBorder = iframe.width = iframe.height = 0;
+iframe.src = "http://mock.http/content-sniffer-test3-frame.txt";
+document.body.appendChild(iframe);
+</script>
+<pre>Do NOT download!!</pre>
+</body>
+</html>
\ No newline at end of file
--- /dev/null
+++ b/content/test/data/coreferencingframe_1.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+<body>
+  A - This page has co-referencing nested iframes.
+  <iframe id="child-0" src="/cross-site/b.com/coreferencingframe_2.html"></iframe>
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/coreferencingframe_2.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+<body>
+  B - This page has co-referencing nested iframes.
+  <iframe id="child-0" src="/cross-site/a.com/coreferencingframe_1.html"></iframe>
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/corp-cross-origin.html
@@ -0,0 +1 @@
+File with "Cross-Origin-Resource-Policy: cross-origin" header set.
--- /dev/null
+++ b/content/test/data/counter.html
@@ -0,0 +1,14 @@
+<html>
+<body>
+  <p> Counting up every 50ms </p>
+  <input id="input" value="0"/>
+  <script>
+  window.addEventListener('load', function() {
+    var inputElement = document.getElementById('input');
+    window.setInterval(function() {
+      inputElement.value = inputElement.value * 1 + 1;
+    }, 50);
+  });
+  </script>
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/cross-origin-opener-policy_redirect_final.html
@@ -0,0 +1 @@
+Document with "Cross-Origin-Opener-Policy: same-origin"
--- /dev/null
+++ b/content/test/data/cross-origin-opener-policy_redirect_initial.html
@@ -0,0 +1 @@
+Document with "Cross-Origin-Opener-Policy: same-origin"
--- /dev/null
+++ b/content/test/data/cross-origin-opener-policy_sandbox_popup.html
@@ -0,0 +1 @@
+<iframe id="frame" sandbox="allow-scripts allow-popups" src='empty.html'></iframe>
--- /dev/null
+++ b/content/test/data/cross-origin-redirect-blocked.html
@@ -0,0 +1,57 @@
+<html>
+<head>
+  <title></title>
+</head>
+<body>
+<script>
+
+function NewXHR(url) {
+  var r = new XMLHttpRequest
+  r.open("GET", url);
+  return r;
+}
+
+function SignalSuccess() {
+  document.location = "title3.html";
+}
+
+function SignalFailure() {
+  document.location = "title1.html";
+}
+
+function CreateDummyRequest() {
+  dummy_request = NewXHR("/title2.html");
+  dummy_request.onload = SignalSuccess;
+  dummy_request.send(null);
+}
+
+function RedirectFailed() {
+  // Good, the redirect was blocked by WebKit.
+  //
+  // We also care that the underlying network stack does not send the redirect.
+  // We cannot detect that from JS, but our test harness is designed to detect
+  // that (see ResourceDispatcherTest::CrossOriginRedirectBlocked).  Before
+  // calling SignalSuccess, we want to allow the browser time to notice a request
+  // to follow the redirect if one should exist.  To do that, we just need to
+  // make another network request.
+  //
+  // The setTimeout call is intended to delay CreateDummyRequest so that any
+  // processing associated with the current "error" handler completes.
+  setTimeout(CreateDummyRequest, 0);
+}
+
+function RedirectSucceeded() {
+  // Oops, the redirect should have been denied!
+  SignalFailure();
+}
+
+// Kick off a request that will attempt a cross-origin redirect.
+var redirect_url = "/server-redirect-302?http://a.com:" + location.port + "/title2.html";
+request = NewXHR(redirect_url);
+request.onerror = RedirectFailed;
+request.onload = RedirectSucceeded;
+request.send(null);
+
+</script>
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/cross_site_iframe_factory.html
@@ -0,0 +1,354 @@
+<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 id="child-0" src="http://b.com:1234/cross_site_iframe_factory.html?b(c(),d())">
+
+Inside of which, then, are created the two leaf iframes:
+
+  <iframe id="child-0" src="http://c.com:1234/cross_site_iframe_factory.html?c()">
+  <iframe id="child-1" src="http://d.com:1234/cross_site_iframe_factory.html?d()">
+
+Add iframe options by enclosing them in '{' and '}' characters after the
+hostname (multiple options can be separated with commas):
+
+  cross_site_iframe_factory.html?a(b{allowfullscreen}(),c{sandbox-allow-scripts}(d))
+
+Will create two iframes:
+
+  <iframe id="child-0" src="http://a.com:1234/cross_site_iframe_factory.html?b()" allowfullscreen>
+  <iframe id="child-1" src="http://c.com:1234/cross_site_iframe_factory.html?c{sandbox-allow-scripts}(d())" sandbox="allow-scripts">
+
+To specify the site for each iframe, you can use a simple identifier like "a"
+or "b", and ".com" will be automatically appended. You can also specify a port
+number to use for each site by using a label like "a:12345". If no port number
+is given, the port will default to the port number of the top level frame.
+You can also specify an arbitrary full URL for any bottommost leaf frame.
+
+These options are supported:
+
+  allowfullscreen
+  allowpaymentrequest
+  allow-{featurename} - Adds {featurename} to the "allow" attribute
+  sandbox-{flagname} - Adds {flagname} to the "sandbox" attribute
+  with-shared-worker - Creates a shared worker in each frame.
+  with-worker - Creates a dedicated worker in each frame.
+
+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: "DejaVu Sans", 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);
+}
+
+function isUrl(siteString) {
+  try {
+    var url = new URL(siteString);
+  } catch (e) {
+    if (e instanceof TypeError)
+      return false;
+  }
+  return true;
+}
+
+/**
+ * Extract the specified port number, if any. Returns empty string if not
+ * specified.
+ */
+function sitePortNumber(siteString) {
+  let index = siteString.indexOf(':');
+  if (index == -1)
+    return ""
+  return siteString.substring(index + 1);
+}
+
+/**
+ * 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. Adds the specified port number, if any, or the default port otherwise.
+ */
+function canonicalizeSiteAndPort(siteString, defaultPort) {
+  var portNumber = sitePortNumber(siteString) || defaultPort;
+  var hostName = siteString.split(':')[0];
+  if (hostName !== "localhost" && hostName.indexOf('.') == -1)
+    hostName = hostName + '.com';
+  return hostName + (portNumber ? ':' + portNumber : "");
+}
+
+/**
+ * Parses the list of iframe options and applies them to the provided element.
+ */
+function applyIFrameOptions(element, options) {
+  var sandbox = false;
+  var sandboxArguments = [];
+  var allowFeatures = [];
+  for (var option of options) {
+    if (option == "allowfullscreen") {
+      element.allowFullscreen = true;
+    }
+    if (option == "allowpaymentrequest") {
+      element.allowPaymentRequest = true;
+    }
+    if (option == "sandbox") {
+      sandbox = true;
+    }
+    if (option.startsWith("sandbox-")) {
+      sandbox = true;
+      sandboxArguments.push(option.slice(8));
+    }
+    if (option.startsWith("allow-")) {
+      allowFeatures.push(option.slice(6))
+    }
+  }
+  if (sandbox) {
+    element.sandbox = sandboxArguments.join(" ");
+  }
+  if (allowFeatures.length) {
+    element.allow = allowFeatures.join(" ");
+  }
+}
+
+/**
+ * Determines whether or not text should be rendered by checking whether
+ * "no-text-render" is among the list of attributes.
+ */
+function shouldRenderText(attributes) {
+  for (var attribute of attributes) {
+    if (attribute == "no-text-render") {
+      return false;
+    }
+  }
+  return true;
+}
+
+/**
+ * Determines whether or not the frame should be positioned outside of
+ * its parent frame by checking whether "out-of-view" is among the list
+ * of attributes.
+ */
+function renderOutsideParentView(attributes) {
+  for (var attribute of attributes) {
+    if (attribute == "out-of-view") {
+      return true;
+    }
+  }
+  return false;
+}
+
+/**
+ * Determines whether or not the frame should create a worker by checking
+ * whether "with-worker" is among the list of attributes.
+ */
+ function shouldCreateWorker(attributes) {
+  for (var attribute of attributes) {
+    if (attribute == "with-worker") {
+      return true;
+    }
+  }
+  return false;
+}
+
+/**
+ * Determines whether or not the frame should create a shared worker by checking
+ * whether "with-shared-worker" is among the list of attributes.
+ */
+ function shouldCreateSharedWorker(attributes) {
+  for (var attribute of attributes) {
+    if (attribute == "with-shared-worker") {
+      return true;
+    }
+  }
+  return false;
+}
+
+/**
+ * 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 = isUrl(frameTree.value)
+                      ? frameTree.value
+                      : canonicalizeSiteAndPort(frameTree.value, "");
+  // If the top-level site doesn't match the hostname, the user is
+  // probably making a mistake, e.g. hostname is "a.test" and it's
+  // likely that subframes are incorrectly being left to default to
+  // ".com". In that case we bail out and refuse to create subframes,
+  // which should hopefully cause the test to fail.
+  if (currentSite != window.location.hostname) {
+    console.error(`The first hostname in the query-string does not match the` +
+      ` hostname of the URL: ${currentSite} != ${window.location.hostname}`);
+    return;
+  }
+
+  // Render text unless the attribute "no-text-render" is specified, in which
+  // case load some bytes anyway in order to trigger histogram recording for
+  // ad metrics tests.
+  if (shouldRenderText(frameTree.attributes)) {
+      document.getElementById('siteNameHeading').appendChild(
+          document.createTextNode(currentSite));
+  }
+
+  // Create a worker with matching query-string if the "with-worker" attribute
+  // is specified.
+  if (shouldCreateWorker(frameTree.attributes)) {
+    let worker_url = 'workers/empty.js';
+    worker_url += '?' + queryString;
+    console.log(worker_url);
+    self.worker = new Worker(worker_url);
+  }
+
+  // Create a shared worker with matching query-string if the "with-worker" 
+  // attribute is specified.
+  if (shouldCreateSharedWorker(frameTree.attributes)) {
+    let worker_url = 'workers/empty.js';
+    worker_url += '?' + queryString;
+    console.log(worker_url);
+    self.worker = new SharedWorker(worker_url);
+  }
+
+  // Apply style to the current document.
+  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.
+    let url = frameTree.children[i].value;
+    let siteAndPort = url;
+    if (!isUrl(url)) {
+      siteAndPort = canonicalizeSiteAndPort(url, window.location.port);
+      const subtreeString = TreeParserUtil.flatten(frameTree.children[i]);
+      url = '';
+      url += window.location.protocol + '//';              // scheme (preserved)
+      url += goCrossSite ? siteAndPort : window.location.host;    // host and port
+      url += window.location.pathname;                     // path (preserved)
+      url += '?' + encodeURIComponent(subtreeString);      // query
+    }
+
+    // Construct the iframe.
+    var iframe = document.createElement('iframe');
+    iframe.src = url;
+    iframe.id = "child-" + i;
+    iframe.style.borderColor = borderColorForSite(siteAndPort);
+    iframe.width = frameTree.children[i].layoutX;
+    iframe.height = frameTree.children[i].layoutY;
+
+    // If needed, position the iframe.
+    if (renderOutsideParentView(frameTree.children[i].attributes)) {
+        iframe.style.position = "fixed";
+        iframe.style.top = "150%";
+        iframe.style.left = "150%";
+    }
+
+    // Apply any additional attributes.
+    applyIFrameOptions(iframe, frameTree.children[i].attributes);
+
+    // Add the iframe to the document.
+    document.body.appendChild(iframe);
+  }
+}
+
+main();
+</script>
+</body></html>
--- /dev/null
+++ b/content/test/data/csp_sandboxed_frame.html
@@ -0,0 +1,8 @@
+<html>
+<title>sandboxed_frames</title>
+<head></head>
+  <body>
+    <iframe id="child-1" src="title1.html"></iframe>
+    <iframe id="child-2" sandbox></iframe>
+  </body>
+</html>
--- /dev/null
+++ b/content/test/data/csp_wasm_eval.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+  <script>
+    WebAssembly.compile(new Uint8Array(
+        // The smallest possible Wasm module. Just the header
+        // (0, "A", "S", "M"), and the version (0x1).
+        [0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00]));
+  </script>
+</html>
--- /dev/null
+++ b/content/test/data/ctrl-click-subframe-link.html
@@ -0,0 +1,23 @@
+<html>
+<head>
+<script>
+  function simulateCtrlClick(target) {
+    var is_mac = navigator.platform.toUpperCase().indexOf('MAC') >= 0;
+    if (is_mac) {
+      var evt = new MouseEvent("click", { "metaKey": true });
+    } else {
+      var evt = new MouseEvent("click", { "ctrlKey": true });
+    }
+    return target.dispatchEvent(evt);
+  }
+  function ctrlClickLink() {
+    simulateCtrlClick(frames[0].document.getElementById("link"));
+  }
+</script>
+</head>
+<body>
+
+<iframe srcdoc="<a href='title1.html' id='link'>link</a>"></iframe>
+
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/dark_color_scheme_meta_slow.html
@@ -0,0 +1,3 @@
+<!doctype html>
+<meta name="color-scheme" content="dark">
+<link rel="stylesheet" href="/slow-response">
--- /dev/null
+++ b/content/test/data/data_url_navigations.html
@@ -0,0 +1,110 @@
+<html>
+
+<h3>HTML mimetype</h3>
+
+<a download href="data:text/html,download" id="download-link">&lt;a download link&gt;</a>
+<br><br>
+<button id='navigate-top-frame-to-html'
+    onclick='top.location.href=`data:text/html,
+        <script>console.log(&quot;NAVIGATION_SUCCESSFUL&quot;)</script>`'>
+  Navigate top frame to data URL HTML
+</button>
+<br>
+<button id='window-open-html'
+    onclick='window.open(`data:text/html,
+        <script>console.log(&quot;NAVIGATION_SUCCESSFUL&quot;)</script>`);'>
+  Open new window with a data URL HTML
+</button>
+<button id='window-open-redirect'
+    onclick='window.open(`/server-redirect?data:text/html,<script>console.log(&quot;NAVIGATION_SUCCESSFUL&quot;)</script>`);'>
+  Open new window with a redirect to data URL
+</button>
+<br>
+<form method="post" action="data:text/html,
+             <script>console.log('NAVIGATION_SUCCESSFUL')</script>">
+  <input type=submit id='form-post-to-html'
+      value="Submit form to data URL HTML">
+</form>
+
+<h3>octet-stream mimetype</h3>
+
+<button id='navigate-top-frame-to-octetstream'
+    onclick='top.location.href=`data:application/octet-stream,test`'>
+  Navigate top frame to data URL octet-stream
+</button>
+<br>
+<button id='window-open-octetstream'
+    onclick='window.open(`data:application/octet-stream,test`)'>
+  Open new window with a data URL octet-stream
+</button>
+<form method="post" action="data:application/octet-stream,test">
+  <input type=submit id='form-post-to-octetstream'
+      value="Submit form to data URL octet-stream">
+</form>
+<h3>PDF mimetype</h3>
+
+<button id='navigate-top-frame-to-pdf'
+    onclick='top.location.href=`data:application/pdf;base64,
+    JVBERi0xLjcKMSAwIG9iaiA8PCAvVHlwZSAvUGFnZSAvUG
+    FyZW50IDMgMCBSIC9SZXNvdXJjZXMgNSAwIFIgL0NvbnRlbnRzIDIgMCBSID4+CmVuZG9iagoy
+    IDAgb2JqIDw8IC9MZW5ndGggNTEgPj4KIHN0cmVhbSBCVAogL0YxIDEyIFRmCiAxIDAgMCAxID
+    EwMCAyMCBUbQogKEhlbGxvIFdvcmxkKVRqCiBFVAogZW5kc3RyZWFtCmVuZG9iagozIDAgb2Jq
+    IDw8IC9UeXBlIC9QYWdlcyAvS2lkcyBbIDEgMCBSIF0gL0NvdW50IDEgL01lZGlhQm94IFsgMC
+    AwIDMwMCA1MF0gPj4KZW5kb2JqCjQgMCBvYmogPDwgL1R5cGUgL0ZvbnQgL1N1YnR5cGUgL1R5
+    cGUxIC9OYW1lIC9GMSAvQmFzZUZvbnQvQXJpYWwgPj4KZW5kb2JqCjUgMCBvYmogPDwgL1Byb2
+    NTZXRbL1BERi9UZXh0XSAvRm9udCA8PC9GMSA0IDAgUiA+PiA+PgplbmRvYmoKNiAwIG9iaiA8
+    PCAvVHlwZSAvQ2F0YWxvZyAvUGFnZXMgMyAwIFIgPj4KZW5kb2JqCnRyYWlsZXIgPDwgL1Jvb3
+    QgNiAwIFIgPj4K`'>
+  Navigate top frame to data URL PDF
+</button>
+<br>
+<button id='window-open-pdf'
+    onclick='window.open(`data:application/pdf;base64,
+    JVBERi0xLjcKMSAwIG9iaiA8PCAvVHlwZSAvUGFnZSAvUG
+    FyZW50IDMgMCBSIC9SZXNvdXJjZXMgNSAwIFIgL0NvbnRlbnRzIDIgMCBSID4+CmVuZG9iagoy
+    IDAgb2JqIDw8IC9MZW5ndGggNTEgPj4KIHN0cmVhbSBCVAogL0YxIDEyIFRmCiAxIDAgMCAxID
+    EwMCAyMCBUbQogKEhlbGxvIFdvcmxkKVRqCiBFVAogZW5kc3RyZWFtCmVuZG9iagozIDAgb2Jq
+    IDw8IC9UeXBlIC9QYWdlcyAvS2lkcyBbIDEgMCBSIF0gL0NvdW50IDEgL01lZGlhQm94IFsgMC
+    AwIDMwMCA1MF0gPj4KZW5kb2JqCjQgMCBvYmogPDwgL1R5cGUgL0ZvbnQgL1N1YnR5cGUgL1R5
+    cGUxIC9OYW1lIC9GMSAvQmFzZUZvbnQvQXJpYWwgPj4KZW5kb2JqCjUgMCBvYmogPDwgL1Byb2
+    NTZXRbL1BERi9UZXh0XSAvRm9udCA8PC9GMSA0IDAgUiA+PiA+PgplbmRvYmoKNiAwIG9iaiA8
+    PCAvVHlwZSAvQ2F0YWxvZyAvUGFnZXMgMyAwIFIgPj4KZW5kb2JqCnRyYWlsZXIgPDwgL1Jvb3
+    QgNiAwIFIgPj4K`)'>
+  Open new window with a data URL PDF
+</button>
+<br>
+<form method="post" action='data:application/pdf;base64,
+    JVBERi0xLjcKMSAwIG9iaiA8PCAvVHlwZSAvUGFnZSAvUG
+    FyZW50IDMgMCBSIC9SZXNvdXJjZXMgNSAwIFIgL0NvbnRlbnRzIDIgMCBSID4+CmVuZG9iagoy
+    IDAgb2JqIDw8IC9MZW5ndGggNTEgPj4KIHN0cmVhbSBCVAogL0YxIDEyIFRmCiAxIDAgMCAxID
+    EwMCAyMCBUbQogKEhlbGxvIFdvcmxkKVRqCiBFVAogZW5kc3RyZWFtCmVuZG9iagozIDAgb2Jq
+    IDw8IC9UeXBlIC9QYWdlcyAvS2lkcyBbIDEgMCBSIF0gL0NvdW50IDEgL01lZGlhQm94IFsgMC
+    AwIDMwMCA1MF0gPj4KZW5kb2JqCjQgMCBvYmogPDwgL1R5cGUgL0ZvbnQgL1N1YnR5cGUgL1R5
+    cGUxIC9OYW1lIC9GMSAvQmFzZUZvbnQvQXJpYWwgPj4KZW5kb2JqCjUgMCBvYmogPDwgL1Byb2
+    NTZXRbL1BERi9UZXh0XSAvRm9udCA8PC9GMSA0IDAgUiA+PiA+PgplbmRvYmoKNiAwIG9iaiA8
+    PCAvVHlwZSAvQ2F0YWxvZyAvUGFnZXMgMyAwIFIgPj4KZW5kb2JqCnRyYWlsZXIgPDwgL1Jvb3
+    QgNiAwIFIgPj4K'>
+  <input type=submit id='form-post-to-pdf'
+      value="Submit form to data URL PDF">
+</form>
+
+<h3>Unknown mimetype</h3>
+
+<button id='navigate-top-frame-to-unknown-mimetype'
+    onclick='top.location.href=`data:unknown/mimetype,test`'>
+  Navigate top frame to data URL unknown mimetype
+</button>
+<br>
+<button id='window-open-unknown-mimetype'
+    onclick='window.open(`data:unknown/mimetype,test`)'>
+  Open new window with a data URL unknown mimetype
+</button>
+<form method="post" action="data:unknown/mimetype,test">
+  <input type=submit id='form-post-to-unknown-mimetype'
+         value='Submit form to data URL unknown mimetype'>
+</form>
+<script>
+  if (window.domAutomationController)
+      window.domAutomationController.send("READY");
+</script>
+</html>
--- /dev/null
+++ b/content/test/data/detach_frame_in_copy.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+<body>
+<iframe></iframe>
+<script>
+window[0].addEventListener('copy', (e) => {
+  document.querySelector('iframe').remove();
+  // Finish asynchronously, to make sure the renderer doesn't crash.
+  window.setTimeout(() => { document.title = 'done'; });
+});
+</script>
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/div_with_touch_action_auto.html
@@ -0,0 +1,9 @@
+<html>
+<head></head>
+<body>
+  <div style="touch-action:auto; width:100px; height:100px;background-color:red; overflow: scroll">
+    This div prohibits pinch-zoom.
+    <div style="width: 120px; height: 500px; background-color: black">Scrollable content!</div>
+  </div>
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/div_with_touch_action_none.html
@@ -0,0 +1,9 @@
+<html>
+<head></head>
+<body>
+  <div style="touch-action:none; width:100px; height:100px;background-color:red; overflow: scroll">
+    This div prohibits pinch-zoom.
+    <div style="width: 120px; height: 500px; background-color: black">Scrollable content!</div>
+  </div>
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/dynamic1.html
@@ -0,0 +1,27 @@
+<html>
+<head>
+<title>Test Title</title>
+<script type="text/javascript">
+function OpenPopup() {
+    // Create a new popup window
+    var oWnd = window.open('','MyPopupName','width=350,height=300');
+
+    // Dynamically generate the HTML content for popup window
+    var sHtml =
+        '<html>'
+            + '<head><title>My Popup Title</title></head>'
+            + '<body></body>'
+        + '</html>';
+
+    // Push the HTML into that window
+    oWnd.document.write(sHtml);
+
+    // Finish
+    oWnd.document.close();
+}
+</script>
+</head>
+<body>
+  <p>This is dynamic1.html</p>
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/dynamic2.html
@@ -0,0 +1,31 @@
+<html>
+<head>
+<title>Test Title</title>
+<script type="text/javascript">
+function OpenPopup() {
+    // Create a new popup window
+    var oWnd = window.open('','MyPopupName','width=350,height=300');
+
+    // Dynamically generate the HTML content for popup window
+    var sHtml =
+        '<html>'
+            + '<head><title>My Popup Title</title></head>'
+            + '<body>'
+                + '<script type="text/javascript">'
+                    + 'document.title="My Dynamic Title";'
+                + '<\/script>'
+            + '</body>'
+        + '</html>';
+
+    // Push the HTML into that window
+    oWnd.document.write(sHtml);
+
+    // Finish
+    oWnd.document.close();
+}
+</script>
+</head>
+<body>
+  <p>This is dynamic2.html</p>
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/echo-referrer.html
@@ -0,0 +1,4 @@
+<!doctype html>
+<script>
+document.title = "Referrer = '" + document.referrer + "'";
+</script>
--- /dev/null
+++ b/content/test/data/event-latency-animation.html
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width">
+    <title>EventLatency vs. Animation</title>
+    <style>
+      #raf-box {
+        display: inline-block;
+        width: 100px;
+        height: 100px;
+        background-color: blue;
+      }
+
+      #main-box {
+        display: inline-block;
+        width: 100px;
+        height: 100px;
+        background-color: red;
+      }
+
+      .pulse-width {
+        animation: pulse-width 1s infinite;
+      }
+
+      @keyframes pulse-width {
+        0% {
+          width: 100px;
+        }
+        100% {
+          width: 200px;
+        }
+      }
+    </style>
+  </head>
+
+  <body>
+    <button id="button">Press here with space</button>
+    <input id="input" placeholder="Type something here">
+    <div>
+      <div id="raf-box"></div>
+      <div id="main-box"></div>
+    </div>
+    <script>
+      var button = document.getElementById('button');
+      var input = document.getElementById('input');
+      var rafBox = document.getElementById('raf-box');
+      var mainBox = document.getElementById('main-box');
+
+      var rafId = null;
+      var rafStart = null;
+
+      var focusButton = function() {
+        button.focus();
+      };
+
+      var focusInput = function() {
+        input.focus();
+      };
+
+      var rafAnimate = function(time) {
+        if (rafStart === null)
+          rafStart = time;
+        var w = Math.floor((time - rafStart) / 10) % 100 + 100;
+        rafBox.style.width = w + 'px';
+        rafId = requestAnimationFrame(rafAnimate);
+      };
+
+      var startAnimations = function() {
+        if (rafId === null)
+          rafId = requestAnimationFrame(rafAnimate);
+        if (!mainBox.classList.contains('pulse-width'))
+          mainBox.classList.add('pulse-width');
+      };
+    </script>
+  </body>
+</html>
--- /dev/null
+++ b/content/test/data/fetch-keepalive.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<!--
+  This page loads the given number of fetch keepalive requests, and then waits for their responses.
+-->
+<title>Fetching</title>
+<script>
+const PARAMS = new URL(location.href).searchParams;
+const NUM_REQUESTS= PARAMS.has('requests') ? Number(PARAMS.get('requests')) : 1;
+const METHOD = PARAMS.get('method') || 'GET';
+
+const promises = [];
+for (let i = 0; i < NUM_REQUESTS; i += 1) {
+  promises.push(fetch('/beacon', {
+    keepalive: true,
+    cache: 'no-store',
+    method: METHOD
+  }));
+}
+document.title = 'Waiting';
+Promise.all(promises).then(() => {
+  document.title = 'Resolved';
+}, () => {
+  document.title = 'Rejected';
+});
+</script>
--- /dev/null
+++ b/content/test/data/file_input.html
@@ -0,0 +1 @@
+<input type="file" id="fileinput" />
--- /dev/null
+++ b/content/test/data/file_input_subframe.html
@@ -0,0 +1,2 @@
+<iframe srcdoc="<form action='title2.html' enctype='multipart/form-data' method='post'><input type='file' id='fileinput' /></form>">
+</iframe>
--- /dev/null
+++ b/content/test/data/filesystem_url_navigations.html
@@ -0,0 +1,161 @@
+<html>
+<body onload="setupHandlers()">
+
+<h3>HTML mimetype</h3>
+
+<a download id="download-link">&lt;a download link&gt;</a>
+<br><br>
+<button id='navigate-top-frame-to-html'>
+  Navigate top frame to filesystem URL HTML
+</button>
+<br>
+<button id='window-open-html'>
+  Open new window with a filesystem URL HTML
+</button>
+<button id='window-open-redirect'>
+  Open new window with a redirect to filesystem URL
+</button>
+<br>
+<form method="post" id="form-post-to-html-form">
+  <input type=submit id='form-post-to-html'
+      value="Submit form to filesystem URL HTML">
+</form>
+
+<h3>octet-stream mimetype</h3>
+
+<button id='navigate-top-frame-to-octetstream'>
+  Navigate top frame to filesystem URL octet-stream
+</button>
+<br>
+<button id='window-open-octetstream'>
+  Open new window with a filesystem URL octet-stream
+</button>
+<form method="post" id="form-post-to-octetstream-form">
+  <input type=submit id="form-post-to-octetstream"
+      value="Submit form to filesystem URL octet-stream">
+</form>
+<h3>PDF mimetype</h3>
+
+<button id='navigate-top-frame-to-pdf'>
+  Navigate top frame to filesystem URL PDF
+</button>
+<br>
+<button id='window-open-pdf'>
+  Open new window with a filesystem URL PDF
+</button>
+<br>
+<form method="post" id="form-post-to-pdf-form">
+  <input type=submit id='form-post-to-pdf'
+      value="Submit form to filesystem URL PDF">
+</form>
+
+<h3>Unknown mimetype</h3>
+
+<button id='navigate-top-frame-to-unknown-mimetype'>
+  Navigate top frame to filesystem URL unknown mimetype
+</button>
+<br>
+<button id="window-open-unknown-mimetype">
+  Open new window with a filesystem URL unknown mimetype
+</button>
+<form method="post" id="form-post-to-unknown-mimetype-form">
+  <input type=submit id='form-post-to-unknown-mimetype'
+         value='Submit form to filesystem URL unknown mimetype'>
+</form>
+
+<script>
+var PDF =
+  "%PDF-1.7\n" +
+  "1 0 obj << /Type /Page /Parent 3 0 R /Resources 5 0 R /Contents 2 0 R >>\n" +
+  "endobj\n" +
+  "2 0 obj << /Length 51 >>\n" +
+  " stream BT\n" +
+  " /F1 12 Tf\n" +
+  " 1 0 0 1 100 20 Tm\n" +
+  " (Hello World)Tj\n" +
+  " ET\n" +
+  " endstream\n" +
+  "endobj\n" +
+  "3 0 obj << /Type /Pages /Kids [ 1 0 R ] /Count 1 /MediaBox [ 0 0 300 50] >>\n" +
+  "endobj\n" +
+  "4 0 obj << /Type /Font /Subtype /Type1 /Name /F1 /BaseFont/Arial >>\n" +
+  "endobj\n" +
+  "5 0 obj << /ProcSet[/PDF/Text] /Font <</F1 4 0 R >> >>\n" +
+  "endobj\n" +
+  "6 0 obj << /Type /Catalog /Pages 3 0 R >>\n" +
+  "endobj\n" +
+  "trailer << /Root 6 0 R >>";
+
+function setupHandlers() {
+  window.webkitRequestFileSystem(window.TEMPORARY, 1024, fs => {
+    // HTML mime type:
+    fs.root.getFile('test.html', {create: true}, entry => {
+      entry.createWriter(w => {
+        w.write(new Blob([
+            "NAVIGATION_SUCCESSFUL" +
+            "<script" + ">console.log('NAVIGATION_SUCCESSFUL')<" + "/script>"],
+            {type: "text/html"}));
+      });
+      document.getElementById("download-link").href = entry.toURL();
+      document.getElementById("navigate-top-frame-to-html").onclick = function() {
+        top.location.href = entry.toURL();
+      };
+      document.getElementById("window-open-html").onclick = function() {
+        window.open(entry.toURL());
+      };
+      document.getElementById("window-open-redirect").onclick = function() {
+        window.open("/server-redirect?" + entry.toURL());
+      };
+      document.getElementById("form-post-to-html-form").action = entry.toURL();
+
+      // PDF:
+      fs.root.getFile('test.pdf', {create: true}, entry => {
+        entry.createWriter(w => {
+          var blob = new Blob([PDF], {type: "application/pdf"});
+          w.write(blob);
+        });
+        document.getElementById("navigate-top-frame-to-pdf").onclick = function() {
+          top.location.href = entry.toURL();
+        };
+        document.getElementById("window-open-pdf").onclick = function() {
+          window.open(entry.toURL());
+        };
+        document.getElementById("form-post-to-pdf-form").action = entry.toURL();
+
+        // Binary mime type:
+        fs.root.getFile('test.binary', {create: true}, entry => {
+          entry.createWriter(w => {
+            w.write(new Blob(["testdata"], {type: "application/octet-stream"}));
+          });
+          document.getElementById("navigate-top-frame-to-octetstream").onclick = function() {
+            top.location.href = entry.toURL();
+          };
+          document.getElementById("window-open-octetstream").onclick = function() {
+            window.open(entry.toURL());
+          };
+          document.getElementById("form-post-to-octetstream-form").action = entry.toURL();
+
+          // Unknown mime type:
+          fs.root.getFile('test.unknown', {create: true}, entry => {
+            entry.createWriter(w => {
+              w.write(new Blob(["test"], {type: "unknown/mimetype"}));
+            });
+            document.getElementById("navigate-top-frame-to-unknown-mimetype").onclick = function() {
+              top.location.href = entry.toURL();
+            };
+            document.getElementById("window-open-unknown-mimetype").onclick = function() {
+              window.open(entry.toURL());
+            };
+            document.getElementById("form-post-to-unknown-mimetype-form").action = entry.toURL();
+
+            if (window.domAutomationController)
+              window.domAutomationController.send("READY");
+          });
+        });
+      });
+    });
+  });
+}
+</script>
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/find_in_dynamic_page.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
+<script>
+  function addNewText() {
+    document.querySelector('div').innerText = 'result result result result result';
+  }
+</script>
+</head>
+<body>
+This is a find-in-page test page with dynamically added text.</br>
+result result result
+<div></div>
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/find_in_embedded_pdf_page.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
+</head>
+<body>
+This a test page for find-in-page with two embedded PDFs. This page should contain 13 matches total for "result".</br>
+<iframe src="find_in_pdf_page.pdf"></iframe></br>
+result</br>
+<iframe src="find_in_pdf_page.pdf"></iframe></br>
+result</br>
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/find_in_hidden_frame.html
@@ -0,0 +1,2 @@
+<div>hello</div><iframe style="display:inline;width:0;height:0;border:none" src="data:text/html,hello"></iframe>
+<iframe style="visibility:hidden" src="data:text/html,hello"></iframe>
--- /dev/null
+++ b/content/test/data/find_in_long_page.html
@@ -0,0 +1,2011 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
+</head>
+<body>
+This is a long test page for find-in-page. Pi to 100,000 digits follows:</br>
+</br>
+3.</br>
+14159265358979323846264338327950288419716939937510</br>
+58209749445923078164062862089986280348253421170679</br>
+82148086513282306647093844609550582231725359408128</br>
+48111745028410270193852110555964462294895493038196</br>
+44288109756659334461284756482337867831652712019091</br>
+45648566923460348610454326648213393607260249141273</br>
+72458700660631558817488152092096282925409171536436</br>
+78925903600113305305488204665213841469519415116094</br>
+33057270365759591953092186117381932611793105118548</br>
+07446237996274956735188575272489122793818301194912</br>
+98336733624406566430860213949463952247371907021798</br>
+60943702770539217176293176752384674818467669405132</br>
+00056812714526356082778577134275778960917363717872</br>
+14684409012249534301465495853710507922796892589235</br>
+42019956112129021960864034418159813629774771309960</br>
+51870721134999999837297804995105973173281609631859</br>
+50244594553469083026425223082533446850352619311881</br>
+71010003137838752886587533208381420617177669147303</br>
+59825349042875546873115956286388235378759375195778</br>
+18577805321712268066130019278766111959092164201989</br>
+38095257201065485863278865936153381827968230301952</br>
+03530185296899577362259941389124972177528347913151</br>
+55748572424541506959508295331168617278558890750983</br>
+81754637464939319255060400927701671139009848824012</br>
+85836160356370766010471018194295559619894676783744</br>
+94482553797747268471040475346462080466842590694912</br>
+93313677028989152104752162056966024058038150193511</br>
+25338243003558764024749647326391419927260426992279</br>
+67823547816360093417216412199245863150302861829745</br>
+55706749838505494588586926995690927210797509302955</br>
+32116534498720275596023648066549911988183479775356</br>
+63698074265425278625518184175746728909777727938000</br>
+81647060016145249192173217214772350141441973568548</br>
+16136115735255213347574184946843852332390739414333</br>
+45477624168625189835694855620992192221842725502542</br>
+56887671790494601653466804988627232791786085784383</br>
+82796797668145410095388378636095068006422512520511</br>
+73929848960841284886269456042419652850222106611863</br>
+06744278622039194945047123713786960956364371917287</br>
+46776465757396241389086583264599581339047802759009</br>
+94657640789512694683983525957098258226205224894077</br>
+26719478268482601476990902640136394437455305068203</br>
+49625245174939965143142980919065925093722169646151</br>
+57098583874105978859597729754989301617539284681382</br>
+68683868942774155991855925245953959431049972524680</br>
+84598727364469584865383673622262609912460805124388</br>
+43904512441365497627807977156914359977001296160894</br>
+41694868555848406353422072225828488648158456028506</br>
+01684273945226746767889525213852254995466672782398</br>
+64565961163548862305774564980355936345681743241125</br>
+15076069479451096596094025228879710893145669136867</br>
+22874894056010150330861792868092087476091782493858</br>
+90097149096759852613655497818931297848216829989487</br>
+22658804857564014270477555132379641451523746234364</br>
+54285844479526586782105114135473573952311342716610</br>
+21359695362314429524849371871101457654035902799344</br>
+03742007310578539062198387447808478489683321445713</br>
+86875194350643021845319104848100537061468067491927</br>
+81911979399520614196634287544406437451237181921799</br>
+98391015919561814675142691239748940907186494231961</br>
+56794520809514655022523160388193014209376213785595</br>
+66389377870830390697920773467221825625996615014215</br>
+03068038447734549202605414665925201497442850732518</br>
+66600213243408819071048633173464965145390579626856</br>
+10055081066587969981635747363840525714591028970641</br>
+40110971206280439039759515677157700420337869936007</br>
+23055876317635942187312514712053292819182618612586</br>
+73215791984148488291644706095752706957220917567116</br>
+72291098169091528017350671274858322287183520935396</br>
+57251210835791513698820914442100675103346711031412</br>
+67111369908658516398315019701651511685171437657618</br>
+35155650884909989859982387345528331635507647918535</br>
+89322618548963213293308985706420467525907091548141</br>
+65498594616371802709819943099244889575712828905923</br>
+23326097299712084433573265489382391193259746366730</br>
+58360414281388303203824903758985243744170291327656</br>
+18093773444030707469211201913020330380197621101100</br>
+44929321516084244485963766983895228684783123552658</br>
+21314495768572624334418930396864262434107732269780</br>
+28073189154411010446823252716201052652272111660396</br>
+66557309254711055785376346682065310989652691862056</br>
+47693125705863566201855810072936065987648611791045</br>
+33488503461136576867532494416680396265797877185560</br>
+84552965412665408530614344431858676975145661406800</br>
+70023787765913440171274947042056223053899456131407</br>
+11270004078547332699390814546646458807972708266830</br>
+63432858785698305235808933065757406795457163775254</br>
+20211495576158140025012622859413021647155097925923</br>
+09907965473761255176567513575178296664547791745011</br>
+29961489030463994713296210734043751895735961458901</br>
+93897131117904297828564750320319869151402870808599</br>
+04801094121472213179476477726224142548545403321571</br>
+85306142288137585043063321751829798662237172159160</br>
+77166925474873898665494945011465406284336639379003</br>
+97692656721463853067360965712091807638327166416274</br>
+88880078692560290228472104031721186082041900042296</br>
+61711963779213375751149595015660496318629472654736</br>
+42523081770367515906735023507283540567040386743513</br>
+62222477158915049530984448933309634087807693259939</br>
+78054193414473774418426312986080998886874132604721</br>
+56951623965864573021631598193195167353812974167729</br>
+47867242292465436680098067692823828068996400482435</br>
+40370141631496589794092432378969070697794223625082</br>
+21688957383798623001593776471651228935786015881617</br>
+55782973523344604281512627203734314653197777416031</br>
+99066554187639792933441952154134189948544473456738</br>
+31624993419131814809277771038638773431772075456545</br>
+32207770921201905166096280490926360197598828161332</br>
+31666365286193266863360627356763035447762803504507</br>
+77235547105859548702790814356240145171806246436267</br>
+94561275318134078330336254232783944975382437205835</br>
+31147711992606381334677687969597030983391307710987</br>
+04085913374641442822772634659470474587847787201927</br>
+71528073176790770715721344473060570073349243693113</br>
+83504931631284042512192565179806941135280131470130</br>
+47816437885185290928545201165839341965621349143415</br>
+95625865865570552690496520985803385072242648293972</br>
+85847831630577775606888764462482468579260395352773</br>
+48030480290058760758251047470916439613626760449256</br>
+27420420832085661190625454337213153595845068772460</br>
+29016187667952406163425225771954291629919306455377</br>
+99140373404328752628889639958794757291746426357455</br>
+25407909145135711136941091193932519107602082520261</br>
+87985318877058429725916778131496990090192116971737</br>
+27847684726860849003377024242916513005005168323364</br>
+35038951702989392233451722013812806965011784408745</br>
+19601212285993716231301711444846409038906449544400</br>
+61986907548516026327505298349187407866808818338510</br>
+22833450850486082503930213321971551843063545500766</br>
+82829493041377655279397517546139539846833936383047</br>
+46119966538581538420568533862186725233402830871123</br>
+28278921250771262946322956398989893582116745627010</br>
+21835646220134967151881909730381198004973407239610</br>
+36854066431939509790190699639552453005450580685501</br>
+95673022921913933918568034490398205955100226353536</br>
+19204199474553859381023439554495977837790237421617</br>
+27111723643435439478221818528624085140066604433258</br>
+88569867054315470696574745855033232334210730154594</br>
+05165537906866273337995851156257843229882737231989</br>
+87571415957811196358330059408730681216028764962867</br>
+44604774649159950549737425626901049037781986835938</br>
+14657412680492564879855614537234786733039046883834</br>
+36346553794986419270563872931748723320837601123029</br>
+91136793862708943879936201629515413371424892830722</br>
+01269014754668476535761647737946752004907571555278</br>
+19653621323926406160136358155907422020203187277605</br>
+27721900556148425551879253034351398442532234157623</br>
+36106425063904975008656271095359194658975141310348</br>
+22769306247435363256916078154781811528436679570611</br>
+08615331504452127473924544945423682886061340841486</br>
+37767009612071512491404302725386076482363414334623</br>
+51897576645216413767969031495019108575984423919862</br>
+91642193994907236234646844117394032659184044378051</br>
+33389452574239950829659122850855582157250310712570</br>
+12668302402929525220118726767562204154205161841634</br>
+84756516999811614101002996078386909291603028840026</br>
+91041407928862150784245167090870006992821206604183</br>
+71806535567252532567532861291042487761825829765157</br>
+95984703562226293486003415872298053498965022629174</br>
+87882027342092222453398562647669149055628425039127</br>
+57710284027998066365825488926488025456610172967026</br>
+64076559042909945681506526530537182941270336931378</br>
+51786090407086671149655834343476933857817113864558</br>
+73678123014587687126603489139095620099393610310291</br>
+61615288138437909904231747336394804575931493140529</br>
+76347574811935670911013775172100803155902485309066</br>
+92037671922033229094334676851422144773793937517034</br>
+43661991040337511173547191855046449026365512816228</br>
+82446257591633303910722538374218214088350865739177</br>
+15096828874782656995995744906617583441375223970968</br>
+34080053559849175417381883999446974867626551658276</br>
+58483588453142775687900290951702835297163445621296</br>
+40435231176006651012412006597558512761785838292041</br>
+97484423608007193045761893234922927965019875187212</br>
+72675079812554709589045563579212210333466974992356</br>
+30254947802490114195212382815309114079073860251522</br>
+74299581807247162591668545133312394804947079119153</br>
+26734302824418604142636395480004480026704962482017</br>
+92896476697583183271314251702969234889627668440323</br>
+26092752496035799646925650493681836090032380929345</br>
+95889706953653494060340216654437558900456328822505</br>
+45255640564482465151875471196218443965825337543885</br>
+69094113031509526179378002974120766514793942590298</br>
+96959469955657612186561967337862362561252163208628</br>
+69222103274889218654364802296780705765615144632046</br>
+92790682120738837781423356282360896320806822246801</br>
+22482611771858963814091839036736722208883215137556</br>
+00372798394004152970028783076670944474560134556417</br>
+25437090697939612257142989467154357846878861444581</br>
+23145935719849225284716050492212424701412147805734</br>
+55105008019086996033027634787081081754501193071412</br>
+23390866393833952942578690507643100638351983438934</br>
+15961318543475464955697810382930971646514384070070</br>
+73604112373599843452251610507027056235266012764848</br>
+30840761183013052793205427462865403603674532865105</br>
+70658748822569815793678976697422057505968344086973</br>
+50201410206723585020072452256326513410559240190274</br>
+21624843914035998953539459094407046912091409387001</br>
+26456001623742880210927645793106579229552498872758</br>
+46101264836999892256959688159205600101655256375678</br>
+56672279661988578279484885583439751874454551296563</br>
+44348039664205579829368043522027709842942325330225</br>
+76341807039476994159791594530069752148293366555661</br>
+56787364005366656416547321704390352132954352916941</br>
+45990416087532018683793702348886894791510716378529</br>
+02345292440773659495630510074210871426134974595615</br>
+13849871375704710178795731042296906667021449863746</br>
+45952808243694457897723300487647652413390759204340</br>
+19634039114732023380715095222010682563427471646024</br>
+33544005152126693249341967397704159568375355516673</br>
+02739007497297363549645332888698440611964961627734</br>
+49518273695588220757355176651589855190986665393549</br>
+48106887320685990754079234240230092590070173196036</br>
+22547564789406475483466477604114632339056513433068</br>
+44953979070903023460461470961696886885014083470405</br>
+46074295869913829668246818571031887906528703665083</br>
+24319744047718556789348230894310682870272280973624</br>
+80939962706074726455399253994428081137369433887294</br>
+06307926159599546262462970706259484556903471197299</br>
+64090894180595343932512362355081349490043642785271</br>
+38315912568989295196427287573946914272534366941532</br>
+36100453730488198551706594121735246258954873016760</br>
+02988659257866285612496655235338294287854253404830</br>
+83307016537228563559152534784459818313411290019992</br>
+05981352205117336585640782648494276441137639386692</br>
+48031183644536985891754426473998822846218449008777</br>
+69776312795722672655562596282542765318300134070922</br>
+33436577916012809317940171859859993384923549564005</br>
+70995585611349802524990669842330173503580440811685</br>
+52653117099570899427328709258487894436460050410892</br>
+26691783525870785951298344172953519537885534573742</br>
+60859029081765155780390594640873506123226112009373</br>
+10804854852635722825768203416050484662775045003126</br>
+20080079980492548534694146977516493270950493463938</br>
+24322271885159740547021482897111777923761225788734</br>
+77188196825462981268685817050740272550263329044976</br>
+27789442362167411918626943965067151577958675648239</br>
+93917604260176338704549901761436412046921823707648</br>
+87834196896861181558158736062938603810171215855272</br>
+66830082383404656475880405138080163363887421637140</br>
+64354955618689641122821407533026551004241048967835</br>
+28588290243670904887118190909494533144218287661810</br>
+31007354770549815968077200947469613436092861484941</br>
+78501718077930681085469000944589952794243981392135</br>
+05586422196483491512639012803832001097738680662877</br>
+92397180146134324457264009737425700735921003154150</br>
+89367930081699805365202760072774967458400283624053</br>
+46037263416554259027601834840306811381855105979705</br>
+66400750942608788573579603732451414678670368809880</br>
+60971642584975951380693094494015154222219432913021</br>
+73912538355915031003330325111749156969174502714943</br>
+31515588540392216409722910112903552181576282328318</br>
+23425483261119128009282525619020526301639114772473</br>
+31485739107775874425387611746578671169414776421441</br>
+11126358355387136101102326798775641024682403226483</br>
+46417663698066378576813492045302240819727856471983</br>
+96308781543221166912246415911776732253264335686146</br>
+18654522268126887268445968442416107854016768142080</br>
+88502800541436131462308210259417375623899420757136</br>
+27516745731891894562835257044133543758575342698699</br>
+47254703165661399199968262824727064133622217892390</br>
+31760854289437339356188916512504244040089527198378</br>
+73864805847268954624388234375178852014395600571048</br>
+11949884239060613695734231559079670346149143447886</br>
+36041031823507365027785908975782727313050488939890</br>
+09923913503373250855982655867089242612429473670193</br>
+90772713070686917092646254842324074855036608013604</br>
+66895118400936686095463250021458529309500009071510</br>
+58236267293264537382104938724996699339424685516483</br>
+26113414611068026744663733437534076429402668297386</br>
+52209357016263846485285149036293201991996882851718</br>
+39536691345222444708045923966028171565515656661113</br>
+59823112250628905854914509715755390024393153519090</br>
+21071194573002438801766150352708626025378817975194</br>
+78061013715004489917210022201335013106016391541589</br>
+57803711779277522597874289191791552241718958536168</br>
+05947412341933984202187456492564434623925319531351</br>
+03311476394911995072858430658361935369329699289837</br>
+91494193940608572486396883690326556436421664425760</br>
+79147108699843157337496488352927693282207629472823</br>
+81537409961545598798259891093717126218283025848112</br>
+38901196822142945766758071865380650648702613389282</br>
+29949725745303328389638184394477077940228435988341</br>
+00358385423897354243956475556840952248445541392394</br>
+10001620769363684677641301781965937997155746854194</br>
+63348937484391297423914336593604100352343777065888</br>
+67781139498616478747140793263858738624732889645643</br>
+59877466763847946650407411182565837887845485814896</br>
+29612739984134427260860618724554523606431537101127</br>
+46809778704464094758280348769758948328241239292960</br>
+58294861919667091895808983320121031843034012849511</br>
+62035342801441276172858302435598300320420245120728</br>
+72535581195840149180969253395075778400067465526031</br>
+44616705082768277222353419110263416315714740612385</br>
+04258459884199076112872580591139356896014316682831</br>
+76323567325417073420817332230462987992804908514094</br>
+79036887868789493054695570307261900950207643349335</br>
+91060245450864536289354568629585313153371838682656</br>
+17862273637169757741830239860065914816164049449650</br>
+11732131389574706208847480236537103115089842799275</br>
+44268532779743113951435741722197597993596852522857</br>
+45263796289612691572357986620573408375766873884266</br>
+40599099350500081337543245463596750484423528487470</br>
+14435454195762584735642161981340734685411176688311</br>
+86544893776979566517279662326714810338643913751865</br>
+94673002443450054499539974237232871249483470604406</br>
+34716063258306498297955101095418362350303094530973</br>
+35834462839476304775645015008507578949548931393944</br>
+89921612552559770143685894358587752637962559708167</br>
+76438001254365023714127834679261019955852247172201</br>
+77723700417808419423948725406801556035998390548985</br>
+72354674564239058585021671903139526294455439131663</br>
+13453089390620467843877850542393905247313620129476</br>
+91874975191011472315289326772533918146607300089027</br>
+76896311481090220972452075916729700785058071718638</br>
+10549679731001678708506942070922329080703832634534</br>
+52038027860990556900134137182368370991949516489600</br>
+75504934126787643674638490206396401976668559233565</br>
+46391383631857456981471962108410809618846054560390</br>
+38455343729141446513474940784884423772175154334260</br>
+30669883176833100113310869042193903108014378433415</br>
+13709243530136776310849135161564226984750743032971</br>
+67469640666531527035325467112667522460551199581831</br>
+96376370761799191920357958200759560530234626775794</br>
+39363074630569010801149427141009391369138107258137</br>
+81357894005599500183542511841721360557275221035268</br>
+03735726527922417373605751127887218190844900617801</br>
+38897107708229310027976659358387589093956881485602</br>
+63224393726562472776037890814458837855019702843779</br>
+36240782505270487581647032458129087839523245323789</br>
+60298416692254896497156069811921865849267704039564</br>
+81278102179913217416305810554598801300484562997651</br>
+12124153637451500563507012781592671424134210330156</br>
+61653560247338078430286552572227530499988370153487</br>
+93008062601809623815161366903341111386538510919367</br>
+39383522934588832255088706450753947395204396807906</br>
+70868064450969865488016828743437861264538158342807</br>
+53061845485903798217994599681154419742536344399602</br>
+90251001588827216474500682070419376158454712318346</br>
+00726293395505482395571372568402322682130124767945</br>
+22644820910235647752723082081063518899152692889108</br>
+45557112660396503439789627825001611015323516051965</br>
+59042118449499077899920073294769058685778787209829</br>
+01352956613978884860509786085957017731298155314951</br>
+68146717695976099421003618355913877781769845875810</br>
+44662839988060061622984861693533738657877359833616</br>
+13384133853684211978938900185295691967804554482858</br>
+48370117096721253533875862158231013310387766827211</br>
+57269495181795897546939926421979155233857662316762</br>
+75475703546994148929041301863861194391962838870543</br>
+67774322427680913236544948536676800000106526248547</br>
+30558615989991401707698385483188750142938908995068</br>
+54530765116803337322265175662207526951791442252808</br>
+16517166776672793035485154204023817460892328391703</br>
+27542575086765511785939500279338959205766827896776</br>
+44531840404185540104351348389531201326378369283580</br>
+82719378312654961745997056745071833206503455664403</br>
+44904536275600112501843356073612227659492783937064</br>
+78426456763388188075656121689605041611390390639601</br>
+62022153684941092605387688714837989559999112099164</br>
+64644119185682770045742434340216722764455893301277</br>
+81586869525069499364610175685060167145354315814801</br>
+05458860564550133203758645485840324029871709348091</br>
+05562116715468484778039447569798042631809917564228</br>
+09873998766973237695737015808068229045992123661689</br>
+02596273043067931653114940176473769387351409336183</br>
+32161428021497633991898354848756252987524238730775</br>
+59555955465196394401821840998412489826236737714672</br>
+26061633643296406335728107078875816404381485018841</br>
+14318859882769449011932129682715888413386943468285</br>
+90066640806314077757725705630729400492940302420498</br>
+41656547973670548558044586572022763784046682337985</br>
+28271057843197535417950113472736257740802134768260</br>
+45022851579795797647467022840999561601569108903845</br>
+82450267926594205550395879229818526480070683765041</br>
+83656209455543461351341525700659748819163413595567</br>
+19649654032187271602648593049039787489589066127250</br>
+79482827693895352175362185079629778514618843271922</br>
+32238101587444505286652380225328438913752738458923</br>
+84422535472653098171578447834215822327020690287232</br>
+33005386216347988509469547200479523112015043293226</br>
+62827276321779088400878614802214753765781058197022</br>
+26309717495072127248479478169572961423658595782090</br>
+83073323356034846531873029302665964501371837542889</br>
+75579714499246540386817992138934692447419850973346</br>
+26793321072686870768062639919361965044099542167627</br>
+84091466985692571507431574079380532392523947755744</br>
+15918458215625181921552337096074833292349210345146</br>
+26437449805596103307994145347784574699992128599999</br>
+39961228161521931488876938802228108300198601654941</br>
+65426169685867883726095877456761825072759929508931</br>
+80521872924610867639958916145855058397274209809097</br>
+81729323930106766386824040111304024700735085782872</br>
+46271349463685318154696904669686939254725194139929</br>
+14652423857762550047485295476814795467007050347999</br>
+58886769501612497228204030399546327883069597624936</br>
+15101024365553522306906129493885990157346610237122</br>
+35478911292547696176005047974928060721268039226911</br>
+02777226102544149221576504508120677173571202718024</br>
+29681062037765788371669091094180744878140490755178</br>
+20385653909910477594141321543284406250301802757169</br>
+65082096427348414695726397884256008453121406593580</br>
+90412711359200419759851362547961606322887361813673</br>
+73244506079244117639975974619383584574915988097667</br>
+44709300654634242346063423747466608043170126005205</br>
+59284936959414340814685298150539471789004518357551</br>
+54125223590590687264878635752541911288877371766374</br>
+86027660634960353679470269232297186832771739323619</br>
+20077745221262475186983349515101986426988784717193</br>
+96649769070825217423365662725928440620430214113719</br>
+92278526998469884770232382384005565551788908766136</br>
+01304770984386116870523105531491625172837327286760</br>
+07248172987637569816335415074608838663640693470437</br>
+20668865127568826614973078865701568501691864748854</br>
+16791545965072342877306998537139043002665307839877</br>
+63850323818215535597323530686043010675760838908627</br>
+04984188859513809103042359578249514398859011318583</br>
+58406674723702971497850841458530857813391562707603</br>
+56390763947311455495832266945702494139831634332378</br>
+97595568085683629725386791327505554252449194358912</br>
+84050452269538121791319145135009938463117740179715</br>
+12283785460116035955402864405902496466930707769055</br>
+48102885020808580087811577381719174177601733073855</br>
+47580060560143377432990127286772530431825197579167</br>
+92969965041460706645712588834697979642931622965520</br>
+16879730003564630457930884032748077181155533090988</br>
+70255052076804630346086581653948769519600440848206</br>
+59673794731680864156456505300498816164905788311543</br>
+45485052660069823093157776500378070466126470602145</br>
+75057932709620478256152471459189652236083966456241</br>
+05195510522357239739512881816405978591427914816542</br>
+63289200428160913693777372229998332708208296995573</br>
+77273756676155271139225880552018988762011416800546</br>
+87365580633471603734291703907986396522961312801782</br>
+67971728982293607028806908776866059325274637840539</br>
+76918480820410219447197138692560841624511239806201</br>
+13184541244782050110798760717155683154078865439041</br>
+21087303240201068534194723047666672174986986854707</br>
+67812051247367924791931508564447753798537997322344</br>
+56122785843296846647513336573692387201464723679427</br>
+87004250325558992688434959287612400755875694641370</br>
+56251400117971331662071537154360068764773186755871</br>
+48783989081074295309410605969443158477539700943988</br>
+39491443235366853920994687964506653398573888786614</br>
+76294434140104988899316005120767810358861166020296</br>
+11936396821349607501116498327856353161451684576956</br>
+87109002999769841263266502347716728657378579085746</br>
+64607722834154031144152941880478254387617707904300</br>
+01566986776795760909966936075594965152736349811896</br>
+41304331166277471233881740603731743970540670310967</br>
+67657486953587896700319258662594105105335843846560</br>
+23391796749267844763708474978333655579007384191473</br>
+19886271352595462518160434225372996286326749682405</br>
+80602964211463864368642247248872834341704415734824</br>
+81833301640566959668866769563491416328426414974533</br>
+34999948000266998758881593507357815195889900539512</br>
+08535103572613736403436753471410483601754648830040</br>
+78464167452167371904831096767113443494819262681110</br>
+73994825060739495073503169019731852119552635632584</br>
+33909982249862406703107683184466072912487475403161</br>
+79699411397387765899868554170318847788675929026070</br>
+04321266617919223520938227878880988633599116081923</br>
+53555704646349113208591897961327913197564909760001</br>
+39962344455350143464268604644958624769094347048293</br>
+29414041114654092398834443515913320107739441118407</br>
+41076849810663472410482393582740194493566516108846</br>
+31256785297769734684303061462418035852933159734583</br>
+03845541033701091676776374276210213701354854450926</br>
+30719011473184857492331816720721372793556795284439</br>
+25481560913728128406333039373562420016045664557414</br>
+58816605216660873874804724339121295587776390696903</br>
+70788285277538940524607584962315743691711317613478</br>
+38827194168606625721036851321566478001476752310393</br>
+57860689611125996028183930954870905907386135191459</br>
+18195102973278755710497290114871718971800469616977</br>
+70017913919613791417162707018958469214343696762927</br>
+45910994006008498356842520191559370370101104974733</br>
+94938778859894174330317853487076032219829705797511</br>
+91440510994235883034546353492349826883624043327267</br>
+41554030161950568065418093940998202060999414021689</br>
+09007082133072308966211977553066591881411915778362</br>
+72927461561857103721724710095214236964830864102592</br>
+88745799932237495519122195190342445230753513380685</br>
+68073544649951272031744871954039761073080602699062</br>
+58076020292731455252078079914184290638844373499681</br>
+45827337207266391767020118300464819000241308350884</br>
+65841521489912761065137415394356572113903285749187</br>
+69094413702090517031487773461652879848235338297260</br>
+13611098451484182380812054099612527458088109948697</br>
+22161285248974255555160763716750548961730168096138</br>
+03811914361143992106380050832140987604599309324851</br>
+02516829446726066613815174571255975495358023998314</br>
+69822036133808284993567055755247129027453977621404</br>
+93182014658008021566536067765508783804304134310591</br>
+80460680083459113664083488740800574127258670479225</br>
+83191274157390809143831384564241509408491339180968</br>
+40251163991936853225557338966953749026620923261318</br>
+85589158083245557194845387562878612885900410600607</br>
+37465014026278240273469625282171749415823317492396</br>
+83530136178653673760642166778137739951006589528877</br>
+42766263684183068019080460984980946976366733566228</br>
+29151323527888061577682781595886691802389403330764</br>
+41912403412022316368577860357276941541778826435238</br>
+13190502808701857504704631293335375728538660588890</br>
+45831114507739429352019943219711716422350056440429</br>
+79892081594307167019857469273848653833436145794634</br>
+17592257389858800169801475742054299580124295810545</br>
+65108310462972829375841611625325625165724980784920</br>
+99897990620035936509934721582965174135798491047111</br>
+66079158743698654122234834188772292944633517865385</br>
+67319625598520260729476740726167671455736498121056</br>
+77716893484917660771705277187601199908144113058645</br>
+57791052568430481144026193840232247093924980293355</br>
+07318458903553971330884461741079591625117148648744</br>
+68611247605428673436709046678468670274091881014249</br>
+71114965781772427934707021668829561087779440504843</br>
+75284433751088282647719785400065097040330218625561</br>
+47332117771174413350281608840351781452541964320309</br>
+57601869464908868154528562134698835544456024955666</br>
+84366029221951248309106053772019802183101032704178</br>
+38665447181260397190688462370857518080035327047185</br>
+65949947612424811099928867915896904956394762460842</br>
+40659309486215076903149870206735338483495508363660</br>
+17848771060809804269247132410009464014373603265645</br>
+18456679245666955100150229833079849607994988249706</br>
+17236744936122622296179081431141466094123415935930</br>
+95854079139087208322733549572080757165171876599449</br>
+85693795623875551617575438091780528029464200447215</br>
+39628074636021132942559160025707356281263873310600</br>
+58910652457080244749375431841494014821199962764531</br>
+06800663118382376163966318093144467129861552759820</br>
+14514102756006892975024630401735148919457636078935</br>
+28555053173314164570504996443890936308438744847839</br>
+61684051845273288403234520247056851646571647713932</br>
+37755172947951261323982296023945485797545865174587</br>
+87713318138752959809412174227300352296508089177705</br>
+06825924882232215493804837145478164721397682096332</br>
+05083056479204820859204754998573203888763916019952</br>
+40918938945576768749730856955958010659526503036266</br>
+15975066222508406742889826590751063756356996821151</br>
+09496697445805472886936310203678232501823237084597</br>
+90111548472087618212477813266330412076216587312970</br>
+81123075815982124863980721240786887811450165582513</br>
+61789030708608701989758898074566439551574153631931</br>
+91981070575336633738038272152798849350397480015890</br>
+51942087971130805123393322190346624991716915094854</br>
+14018710603546037946433790058909577211808044657439</br>
+62806186717861017156740967662080295766577051291209</br>
+90794430463289294730615951043090222143937184956063</br>
+40561893425130572682914657832933405246350289291754</br>
+70872564842600349629611654138230077313327298305001</br>
+60256724014185152041890701154288579920812198449315</br>
+69990591820118197335001261877280368124819958770702</br>
+07532406361259313438595542547781961142935163561223</br>
+49666152261473539967405158499860355295332924575238</br>
+88101362023476246690558164389678630976273655047243</br>
+48643071218494373485300606387644566272186661701238</br>
+12771562137974614986132874411771455244470899714452</br>
+28856629424402301847912054784985745216346964489738</br>
+92062401943518310088283480249249085403077863875165</br>
+91130287395878709810077271827187452901397283661484</br>
+21428717055317965430765045343246005363614726181809</br>
+69976933486264077435199928686323835088756683595097</br>
+26557481543194019557685043724800102041374983187225</br>
+96773871549583997184449072791419658459300839426370</br>
+20875635398216962055324803212267498911402678528599</br>
+67340524203109179789990571882194939132075343170798</br>
+00237365909853755202389116434671855829068537118979</br>
+52626234492483392496342449714656846591248918556629</br>
+58932990903523923333364743520370770101084388003290</br>
+75983421701855422838616172104176030116459187805393</br>
+67447472059985023582891833692922337323999480437108</br>
+41965947316265482574809948250999183300697656936715</br>
+96893644933488647442135008407006608835972350395323</br>
+40179582557036016936990988671132109798897070517280</br>
+75585519126993067309925070407024556850778679069476</br>
+61262980822516331363995211709845280926303759224267</br>
+42575599892892783704744452189363203489415521044597</br>
+26188380030067761793138139916205806270165102445886</br>
+92476492468919246121253102757313908404700071435613</br>
+62316992371694848132554200914530410371354532966206</br>
+39210547982439212517254013231490274058589206321758</br>
+94943454890684639931375709103463327141531622328055</br>
+22972979538018801628590735729554162788676498274186</br>
+16421878988574107164906919185116281528548679417363</br>
+89066538857642291583425006736124538491606741373401</br>
+73572779956341043326883569507814931378007362354180</br>
+07061918026732855119194267609122103598746924117283</br>
+74931261633950012395992405084543756985079570462226</br>
+64619000103500490183034153545842833764378111988556</br>
+31877779253720116671853954183598443830520376281944</br>
+07615941068207169703022851522505731260930468984234</br>
+33152732131361216582808075212631547730604423774753</br>
+50595228717440266638914881717308643611138906942027</br>
+90881431194487994171540421034121908470940802540239</br>
+32942945493878640230512927119097513536000921971105</br>
+41209668311151632870542302847007312065803262641711</br>
+61659576132723515666625366727189985341998952368848</br>
+30999302757419916463841427077988708874229277053891</br>
+22717248632202889842512528721782603050099451082478</br>
+35729056919885554678860794628053712270424665431921</br>
+45281760741482403827835829719301017888345674167811</br>
+39895475044833931468963076339665722672704339321674</br>
+54218245570625247972199786685427989779923395790575</br>
+81890622525473582205236424850783407110144980478726</br>
+69199018643882293230538231855973286978092225352959</br>
+10173414073348847610055640182423921926950620831838</br>
+14546983923664613639891012102177095976704908305081</br>
+85470419466437131229969235889538493013635657618610</br>
+60622287055994233716310212784574464639897381885667</br>
+46260879482018647487672727222062676465338099801966</br>
+88368099415907577685263986514625333631245053640261</br>
+05696055131838131742611844201890888531963569869627</br>
+95036738424313011331753305329802016688817481342988</br>
+68158557781034323175306478498321062971842518438553</br>
+44276201282345707169885305183261796411785796088881</br>
+50329602290705614476220915094739035946646916235396</br>
+80920139457817589108893199211226007392814916948161</br>
+52738427362642980982340632002440244958944561291670</br>
+49508235812487391799648641133480324757775219708932</br>
+77226234948601504665268143987705161531702669692970</br>
+49283162855042128981467061953319702695072143782304</br>
+76875280287354126166391708245925170010714180854800</br>
+63692325946201900227808740985977192180515853214739</br>
+26532515590354102092846659252999143537918253145452</br>
+90598415817637058927906909896911164381187809435371</br>
+52133226144362531449012745477269573939348154691631</br>
+16249288735747188240715039950094467319543161938554</br>
+85207665738825139639163576723151005556037263394867</br>
+20820780865373494244011579966750736071115935133195</br>
+91971209489647175530245313647709420946356969822266</br>
+73775209945168450643623824211853534887989395673187</br>
+80660610788544000550827657030558744854180577889171</br>
+92078814233511386629296671796434687600770479995378</br>
+83387870348718021842437342112273940255717690819603</br>
+09201824018842705704609262256417837526526335832424</br>
+06612533115294234579655695025068100183109004112453</br>
+79015332966156970522379210325706937051090830789479</br>
+99900499939532215362274847660361367769797856738658</br>
+46709366795885837887956259464648913766521995882869</br>
+33801836011932368578558558195556042156250883650203</br>
+32202451376215820461810670519533065306060650105488</br>
+71672453779428313388716313955969058320834168984760</br>
+65607118347136218123246227258841990286142087284956</br>
+87963932546428534307530110528571382964370999035694</br>
+88852851904029560473461311382638788975517885604249</br>
+98748316382804046848618938189590542039889872650697</br>
+62020199554841265000539442820393012748163815853039</br>
+64399254702016727593285743666616441109625663373054</br>
+09219519675148328734808957477775278344221091073111</br>
+35182804603634719818565557295714474768255285786334</br>
+93428584231187494400032296906977583159038580393535</br>
+21358860079600342097547392296733310649395601812237</br>
+81285458431760556173386112673478074585067606304822</br>
+94096530411183066710818930311088717281675195796753</br>
+47188537229309616143204006381322465841111157758358</br>
+58113501856904781536893813771847281475199835050478</br>
+12977185990847076219746058874232569958288925350419</br>
+37958260616211842368768511418316068315867994601652</br>
+05774052942305360178031335726326705479033840125730</br>
+59123396018801378254219270947673371919872873852480</br>
+57421248921183470876629667207272325650565129333126</br>
+05950577772754247124164831283298207236175057467387</br>
+01282095755443059683955556868611883971355220844528</br>
+52640081252027665557677495969626612604565245684086</br>
+13923826576858338469849977872670655519185446869846</br>
+94784957346226062942196245570853712727765230989554</br>
+50193037732166649182578154677292005212667143463209</br>
+63789185232321501897612603437368406719419303774688</br>
+09992968775824410478781232662531818459604538535438</br>
+39114496775312864260925211537673258866722604042523</br>
+49108702695809964759580579466397341906401003636190</br>
+40420331135793365424263035614570090112448008900208</br>
+01478056603710154122328891465722393145076071670643</br>
+55682743774396578906797268743847307634645167756210</br>
+30986040927170909512808630902973850445271828927496</br>
+89212106670081648583395537735919136950153162018908</br>
+88748421079870689911480466927065094076204650277252</br>
+86507289053285485614331608126930056937854178610969</br>
+69202538865034577183176686885923681488475276498468</br>
+82194973972970773718718840041432312763650481453112</br>
+28509900207424092558592529261030210673681543470152</br>
+52348786351643976235860419194129697690405264832347</br>
+00991115424260127343802208933109668636789869497799</br>
+40012601642276092608234930411806438291383473546797</br>
+25399262338791582998486459271734059225620749105308</br>
+53153718291168163721939518870095778818158685046450</br>
+76993439409874335144316263303172477474868979182092</br>
+39480833143970840673084079589358108966564775859905</br>
+56376952523265361442478023082681183103773588708924</br>
+06130313364773710116282146146616794040905186152603</br>
+60092521947218890918107335871964142144478654899528</br>
+58234394705007983038853886083103571930600277119455</br>
+80219119428999227223534587075662469261776631788551</br>
+44350218287026685610665003531050216318206017609217</br>
+98468493686316129372795187307897263735371715025637</br>
+87335797718081848784588665043358243770041477104149</br>
+34927438457587107159731559439426412570270965125108</br>
+11554824793940359768118811728247215825010949609662</br>
+53933953809221955919181885526780621499231727631632</br>
+18339896938075616855911752998450132067129392404144</br>
+59386239880938124045219148483164621014738918251010</br>
+90967738690664041589736104764365000680771056567184</br>
+86281496371118832192445663945814491486165500495676</br>
+98269030891118568798692947051352481609174324301538</br>
+36847072928989828460222373014526556798986277679680</br>
+91469798378268764311598832109043715611299766521539</br>
+63546442086919756737000573876497843768628768179249</br>
+74694384274652563163230055513041742273416464551278</br>
+12784577772457520386543754282825671412885834544435</br>
+13256205446424101103795546419058116862305964476958</br>
+70540721419852121067343324107567675758184569906930</br>
+46047522770167005684543969234041711089888993416350</br>
+58515788735343081552081177207188037910404698306957</br>
+86854739376564336319797868036718730796939242363214</br>
+48450354776315670255390065423117920153464977929066</br>
+24150832885839529054263768766896880503331722780018</br>
+58850697362324038947004718976193473443084374437599</br>
+25034178807972235859134245813144049847701732361694</br>
+71976571535319775499716278566311904691260918259124</br>
+98903676541769799036237552865263757337635269693443</br>
+54400473067198868901968147428767790866979688522501</br>
+63694985673021752313252926537589641517147955953878</br>
+42784998664563028788319620998304945198743963690706</br>
+82762657485810439112232618794059941554063270131989</br>
+89570376110532360629867480377915376751158304320849</br>
+87209202809297526498125691634250005229088726469252</br>
+84666104665392171482080130502298052637836426959733</br>
+70705392278915351056888393811324975707133102950443</br>
+03467159894487868471164383280506925077662745001220</br>
+03526203709466023414648998390252588830148678162196</br>
+77519458316771876275720050543979441245990077115205</br>
+15461993050983869825428464072555409274031325716326</br>
+40792934183342147090412542533523248021932277075355</br>
+54679587163835875018159338717423606155117101312352</br>
+56334858203651461418700492057043720182617331947157</br>
+00867578539336078622739558185797587258744102542077</br>
+10547536129404746010009409544495966288148691590389</br>
+90718659805636171376922272907641977551777201042764</br>
+96949611056220592502420217704269622154958726453989</br>
+22769766031052498085575947163107587013320886146326</br>
+64125911486338812202844406941694882615295776253250</br>
+19870359870674380469821942056381255833436421949232</br>
+27593722128905642094308235254408411086454536940496</br>
+92714940033197828613181861888111184082578659287574</br>
+26384450059944229568586460481033015388911499486935</br>
+43603022181094346676400002236255057363129462629609</br>
+61987605642599639461386923308371962659547392346241</br>
+34597795748524647837980795693198650815977675350553</br>
+91899115133525229873611277918274854200868953965835</br>
+94219633315028695611920122988898870060799927954111</br>
+88269023078913107603617634779489432032102773359416</br>
+90865007193280401716384064498787175375678118532132</br>
+84082165711075495282949749362146082155832056872321</br>
+85574065161096274874375098092230211609982633033915</br>
+46949464449100451528092508974507489676032409076898</br>
+36529406579201983152654106581368237919840906457124</br>
+68948470209357761193139980246813405200394781949866</br>
+20262400890215016616381353838151503773502296607462</br>
+79529103840686855690701575166241929872444827194293</br>
+31004854824454580718897633003232525821581280327467</br>
+96200281476243182862217105435289834820827345168018</br>
+61317195933247110746622285087106661177034653528395</br>
+77625997744672185715816126411143271794347885990892</br>
+80848669491413909771673690027775850268664654056595</br>
+03948678411107901161040085727445629384254941675946</br>
+05487117235946429105850909950214958793112196135908</br>
+31588262068233215615308683373083817327932819698387</br>
+50870834838804638847844188400318471269745437093732</br>
+98362402875197920802321878744882872843727378017827</br>
+00805878241074935751488997891173974612932035108143</br>
+27032514090304874622629423443275712600866425083331</br>
+87688650756429271605525289544921537651751492196367</br>
+18104943531785838345386525565664065725136357506435</br>
+32365089367904317025978781771903148679638408288102</br>
+09461490079715137717099061954969640070867667102330</br>
+04867263147551053723175711432231741141168062286420</br>
+63889062101923552235467116621374996932693217370431</br>
+05987225039456574924616978260970253359475020913836</br>
+67377289443869640002811034402608471289900074680776</br>
+48440887113413525033678773167977093727786821661178</br>
+65344231732264637847697875144332095340001650692130</br>
+54647689098505020301504488083426184520873053097318</br>
+94929164253229336124315143065782640702838984098416</br>
+02950309241897120971601649265613413433422298827909</br>
+92178604267981245728534580133826099587717811310216</br>
+73402565627440072968340661984806766158050216918337</br>
+23680399027931606420436812079900316264449146190219</br>
+45822969099212278855394878353830564686488165556229</br>
+43156731282743908264506116289428035016613366978240</br>
+51770155219626522725455850738640585299830379180350</br>
+43287670380925216790757120406123759632768567484507</br>
+91511473134400018325703449209097124358094479004624</br>
+94313455028900680648704293534037436032625820535790</br>
+11839564908935434510134296961754524957396062149028</br>
+87289327925206965353863964432253883275224996059869</br>
+74759882329916263545973324445163755334377492928990</br>
+58117578635555562693742691094711700216541171821975</br>
+05198317871371060510637955585889055688528879890847</br>
+50915764639074693619881507814685262133252473837651</br>
+19299015610918977792200870579339646382749068069876</br>
+91681974923656242260871541761004306089043779766785</br>
+19661891404144925270480881971498801542057787006521</br>
+59400928977760133075684796699295543365613984773806</br>
+03943688958876460549838714789684828053847017308711</br>
+17761159663505039979343869339119789887109156541709</br>
+13308260764740630571141109883938809548143782847452</br>
+88383680794188843426662220704387228874139478010177</br>
+21392281911992365405516395893474263953824829609036</br>
+90028835932774585506080131798840716244656399794827</br>
+57836501955142215513392819782269842786383916797150</br>
+91262410548725700924070045488485692950448110738087</br>
+99654748156891393538094347455697212891982717702076</br>
+66136024895814681191336141212587838955773571949863</br>
+17210844398901423948496659251731388171602663261931</br>
+06536653504147307080441493916936326237376777709585</br>
+03132559900957627319573086480424677012123270205337</br>
+42667053142448208168130306397378736642483672539837</br>
+48769098060218278578621651273856351329014890350988</br>
+32706172589325753639939790557291751600976154590447</br>
+71692265806315111028038436017374742152476085152099</br>
+01615858231257159073342173657626714239047827958728</br>
+15050956330928026684589376496497702329736413190609</br>
+82740633531089792464242134583740901169391964250459</br>
+12881340349881063540088759682005440836438651661788</br>
+05576089568967275315380819420773325979172784376256</br>
+61184319891025007491829086475149794003160703845549</br>
+46538594602745244746681231468794344161099333890899</br>
+26384118474252570445725174593257389895651857165759</br>
+61481266020310797628254165590506042479114016957900</br>
+33835657486925280074302562341949828646791447632277</br>
+40055294609039401775363356554719310001754300475047</br>
+19144899841040015867946179241610016454716551337074</br>
+07395026044276953855383439755054887109978520540117</br>
+51697475813449260794336895437832211724506873442319</br>
+89878844128542064742809735625807066983106979935260</br>
+69339213568588139121480735472846322778490808700246</br>
+77763036055512323866562951788537196730346347012229</br>
+39581606792509153217489030840886516061119011498443</br>
+41235012464692802880599613428351188471544977127847</br>
+33617662850621697787177438243625657117794500644777</br>
+18370221999106695021656757644044997940765037999954</br>
+84500271066598781360380231412683690578319046079276</br>
+52972776940436130230517870805465115424693952651271</br>
+01052927070306673024447125973939950514628404767431</br>
+36373997825918454117641332790646063658415292701903</br>
+02760173394748669603486949765417524293060407270050</br>
+59039503148522921392575594845078867977925253931765</br>
+15641619716844352436979444735596426063339105512682</br>
+60615957262170366985064732812667245219890605498802</br>
+80782881429796336696744124805982192146339565745722</br>
+10229867759974673812606936706913408155941201611596</br>
+01902377535255563006062479832612498812881929373434</br>
+76862689219239777833910733106588256813777172328315</br>
+32908252509273304785072497713944833389255208117560</br>
+84529665905539409655685417060011798572938139982583</br>
+19293679100391844099286575605993598910002969864460</br>
+97471471847010153128376263114677420914557404181590</br>
+88000649432378558393085308283054760767995243573916</br>
+31221886057549673832243195650655460852881201902363</br>
+64471270374863442172725787950342848631294491631847</br>
+53475314350413920961087960577309872013524840750576</br>
+37199253650470908582513936863463863368042891767107</br>
+60211115982887553994012007601394703366179371539630</br>
+61398636554922137415979051190835882900976566473007</br>
+33879314678913181465109316761575821351424860442292</br>
+44530411316065270097433008849903467540551864067734</br>
+26035834096086055337473627609356588531097609942383</br>
+47382222087292464497684560579562516765574088410321</br>
+73134562773585605235823638953203853402484227337163</br>
+91239732159954408284216666360232965456947035771848</br>
+73442034227706653837387506169212768015766181095420</br>
+09770836360436111059240911788954033802142652394892</br>
+96864398089261146354145715351943428507213534530183</br>
+15875628275733898268898523557799295727645229391567</br>
+47756667605108788764845349363606827805056462281359</br>
+88858792599409464460417052044700463151379754317371</br>
+87756039815962647501410906658866162180038266989961</br>
+96558058720863972117699521946678985701179833244060</br>
+18115756580742841829106151939176300591943144346051</br>
+54047710570054339000182453117733718955857603607182</br>
+86050635647997900413976180895536366960316219311325</br>
+02238517916720551806592635180362512145759262383693</br>
+48222665895576994660491938112486609099798128571823</br>
+49400661555219611220720309227764620099931524427358</br>
+94887105766238946938894464950939603304543408421024</br>
+62401048723328750081749179875543879387381439894238</br>
+01176270083719605309438394006375611645856094312951</br>
+75977139353960743227924892212670458081833137641658</br>
+18269562105872892447740035947009268662659651422050</br>
+63007859200248829186083974373235384908396432614700</br>
+05324235406470420894992102504047267810590836440074</br>
+66380020870126664209457181702946752278540074508552</br>
+37772089058168391844659282941701828823301497155423</br>
+52359117748186285929676050482038643431087795628929</br>
+25405638946621948268711042828163893975711757786915</br>
+43016505860296521745958198887868040811032843273986</br>
+71986213062055598552660364050462821523061545944744</br>
+89908839081999738747452969810776201487134000122535</br>
+52224669540931521311533791579802697955571050850747</br>
+38747507580687653764457825244326380461430428892359</br>
+34852961058269382103498000405248407084403561167817</br>
+17051281337880570564345061611933042444079826037795</br>
+11985486945591520519600930412710072778493015550388</br>
+95360338261929343797081874320949914159593396368110</br>
+62755729527800425486306005452383915106899891357882</br>
+00194117865356821491185282078521301255185184937115</br>
+03422159542244511900207393539627400208110465530207</br>
+93286725474054365271759589350071633607632161472581</br>
+54076420530200453401835723382926619153083540951202</br>
+26329165054426123619197051613839357326693760156914</br>
+42994494374485680977569630312958871916112929468188</br>
+49363386473927476012269641588489009657170861605981</br>
+47204467428664208765334799858222090619802173211614</br>
+23041947775499073873856794118982466091309169177227</br>
+42072333676350326783405863019301932429963972044451</br>
+79288122854478211953530898910125342975524727635730</br>
+22628138209180743974867145359077863353016082155991</br>
+13141442050914472935350222308171936635093468658586</br>
+56314855575862447818620108711889760652969899269328</br>
+17870557643514338206014107732926106343152533718224</br>
+33852635202177354407152818981376987551575745469397</br>
+27150488469793619500477720970561793913828989845327</br>
+42622728864710888327017372325881824465843624958059</br>
+25603381052156062061557132991560848920643403033952</br>
+62263451454283678698288074251422567451806184149564</br>
+68611163540497189768215422772247947403357152743681</br>
+94098920501136534001238467142965518673441537416150</br>
+42563256713430247655125219218035780169240326699541</br>
+74608759240920700466934039651017813485783569444076</br>
+04702325407555577647284507518268904182939661133101</br>
+60131119077398632462778219023650660374041606724962</br>
+49013743321724645409741299557052914243820807609836</br>
+48234659738866913499197840131080155813439791948528</br>
+30436739012482082444814128095443773898320059864909</br>
+15950532285791457688496257866588599917986752055455</br>
+80990045564611787552493701245532171701942828846174</br>
+02736649978475508294228020232901221630102309772151</br>
+56944642790980219082668986883426307160920791408519</br>
+76952355534886577434252775311972474308730436195113</br>
+96119080030255878387644206085044730631299277888942</br>
+72918972716989057592524467966018970748296094919064</br>
+87646937027507738664323919190422542902353189233772</br>
+93166736086996228032557185308919284403805071030064</br>
+77684786324319100022392978525537237556621364474009</br>
+67605394398382357646069924652600890906241059042154</br>
+53927904411529580345334500256244101006359530039598</br>
+86446616959562635187806068851372346270799732723313</br>
+46939714562855426154676506324656766202792452085813</br>
+47717608521691340946520307673391841147504140168924</br>
+12131982688156866456148538028753933116023229255561</br>
+89410429953356400957864953409351152664540244187759</br>
+49316930560448686420862757201172319526405023099774</br>
+56764783848897346431721598062678767183800524769688</br>
+40849891850861490034324034767426862459523958903585</br>
+82135006450998178244636087317754378859677672919526</br>
+11121385919472545140030118050343787527766440276261</br>
+89410175768726804281766238606804778852428874302591</br>
+45247073950546525135339459598789619778911041890292</br>
+94381856720507096460626354173294464957661265195349</br>
+57018600154126239622864138977967333290705673769621</br>
+56498184506842263690367849555970026079867996261019</br>
+03933126376855696876702929537116252800554310078640</br>
+87289392257145124811357786276649024251619902774710</br>
+90335933309304948380597856628844787441469841499067</br>
+12376478958226329490467981208998485716357108783119</br>
+18486302545016209298058292083348136384054217200561</br>
+21989353669371336733392464416125223196943471206417</br>
+37549121635700857369439730597970971972666664226743</br>
+11177621764030686813103518991122713397240368870009</br>
+96862922546465006385288620393800504778276912835603</br>
+37254825579391298525150682996910775425764748832534</br>
+14121328006267170940090982235296579579978030182824</br>
+28490221470748111124018607613415150387569830918652</br>
+78065889668236252393784527263453042041880250844236</br>
+31903833183845505223679923577529291069250432614469</br>
+50109861088899914658551881873582528164302520939285</br>
+25807796973762084563748211443398816271003170315133</br>
+44023095263519295886806908213558536801610002137408</br>
+51154484912685841268695899174149133820578492800698</br>
+25519574020181810564129725083607035685105533178784</br>
+08290000415525118657794539633175385320921497205266</br>
+07831260281961164858098684587525129997404092797683</br>
+17663991465538610893758795221497173172813151793290</br>
+44311218158710235187407572221001237687219447472093</br>
+49312324107065080618562372526732540733324875754482</br>
+96757345001932190219911996079798937338367324257610</br>
+39389853492787774739805080800155447640610535222023</br>
+25409443567718794565430406735896491017610775948364</br>
+54082348613025471847648518957583667439979150851285</br>
+80206078205544629917232020282229148869593997299742</br>
+97471155371858924238493855858595407438104882624648</br>
+78805330427146301194158989632879267832732245610385</br>
+21970111304665871005000832851773117764897352309266</br>
+61234588873102883515626446023671996644554727608310</br>
+11878838915114934093934475007302585581475619088139</br>
+87523578123313422798665035227253671712307568610450</br>
+04548970360079569827626392344107146584895780241408</br>
+15840522953693749971066559489445924628661996355635</br>
+06526234053394391421112718106910522900246574236041</br>
+30093691889255865784668461215679554256605416005071</br>
+27664176605687427420032957716064344860620123982169</br>
+82717231978268166282499387149954491373020518436690</br>
+76723577400053932662622760323659751718925901801104</br>
+29038427418550789488743883270306328327996300720069</br>
+80122443651163940869222207453202446241211558043545</br>
+42064215121585056896157356414313068883443185280853</br>
+97592773443365538418834030351782294625370201578215</br>
+73732655231857635540989540332363823192198921711774</br>
+49469403678296185920803403867575834111518824177439</br>
+14507736638407188048935825686854201164503135763335</br>
+55094403192367203486510105610498727264721319865434</br>
+35450409131859513145181276437310438972507004981987</br>
+05217627249406521461995923214231443977654670835171</br>
+47493679861865527917158240806510637995001842959387</br>
+99158350171580759883784962257398512129810326379376</br>
+21832245659423668537679911314010804313973233544909</br>
+08249104991433258432988210339846981417157560108297</br>
+06583065211347076803680695322971990599904451209087</br>
+27577622535104090239288877942463048328031913271049</br>
+54785991801969678353214644411892606315266181674431</br>
+93550817081875477050802654025294109218264858213857</br>
+52668815558411319856002213515888721036569608751506</br>
+31875330029421186822218937755460272272912905042922</br>
+59787710667873840000616772154638441292371193521828</br>
+49982435092089180168557279815642185819119749098573</br>
+05703326676464607287574305653726027689823732597450</br>
+84479649545648030771598153955827779139373601717422</br>
+99602735310276871944944491793978514463159731443535</br>
+18504914139415573293820485421235081739125497498193</br>
+08714396615132942045919380106231421774199184060180</br>
+34794988769105155790555480695387854006645337598186</br>
+28464199052204528033062636956264909108276271159038</br>
+56995051246529996062855443838330327638599800792922</br>
+84665950355121124528408751622906026201185777531374</br>
+79493620554964010730013488531507354873539056029089</br>
+33526400713274732621960311773433943673385759124508</br>
+14933573691166454128178817145402305475066713651825</br>
+82848980995121391939956332413365567770980030819102</br>
+72040997148687418134667006094051021462690280449159</br>
+64654533010775469541308871416531254481306119240782</br>
+11886900560277818242350226961893443525476335735364</br>
+85619363254417756613981703930632872166905722259745</br>
+20919291726219984440964615826945638023950283712168</br>
+64465617852355651641277128269186886155727162014749</br>
+34052276946595712198314943381622114006936307430444</br>
+17328478610177774383797703723179525543410722344551</br>
+25555899986461838767649039724611679590181000350989</br>
+28641204195163551108763204267612979826529425882951</br>
+14127584126273279079880755975185157684126474220947</br>
+97218433093529726652100156625145529947451276315509</br>
+17636730259462132930190402837954246323258550301096</br>
+70692272022707486341900543830265068121414213505715</br>
+41750575086399076739463351462090828889349383764393</br>
+99256900604067311422093312195936202982972351163259</br>
+38677224147791162957278075239505625158160313335938</br>
+23115005186268905306583681299881086632632719806112</br>
+71548858798093487912913707498230575929091862939195</br>
+01472119758606727009254771802575033773079939713453</br>
+95326461952699965963856549175904583335857991020127</br>
+13204583903200853878881633637685182083727885131175</br>
+22776960978796214237216254521459128183179821604411</br>
+13116714069148271709810154577819392023115638719508</br>
+05024679725792497605772625913328559726371211201905</br>
+72077140914864507409492671803581515757151405039761</br>
+09638467555692989703835473141002238025834687673501</br>
+29775413279532060971154506484212185936490997917766</br>
+87477448188287063231551586503289816422828823274686</br>
+61065927321979071623846421534898524762167890502609</br>
+98045266483929542357287343977680495774091449538391</br>
+57556548545905897649519851380100795801078375994577</br>
+52991967005476022525520344539887125387801719607181</br>
+64078124847847257912407824544361682345239570689514</br>
+27226975043187363326301110305342333582160933319121</br>
+88066082683414289104151732472160533558499932245487</br>
+30778822905252324234861531520976938461042582849714</br>
+96347534183756200301491570327968530186863157248840</br>
+15266398356895636346574353217834931998255421173084</br>
+67745297085839507616458229630324424328237737450517</br>
+02856069806788952176819815671078163340526675953942</br>
+49262807569683261074953233905362230908070814559198</br>
+37355377748742029039018142937311529334644468151212</br>
+94509759653430628421531944572711861490001765055817</br>
+70953024688752632501197052094761594167687277844720</br>
+00192789137251841622857783792284439084301181121496</br>
+36642465903363419454065718354477191244662125939265</br>
+66203068885200555991212353637182269225317814587925</br>
+93750441448933981608657900876165024635197045828895</br>
+48179375668104647461410514249887025213993687050937</br>
+23054477341126413548928068410591077166778212383328</br>
+10262185587751312721179344448201440425745083063944</br>
+73836379390628300897330624138061458941422769474793</br>
+16657176231824721683506780764875734204915576282175</br>
+83972975134478990696589532548940335615613167403276</br>
+47246921250575911625152965456854463349811431767025</br>
+72956618447754874693784642337372389819206620485118</br>
+94378868224807279352022501796545343757274163910791</br>
+97295295081294292220534771730418447791567399173841</br>
+83117103625243957161527146690058147000026330104526</br>
+43547865903290733205468338872078735444762647925297</br>
+69017091200787418373673508771337697768349634425241</br>
+99499513883150748775374338494582597655609965559543</br>
+18040920178497184685497370696212088524377013853757</br>
+68141663272241263442398215294164537800049250726276</br>
+51507890850712659970367087266927643083772296859851</br>
+69122305037462744310852934305273078865283977335246</br>
+01746352770320593817912539691562106363762588293757</br>
+13738407544064689647831007045806134467312715911946</br>
+08435935825987782835266531151065041623295329047772</br>
+17408355934972375855213804830509000964667608830154</br>
+06128243087406455944318534137552201663058121110334</br>
+53120745086824339432159043594430312431227471385842</br>
+03039010607094031523555617276799416002039397509989</br>
+76293353258555756248089966918298642226775023601932</br>
+57974726742578211119734709402357457222271212526852</br>
+38429587427350156366009318804549333898974157149054</br>
+41825597380808715652814301026704602843168192303925</br>
+35297795765862414392701549740879273131051636119137</br>
+57700892956482332364829826302460797587576774537716</br>
+01024908046243018565241617566556001608591215345562</br>
+67602192689982855377872583145144082654583484409478</br>
+46317877737479465358016996077940556870119232860804</br>
+11309046293508718271259346687127666948738998245985</br>
+27786499569165464029458935064964335809824765965165</br>
+14209098675520380830920323048734270346828875160407</br>
+15466538346196112230137594515792526967436425319273</br>
+90036038608236450762698827497618723575476762889950</br>
+75211480485252795084503395857083813047693788132112</br>
+36742813194879502280663201700224603319896719706491</br>
+63741175854851878484012054844672588851401562725019</br>
+82171906696081262778548596481836962141072171421498</br>
+63619187747545096503089570994709343378569816744658</br>
+28267911940611956037845397855839240761276344105766</br>
+75102430755981455278616781594965706255975507430652</br>
+10853015979080733437360794328667578905334836695554</br>
+86803913433720156498834220893399971641479746938696</br>
+90548008919306713805717150585730714881564992071408</br>
+67582596028760564597824237702424698053280566327870</br>
+41926768467116266879463486950464507420219373945259</br>
+26266861355294062478136120620263649819999949840514</br>
+38682852589563422643287076632993048917234007254717</br>
+64188685351372332667877921738347541480022803392997</br>
+35793615241275582956927683723123479898944627433045</br>
+45667900620324205163962825884430854383072014956721</br>
+06460533238537203143242112607424485845094580494081</br>
+82092763914000854042202355626021856434899414543995</br>
+04109805918179488826280520664410863190016885681551</br>
+69229486203010738897181007709290590480749092427141</br>
+01893354281842999598816966099383696164438152887721</br>
+40852680887574882932587358099056707558170179491619</br>
+06114001908553744882726200936685604475596557476485</br>
+67400817738170330738030547697360978654385938218722</br>
+05839023444435088674998665060406458743460053318274</br>
+36296177862518081893144363251205107094690813586440</br>
+51922951293245007883339878842933934243512634336520</br>
+43858129128343452973086529097833006712617981303167</br>
+94385535726296998740359570458452230856390098913179</br>
+47594875212639707837594486113945196028675121056163</br>
+89760088800927461158608002078033415914517970730368</br>
+35196977766076373785333012024120112046988609209339</br>
+08536577322239241244905153278095095586645947763448</br>
+22699860748132973026309750288121035177231244650953</br>
+49653693090018637764094094349837313251321862080214</br>
+80992268550294845466181471555744470966953017769043</br>
+42720318927706047177845279391604722815343798035396</br>
+79861424370956683221491465438014593829277393396032</br>
+75404800955223181666738035718393275707714204672383</br>
+86246178039762923771312095807893638414479298025880</br>
+65522129262093623930637313496640186619510811583471</br>
+17331202580586672763999276357907806381881306915636</br>
+62741254312595899361196476261014055635033995231403</br>
+23113819656236327198961837254845333702062563464223</br>
+95276694356837676136871196292181875457608161705303</br>
+15907288287007123136663087227549186613957737305460</br>
+65997437810987649802414011242142773668082751390959</br>
+31340415582626678951084677611866595766016599817808</br>
+94149857549762843878561002637965431783136340251358</br>
+14161151902096499133548733131115022700681930135929</br>
+59597164019719605362503355847998096348871803911161</br>
+28135959685654788683258564378961731597620024196215</br>
+52896297904819822199462269487137462444729093456470</br>
+02853769495885959160678928249105441251599630078136</br>
+83674902093749157328962700286568293444313423473512</br>
+39298259166739503425995868970697267332582735903121</br>
+28874666045146148785034614282776599160809039865257</br>
+57172630818334944418201935333850712923457743755793</br>
+44062178711330063106003324053991693682603746176638</br>
+56575887758020122936635327026710068126182517291460</br>
+82025418928859352444910701382062115538277935652969</br>
+14576502048643282865557934707209634807372692141186</br>
+89546732276775133569019015372366903686538916129168</br>
+88878764075254934942497334271811788927599315967193</br>
+54758988097924525262363659036320070854440784544797</br>
+34829180208204492667063442043755532505052752283377</br>
+88870408040335319234076856301093477721256390886404</br>
+13101073817853338316038135280828119040832564401842</br>
+05374679299262203769871801806112262449090924264198</br>
+58208617511771137890516091403815750033664241560952</br>
+16328197122335023167422600567941281406217219641842</br>
+70578432895980288233505982820819666624903585778994</br>
+03331522748177769528436816300885317696947836905806</br>
+71064828083598046698841098135158654906933319522394</br>
+36328792399053481098783027450017206543369906611778</br>
+45543646877236318444647680691428280045510746866453</br>
+92805399409108754939166095731619715033166968309929</br>
+46634914279878084225722069714887558063748030886299</br>
+51184731871247772919100702275888934869394562895158</br>
+02965372150409603107761289831263589964893410247036</br>
+03664505868728758905140684123812424738638542790828</br>
+27338279733268855049358743031602747490631295723497</br>
+42611221517417153133618622410913869500688835898962</br>
+34927631731647834007746088665559873338211382992877</br>
+69114954921841920877716060684728746736818861675072</br>
+21017261103830671787856694812948785048943063086169</br>
+94879870316051588410828235127415353851336589533294</br>
+86294944950618685147791058046960390693726626703865</br>
+12905201137810858616188886947957607413585534585151</br>
+76805197333443349523012039577073962377131603024288</br>
+72005373209982530089776189731298178819446717311606</br>
+47231476248457551928732782825127182446807824215216</br>
+46956781929409823892628494376024885227900362021938</br>
+66964822156280936053731780408637272684266964219299</br>
+46819214908701707533361094791381804063287387593848</br>
+26953558307739576144799727000347288018278528138950</br>
+32179863452161110666088393140532269449054555278678</br>
+94417579202440021450780192099804461382547805858048</br>
+44241640477503153605490659143007815837243012313751</br>
+15622840158386442708907182848167575271238467824595</br>
+34334449622010096071051370608461801187543120725491</br>
+33499424761711563332140893460915656155060031738421</br>
+87015702261031019166038870646614388977363187809407</br>
+11527528174689576401581047016965247557740891644568</br>
+67771715850058326994340167720215676772406812836656</br>
+52641229824394651331973591997094032759385026695574</br>
+70231813203243716420586141033606524536939160050644</br>
+95306016126782264894243739716671766123104897503188</br>
+57321655549883421218028469125290861014855278152776</br>
+25623750456375769497734336846015607727035509629049</br>
+39248708840628106794362241870474700836884267102255</br>
+83024035998416459511224852726336326451140173952480</br>
+86194635840783753556885622317115520947223065437092</br>
+60679735100056554938122457548372854571179739361575</br>
+61676416928958052572975223385586113883221711073622</br>
+65816218842443178857488798109026653793426664216990</br>
+91405653643224930133486798815488662866505234699723</br>
+55747384248305904236771432787923164224038777643301</br>
+92600192284778313837632536121025336935812624086866</br>
+69973827597736568222790721583247888864236934639616</br>
+43633087301398142114303060087306661648036789840913</br>
+35926293402304324974926887831643602681011309570716</br>
+14191283068657732353263965367739031766136131596555</br>
+35849993986005651559219367599777179330197446881483</br>
+71103206503693192894521402650915465184309936553493</br>
+33718342529843367991593941746622390038952767381333</br>
+06177476295749438687169784537672194935065908757119</br>
+17720875477107189937960894774512654757501871194870</br>
+73873678589020061737332107569330221632062843206567</br>
+11920969505857611739616323262177089454262146098584</br>
+10237813215817727602222738133495410481003073275107</br>
+79994899197796388353073444345753297591426376840544</br>
+22647842160631227696469671564739990437159033239065</br>
+60726644116438605404838847161912109008701019130726</br>
+07104411414324197679682854788552477947648180295973</br>
+60494397004795960402927462992035720997619501403483</br>
+15380947714601056333446998820822120587281510729182</br>
+97121191787642488035467231691654185225672923442918</br>
+71281632325969654135485895771332083399112887759172</br>
+26115273379010341362085614577992398778325083550730</br>
+19981845902595835598926055329967377049172245493532</br>
+96833000022301815172265757875240588322490858212800</br>
+89747909326100762578770428656006996176212176845478</br>
+99644070506624171021332748679623743022915535820078</br>
+01411653480656474882306150033920689837947662550365</br>
+49822805329662862117930628430170492402301985719978</br>
+94883689718304380518217441914766042975243725168343</br>
+54112170386313794114220952958857980601529387527537</br>
+99030938871683572095760715221900279379292786303637</br>
+26876582268124199338480816602160372215471014300737</br>
+75377926990695871212892880190520316012858618254944</br>
+13353820784883465311632650407642428390870121015194</br>
+23196165226842200371123046430067344206474771802135</br>
+30701240988603533991526679238711017062218658835737</br>
+81210935179775604425634694999787251125440854522274</br>
+81091487430725986960204027594117894258128188215995</br>
+23596589791811440776533543217575952555361581280011</br>
+63846720319346507296807990793963714961774312119402</br>
+02129757312516525376801735910155733815377200195244</br>
+45436200718484756634154074423286210609976132434875</br>
+48847434539665981338717466093020535070271952983943</br>
+27142537115576660002578442303107342955153394506048</br>
+62227649666876240793243531929926392537310768921353</br>
+52572321080889819339168668278948281170472624501948</br>
+40970097576092098372409007471797334078814182519584</br>
+25980962417476101382526439551352593118850456362641</br>
+88300338539652435997416931322894719878308427600401</br>
+36807470390409723847394583489618653979059411859931</br>
+03561684368692194853820557803957738813606795499000</br>
+85123259442529724486666766834641402189915944565309</br>
+42344065066785194841776677947047204195882204329538</br>
+03263105374948831221803912796784461001397267538921</br>
+95119117836587662528083690053249004597410947068772</br>
+91232821430463533728351995364827432583311914445901</br>
+78096077828835837301118575436599589827245319253105</br>
+88115026307542571493943024453931870179923608166611</br>
+30542625399583389794297160207033876781503301028012</br>
+00959972522222808014235710947603519255444349299867</br>
+67817891045559063015953809761875920358937341978962</br>
+35893112598390259831026719330418921510968915622506</br>
+96591198283234555030590817307351955037216658702880</br>
+53992138576037035377105178021280129566841984140362</br>
+87272562321442875430221090947272107347413497551419</br>
+07370433182766261772759968888260272252471336833534</br>
+52816692779591328861381766349857728936900965749562</br>
+28710302436259077241221909430087175569262575806570</br>
+99120166596224360802428700245473620363948412559548</br>
+81727272473653467783647201918303998717627037515724</br>
+64992228946793232269361917764161461879561395669956</br>
+77830682903165896994307673335082349907906241002025</br>
+06134057344300695745474682175690441651540636584680</br>
+46369262127421107539904218871612761778701425886482</br>
+57752238891845995233762923779155857445494773612955</br>
+25952226578636462118377598473700347971408206994145</br>
+58071908021359073226923310083175951065901912129479</br>
+54086036407573587502058902087045796700070552625058</br>
+11420663907459215273309406823649441590891009220296</br>
+68052332526619891131184201629163107689408472356436</br>
+68081821686572196882683584027855007828040434537101</br>
+83651096951782335743030504852653738073531074185917</br>
+70561039739506264035544227515610110726177937063472</br>
+38049906669221619711942591204450846417463835899382</br>
+39946517395509000859479990136026674261494290066467</br>
+11506717542217703877450767356374215478290591101261</br>
+91575558702389570014051178226469899449179083017954</br>
+75876760168094100135837613578591356924455647764464</br>
+17866711539195135769610486492249008344671548638305</br>
+44779143300976804868783481846727337584368927243104</br>
+47406807685278625585165092088263813233623148733336</br>
+71476452045087662761495038994950480956046098960432</br>
+91233583488599902945264002849942808786240398118148</br>
+84767301216754161106629995553668193123287425702063</br>
+73835202008686369131173346973174121915363324674532</br>
+56308713473027921749562270146873258678917345583799</br>
+64351358800959350877556356248810493852999007675135</br>
+51352779241242927748856588856651324730251471021057</br>
+53525165118148509027504768455182520963318990685276</br>
+14435138213662152368890578786699432288816028377482</br>
+03550601602989400911971385017987168363374413927597</br>
+36440170070147637066557035043381211135764150184518</br>
+21413619823495159601064752712575935185304332875537</br>
+78305750956742544268471221961870917856078393614451</br>
+13833356491032564057338986671781239722375193164306</br>
+17013859539474367843392670986712452211189690840236</br>
+32741149660124348309892994173803058841716661307304</br>
+00675883804321115553794406054977217059428215148861</br>
+65672771240903387727745629097110134885184374118695</br>
+65544974573684521806698291104505800429988795389902</br>
+78043835962824094218605562877884288021275538848037</br>
+28640019441614257499904272009595204654170598104989</br>
+96750451193647117277222043610261407975080968697517</br>
+66002371877483480161203102346805671126447661237476</br>
+27852190241202569943534716226660893675219833111813</br>
+51114650385489502512065577263614547360442685949807</br>
+43969323312971273771573470997139522911826534851555</br>
+87137336629120242714302503763269501350911612952993</br>
+78586468130722648600827088133353819370368259886789</br>
+33212383270532976258573827900978264605455985551318</br>
+36688844628265133798491667839409761353766251798258</br>
+24966345877195012438404035914084920973375464247448</br>
+81761840700235695801774101776969250778148933866725</br>
+57898564589851056891960924398841569280696983352240</br>
+22563457049731224526935419383700484318335719651662</br>
+67215755241934019330990183193091965829209696562476</br>
+67683659647019595754739345514337413708761517323677</br>
+20422738567427917069820454995309591887243493952409</br>
+44416789988463198455048523936629720797774528143994</br>
+18256789457795712552426826089940863317371538896262</br>
+88962940211210888442737656862452761213037101730078</br>
+51357154045330415079594477761435974378037424366469</br>
+73247138410492124314138903579092416036406314038149</br>
+83148190525172093710396402680899483257229795456404</br>
+27017577229041732347960736187878899133183058430693</br>
+94825961318713816423467218730845133877219086975104</br>
+94284376932502498165667381626061594176825250999374</br>
+16728839517440669325496534031014522253161890092353</br>
+76486378482881344209870048096227171226407489571939</br>
+00291857330746010436072919094576799461492929042798</br>
+16877294264877299528584346477753869069501489841339</br>
+24540394144680263625402118614317031251117577642829</br>
+91464453340892097696169909837265236176874560589470</br>
+49681701369749095230720826828878907301900182534258</br>
+05343421705928713931737993142410852647390948284596</br>
+41809361413847583113613057610846236683723769591349</br>
+26158245162215521348792441450417568480641206365201</br>
+70386330129532777699023118648020067556905682295016</br>
+35493199230591424639621702532974757311409422018019</br>
+93680350264956369558664259067626856873721103391567</br>
+93839895765565193177883000241613539562437777840801</br>
+74881937309502069990089089932808839743036773659552</br>
+48913001566332940779071396154645340887915103006513</br>
+21934486673248275907946807879819425019582622320395</br>
+13125201410996053126069655540424867054998678692302</br>
+17469890095478507256729787947698888310934874644264</br>
+00718183160331655511534276155622405474473378049246</br>
+21495213325852769884733626918264917433898782478927</br>
+84689188280546699823036899397834137475870258057163</br>
+49413568433929396068192061773331791738208562436433</br>
+63535986349449689078106401967407443658366707158692</br>
+45211829978938040771375012908586465789057714268335</br>
+82768978554717687184427726120509266486102051535642</br>
+84063236848180728794071712796682006072755955590404</br>
+02331787494473464547606281895415121391629184442976</br>
+51066947969354016866010055196077687335396511614930</br>
+93757096855455938151378956903925101495326562814701</br>
+19983269922000663928753747131352364215892651262040</br>
+72887716578358405219646054105435443642166562244565</br>
+04299901025658692727914275293117208279393775132610</br>
+60528812353734510683729398935808712438693859343891</br>
+75713376300720319760816604464683937725806909237297</br>
+52348670291691042636926209019960520412102407764819</br>
+03160140858635584276095370865581642739953493465463</br>
+14504040199528537252004957805254656251154109252437</br>
+99132626271360909940290226206283675213230506518393</br>
+40574501120993414649184333236465693717259144893241</br>
+59006242020612885732926133596808726500045628284557</br>
+57459659212053034131011182750130696150983551563200</br>
+43107846019065654938065425252291619918199596027523</br>
+27702249855738824899882707465936355768582560518068</br>
+96428537685077201222034792099393617926820659014216</br>
+56159253067379445689490708532635681968318617722682</br>
+49911472615732035807646298116244013316737892788689</br>
+22903259334986179702199498192573961767307583441709</br>
+85592221701718257127775344915082052784309046194608</br>
+35217402005838672849709411023266953921445461066215</br>
+00641067474020700918991195137646690448126725369153</br>
+71622907913854039375600778351533741677479421003840</br>
+02308951850994548779039346122220865060160500351776</br>
+26483161115332558770507354127924990985937347378708</br>
+11942530551214369797499149518605359204038302357163</br>
+52727630874693219622190064260886183676103346002255</br>
+47747781364101269190656968649501268837629690723396</br>
+12762872230411418136100602640440300359969889199458</br>
+27397624114613744804059697062576764723766065541618</br>
+57469052722923822827518679915698339074767114610302</br>
+27766060200612468764777288190967916133540198814027</br>
+57992174167678799231603963569492851513633647219540</br>
+61117176738737255572852294005436178517650230754469</br>
+38693078734991103521825329297260445532107978877114</br>
+49898870911511237250604238753734841257086064069052</br>
+05845212275453384800820530245045651766951857691320</br>
+00428167580549248117805198326460324457928297301291</br>
+05318385636821206215531288668564956512613892261367</br>
+06409395333457052698695969235035309422454386527867</br>
+76730275404027022463844835532399147513634410440500</br>
+92330361271496081355490531539021002299595756583705</br>
+38126196568314428605795669662215472169562087001372</br>
+77685369608407048333251327931122325071486302069512</br>
+45395003735723346807094656483089209801534878705633</br>
+49109236605755405086411152144148143463043727327104</br>
+50277686619531078583233348578402971609252153260925</br>
+58932655600672124359464255065996771770388445396181</br>
+63287961446081778927217183690888012677820743010642</br>
+25246348074543004764928855534090621851536543554741</br>
+25476152769772667769772777058315801412185688011705</br>
+02836527554321480348800444297999806215790456416195</br>
+72127845089284898064264974270905791290692178072987</br>
+69477975112447305991406050629946894280931034216416</br>
+62993561482813099887074529271604843363081840412646</br>
+96379258430941854422163590845761460785585624738149</br>
+31427078266215185541603870206876980461747400808324</br>
+34366538235455510944949843109349475994467267366535</br>
+25176627067721941831919771963780157021699336750837</br>
+60057163454643671776723387588643405644871566964321</br>
+04128259564534984138841289042068204700761559691684</br>
+30389993483667935425492103281133631847225923055543</br>
+83058206941675629992013373175489122037230349072681</br>
+06853445403599356182357631283776764063101312533521</br>
+21419946118693508331765878520471123643312267651299</br>
+64171325217513553261867681942338790365468908001827</br>
+13528358488844411176123410117991870923650718485785</br>
+62210211040097769944531217950224795780695065329659</br>
+40383987369907240797679040826794007618729547835963</br>
+49279390457697366164340535979221928587057495748169</br>
+66940623342726197335181366260637359825755524965098</br>
+07260123668283605928341855848026958413772558970883</br>
+78994291054980033111388460340193916612218669605849</br>
+15714857335682861495000190975911252188003964197621</br>
+63559375743718011480559442298730418196808085647265</br>
+71354761283162920044988031540210553059707666636274</br>
+93283089168809323592900817874119857383171926167288</br>
+34918402429721290434965526942726402559641463525914</br>
+34840067586769035038232057293413298159353304444649</br>
+68294413673234421583807616948312193331198190610961</br>
+42952201536170298575105594326461468505452684975764</br>
+80780800922133581137819774927176854507553832876887</br>
+44745915937311624706010912446098294248412875202244</br>
+62594477638749491997840446829257360968534549843266</br>
+53686284448936570411181779380644161653122360021491</br>
+87687694673984075171763075168498563592014868929431</br>
+05940202457969622924566644881967576294349535326382</br>
+17161339575779076637076456957025973880043841580589</br>
+43361371065518599876007549241872117148892952217377</br>
+21146081154344982665479872580056674724051122007383</br>
+45927157572771521858994694811794064446639943237004</br>
+42911407472181802248258377360173466853007449855647</br>
+15420036123593397312914458591522887408719508708632</br>
+21883728826282288463184371726190330577714765156414</br>
+38223067918473860391476831081413582757558536435977</br>
+21650028277803713422869688787349795096031108899196</br>
+14338666406845069742078770028050936720338723262963</br>
+78560386532164323488155575570184690890746478791224</br>
+36375556668678067610544955017260791142930831285761</br>
+25448194444947324481909379536900820638463167822506</br>
+48095318104065702543276043857035059228189198780658</br>
+65412184299217273720955103242251079718077833042609</br>
+08679427342895573555925272380551144043800123904168</br>
+77164451802264916816419274011064516224311017000566</br>
+91121733189423400547959684669804298017362570406733</br>
+28212996215368488140410219446342464622074557564396</br>
+04529853130714090846084996537678037932018991408658</br>
+14662175319337665970114330608625009829566917638846</br>
+05676297293146491149370462446935198403953444913514</br>
+11936679333019366176636525551491749823079870722808</br>
+60859626112660504289296966535652516688885572112276</br>
+80277274370891738963977225756489053340103885593112</br>
+56799915165890250164869614272070059160561661597024</br>
+51989051832969278935550303934681219761582183980483</br>
+96056252309146263844738629603984892438618729850777</br>
+59287927220685548072104978176532862101874767668972</br>
+48841139560349480376727036316921007350834073865261</br>
+68450748249644859742813493648037242611670426687083</br>
+19250409976153190768557703274217850100064419841242</br>
+07396400139603601583810565928413684574119102736420</br>
+27416372348821452410134771652960312840865841978795</br>
+11165115298278146203791398550063999603265912485253</br>
+08493690313130100799977191362230866011099929142871</br>
+24938854161203802041134018888721969347790449752745</br>
+42880728035093058287544207551348166609278793535665</br>
+21255620139988249628478726214432362853676502591450</br>
+46837763528258765213915648097214192967554938437558</br>
+26002531685363567313792624758780494459441834291727</br>
+56988376226261846365452743497662411138451305481449</br>
+83631178978448973207671950878415861887969295581973</br>
+32506999514026015116755297505754378102422389579257</br>
+86562128432731202200716730574069286869363930186765</br>
+95825132649914595026091706934751940897535746401683</br>
+08117988464524736189560564794263580705625632811892</br>
+69663026479535951097127659136233180866921535788607</br>
+81275991053717140220450618607537486630635059148391</br>
+64676567232057145168861707909846959322367249467375</br>
+83099607042589220481550799132752088583781117685214</br>
+26933478692189524062265792104362034885292626798401</br>
+39532164587911515790504605797108389833718640380244</br>
+17511347226472547010794793996953554669619726763255</br>
+22991465493349966323418595145036098034409221220671</br>
+25676987234279407088570704742931733291885238967219</br>
+71353924492426178641188637790962814486917869468177</br>
+59171715066911148002075943201206196963779510322708</br>
+90295660855622254526026104607361313688690092817210</br>
+68198618553780982018471154163630326265699283424155</br>
+02360097804641710852553761272890533504550613568414</br>
+37758544296779770146602943876872251153638011917581</br>
+54028120818255606485410787933598921064427244898618</br>
+96162941341800129513068363860929410008313667337215</br>
+30083526962357371753307386533382048421903081864491</br>
+84093723944033405244909554558016406460761581010301</br>
+76748847501766190869294609876920169120218168829104</br>
+08707095609514704169211470274133900522533408348128</br>
+70353031023919699978597413908593605433599697075604</br>
+46013424245368249609877258131102473279856207212657</br>
+24990034682938868723048955622532044636026398542252</br>
+58416464324271611419817802482595563544907219226583</br>
+86366266375083594431487763515614571074552801615967</br>
+70484427141944351832756984075526779264112617652506</br>
+15965235457187956673170913319358761628255920783080</br>
+18520689015150471334038610031005591481785211038475</br>
+45429333891884441205179439699701941126951195265649</br>
+19594189975418393234647424290702718875223534393673</br>
+63366320030723274703740712398256202466265197409019</br>
+97624520561985576257600087081730832883443818310700</br>
+54514493545885422678578551915372292379555494333410</br>
+17442016960009069641561273229777022121795186837635</br>
+90822551288164700219923488640439591530184640047143</br>
+21186360622527011541122283802778538911098490201342</br>
+74101412155976996543887719748537643115822983853312</br>
+30717511329619045590079380642766958190148426279912</br>
+21792947987348901868471676503827328552059082984529</br>
+80625925035212845192592798659350613296194679625237</br>
+39725655841578537445675589980324054921869628884903</br>
+32560851455344391660226257775512916200772796852629</br>
+38793753045418108072928589198971538179734349618723</br>
+29276147478501926114504132748732429705834084711123</br>
+33746274617274626582415324271059322506255302314738</br>
+75925172478732288149145591560503633457542423377916</br>
+03749525024930223514819613811625639114156103268449</br>
+58072508273431765944054098269765269344579863479709</br>
+74312449827193311386387315963636121862349726140955</br>
+60799206283169994200720548115253533939460768500199</br>
+09886553861433495781650089961649079678142901148387</br>
+64568217491407562376761845377514403147541120676016</br>
+07264605568592577993220703373333989163695043466906</br>
+94828436629980037414527627716547623825546170883189</br>
+81086880684785370553648046935095881802536052974079</br>
+35386765111950793732820831462689600710751755206144</br>
+33784114549950136432446328193346389050936545714506</br>
+90086448344018042836339051357815727397333453728426</br>
+33721740657757710798305175557210367959769018899584</br>
+94130195999573017901240193908681356585539661941371</br>
+79448763207986880037160730322054742357226689680188</br>
+21234243918859841689722776521940324932273147936692</br>
+34004848976059037958094696041754279613782553781223</br>
+94764614783292697654516229028170110043784603875654</br>
+41517394339600489153188175766505009516974024156447</br>
+71293656614253949368884230517400129920556854289853</br>
+89794266995677702708914651373689220610441548166215</br>
+68042198384767308717875902792091759006952734566820</br>
+26513373111518000181434120962601658629821076663523</br>
+36177400783778342370915264406305407180784335806107</br>
+29611055500204151316963730468492133568372654003075</br>
+09829089364612047891114753037049893952833457824082</br>
+81738644132271000296831194020332345642082647327623</br>
+38302946393789983758365545599193408662350909679611</br>
+34004867027123176526663710778725111860354037554487</br>
+41869351973365662177235922939677646325156202348757</br>
+01137957120962377234313702120310049651521119760131</br>
+76419408203437348512852602913334915125083119802850</br>
+17785571072537314913921570910513096505988599993156</br>
+08636554774035518981667335358800482146650997414337</br>
+61182777723351910741217572841592580872591315074606</br>
+02563490377726337391446137703802131834744730111303</br>
+26702969173350477016321066162278300272692833655840</br>
+11791419447808748253360714403296252285775009808599</br>
+60904093631263562132816207145340610422411208301000</br>
+85872642521122624801426475194261843258533867538740</br>
+54743491072710049754281159466017136122590440158991</br>
+60022982780179603519408004651353475269877760952783</br>
+99843680869089891978396935321799801391354425527179</br>
+10225397010810632143048511378291498511381969143043</br>
+49750018998068164441212327332830719282436240673319</br>
+65546926778511931527751134464689055042481133614349</br>
+84604849051258345683266441528489713972376040328212</br>
+66025351669391408204994732048602162775979177123475</br>
+10975024030789357599377150950217516935558270725339</br>
+11892334070223832077585802137174778378778391015234</br>
+13209848942345961369234049799827930414446316270721</br>
+47961174569757196812392919137409829258055619552074</br>
+34243295982898980529233366415419256367380689494201</br>
+47124134052507220406179435525255522500874879008656</br>
+83145428351677505422948032747830440564385815919526</br>
+66758282929705226127628711040134801787224801789684</br>
+05240792436058274246744307672164527031345135416764</br>
+96689012747868010102951338626986497482121186290403</br>
+37691568576240699296372493097201628707200189835423</br>
+69036414927023696193854737248032985504511208919287</br>
+98298744678641291594175316756025334353106267452545</br>
+07114181483239880607297140234725520713490798398982</br>
+35526872395090936566787899238371257897624875599044</br>
+32288953883773173489411227570714109597900479193010</br>
+46740750411435381782464630795989555638991884773781</br>
+34134707024674736211204898622699188851745625173251</br>
+93413520381158633501239130544419100736284475675141</br>
+61050410973505852762044489190978901984315485280533</br>
+98577784431393388399431044446566924455088594631408</br>
+17512203313906815965925105468580131338381521764182</br>
+10433429788826119630443111388796258746090226130900</br>
+84997543039577124323061690626291940392143974027089</br>
+47776637024881554993224588259790206312574369109463</br>
+93252806241642476868495455324938017639371615636847</br>
+85982371590238542126584061536722860713170267474013</br>
+11452610637653833903159219434698176053583803106128</br>
+87852051546933639241088467632009567089718367490578</br>
+16308515813816196688222204757043759061433804072585</br>
+38620835651769984267745231958241826836982701602374</br>
+14938363496629351576854061397342746470899685618170</br>
+16055110488097155485911861718966802597354170542398</br>
+51355600187203350790609464212711439931960465274240</br>
+50882225359773481519135438571253258540493946010865</br>
+79379805862014336607882521971780902581737087091646</br>
+04527279771535099103407364250203863867182205228796</br>
+94458387652947951048660717390229327455426785669776</br>
+86593992341683412227466301506215532050265534146099</br>
+52493560508549217565491348309589065361756938176374</br>
+73644183378974229700703545206663170929607591989627</br>
+73242309025239744386101426309868773391388251868431</br>
+65010279649114977375828889134503411488659486702154</br>
+92101084328080783428089417298008983297536940644969</br>
+90312539986391958160146899522088066228540841486427</br>
+47862819755466292788146216071713818801808405720847</br>
+15868906836919393381864278454537956719272397972364</br>
+65166759201105799566396259853551276355876814021340</br>
+98290162968734298507924718460568748283313812591619</br>
+62476156902875901072733103299140623864608333378638</br>
+25792630239159000355760903247728133888733917809696</br>
+66014696150317542267511259933155296742133363002229</br>
+64906480934582008181061802100227664580400278213336</br>
+75857301901137175467276305904435313131903609248909</br>
+72464279284555499134900051802957070829190525567818</br>
+89913899625138662319380053611346224294610248954072</br>
+40485712325662888893172211643294781619055486805494</br>
+34410340906807160880282279596869501336438142682521</br>
+70472870863010137301155236861416908375675747637239</br>
+76318575703810944339056456446852418302814810799837</br>
+69185121272019350440418046047216269394457883770901</br>
+05974693219720558114078775989772072009689382249303</br>
+23683051586265728111463799698313751793762321511125</br>
+23497343052406221052442343537329056551634066695061</br>
+65892878218707756794176080712973781335187117931650</br>
+03315552382248773065344417945341539520242444970341</br>
+01208740721881093882681675120422994049481794494727</br>
+32894770111574139441228455521828424922240658752689</br>
+17227278060711675404697300803703961878779669488255</br>
+56146743843925701158295466613586786718976612973112</br>
+67200072971553613027503556167817765442287442114729</br>
+88161480270524380681765357327557860250584708401320</br>
+88379328160087690813004924914736825170353822196190</br>
+39014999523495387105997351143478292339499187936608</br>
+69230137559636853237380670359114424326856151210940</br>
+42595826393016780171286692392832310576588517140202</br>
+11196957064799814031505633045141564414623163763809</br>
+90440281625691757648914256971416359843931743327023</br>
+78123369380430128926263753826677950341693343236075</br>
+00248175741808750388475094939454896209740485442635</br>
+63716499594992098088429479036366629752600324385635</br>
+29458447289445471662092974954966168774141208821304</br>
+77022816116456044007236351581149729739218966737382</br>
+64720472264222124201656015028497130633279581430251</br>
+60136948255670147809357908896571349261581613469018</br>
+06965089556310121218491805847922720691871696316330</br>
+04485802010286065785859126997463766174146393415956</br>
+95395542033146280265189511679380745733157598460861</br>
+73702687867602943677780500244673391332431669880354</br>
+07323238828184750105164133118953703648842269027047</br>
+80527424906034920829547550540034571601840725745369</br>
+38145531175354210726557835615499874447480427323457</br>
+88006187314934156604635297977945507535930479568720</br>
+93167245365472083816858556060438019770307642460834</br>
+89876101345709394877002946175792061952549255757109</br>
+03852517148852526567104534981341980339064152987634</br>
+36954202560802776144219143189213939088345431317696</br>
+85101840103844472348948869520981943531906506555354</br>
+61733581404554483788475252625394966586999205841765</br>
+27801253410338964698186424300341467913806190280596</br>
+07854888010789705516946215228773090104467462497979</br>
+99262712095168477956848258334140226647721084336243</br>
+75937416105367340419547389641978954253350363018614</br>
+00951534766961476255651873823292468547356935802896</br>
+01153679178730355315937836308224861517777054157757</br>
+65617593585120166929431111388635821596676188303261</br>
+04164651714846979385422621687161400122378213779774</br>
+13126897726671299202592201740877007695628347393220</br>
+10881593562862819285635718933849588506038531581797</br>
+60679479840878360975960149733420572704603521790605</br>
+64760328556927627349518220323614411258418242624771</br>
+20120357763888959743182328278713146080535335744942</br>
+97621796789034568169889553518504478325616380709476</br>
+95169908624710001974880920500952194363237871976487</br>
+03392238115403634754886268459561597551937654101150</br>
+14067001226927474393888589943859730245414801061235</br>
+90803627458528849356325158538438324249325266608758</br>
+89083187007091002373771065769850564339288543376583</br>
+42596750653715005333514489908293887737352051459333</br>
+04962653141514138612443793588507094468804548697535</br>
+81702129084907873478068143663233228194158273456713</br>
+56443171537967818058195852464840084032909981943781</br>
+71817730231700398973305049538735611626102399943325</br>
+97801268934326055847102787649010709234438846340117</br>
+35556865903585244919370181041626208504299258697435</br>
+81709813389404593447193749387762423240985283276226</br>
+66049423851297094532455862521036008292866497241749</br>
+19141988966129558076770979594795306013119159011773</br>
+94310420904907942444886851308684449370590902600612</br>
+06494257447103535476578592427081304106185462198818</br>
+30090634588187038755856274911587375421064667951346</br>
+48758677154383801852134828191581246259933516019893</br>
+55951679689328522058247994210345127158771633452229</br>
+95418839680448835529753361286837225935390079201666</br>
+94133909116875880398882886921600237325736158820716</br>
+35162713328105181876021048521806755266486739089009</br>
+07195138058626735124312215691637902277328705410842</br>
+03784152568328871804698795251307326634027851905941</br>
+73389203585403956770356113293544825856282876106106</br>
+98229721420961993509331312171187891078766872044548</br>
+87608941017479864713788246215395593333327556200943</br>
+95804345379197822805903959599274369137937786649409</br>
+64048777841748336432684026282932406260081908081804</br>
+39091455635193685606304508914228964521998779884934</br>
+74777291327972660276584016678901364905087411421268</br>
+61969862044126965282981087045479861559545338021201</br>
+15564697997678573892018624359932677768945406050821</br>
+88382279098336271671244900267611784982643770330020</br>
+81844590009717235204331994708242098771514449751017</br>
+05564302954282181967000920251561584417420593365814</br>
+81349026931115170938722600264586305613256057925609</br>
+27332265579346280805683443921373688405650434307396</br>
+57406101777937014142461549307074136080544210029560</br>
+00956635889778992676305177187819437067614982175641</br>
+86590116160865408635391513039201316805769034172596</br>
+45369235080641744656235152392905040947995318407486</br>
+21512105618338545661766526063937136588025216662235</br>
+76132201941701372664966073252010771947931265282763</br>
+30241380516490717456596485374835466919452358031530</br>
+19691604809946068149040378198297323609300871357607</br>
+98621425422096419004367905479049930078372421581954</br>
+53541837112936865843055384271762803527912882112930</br>
+83515756565999447417884383815651484342298587042455</br>
+92434693295232821803508333726283791830216591836181</br>
+55421715744846577842013432998259456688455826617197</br>
+90121808494803324487872581837748055222681510113717</br>
+45368417870280274452442905474518234674919564188551</br>
+24442133778352142386597992598820328708510933838682</br>
+99065719946149062902574276860388505110326385445404</br>
+19184958866538545040571323629681069146814847869659</br>
+16686184275679846004186876229805556296304595322792</br>
+30516167215919686758495236352989357885077460815373</br>
+21454642984792310511676357749494622952569497660359</br>
+47396243099534331040499420967788382700271447849406</br>
+90370732491064441516960532565605867787574174721108</br>
+27435774315194060757983563629143326397812218946287</br>
+44779811980722564671466405485013100965678631488009</br>
+03037493388753641831651349825466946733161181233648</br>
+54397649325026179549357204305402182974871251107404</br>
+01161140589991109306249231281311634054926257135672</br>
+18186289327861388337180285350565035919527414008695</br>
+10926167541476792668032109237467087213606278332922</br>
+38641361959412133927803611827632410600474097111104</br>
+81400036233427145144833346416754663546997314947566</br>
+43423659493496845884551524150756376605086632827424</br>
+79413606287604129064491382851945640264315322585862</br>
+40431418386695906332450630003922131926476259626915</br>
+10904457695301444054618037857503036686212462278639</br>
+75274666787012100339298487337501447560032210062235</br>
+80293437749550320370127384681630610265703008722754</br>
+62966796880890587127676361066225722352229739206443</br>
+09352432722810085997309513252863060110549791564479</br>
+18450046180467624089289256809129305929606423570210</br>
+61524646205023248966593987324933967376952023991760</br>
+89847457184353193664652912584806448019652016283879</br>
+51894993367592414856261369959453072872545324632915</br>
+29110128763770605570609531377527751867923292134955</br>
+24513308986796916512907384130216757323863757582008</br>
+03635757280027544903279530799007994425411087256931</br>
+88014667935595834676432868876966610097395749967836</br>
+59339784634695994895061049038364740950469522606385</br>
+80467580730699122904740898791668721171475276447116</br>
+04401952718169508289733537148530928937046384420893</br>
+29977112585684084660833993404568902678751600877546</br>
+12679880154658565220612109534907967073655397025761</br>
+99431376639960606061106406959330828171876426043573</br>
+42536175694378484849525010826648839515970049059838</br>
+08121052211110919433239511360514464598342107990580</br>
+82093716464523127704023160072138543723461267260997</br>
+87038565709199850759563461324846018840985019428768</br>
+79022687345565005191215465440638292538512763176639</br>
+22050938345204300773017029940362615434001322763910</br>
+91298832786392041230044555168405488980908077917463</br>
+60924393349126411642400938807463566072623366958427</br>
+64583698268734815881961058571835767462009650526065</br>
+92926354829149904576830721089324585707370166071739</br>
+81944850288426039636607460311847862258310565808708</br>
+70305567595861341700745402965687634774176431051751</br>
+03673286924555858208237203860178173940517513043799</br>
+48688223200443780431031709210342616749980000730160</br>
+94814586374488778522273076330495383944345382770608</br>
+76076354209844500830624763025357278103278346176697</br>
+05442871553153400164970766571959850417481990872014</br>
+90875686037783591994719343352772947285537925787684</br>
+83230110185936580071729118696761765505377503029303</br>
+38307064489128114120255061508964110076238245744886</br>
+55182581058140345320124754723269087547507078577659</br>
+73254284445935304499207001453874894822655644222369</br>
+63655441942254413382122254774975354946248276805333</br>
+36983284156138692363443358553868471111430498248398</br>
+99180316545863828935379913053522283343013795337295</br>
+40162576232280811384994918761441413229337671065634</br>
+92528814528239506209022357876684650116660097382753</br>
+66040544694165342223905210831458584703552935221992</br>
+82727605748212660652913855303455497445514703449394</br>
+86863429459658431024190785923680224560763936784166</br>
+27051855517870290407355730462063969245330779578224</br>
+59497104201880430001838814290081730394505073427870</br>
+13124466860092778581811040911511729374873627887874</br>
+90746528556543474888683106411005102302087510776891</br>
+87815256227352515503795324448577872776170019648537</br>
+03555167655209119339343762866284619844026295252183</br>
+67852236747510880978150709897841308624588152266096</br>
+35514018744958369269177990471207264949057372642860</br>
+05211403581231076006699518536124862746756375896225</br>
+29911649606687650826173417848478933729505673900787</br>
+86179253514406210453662506404637288156982323175005</br>
+96261080921955211150859302955654967538862612972339</br>
+91462835847604862762702730973920200143224870758233</br>
+73549152460856082103288829741839064788699232736913</br>
+60048837436615223517058437705545210815513361262142</br>
+91181561530175888257359489250710887926212864139244</br>
+33093837973338678061317952373152667738208580247014</br>
+33527009243803266951742119507670884326346442749127</br>
+55890774686358216216604274131517021245858605623363</br>
+14931646469139465624974717419583542186077487110573</br>
+38458433689939645913740603382159352243594751626239</br>
+18868530782282176398323730618020424656047752794310</br>
+47961897242995330297924974816840528937910449470045</br>
+90864991872727345413508101983881864673609392571930</br>
+51196864560185578245021823106588943798652243205067</br>
+73799661969554724405859224179530068204517953700434</br>
+72451762893566770508490213107736625751697335527462</br>
+30294303120359626095342357439724965921101065781782</br>
+61087453188748031874308235736991951563409571627009</br>
+92444929749105489851519658664740148225106335367949</br>
+73714251022934188258511737199449911509758374613010</br>
+55050641977215319293548753711916302620303285886585</br>
+28480193509225875775597425276584011721342323648084</br>
+02714335636754204637518255252494432965704386138786</br>
+59019657388028684018940876728167141370336617326501</br>
+20578653915780703088714261519075001492576112927675</br>
+19309672845397116021360630309054224396632067432358</br>
+27978893323244057791992784846333397777376559018705</br>
+74806828678347965624146102899508487399692970750432</br>
+75302997287229732793444298864641272534816060377970</br>
+72982991730292963086958019963124133049393504933254</br>
+12355071054461182591141116454534710329881047844067</br>
+78013807713146540009938630648126661433085820681139</br>
+58383191695455582594268957698414288937434670841079</br>
+46318932539106963955780706021245974898293564613560</br>
+78898347241997947856436204209461341238761319886535</br>
+23583129968622689486084084566556068769545012744866</br>
+31405054735351746873009806322780468912246821460806</br>
+72762770840240226615548502400895289165711761743902</br>
+03375848778429112896232470591918746910420058483261</br>
+40677333751027195653994697162517248312230633919328</br>
+70798380074848572651612343493327335666447335855643</br>
+02352808839243482787608861649432893991663992104883</br>
+07847777048045728491456303353265070029588906265915</br>
+49850940797276756712979501009822947622896189159144</br>
+15200322838787734851309790810191292672271037788980</br>
+53964156362364169154985768408398468861684375407065</br>
+12103906250612810766379904790887967477806973847317</br>
+04752534421563903872012388063236880370179493089549</br>
+00776331523063548374256816653361606641980030188287</br>
+12376748189833024683637148830925928337590227894258</br>
+80600872860388591688497306939480205112217663591382</br>
+51524278670094406942355120201568377778851824670025</br>
+65170850924962374772681369428435006293881442998790</br>
+53010562173754591826799732177350293689280652100253</br>
+96268807498092643458011655715886700443503976505323</br>
+47828732736884086354000274067678382196352222653929</br>
+09398073673913640828987220177767471681181958561337</br>
+21583119054682936083236976113450281757830202934845</br>
+98292500089568263027126329586629214765314223335179</br>
+30933879513570953463771836840924444220963193312956</br>
+20305575517340067973740614162107923633423805646850</br>
+09203716715264255637185388957141641977238742261059</br>
+66673969971731681694154350952831935564177056686222</br>
+15217991151355639707143312893657553844648326201206</br>
+42433801695586269856102246064606933079384785881436</br>
+74070005997697036490192733288261353293631124036506</br>
+98652160638987250267238087403396744397830258296894</br>
+25689674186433613497947524552629142652284241924308</br>
+33881035800537870239995421721136865502753413622116</br>
+93140694669513186928102574795985605145005021715913</br>
+31775160995786555198188619321128211070944228724044</br>
+24811534060558959583558152320121846058205635926993</br>
+03478851132068626627588771446035996656108430725696</br>
+50056306448918759946659677284717153957361210818084</br>
+15472731426617489331341746326623542220726001460127</br>
+01206934639520564445543291662986660783089068118790</br>
+09081529506362678207561438881578135113469536630387</br>
+84120923469428687308393204323338727754968052103028</br>
+21544324723388845215343727250128589747691460808314</br>
+40412586818154004918777228786980185345453700652665</br>
+56491709154295227567092222174741120627206566229898</br>
+06032891672068743654948246108697367225547404812889</br>
+24247185432360575341167285075755205713115669795458</br>
+48873987422281358879858407831350605482905514827852</br>
+94891121905383195624228719484759407859398047901094</br>
+19407067176443903273071213588738504999363883820550</br>
+16834027774960702768448802819122206368886368110435</br>
+69529300652195528261526991271637277388418993287130</br>
+56346468822739828876319864570983630891778648708667</br>
+61854856800476725526754147428510281458074031529921</br>
+97814557756843681110185317498167016426647884090262</br>
+68282444825802753209454991510451851771654631180490</br>
+45679857132575281179136562781581112888165622858760</br>
+30875974963849435275676612168959261485030785362045</br>
+27450775295063101248034180458405943292607985443562</br>
+00937080918215239203717906781219922804960697382387</br>
+43312626730306795943960954957189577217915597300588</br>
+69364684557667609245090608820221223571925453671519</br>
+18348725874239194108904441159599327600445065562064</br>
+61164655665487594247369252336955993030355095817626</br>
+17623184956190649483967300203776387436934399982943</br>
+02091470736189479326927624451865602395590537051289</br>
+78163455423320114975994896278424327483788032701418</br>
+67695262118097500640514975588965029300486760520801</br>
+04915378854139094245316917199876289412772211294645</br>
+68294860281493181560249677887949813777216229359437</br>
+81100444806079767242927624951078415344642915084276</br>
+45200020427694706980417758322090970202916573472515</br>
+82904630910359037842977572651720877244740952267166</br>
+30600546971638794317119687348468873818665675127929</br>
+85750163634113146275304990191356468238043299706957</br>
+70150789337728658035712790913767420805655493624646</br>
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/find_in_page.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
+</head>
+<body>
+This a find-in-page match rect test.</br>
+result 00</br>
+<iframe src="find_in_page_frame.html" id="frame" height="300" scrolling="yes"></iframe>
+</br>
+result 01
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/find_in_page_desktop.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <style>
+    iframe {
+      margin-top: 3000px;
+      margin-left: 1500px;
+    }
+    body {
+      height: 6000px;
+      width: 6000px;
+    }
+  </style>
+</head>
+<body>
+This a find-in-page match rect test.</br>
+result 00</br>
+<iframe src="find_in_page_frame.html" id="frame" height="300" scrolling="yes"></iframe>
+</br>
+result 01
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/find_in_page_frame.html
@@ -0,0 +1,109 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
+<style type="text/css">
+div.transform {
+  background-color: #00FF00;
+  width: 100px;
+  height: 100px;
+  padding-left: 30px;
+  padding-top: 20px;
+  transform:matrix(0.866,0.5,-0.5,0.866,120,150);
+}
+div.scroll {
+  background-color: #00FFFF;
+  width: 100px;
+  height: 100px;
+  overflow: scroll;
+}
+div.fixed {
+  background-color: #FF0000;
+  left: 200px;
+  top: 50px;
+  width: 100px;
+  height: 100px;
+  position: fixed;
+}
+div.relative-overflow {
+  position: relative;
+  overflow: hidden;
+  height: 100px;
+}
+div.margin-overflow {
+  overflow: hidden;
+  margin-top:20px;
+  height: 50px;
+}
+div.absolute {
+  position: absolute;
+  top: 0px;
+  height: 20px;
+  width: 80px;
+  background-color: "green";
+}
+</style>
+</head>
+<body>
+This is a test.
+<br>
+<br>
+<br>
+Foo bar.
+<br>
+<br>
+<br>
+result 02
+<div class="transform">
+result 03
+</div>
+result 04
+<div class="fixed">
+result 05
+</div>
+result 06
+<div class="scroll">
+result 07
+Foo bar.
+<br>
+<br>
+<br>
+result 08
+</div>
+result 09
+<br>
+result 10
+<table border="1" cellpadding="10" cellspacing="10">
+<tr>
+  <th>Foo</th>
+  <th>Bar</th>
+  <th>result 11</th>
+</tr>
+<tr>
+  <td rowspan="2">result 12</td>
+  <td colspan="2">result 13</td>
+</tr>
+<tr>
+  <td colspan="2">result 14</td>
+</tr>
+</table>
+result 15
+<div class="transform">
+<div style="height:0px">
+  <div style="float:left;">
+    result 16
+  </div>
+</div>
+</div>
+<div class="relative-overflow">
+  <div class="margin-overflow">
+    result 17
+    <div class="absolute">result 18</div>
+  </div>
+</div>
+<!-- Results below this line will not yield matches. -->
+<select>
+<option>result 19</option>
+</select>
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/find_in_page_multi_frame.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
+</head>
+<body>
+This a find-in-page test with multiple frames.</br>
+top result</br>
+<iframe id="frame1" height="300" scrolling="yes"></iframe></br>
+middle result</br>
+<iframe id="frame2" height="300" scrolling="yes"></iframe></br>
+bottom result</br>
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/find_in_page_simple_frame.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
+<body>
+  this frame contains a result.
+  and another result.
+  and another result.
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/find_in_page_two_frames.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+<body>
+Making two frames to reproduce crbug.com/889075.
+<iframe src="find_in_page_simple_frame.html"></iframe>
+<iframe src="find_in_page_simple_frame.html"></iframe>
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/find_in_page_with_portal.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
+</head>
+<body>
+This a find-in-page match rect test.</br>
+result 00</br>
+<portal src="find_in_page_frame.html"></portal>
+</br>
+result 01
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/find_in_simple_page.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+  <body>
+    This is a simple page with 5 matches for "result".
+
+    result Result RESULT rEsUlT
+  </body>
+</html>
--- /dev/null
+++ b/content/test/data/font_src_local_matching.html
@@ -0,0 +1,130 @@
+<meta charset="utf-8">
+<style id="fontfaces">
+</style>
+<body>
+<script>
+function getFontsWithTestCharsForOS() {
+  if (navigator.userAgent.indexOf("Android") !== -1) {
+    // Intersection of available fonts of Android Kitkat to Oreo that kept the
+    // same family names to compare against for these postscript or full font
+    // names. (RobotoThin* and RobotoLight* had differing family names on
+    // different Android versions.
+    return [
+      ["AndroidClock-Regular", "0"],
+      ["Roboto-Bold", "0"],
+      ["DroidSansMono", "0"],
+      ["Roboto-Regular", "0"],
+      ["Noto Color Emoji", "☺"],
+      ["NotoSansBengali-Bold", "0"],
+      ["NotoSansBengaliUI-Bold", "0"],
+      ["NotoSansDevanagari-Bold", "0"],
+      ["NotoSansDevanagariUI-Bold", "0"],
+      ["NotoSansKannada-Bold", "0"],
+      ["NotoSansKannada", "0"],
+      ["NotoSansKannadaUI-Bold", "0"],
+      ["NotoSansKannadaUI", "0"],
+      ["NotoSansLao-Bold", "0"],
+      ["NotoSansLao", "0"],
+      ["NotoSansLaoUI-Bold", "0"],
+      ["NotoSansLaoUI", "0"],
+      ["NotoSansMalayalam-Bold", "0"],
+      ["NotoSansMalayalamUI-Bold", "0"],
+      ["NotoSansTamil-Bold", "0"],
+      ["NotoSansTamilUI-Bold", "0"],
+      ["NotoSansTelugu-Bold", "0"],
+      ["NotoSansTelugu", "0"],
+      ["NotoSansTeluguUI-Bold", "0"],
+      ["NotoSansTeluguUI", "0"],
+      ["NotoSansThai-Bold", "๐"],
+      ["NotoSansThai", "๐"],
+      ["NotoSansThaiUI-Bold", "๐"],
+      ["NotoSansThaiUI", "๐"],
+      ["Roboto-BoldItalic", "0"],
+      ["RobotoCondensed-BoldItalic", "0"],
+      ["RobotoCondensed-Bold", "0"],
+      ["RobotoCondensed-Italic", "0"],
+      ["RobotoCondensed-Regular", "0"],
+      ["Roboto-Italic", "0"],
+    ];
+  } else if (navigator.userAgent.indexOf("Linux") !== -1
+             || navigator.userAgent.indexOf("CrOS") !== -1) {
+    return [
+      ["Ahem", "0"],
+      ["Arimo-Bold", "0"],
+      ["Arimo-BoldItalic", "0"],
+      ["Arimo-Italic", "0"],
+      ["Arimo-Regular", "0"],
+      ["Cousine-Bold", "0"],
+      ["Cousine-BoldItalic", "0"],
+      ["Cousine-Italic", "0"],
+      ["Cousine-Regular", "0"],
+      ["DejaVuSans", "0"],
+      ["DejaVuSans-Bold", "0"],
+      ["Garuda", "0"],
+      ["Gelasio-Bold", "0"],
+      ["Gelasio-BoldItalic", "0"],
+      ["Gelasio-Italic", "0"],
+      ["Gelasio-Regular", "0"],
+      ["Lohit-Devanagari", "0"],
+      ["Lohit-Gurmukhi", "0"],
+      ["Lohit-Tamil", "0"],
+      ["NotoSansKhmer-Regular", "០"],
+      ["Tinos-Bold", "0"],
+      ["Tinos-BoldItalic", "0"],
+      ["Tinos-Italic", "0"],
+      ["Tinos-Regular", "0"],
+      ["muktinarrow", "0"],
+      ["Tinos-Regular", "0"]
+    ];
+  } else if (navigator.userAgent.indexOf("Macintosh") !== -1) {
+    return [
+      [ "AmericanTypewriter-CondensedLight", "0" ],
+      [ "ArialNarrow-BoldItalic", "0" ],
+      [ "Baskerville-SemiBoldItalic", "0" ],
+      [ "DevanagariMT", "0" ],
+      [ "DINAlternate-Bold", "0" ],
+      [ "GillSans-LightItalic", "0" ],
+      [ "IowanOldStyle-Titling", "0" ],
+      [ "MalayalamSangamMN", "0" ],
+      [ "HiraMaruPro-W4", "0" ],
+      [ "HiraKakuStdN-W8", "0" ],
+    ];
+  } else if (navigator.userAgent.indexOf("Windows") !== -1) {
+    return [
+      ["CambriaMath", "0"],
+      ["Ming-Lt-HKSCS-ExtB", "0"],
+      ["NSimSun", "0"],
+      ["calibri-bolditalic", "0"]
+      ];
+  }
+  return [];
+}
+
+function stripSpaces(fontName) {
+  return fontName.replace(/\s+/g, '');
+}
+
+async function addTestNodes() {
+  var containerDiv = document.createElement("div");
+  var fontFaceDeclarations = "";
+    for (font_name_and_testchars of getFontsWithTestCharsForOS()) {
+    var font_name = font_name_and_testchars[0];
+    // Add cursive to font stack to avoid ignoring failures when Roboto is used as fallback.
+    fontFaceDeclarations +=
+      `@font-face { font-family: ${stripSpaces(font_name)}_webfont; \
+      src: local("${font_name}"); } .${stripSpaces(font_name)}_style \
+      { font-family: ${stripSpaces(font_name)}_webfont, cursive; } `;
+    var testElement = document.createElement("div");
+    testElement.classList += `testnode ${stripSpaces(font_name)}_style`;
+    testElement.innerText = font_name_and_testchars[1];
+    containerDiv.appendChild(testElement);
+  }
+  fontfaces.innerText = fontFaceDeclarations;
+  document.body.appendChild(containerDiv);
+  await document.fonts.ready;
+  // Force layout so that the DevTools side of the test can start accessing
+  // nodes' font information reliably.
+  document.body.offsetTop;
+  return containerDiv.children.length;
+}
+</script>
--- /dev/null
+++ b/content/test/data/form_that_posts_cross_site.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+  <head></head>
+  <meta name="viewport" content="width=device-width">
+  <body>
+    <form id="text-form" method="POST" action="/cross-site-307/i.com/cross-site-307/x.com/echoall">
+      <input type="text" id="text" name="text" value="value">
+      <input type="submit">
+    </form>
+    <form id="file-form" method="POST" action="/cross-site-307/x.com/echoall"
+          enctype="multipart/form-data">
+      <input type="file" id="file" name="file">
+      <input type="submit">
+    </form>
+  </body>
+</html>
--- /dev/null
+++ b/content/test/data/form_that_posts_to_echoall.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+  <head></head>
+  <body>
+    <form id="form" method="POST" action="/echoall">
+      <input type="text" name="text" value="value">
+      <input type="submit">
+    </form>
+    <form id="file-form" method="POST" action="/echoall" enctype="multipart/form-data">
+      <input type="file" id="file" name="file">
+      <input type="submit">
+    </form>
+  </body>
+</html>
--- /dev/null
+++ b/content/test/data/form_that_posts_to_echoall_nocache.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+  <head></head>
+  <body>
+    <form id="form" method="POST" action="/echoall/nocache">
+      <input type="text" name="text" value="value">
+      <input type="submit">
+    </form>
+  </body>
+</html>
--- /dev/null
+++ b/content/test/data/frame-ancestors-none.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>This page should NOT load in a frame</title>
+</head>
+<body>
+This page cannot be loaded in an iframe because its CSP specifies frame-ancestors 'none'.
+</body>
+</html>
+
--- /dev/null
+++ b/content/test/data/frame-src-self-and-b.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>This page should only allow subframes from the same origin or b.com</title>
+</head>
+<body>
+This page should only allow subframes from the same origin or from b.com,
+because its CSP headers specify frame-src 'self' and 'b.com'.
+<iframe src="/cross-site/b.com/title2.html"></iframe>
+<iframe srcdoc="
+  <html>
+    <head>
+      <title>subtitle1</title>
+    </head>
+    <body>
+      <iframe src='/cross-site/b.com/title2.html'></iframe>
+    </body>
+  </html>"></iframe>
+</body>
+</html>
+
--- /dev/null
+++ b/content/test/data/frame_owner_properties_csp.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<body>
+  <iframe id="child-1"
+      csp="object-src 'none'"
+      src="/cross-site/b.com/title1.html">
+  </iframe>
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/frame_owner_properties_margin.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<body>
+  <iframe id="child-1"
+      marginwidth="10"
+      marginheight="50"
+      src="/cross-site/b.com/title1.html"></iframe>
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/frame_owner_properties_scrolling.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<body>
+  <iframe
+      id="child-1"
+      height="200"
+      width="200"
+      frameborder="0"
+      src="/cross-site/b.com/tall_page.html"></iframe>
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/frame_with_load_event.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+  This page has a cross-site iframe with a load event.
+  <iframe id='test' onload="document.title += 'LOADED';" src="/cross-site/baz.com/title1.html"></iframe>
+</body>
+</html>
+
--- /dev/null
+++ b/content/test/data/fullscreen_frame.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+
+var expectedWidth, expectedHeight;
+
+function onResize() {
+  if (window.innerWidth == expectedWidth && window.innerHeight == expectedHeight) {
+    domAutomationController.send("resize");
+    removeResizeListener();
+  }
+}
+
+function addResizeListener(width, height) {
+  expectedWidth = width;
+  expectedHeight = height;
+  window.addEventListener('resize', onResize);
+}
+
+function removeResizeListener() {
+  window.removeEventListener('resize', onResize);
+}
+
+function activateFullscreen() {
+  document.getElementById("fullscreen-div").webkitRequestFullscreen();
+}
+
+function exitFullscreen() {
+  document.webkitExitFullscreen();
+}
+
+</script>
+</head>
+<body>
+
+<div>This page contains a div that will go fullscreen</div>
+
+<div id="fullscreen-div" style="border: 1px solid black;">
+  This div will go fullscreen
+</div>
+
+<iframe id="child-0" srcdoc="This should not go fullscreen"></iframe>
+
+</body>
+</html>
+
--- /dev/null
+++ b/content/test/data/hang_before_unload.html
@@ -0,0 +1,29 @@
+<html>
+
+  <head><title>BeforeUnload dialog</title>
+  <script>
+    function clickLink() {
+      var evt = document.createEvent("MouseEvents");
+      evt.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, false,
+                         false, false, 0, null);
+      var target = document.getElementById("link");
+      target.dispatchEvent(evt);
+    }
+
+    function clickLinkSoon() {
+      window.setTimeout(clickLink, 100);
+      return true;
+    }
+
+    window.addEventListener("beforeunload", function(e) {
+      var confirmationMessage = "foo";
+      e.returnValue = confirmationMessage;
+      return confirmationMessage;
+    });
+
+  </script>
+  </head>
+
+<a href="title2.html" id="link">link</a><br>
+
+</html>
--- /dev/null
+++ b/content/test/data/hello.html
@@ -0,0 +1,2 @@
+<!doctype html>
+<p>hello</p>
--- /dev/null
+++ b/content/test/data/https_upgrade_cross_site.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<title>Cross Site HTTPS upgrade</title>
+
+<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">
+
+<body>
+  <iframe src="http://other.com/title1.html"></iframe>
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/https_upgrade_same_site.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<title>Same Site HTTPS upgrade</title>
+
+<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">
+
+<body>
+  <iframe src="/title1.html"></iframe>
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/iframe_clipped.html
@@ -0,0 +1,136 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <style>
+    body, html {
+      width: 100%;
+      height: 100%;
+      margin: 0;
+    }
+
+    #iframe {
+      width: 100vw;
+      height: 100vh;
+      border: 0;
+      padding: 0;
+      overflow: scroll;
+    }
+
+    #clip {
+      width: 50%;
+      height: 20%;
+      overflow: hidden;
+    }
+  </style>
+</head>
+<body>
+  <div id="clip">
+    <iframe id="iframe">
+    </iframe>
+  </div>
+  <script>
+  window.loaded = false;
+  window.notifyBrowser = false;
+  const kMarginOfError = 1;
+  const tinyTimeout = 50;
+
+  window.addEventListener("load", () => {
+    window.loaded = true;
+    if (window.notifyBrowser)
+      notifyWhenLoaded();
+  });
+
+  function notifyWhenLoaded() {
+    if (loaded)
+      window.domAutomationController.send("LOADED");
+    else
+      window.notifyBrowser = true;
+  }
+
+  // Waits until the |visualViewport| reaches the given size (approximately).
+  // Notifies the browser when this happens.
+  function notifyVisualViewportChanged(width, height) {
+    if ((Math.abs(visualViewport.width - width) < kMarginOfError) &&
+        (Math.abs(visualViewport.height - height) < kMarginOfError)) {
+      return window.domAutomationController.send("SIZED");
+    }
+
+    window.setTimeout(() => {
+      notifyVisualViewportChanged(width, height);
+    }, tinyTimeout);
+  }
+
+  function isEmptyRect(rect) {
+    return (rect.width * rect.height) === 0;
+  }
+
+  function intersect(rect1, rect2) {
+    let minX1 = rect1.x, maxX1 = minX1 + rect1.width
+        minY1 = rect1.y, maxY1 = minY1 + rect1.height,
+        minX2 = rect2.x, maxX2 = minX2 + rect2.width,
+        minY2 = rect2.y, maxY2 = minY2 + rect2.height;
+
+    return !(isEmptyRect(rect1) ||
+             isEmptyRect(rect2) ||
+             (minX1 > maxX2) ||
+             (minX2 > maxX1) ||
+             (minY1 > maxY2) ||
+             (minY2 > maxY1));
+  }
+
+  function visualViewportAsRect() {
+    return {
+      x: visualViewport.offsetLeft,
+      y: visualViewport.offsetTop,
+      width: Math.round(visualViewport.width),
+      height: Math.round(visualViewport.height)
+    };
+  }
+
+  function rectAsString(rect) {
+    return `${rect.x},${rect.y},${rect.width},${rect.height}`;
+  }
+
+  // Waits until the given element is inside the |visualViewport|.
+  function notifyWhenVisible(el) {
+    if (intersect(el.getBoundingClientRect(), visualViewportAsRect())) {
+      window.domAutomationController.send("VISIBLE");
+    } else {
+      window.setTimeout(() => {
+        notifyWhenVisible(el);
+      }, tinyTimeout);
+    }
+  }
+
+  let lastLayoutViewportX;
+  let lastLayoutViewportY;
+  let lastVisualViewportX;
+  let lastVisualViewportY;
+  let lastScale;
+  function notifyWhenViewportStable(numFrames) {
+    const kUnchangedFramesConsideredStable = 10;
+
+    if (lastLayoutViewportX !== window.scrollX ||
+        lastLayoutViewportY !== window.scrollY ||
+        lastVisualViewportX !== window.visualViewport.offsetLeft ||
+        lastVisualViewportY !== window.visualViewport.offsetTop ||
+        lastScale !== window.visualViewport.scale) {
+      lastLayoutViewportX = window.scrollX;
+      lastLayoutViewportY = window.scrollY;
+      lastVisualViewportX = window.visualViewport.offsetLeft;
+      lastVisualViewportY = window.visualViewport.offsetTop;
+      lastScale = window.visualViewport.scale;
+      numFrames = 0;
+    }
+
+    numFrames++;
+
+    if (numFrames == kUnchangedFramesConsideredStable) {
+      window.domAutomationController.send("VIEWPORT_STABLE");
+    } else {
+      requestAnimationFrame(notifyWhenViewportStable.bind(null, numFrames));
+    }
+  }
+  </script>
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/iframe_out_of_view.html
@@ -0,0 +1,120 @@
+<!DOCTYPE html>
+<html>
+<body>
+  <style>
+  body {
+    width: 100%;
+    height: 100%;
+    margin: 0;
+  }
+
+  iframe {
+    width: 400px;
+    height: 400px;
+    margin: 300%;
+    border: 0;
+    padding: 0;
+    overflow: scroll;
+  }
+
+  input {
+    margin: 0;
+    padding: 0;
+    border: 0;
+    height: 1px;
+    font-size: 1px;
+  }
+  </style>
+  <p> Single &lt;iframe&gt; which is positioned out of view. </p>
+  <iframe id="test_iframe">
+  </iframe>
+  <p> Iframe should be positioned way above </p>
+  <script>
+  window.loaded = false;
+  window.notifyBrowser = null;
+  const kInfinity = 1000000;
+  const kMarginOfError = 1;
+  const tinyTimeout = 50;
+
+  window.addEventListener("load", () => {
+    document.scrollingElement.scrollTop = kInfinity;
+    document.scrollingElement.scrollLeft = kInfinity;
+    window.loaded = true;
+    if (window.notifyBrowser) {
+      window.notifyBrowser("LOADED");
+      window.notifyBrowser = null;
+    }
+  });
+
+  function notifyWhenLoaded() {
+    return new Promise(resolve => {
+      if (loaded)
+        resolve("LOADED");
+      else
+        window.notifyBrowser = resolve;
+    });
+  }
+
+  // Waits until the |visualViewport| reaches the given size (approximately).
+  // Notifies the browser when this happens.
+  function notifyVisualViewportChanged(width, height) {
+    if ((Math.abs(visualViewport.width - width) < kMarginOfError) &&
+        (Math.abs(visualViewport.height - height) < kMarginOfError)) {
+      return window.domAutomationController.send("SIZED");
+    }
+
+    window.setTimeout(() => {
+      notifyVisualViewportChanged(width, height);
+    }, tinyTimeout);
+  }
+
+  function isEmptyRect(rect) {
+    return (rect.width * rect.height) === 0;
+  }
+
+  function intersect(rect1, rect2) {
+    let minX1 = rect1.x, maxX1 = minX1 + rect1.width
+        minY1 = rect1.y, maxY1 = minY1 + rect1.height,
+        minX2 = rect2.x, maxX2 = minX2 + rect2.width,
+        minY2 = rect2.y, maxY2 = minY2 + rect2.height;
+
+    return !(isEmptyRect(rect1) ||
+             isEmptyRect(rect2) ||
+             (minX1 > maxX2) ||
+             (minX2 > maxX1) ||
+             (minY1 > maxY2) ||
+             (minY2 > maxY1));
+  }
+
+  function visualViewportAsRect() {
+    return {
+      x: visualViewport.offsetLeft,
+      y: visualViewport.offsetTop,
+      width: Math.round(visualViewport.width),
+      height: Math.round(visualViewport.height)
+    };
+  }
+
+  function rectAsString(rect) {
+    return `${rect.x},${rect.y},${rect.width},${rect.height}`;
+  }
+
+  function addFocusedInputField() {
+    var input = document.createElement("input");
+    input.value="some text";
+    document.body.insertBefore(input, document.body.firstChild);
+    input.focus();
+  }
+
+  // Waits until the given element is inside the |visualViewport|.
+  async function notifyWhenVisible(el) {
+    while (!intersect(el.getBoundingClientRect(), visualViewportAsRect())) {
+      await new Promise(resolve => {
+        window.setTimeout(resolve, tinyTimeout);
+      });
+    }
+    return "VISIBLE";
+  }
+  </script>
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/infinitely_loading_image.html
@@ -0,0 +1 @@
+<img src="/hung">
--- /dev/null
+++ b/content/test/data/input_box.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <style>
+    body, html {
+      width: 100%;
+      height: 100%;
+      margin: 0;
+    }
+
+    #space {
+      width: 10000px;
+      height: 10000px;
+    }
+
+    #input {
+      position: absolute;
+      top: 4000px;
+      left: 3000px;
+      border:0;
+      padding:0;
+    }
+  </style>
+  <script>
+    notifyBrowser = false;
+    function notifyWhenLoaded() {
+      if (loaded)
+        window.domAutomationController.send("LOADED");
+      else
+        window.notifyBrowser = true;
+    }
+
+    function rectAsString(rect) {
+      return `${rect.x},${rect.y},${rect.width},${rect.height}`;
+    }
+
+
+    window.addEventListener('load', () => {
+      document.getElementById("input").focus({'preventScroll': true});
+      window.scrollTo(0, 0);
+
+      window.loaded = true;
+      if (window.notifyBrowser)
+        notifyWhenLoaded();
+    });
+  </script>
+</head>
+<body>
+  <div id="space"></div>
+  <input id="input" type="text">
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/input_in_iframe.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <style>
+    body, html, iframe {
+      width: 100%;
+      height: 100%;
+      margin: 0;
+    }
+
+    iframe {
+      border: 0;
+    }
+
+    #space {
+      width: 200px;
+      height: 200px;
+    }
+
+  </style>
+  <script>
+    loaded = false;
+    notifyBrowser = null;
+    function notifyWhenLoaded() {
+      if (loaded)
+        return "OUTER_LOADED";
+      return new Promise(resolve => {
+        window.notifyBrowser = resolve;
+      });
+    }
+    window.addEventListener('load', () => {
+      window.loaded = true;
+      if (window.notifyBrowser)
+        window.notifyBrowser('OUTER_LOADED');
+    });
+  </script>
+</head>
+<body>
+  <div id="space"></div>
+  <iframe src="input_box.html" >
+
+ </body>
+</html>
\ No newline at end of file
--- /dev/null
+++ b/content/test/data/interaction-mq-dynamic.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>Dynamic interaction media query test</title>
+    <script type="text/javascript">
+      function handleMediaChange(mediaQueryList) {
+        if (mediaQueryList.matches) {
+          pass();
+        } else {
+          fail();
+        }
+      }
+
+      function start() {
+        var mql = window.matchMedia("(pointer: coarse) and (hover:hover)");
+        mql.addListener(handleMediaChange);
+      }
+
+      function pass() {
+        document.title = 'SUCCESS';
+      }
+
+      function fail() {
+        document.title = 'FAIL';
+      }
+    </script>
+</head>
+<body onLoad="start()">
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/isolated_base_origin_with_subframe.html
@@ -0,0 +1,7 @@
+<html>
+  <head></head>
+  <body>
+    <iframe style="width: 50%; height: 50%"></iframe>
+    <iframe style="width: 50%; height: 50%"></iframe>
+  </body>
+</html>
--- /dev/null
+++ b/content/test/data/large-cursor.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<style>
+body {
+  height: 1000px;
+  width: 1000px;
+  cursor: -webkit-image-set(url(single_face.jpg) 1x) 120 120, auto;
+}
+</style>
+
--- /dev/null
+++ b/content/test/data/link_rel_prefetch.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+<body>
+  <link rel="prefetch" href="image.jpg" />
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/link_rel_prefetch_referrer_policy.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta content="origin" name="referrer">
+  </head>
+  <body>
+    <link rel="prefetch" href="image.jpg" />
+  </body>
+</html>
--- /dev/null
+++ b/content/test/data/local_storage.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML>
+<body>
+  <script>
+    var pagehide_storage_at_load =
+      localStorage.getItem('pagehide_storage');
+    var visibilitychange_storage_at_load =
+      localStorage.getItem('visibilitychange_storage');
+    var unload_storage_at_load =
+      localStorage.getItem('unload_storage');
+  </script>
+  This page gets the value of the items "pagehide_storage",
+  "visibilitychange_storage" and "unload_storage" from
+  localStorage at first load.
+</body>
--- /dev/null
+++ b/content/test/data/navigate_on_load.html
@@ -0,0 +1,12 @@
+<html>
+<head>
+<title>Navigate On Load</title>
+<script type="text/javascript">
+window.onload = () => {
+  location.href = '/hello.html';
+};
+</script>
+</head>
+<body>
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/navigate_opener.html
@@ -0,0 +1,12 @@
+<html>
+
+ <head><title>Navigate opener</title>
+ <script>
+  function navigateOpener() {
+    window.opener.location.href = "title2.html";
+    return true;
+  }
+ </script>
+ </head>
+
+</html>
--- /dev/null
+++ b/content/test/data/nested-unload-0.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<html>
+  <body><iframe src="/cross-site/b.com/nested-unload-1.html"></iframe></body>
+</html>
--- /dev/null
+++ b/content/test/data/nested-unload-1.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<html>
+  <body><iframe src="/cross-site/a.com/nested-unload-2.html"></iframe></body>
+</html>
--- /dev/null
+++ b/content/test/data/nested-unload-2.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+  <script>
+    window.addEventListener('unload', () => {
+      window.top.document.querySelector('iframe').remove();
+    });
+  </script>
+</html>
--- /dev/null
+++ b/content/test/data/nested_page_with_subresources.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<body>
+<script>
+  var i = document.createElement("iframe");
+  i.src = location.protocol + "//not-" + location.hostname + (location.port ? (":" + location.port) : "") + "/page_with_subresources.html";
+  document.body.appendChild(i);
+</script>
+</body>
--- /dev/null
+++ b/content/test/data/net_info.html
@@ -0,0 +1,34 @@
+<html>
+<head></head>
+<script>
+
+function getType() {
+  return navigator.connection.type;
+}
+
+function getDownlinkMax() {
+  return navigator.connection.downlinkMax;
+}
+
+function getOnLine() {
+  return navigator.onLine;
+}
+
+function getEffectiveType() {
+  return navigator.connection.effectiveType;
+}
+
+function getRtt() {
+  return navigator.connection.rtt;
+}
+
+function getDownlink() {
+  return navigator.connection.downlink;
+}
+
+function getSaveData() {
+  return navigator.connection.saveData;
+}
+
+</script>
+</html>
--- /dev/null
+++ b/content/test/data/no_color_scheme_meta_slow.html
@@ -0,0 +1,2 @@
+<!doctype html>
+<link rel="stylesheet" href="/slow-response">
--- /dev/null
+++ b/content/test/data/nocache-with-etag.html
@@ -0,0 +1,4 @@
+<html>
+<head><title>Nocache Test Page</title></head>
+<body>Nocache test body</body>
+</html>
--- /dev/null
+++ b/content/test/data/nosniff-test.html
@@ -0,0 +1,4 @@
+<html>
+<head><title>nosniff Test</title></head>
+<body>We should NOT sniff this content as HTML.</body>
+</html>
--- /dev/null
+++ b/content/test/data/nostore.html
@@ -0,0 +1,4 @@
+<html>
+<head><title>Cache-Control: no-store Test Page</title></head>
+<body>Cache-Control: no-store test body</body>
+</html>
--- /dev/null
+++ b/content/test/data/object-frame.html
@@ -0,0 +1,4 @@
+<html>
+<head></head>
+<body><object data="/title1.html"><p>fallback</p></object></body>
+</html>
--- /dev/null
+++ b/content/test/data/onunload_cookie.html
@@ -0,0 +1,18 @@
+<html> 
+  <head>
+    <title>Loading...</title>
+  </head>
+  <body onload="attachUnloadListener()"> 
+    <script> 
+      function attachUnloadListener() {
+        window.addEventListener('unload', onUnload, false);
+        document.title = "set cookie on unload";
+      }
+
+      function onUnload() {
+        document.cookie = "onunloadCookie=foo";
+      }
+    </script> 
+    <p>Navigate to another page to set cookie</p>
+  </body> 
+</html> 
--- /dev/null
+++ b/content/test/data/overlapping_cross_site_iframe.html
@@ -0,0 +1,18 @@
+<html>
+<title>Overlapping cross site iframe</title>
+
+This is the main body content.
+<iframe id="iframe" style="width:300px; height:300px; position:absolute; left:100px; top:100px"></iframe>
+<div id="target" style="background-color: yellow; position: absolute; top: 120px; left: 120px; width: 100px; height: 100px"></div>
+
+<script>
+
+  var url = window.location.protocol + '//b.com';
+  if (window.location.port)
+    url += ':' + window.location.port;
+  url += "/cross_site_iframe_factory.html?b()";
+
+  document.getElementById('iframe').src = url;
+</script>
+
+</html>
--- /dev/null
+++ b/content/test/data/overscroll_navigation.html
@@ -0,0 +1,90 @@
+<html>
+<title>Title: 0</title>
+<style>
+
+.large {
+  width: 300px;
+  height: 100px;
+  background-color: red;
+  margin: 300px;
+}
+
+::-webkit-scrollbar {
+    display: none;
+}
+
+</style>
+
+<div name='0' class='large'>A Large Div</div>
+<div name='1' class='large'>A Large Div</div>
+<div name='2' class='large'>A Large Div</div>
+<div name='3' class='large'>A Large Div</div>
+<div name='4' class='large'>A Large Div</div>
+<div name='5' class='large'>A Large Div</div>
+<div name='6' class='large'>A Large Div</div>
+<div name='7' class='large'>A Large Div</div>
+<div name='8' class='large'>A Large Div</div>
+<div name='9' class='large'>A Large Div</div>
+
+<script>
+
+window.touchmoveCount = 0;
+
+function get_current() {
+  if (location.hash.length == 0)
+    return 0;
+  return parseInt(location.hash.substr(1));
+}
+
+function navigate_next() {
+  var current = get_current();
+  current = (current + 1) % 10;
+  location.hash = "#" + current;
+}
+
+function navigate_prev() {
+  var current = get_current();
+  current = (current + 9) % 10;
+  location.hash = "#" + current;
+}
+
+function touch_start_handler() {
+}
+
+function touch_move_handler() {
+  window.touchmoveCount++;
+}
+
+function install_touch_handler() {
+  document.addEventListener('touchstart', touch_start_handler);
+}
+
+function install_touchmove_handler() {
+  document.addEventListener('touchmove', touch_move_handler);
+}
+
+function reset_touchmove_count() {
+  window.touchmoveCount = 0;
+}
+
+function uninstall_touch_handler() {
+  document.removeEventListener('touchstart', touch_start_handler);
+}
+
+function use_replace_state() {
+  window.history.replaceState({}, 'foo');
+}
+
+function use_push_state() {
+  window.history.pushState({}, 'foo2', 'newpath');
+}
+
+onload = function() {
+  window.onhashchange = function() {
+    document.title = "Title: " + location.hash;
+  }
+}
+
+</script>
+
+</html>
--- /dev/null
+++ b/content/test/data/page-executing-trust-token-signing-from-204-subframe.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+
+<script>
+  "use strict";
+  const createChildAndExecuteSigningOp = function() {
+    // Assuming the /nocontent URL is handled by a server returning HTTP 204,
+    // which is the case for EmbeddedTestServers using the default handlers,
+    // |child| will not commit a navigation.
+    const child = document.createElement('iframe');
+    child.src = '/nocontent';
+    document.body.appendChild(child);
+
+    // Without the fix to crbug.com/1111735, the following will lead to a
+    // renderer kill.
+    const childDocument = child.contentDocument;
+    const myFrame = childDocument.createElement('iframe');
+    myFrame.trustToken = JSON.stringify({
+      type: 'send-redemption-record',
+      issuers: ['https://issuer.example']
+    });
+    myFrame.src = '/title1.html'; // arbitrary URL, no need for handling logic
+    childDocument.body.appendChild(myFrame);
+  };
+
+  window.onload = createChildAndExecuteSigningOp;
+</script>
--- /dev/null
+++ b/content/test/data/page-with-trust-token-permissions-policy-disabled.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title></title>
+
+<body>
+  <p>This page has the trust-token-redemption Feature Policy feature disabled (via its mock headers).</p>
+  <p>It also has an iframe! Yay for iframes.</p>
+
+  <iframe src="/cross-site/b.com/title1.html" id="test_iframe" />
+</body>
--- /dev/null
+++ b/content/test/data/page204.html
@@ -0,0 +1 @@
+<title>This page intentionally left blank</title>
--- /dev/null
+++ b/content/test/data/page404.html
@@ -0,0 +1,2 @@
+<title>SUCCESS</title>
+ 
\ No newline at end of file
--- /dev/null
+++ b/content/test/data/page500.html
@@ -0,0 +1 @@
+<title>Server Error</title>
--- /dev/null
+++ b/content/test/data/page_with_allowfullscreen_frame.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+<body>
+  This page has one cross-site iframe with permission to enter fullscreen.
+  <iframe id="child-0" src="/cross-site/b.com/title1.html" allowFullscreen></iframe>
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/page_with_animation.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<html>
+  <head>
+    <style>
+      @keyframes move {
+        from {
+          transform: translate(-200px, 0);
+        }
+        to {
+          transform: translate(200px, 0);
+        }
+      }
+
+      div {
+        height: 200px;
+        width: 200px;
+
+        animation-duration: 1.5s;
+        animation-iteration-count: infinite;
+        animation-name: move;
+
+        background: black;
+      }
+    </style>
+  </head>
+  <body>
+    <div></div>
+  </body>
+</html>
--- /dev/null
+++ b/content/test/data/page_with_blank_iframe.html
@@ -0,0 +1,7 @@
+<html>
+<head></head>
+<body>
+  <p>This page has blank iframe. Yay for iframes!
+  <p><iframe id="test_iframe" name="test_iframe"></iframe>
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/page_with_blank_iframe_tree.html
@@ -0,0 +1,17 @@
+<html>
+<head>
+<script>
+function makeDeeper() {
+  // Create an about:blank iframe nested inside the existing #f1 about:blank
+  // iframe.
+  var iframe = document.getElementById("f1");
+  var grandKid = iframe.contentDocument.createElement("iframe");
+  iframe.contentDocument.body.appendChild(grandKid);
+}
+</script>
+</head>
+<body onload="makeDeeper()">
+  <p>This page has an iframe. Yay for iframes!
+  <p><iframe src="about:blank" id="f1"></iframe>
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/page_with_blur.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+
+window.onload = function() {
+  document.body.onblur = function() {
+    domAutomationController.send("document-blur");
+  }
+}
+
+</script>
+</head>
+
+<body>
+This page has a blur event.
+</body>
+</html>
+
--- /dev/null
+++ b/content/test/data/page_with_cached_subresource.html
@@ -0,0 +1,7 @@
+<html>
+<head>
+<script src="cachetime"></script>
+</head>
+<body>
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/page_with_click_handler.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html style="width: 100%; height: 100%">
+<head>
+  <script>
+    clicksReceived = 0;
+
+    function clickHandler(e) {
+      var clickResult = document.getElementById("click-results");
+      clicksReceived++;
+      if (clicksReceived == 1)
+        clickResult.textContent = "1 click received";
+      else
+        clickResult.textContent = clicksReceived + " clicks received";
+    }
+
+    function getClickStatus() {
+      var clickResult = document.getElementById("click-results");
+      return clickResult.textContent;
+    }
+
+    window.addEventListener('load', function(){
+      document.body.addEventListener('click',
+                                      function(e){
+                                        clickHandler(e);
+                                      }, false);
+    }, false);
+  </script>
+</head>
+
+<!-- Be sure to set 100% width/height so the test can determine an appropriate
+     screen region for targeting events. -->
+<body style="width: 100%; height: 100%">
+  Page with Click Handler
+  <div id="click-results">0 clicks received</div>
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/page_with_data_iframe_and_allow.html
@@ -0,0 +1,8 @@
+<html>
+<head></head>
+<body>
+  <p>This page has a data URL iframe which is initially allowed to use the
+  Geolocation API.
+  <p><iframe allow="geolocation" src="data:text/html,Subframe"></iframe>
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/page_with_frameset.html
@@ -0,0 +1,6 @@
+<html>
+<frameset cols="50%,50%">
+  <frame src="title1.html"/>
+  <frame src="title1.html"/>
+</frameset>
+</html>
\ No newline at end of file
--- /dev/null
+++ b/content/test/data/page_with_hidden_iframe.html
@@ -0,0 +1,7 @@
+<html>
+<head></head>
+<body>
+  <p>This page has a hidden iframe.
+  <p><iframe src="title1.html" id="test_iframe" style="display: none"></iframe>
+</body>
+</html>
\ No newline at end of file
--- /dev/null
+++ b/content/test/data/page_with_iframe.html
@@ -0,0 +1,8 @@
+<html>
+<head></head>
+<body>
+  <img src="image.jpg"/>
+  <p>This page has an iframe. Yay for iframes!
+  <p><iframe src="title1.html" id="test_iframe" name="test_iframe"></iframe>
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/page_with_iframe_and_image.html
@@ -0,0 +1,9 @@
+<html>
+<head></head>
+<body>
+  <img src="image.jpg"/>
+  <p>This page has an iframe. Yay for iframes!
+  <p>
+    <iframe src="page_with_image.html" id="test_iframe"></iframe>
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/page_with_iframe_and_link.html
@@ -0,0 +1,31 @@
+<html>
+
+ <head><title>Page with iframe and link</title>
+ <script>
+  function simulateClick(target) {
+    var evt = document.createEvent("MouseEvents");
+    evt.initMouseEvent("click", true, true, window,
+                       0, 0, 0, 0, 0, false, false,
+                       false, false, 0, null);
+
+    return target.dispatchEvent(evt);
+  }
+
+  function setPortNumber(portNumber) {
+    var link = document.getElementById("cross_site_link");
+    link.setAttribute("href", "http://foo.com:" + portNumber + "/title2.html");
+    return true;
+  }
+
+  function clickCrossSiteLink() {
+    return simulateClick(document.getElementById("cross_site_link"));
+  }
+ </script>
+ </head>
+
+<a href="http://foo.com/title2.html" id="cross_site_link">cross-site</a><br>
+
+<p>This page has an iframe. Yay for iframes!
+<p><iframe src="about:blank"></iframe>
+
+</html>
--- /dev/null
+++ b/content/test/data/page_with_image.html
@@ -0,0 +1,7 @@
+<html>
+<head></head>
+<body>
+  <img src="./blank.jpg"/>
+  <p>This page has an image. Yay for images!
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/page_with_image_redirect.html
@@ -0,0 +1,4 @@
+<html>
+This page has an image that is going through a redirect.
+<img src="server-redirect?blank.jpg"></img>
+</html>
\ No newline at end of file
--- /dev/null
+++ b/content/test/data/page_with_input_field.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="./result_queue.js"></script>
+<script>
+
+var resultQueue = new ResultQueue();
+
+function focusInputField() {
+  var inputField = document.getElementById("text-field");
+  inputField.focus();
+  return resultQueue.pop();
+}
+
+function getInputFieldText() {
+  var inputField = document.getElementById("text-field");
+  return inputField.value;
+}
+
+async function waitForBlur() {
+  while (true) {
+    const result = await resultQueue.pop();
+    if (result == "input-blur") {
+      return true;
+    }
+  }
+}
+
+async function waitForInput() {
+  while (true) {
+    const result = await resultQueue.pop();
+    if (result != "input-focus" && result != "input-blur") {
+      return result;
+    }
+  }
+}
+
+function onInputFocus() {
+  resultQueue.push("input-focus");
+}
+
+function onInputBlur() {
+  resultQueue.push("input-blur");
+}
+
+</script>
+</head>
+
+<body>
+This page has a cross-site iframe and a text input field.
+<input type='text' id='text-field'
+       onfocus="onInputFocus()" onblur="onInputBlur()"
+       style="width: 120px;height: 20px;"><br>
+<iframe id='subframe' src="/cross-site/b.com/page_with_blur.html"></iframe>
+</body>
+</html>
+
--- /dev/null
+++ b/content/test/data/page_with_meta_refresh_frame.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<html>
+  <head>
+    <title>test</title>
+    <script>
+      window.addEventListener("load",  populateIframe);
+      function populateIframe() {
+        html = "<!doctype html>" +
+        "<html>" +
+        "  <head>" +
+        "    <meta http-equiv=\"refresh\" content=\"0\">" +
+        "    <title>meta refresh in iframe reloads parent</title>" +
+        "  </head>" +
+        "  <body>" +
+        "    iframe" +
+        "  </body>" +
+        "</html>";
+        doc = document.getElementById("frame").contentDocument;
+        // Opening the IFrame's document to write to it will cause the IFrame's 
+        // document's URL to be set to the URL of the parent document 
+        doc.open();
+        doc.write(html);
+        doc.close();
+      }
+    </script>
+  </head>
+  <body>
+    <div>parent</div>
+    <iframe id="frame"></iframe>
+  </body>
+</html>
--- /dev/null
+++ b/content/test/data/page_with_multiple_cached_subresources.html
@@ -0,0 +1,8 @@
+<html>
+<head>
+<script src="cachetime"></script>
+</head>
+<body>
+<script src="cachetime"></script>
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/page_with_multiple_iframes.html
@@ -0,0 +1,11 @@
+<html>
+<head></head>
+<body>
+  <p>This page has several iframes. Yay for iframes!</p>
+  <p><iframe src="page_with_image.html" id="test_iframe_1"></iframe></p>
+  <p><iframe src="page_with_popup.html" id="test_iframe_2"></iframe></p>
+  <p><iframe src="page_with_frameset.html" id="test_iframe_3"></iframe></p>
+  <p><iframe src="page_with_allowfullscreen_frame.html" id="test_iframe_4"></iframe></p>
+  <p><iframe src="page_with_iframe_and_link.html" id="test_iframe_5"></iframe></p>
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/page_with_non_webby_iframe.html
@@ -0,0 +1,7 @@
+<html>
+<head></head>
+<body>
+  <p>This page has an iframe with a non-webby scheme.</p>
+  <p><iframe src="foo://bar" id="test_iframe"></iframe>
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/page_with_nostore_iframe.html
@@ -0,0 +1,8 @@
+<html>
+  <head></head>
+  <body>
+    <iframe src="nostore.html"></iframe>
+    Main Frame, normal headers.
+    <img src="nostore.jpg">
+  </body>
+</html>
--- /dev/null
+++ b/content/test/data/page_with_object_fallback.html
@@ -0,0 +1,38 @@
+<!doctype html>
+<html>
+<head>
+<title>Test page for &lt;object&gt; element rendering fallback</title>
+</head>
+<body>
+<object data="about:blank" type="text/html">
+  <p id="fallback-content">Fallback Content</p>
+</object>
+<p>Some text so the document is ready for input.</p>
+<script>
+  var fallback = document.getElementById("fallback-content");
+  var object = document.querySelector("object");
+
+  function setUrl(url) {
+    return new Promise(resolve => {
+      object.addEventListener("load", () => {
+        resolve("OBJECT_LOAD");
+      }, {once: true});
+      object.data = url;
+    });
+  }
+
+  function fallbackVisible() {
+    let rect = fallback.getBoundingClientRect();
+    let hasLayout = (rect.width * rect.height) > 0;
+    return hasLayout;
+  }
+
+  async function notifyWhenFallbackShown() {
+    while (!fallbackVisible()) {
+      await new Promise(resolve => window.setTimeout(resolve, 10));
+    }
+    return true;
+  }
+</script>
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/page_with_popup.html
@@ -0,0 +1,7 @@
+<html>
+<title>page with a iframe for popup</title>
+<head></head>
+  <body>
+    <iframe id="test"></iframe>
+  </body>
+</html>
--- /dev/null
+++ b/content/test/data/page_with_raf_counter.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<h1>This page contains a RAF loop and does some measurements of the interval</h1>
+<script>
+var recent_time_stamps = new Array(10);
+var raf_count = 0;
+
+function reset_count() {
+  raf_count = 0;
+}
+function on_raf(t) {
+  raf_count++;
+  recent_time_stamps.shift();
+  recent_time_stamps.push(t);
+  window.requestAnimationFrame(on_raf);
+}
+window.requestAnimationFrame(on_raf);
+</script>
--- /dev/null
+++ b/content/test/data/page_with_resize_handler.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<script>
+var resize_count = 0;
+window.addEventListener('resize', () => {resize_count++;});
+</script>
+<body>
+Page that counts the number of times it receives a resize event.
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/page_with_rss.html
@@ -0,0 +1,23 @@
+<html>
+<head>
+  <link type="application/rss+xml" rel="alternate" href="rss.xml" />
+  <link type="application/RSS+atom" rel="service.feed" href="atom.xml" />
+  <link type="application/atom+XML" rel="AlTeRnAtE"
+        href="https://some/path.xml" />
+  <!--Some non-RSS link tags-->
+  <link />
+  <link type="other" href="other" />
+  <link type="application/rss+xml" />
+  <link type="application/rss+xml" rel="alternate" href="https://badurl:💩/" />
+  <link type="application/rss+xml" href="no-rel.xml" />
+  <link type="application/rss+xml" rel="bad-rel" href="bad-rel.xml" />
+  <link type="application/rss+xml" rel="non-ascii-💩"
+        href="non-ascii-rel.xml" />
+  <link type="application/rss+xml+💩" rel="service.feed"
+        href="non-ascii-type.xml" />
+</head>
+<body>
+  <p>This page has valid rss links in its head and body.</p>
+  <link type="application/rss+xml" rel="alternate" href="rss-in-body.xml" />
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/page_with_rss_in_body.html
@@ -0,0 +1,35 @@
+<html>
+<head>
+  <!--Some non-RSS link tags-->
+  <link />
+  <link type="other" href="other" />
+  <link type="application/rss+xml" />
+  <link type="application/rss+xml" rel="alternate" href="https://badurl:💩/" />
+  <link type="application/rss+xml" href="no-rel.xml" />
+  <link type="application/rss+xml" rel="bad-rel" href="bad-rel.xml" />
+  <link type="application/rss+xml" rel="non-ascii-💩"
+        href="non-ascii-rel.xml" />
+  <link type="application/rss+xml+💩" rel="service.feed"
+        href="non-ascii-type.xml" />
+</head>
+<body>
+  <p>This page has valid rss links in its body.</p>
+  <link type="application/rss+xml" rel="alternate" href="rss-in-body.xml" />
+  <link type="application/RSS+atom" rel="service.feed"
+        href="atom-in-body.xml" />
+  <link type="application/atom+XML" rel="AlTeRnAtE"
+        href="https://some/path-in-body.xml" />
+  <!--Some non-RSS link tags-->
+  <link />
+  <link type="other" href="other-in-body" />
+  <link type="application/rss+xml" />
+  <link type="application/rss+xml" rel="alternate"
+        href="https://badurl:💩/in-body" />
+  <link type="application/rss+xml" href="no-rel-in-body.xml" />
+  <link type="application/rss+xml" rel="bad-rel" href="bad-rel-in-body.xml" />
+  <link type="application/rss+xml" rel="non-ascii-💩"
+        href="non-ascii-rel-in-body.xml" />
+  <link type="application/rss+xml+💩" rel="service.feed"
+        href="non-ascii-type-in-body.xml" />
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/page_with_samesite_iframe.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<html>
+<head></head>
+<body>
+  <img src="image.jpg"/>
+  <p>This page has an iframe. Yay for iframes!
+  <p><iframe srcdoc="Initial page" id="test_iframe"></iframe>
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/page_with_sandboxed_iframe.html
@@ -0,0 +1,11 @@
+<html>
+
+<head></head>
+
+<body>
+  <img src="image.jpg" />
+  <p>This page has an iframe. Yay for iframes!
+    <p><iframe sandbox="allow-scripts" allow="private-state-token-issuance" src="title1.html" id="test_iframe"></iframe>
+</body>
+
+</html>
--- /dev/null
+++ b/content/test/data/page_with_scrollable_div.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <style>
+      div {
+        width: 200px;
+        height: 100px;
+        overflow: scroll;
+      }
+    </style>
+  </head>
+  <body>
+    <div id="scrollable_div">
+      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse
+      finibus massa at leo hendrerit, in pulvinar justo tempor. Suspendisse
+      potenti. Suspendisse ex lectus, faucibus sed nisi vitae, sagittis molestie
+      dolor. Morbi aliquet gravida risus, vel tempus diam accumsan eu. Nam massa
+      lacus, commodo eu mi nec, feugiat semper risus. Cras aliquam convallis
+      venenatis. Interdum et malesuada fames ac ante ipsum primis in faucibus.
+      Nullam vel justo lectus.
+    </div>
+    <br />This page has a scrollable div.
+  </body>
+</html>
--- /dev/null
+++ b/content/test/data/page_with_subresources.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <link rel="stylesheet" href="style"></link>
+  <link rel="prefetch" href="prefetch"></link>
+</head>
+<body>
+  <img src="image-inline">
+  <a href="javascript:" ping="ping">link</a>
+
+  <!-- All script-based loading at the bottom to avoid duplicating requests
+       due to the preload scanner -->
+  <script src="script"></script>
+  <script>
+    // <img>
+    var i = document.createElement('img');
+    i.src = "image-script";
+
+    // <a ping>
+    document.querySelector('a').click();
+
+    // Beacon
+    navigator.sendBeacon("/beacon", "foo");
+  </script>
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/page_with_touch_handler.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<html style="width: 100%; height: 100%">
+<head>
+  <script>
+    function touchHandler(e, type) {
+      var touchResult = document.getElementById("last-touch-event");
+      touchResult.innerHTML = type;
+    }
+
+    function getLastTouchEvent() {
+      var touchResult = document.getElementById("last-touch-event");
+      return touchResult.innerHTML;
+    }
+
+    window.addEventListener('load', function(){ // on page load
+      document.body.addEventListener('touchstart',
+                                      function(e){
+                                        touchHandler(e, "touchstart");
+                                      }, false);
+
+      document.body.addEventListener('touchmove',
+                                      function(e){
+                                        touchHandler(e, "touchmove");
+                                      }, false);
+
+      document.body.addEventListener('touchend',
+                                      function(e){
+                                        touchHandler(e, "touchend");
+                                      }, false);
+    }, false);
+  </script>
+</head>
+
+<!-- Be sure to set 100% width/height so the test can determine an appropriate
+     screen region for targeting events. -->
+<!-- Make the body touch-action: none to force sending the touch action back
+     to the browser during the test, so we can verify that functionality. -->
+<body style="width: 100%; height: 100%; touch-action: none">
+  Page with Touch Handler
+  <div id="last-touch-event">noevent</div>
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/page_with_touch_start_default_prevented.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html style="width: 100%; height: 100%">
+<head>
+  <script>
+    window.addEventListener('load', function(){ // on page load
+      document.body.addEventListener('touchstart',
+                                      function(e){
+                                        e.preventDefault();
+                                      }, { passive: false });
+    }, false);
+  </script>
+</head>
+
+<!-- Be sure to set 100% width/height so the test can determine an appropriate
+     screen region for targeting events. -->
+<body style="width: 100%; height: 100%">
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/page_with_touch_start_janking_main_thread.html
@@ -0,0 +1,19 @@
+<html>
+<style>
+#janktest{
+  width: 1000px;
+  height: 10000px;
+}
+</style>
+<body onload='setup()'>
+ <div id="janktest"></div>
+</body>
+<script>
+function setup() {
+  document.getElementById('janktest').addEventListener('touchstart', (e) => {
+    var end = performance.now() + 1200;
+    while (performance.now() < end);
+  }, {passive: false});
+}
+</script>
+</html>
--- /dev/null
+++ b/content/test/data/page_with_webotp.html
@@ -0,0 +1,9 @@
+<input autocomplete="one-time-code">
+<script>
+  const ac = document.querySelector('input[autocomplete="one-time-code"]');
+  navigator.credentials.get({
+    otp: {transport:['sms']}
+  })
+  .then(content => ac.value = content.code);
+</script>
+
--- /dev/null
+++ b/content/test/data/page_with_wheel_handler.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <style>
+    html,body {
+      width: 100%;
+      height: 100%;
+    }
+  </style>
+  <script>
+    var handlerPromise = new Promise(function(resolve) {
+      window.addEventListener('load', function() {
+        document.body.addEventListener(
+            'wheel', resolve, {passive: false});
+      }, false);
+    });
+  </script>
+</head>
+<body>
+  Page with Wheel Handler
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/paste_listener.html
@@ -0,0 +1,15 @@
+<title>Waiting for paste</title>
+<body>
+<script>
+document.body.contentEditable = true;
+document.body.addEventListener('paste', (event) => {
+  Array.prototype.forEach.call(event.clipboardData.items, (item) => {
+    if (item.type != 'text/rtf') {
+      return;
+    }
+    item.getAsString((s) => {
+      document.title = s;
+    });
+  });
+});
+</script>
--- /dev/null
+++ b/content/test/data/pause_schedule_task.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<body>
+
+<input id="textfield" type="text" name="fname" value="">
+<input type="submit" value="Submit">
+
+<script type="text/javascript">
+var input_text = "a";
+var id = setInterval(function() {
+  document.getElementById("textfield").value = input_text;
+  input_text = input_text + "a";
+}, 1);
+</script>
+
+</body>
+</html>
+
--- /dev/null
+++ b/content/test/data/permissions-policy-main.html
@@ -0,0 +1,5 @@
+<html>
+<head></head>
+<body>This page has a permissions policy header.</body>
+<iframe src="permissions-policy1.html"></iframe>
+</html>
--- /dev/null
+++ b/content/test/data/permissions-policy1.html
@@ -0,0 +1,4 @@
+<html>
+<head></head>
+<body>This page has a permissions policy header.</body>
+</html>
--- /dev/null
+++ b/content/test/data/permissions-policy2.html
@@ -0,0 +1,4 @@
+<html>
+<head></head>
+<body>This page has a different permissions policy header.</body>
+</html>
--- /dev/null
+++ b/content/test/data/permissions-policy3.html
@@ -0,0 +1,9 @@
+<html>
+<head></head>
+<body>This page has a different permissions policy header.
+<p>
+Note that we can't use use allow="geolocation" here, as this will redirect to a
+different origin, and we don't know the port which will be used.
+<iframe src="/cross-site/b.com/title2.html" allow="geolocation *"></iframe>
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/permissions-policy4.html
@@ -0,0 +1,9 @@
+<html>
+<head></head>
+<body>This page has a different permissions policy header.
+<p>
+Note that we can't use use allow="geolocation" here, as this will redirect to a
+different origin, and we don't know the port which will be used.
+<iframe src="/cross-site/b.com/title2.html" allow="geolocation *"></iframe>
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/pointerlock_on_dropped_elem.html
@@ -0,0 +1,20 @@
+<div id="target">
+  <div></div>
+</div>
+<script>
+  function lock() {
+      document.getElementById("target").firstElementChild.requestPointerLock();
+  }
+
+  function replace() {
+      let target = document.getElementById("target");
+      target.removeChild(target.firstElementChild);
+      target.appendChild(document.createElement("div"));
+  }
+
+  document.addEventListener("click", () => {
+      setTimeout(lock, 0);
+      setTimeout(replace, 0);
+      setTimeout(lock, 0);
+  });
+</script>
--- /dev/null
+++ b/content/test/data/popup.html
@@ -0,0 +1,35 @@
+<html>
+<head>
+<meta charset="utf8">
+<title>Test Popup Overlay</title>
+<style type="text/css">
+.overlay {
+  position: fixed;
+  z-index: 1000;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  background: rgba(0,0,0,0.5);
+}
+.modal {
+  position: fixed;
+  z-index: 1001;
+  left: 50%;
+  top: 50%; 
+  width: 200px;
+  height: 100px;
+  margin-left: -100px;
+  margin-top: -50px;
+  background-color: #ffffff;
+  text-align: center;
+  line-height: 100px;
+}
+</style>
+</head>
+<body>
+<div class="overlay"></div>
+<div class="modal">Popup</div>
+<div>Some text.</div>
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/post_message.html
@@ -0,0 +1,97 @@
+<html>
+
+ <head><title>Post message tests</title>
+ <script>
+  // Send a message to our opener, and it will reply.
+  function postToOpener(msg, origin) {
+    window.opener.postMessage(msg, origin);
+    return true;
+  }
+
+  // Send a message to our opener's opener.
+  function postToOpenerOfOpener(msg, origin) {
+    window.opener.opener.postMessage(msg, origin);
+    return true;
+  }
+
+  // Send a message to our parent's opener.
+  function postToOpenerOfParent(msg, origin) {
+    parent.opener.postMessage(msg, origin);
+    return true;
+  }
+
+  // Send a message to our parent's opener.
+  function postToOpenerOfSibling(siblingName, msg, origin) {
+    parent.frames[siblingName].opener.postMessage(msg, origin);
+    return true;
+  }
+
+  // Send a message to a window named "foo".
+  function postToFoo(msg) {
+    var w = window.open("", "foo");
+    w.postMessage(msg, "*");
+    return true;
+  }
+  
+  function openPopup(url) {
+    popup = window.open(url);
+  }
+
+  function postToPopup(msg) {
+    popup.postMessage(msg, "*");
+    return true;
+  }
+
+  // Send a message to parent.
+  function postToParent(msg) {
+    parent.postMessage(msg, "*");
+    return true;
+  }
+
+  // Send a message to sibling.
+  function postToSibling(msg, sibling) {
+    parent.frames[sibling].postMessage(msg, "*");
+    return true;
+  }
+
+  // Send a message to a subframe of window named "foo".
+  function postToFooFrame(msg) {
+    var w = window.open("", "foo");
+    w.frames[0][0].postMessage(msg, "*");
+    return true;
+  }
+
+  function registerUnload() {
+    window.addEventListener('unload', function(e) {
+	postToParent("message-from-unload", "*");
+    });
+  }
+
+  // Listen to incoming messages.
+  var receivedMessages = 0;
+  var receivedMessagesWithPort = 0;
+  window.addEventListener("message", messageReceived, false);
+  function messageReceived(event) {
+    receivedMessages++;
+    if (event.data === "2-1-reply") {
+      event.source.postMessage("msg4", "*");
+    } else if (event.data === "subframe-msg") {
+      event.source.postMessage("subframe-msg-reply", "*");
+    } else if (event.data === "subframe-msg-reply") {
+      domAutomationController.send("done-" + window.name);
+    }
+    if (event.ports && event.ports.length > 0) {
+      receivedMessagesWithPort++;
+      var port = event.ports[0];
+      port.start();
+      port.postMessage("msg-back-via-port");
+      // Change the title to generate a notification.
+      document.title = event.data.message;
+    } else {
+      document.title = event.data;
+    }
+  }
+ </script>
+ </head>
+
+</html>
--- /dev/null
+++ b/content/test/data/post_message2.html
@@ -0,0 +1,21 @@
+<html>
+
+<head><title>Post message subframe tests</title>
+<script>
+  function postToOpenerFrame(msg, origin) {
+    window.opener.frames[0][1].postMessage(msg, origin);
+    return true;
+  }
+
+  window.addEventListener("message", messageReceived, false);
+
+  function messageReceived(event) {
+    // Change the title to generate a notification.
+    document.title = event.data;
+  }
+</script>
+</head>
+
+<iframe id="frame1" src="frame_tree/1-1.html"></iframe>
+
+</html>
--- /dev/null
+++ b/content/test/data/push_state.html
@@ -0,0 +1,12 @@
+<html>
+<head>
+<script>
+window.onload = function() {
+  setTimeout(function() {
+    history.pushState("", "", "#pushState");
+    document.title = "pushState";
+  }, 0);
+}
+</script>
+</head>
+</html>
--- /dev/null
+++ b/content/test/data/register_service_worker.html
@@ -0,0 +1,11 @@
+<script>
+async function register(worker_url, scope) {
+  try {
+    await navigator.serviceWorker.register(worker_url);
+    await navigator.serviceWorker.ready;
+    return 'DONE';
+  } catch (error) {
+    return `${error}`;
+  }
+}
+</script>
--- /dev/null
+++ b/content/test/data/remove_frame_on_load.html
@@ -0,0 +1,19 @@
+<html>
+  <head>
+    <title>Remove frame on load of about:blank</title>
+    <script>
+      var f;
+      window.addEventListener('load', function() {
+        f = document.querySelector('iframe')
+        f.onload = function() {
+          if (f.contentWindow.location.href == 'about:blank')
+            f.parentNode.removeChild(f);
+        };
+      });
+    </script>
+  </head>
+  <body>
+    <p>Navigate the frame to about:blank page to cause it to be removed.</p>
+    <iframe src="/cross-site/b.com/title2.html"></iframe>
+  </body>
+</html>
--- /dev/null
+++ b/content/test/data/remove_frame_on_unload.html
@@ -0,0 +1,21 @@
+<html>
+  <head>
+    <title>Remove Frame on Unload</title>
+    <script>
+      function openWindow() {
+        var w = window.open("title1.html");
+        return true;
+      }
+
+      window.addEventListener('unload', function() {
+        var f = document.getElementById("f");
+        document.body.removeChild(f);
+      }, false);
+    </script>
+  </head>
+  <body>
+    <button onclick="openWindow()">Open Window</button>
+    <p>Navigate to another page to cause frame to be removed.</p>
+    <iframe id="f" src="about:blank"></iframe>
+  </body>
+</html>
--- /dev/null
+++ b/content/test/data/remove_object_element_with_invalid_data.html
@@ -0,0 +1,17 @@
+<html>
+<head>
+<script>
+
+function DoLoad() {
+  let test_div = document.getElementById("test_div");
+  document.body.removeChild(test_div);
+}
+
+</script>
+</head>
+<body onload="DoLoad();">
+<div id="test_div">
+  <object data="INVALID"></object>
+</div>
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/run_async_code_on_worker.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<title>Run async code on a worker helper</title>
+<script>
+const workerCode = `
+onmessage = function(e) {
+  let AsyncFunction = Object.getPrototypeOf(async function(){}).constructor;
+  let receivedFunction = new AsyncFunction(e.data);
+  receivedFunction().then(workerResult => {postMessage(workerResult);});
+}
+`
+const workerBlob = new Blob ([workerCode], {type: 'text/javascript'});
+const worker = new Worker(window.URL.createObjectURL(workerBlob));
+
+async function runOnWorkerAndWaitForResult(code) {
+  worker.postMessage(code);
+  return new Promise((resolve, reject) => {
+    worker.onmessage = result => {
+      resolve(result.data);
+    }
+    worker.onerror = error => {
+      reject(error);
+    }
+  });
+}
+</script>
--- /dev/null
+++ b/content/test/data/rwh_simple.html
@@ -0,0 +1,4 @@
+<html>
+<head><title>The background is red.</title></head>
+<body style="background-color:#FF0000"></body>
+</html>
--- /dev/null
+++ b/content/test/data/rwhv_compositing_animation.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>GPU Feature Testing: Accelerated Compositing</title>
+<style>
+body {
+  -webkit-transform: translateZ(0);
+}
+
+@-webkit-keyframes spin {
+  from { -webkit-transform: rotateY(0deg);    }
+  to   { -webkit-transform: rotateY(-360deg); }
+}
+
+#spin {
+  -webkit-animation-name: spin;
+  -webkit-animation-timing-function: linear;
+  -webkit-animation-iteration-count: infinite;
+  -webkit-animation-duration: 6s;
+  -webkit-transform-style: preserve-3d;
+}
+</style>
+</head>
+<body>
+<div id="spin">Chrome Rocks</div>
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/sandbox_main_frame_csp.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>CSP sandbox test</title>
+</head>
+<body>
+This is a sandboxed document: <pre>Content-Security-Policy: sandbox allow-popups allow-pointer-lock allow-scripts</pre><br>
+<iframe id="frame" sandbox="allow-scripts allow-popups allow-orientation-lock" src='sandboxed_child_frame.html'>
+</iframe>
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/sandboxed_child_frame.html
@@ -0,0 +1,5 @@
+<html>
+<title>sandboxed_child_frame</title>
+<head></head>
+<body>Document sandboxed with Content-Security-Policy HTTP header</body>
+</html>
--- /dev/null
+++ b/content/test/data/sandboxed_frames.html
@@ -0,0 +1,9 @@
+<html>
+<title>sandboxed_frames</title>
+<head></head>
+  <body>
+    <iframe id="child-1" sandbox></iframe>
+    <iframe id="child-2" sandbox="allow-scripts"></iframe>
+    <iframe id="child-3" sandbox="allow-scripts allow-same-origin" src="frame_tree/1-3.html"></iframe>
+  </body>
+</html>
--- /dev/null
+++ b/content/test/data/sandboxed_frames_csp.html
@@ -0,0 +1,8 @@
+<html>
+<title>sandboxed_frames</title>
+<head></head>
+  <body>
+    <iframe id="child-1" src="sandboxed_child_frame.html"></iframe>
+    <iframe id="child-2" sandbox="allow-scripts allow-pointer-lock allow-orientation-lock" src="sandboxed_child_frame.html"></iframe>
+  </body>
+</html>
--- /dev/null
+++ b/content/test/data/sandboxed_main_frame_script.html
@@ -0,0 +1,7 @@
+<html>
+<title>sandboxed_main_frame_script</title>
+<head></head>
+  <body>
+    <iframe id="child" sandbox="allow-scripts"></iframe>
+  </body>
+</html>
--- /dev/null
+++ b/content/test/data/scrollable_page.html
@@ -0,0 +1,10 @@
+<html>
+<head>
+  <meta name='viewport' content='width=device-width, minimum-scale=1'>
+</head>
+<style>
+body {
+  height: 10000px;
+  overflow: scroll;
+}
+</style>
--- /dev/null
+++ b/content/test/data/scrollable_page_with_anchor.html
@@ -0,0 +1,34 @@
+<html>
+  <head>
+    <meta name="viewport" content="width=device-width, minimum-scale=1.0">
+    <script>
+      document.addEventListener("DOMContentLoaded", function(event) {
+        document.getElementById("focusableElement1").addEventListener('focus', () => {
+          window.domAutomationController.send('FocusDone1');
+        });
+        document.getElementById("focusableElement2").addEventListener('focus', () => {
+          window.domAutomationController.send('FocusDone2');
+        });
+        document.getElementById("focusableElement3").addEventListener('focus', () => {
+          window.domAutomationController.send('FocusDone3');
+        });
+      });
+    </script>
+    <style>
+      p {
+        position: absolute;
+        left: 10000px;
+        top: 10000px;
+      }
+    </style>
+  </head>
+  <body>
+    <p id="text">
+      <a href="#" id="focusableElement1">Test1</a>
+      Some text
+      <a href="#" id="focusableElement2">Test2</a>
+      Even more
+      <a href="#" id="focusableElement3">Test3</a>
+    </p>
+  </body>
+</html>
--- /dev/null
+++ b/content/test/data/scrollable_page_with_content.html
@@ -0,0 +1,22 @@
+<html>
+  <head>
+    <meta name="viewport" content="width=device-width, minimum-scale=1.0">
+    <script>
+      let did_scroll = false;
+      window.addEventListener('scroll', () => {
+        did_scroll = true;
+      });
+    </script>
+    <style>
+      p {
+        position: absolute;
+        left: 10000px;
+        top: 10000px;
+      }
+    </style>
+  </head>
+  <body>
+    <a id="link" href="#text">Go Down</a>
+    <p id="text">Some text</p>
+  </body>
+</html>
--- /dev/null
+++ b/content/test/data/scrollable_page_with_iframe.html
@@ -0,0 +1,18 @@
+<html>
+<head>
+  <meta name='viewport' content='width=device-width, minimum-scale=1'>
+</head>
+<style>
+body {
+  height: 10000px;
+  overflow: scroll;
+}
+</style>
+<body>
+  <div style="width:100px; height:100px; overflow: scroll">
+    <div style="width:150px; height:150px; overflow: scroll">
+      <iframe style="width:200px; height:200px"> </iframe>
+    </div>
+  </div>
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/send-beacon.html
@@ -0,0 +1,4 @@
+<!doctype html>
+<script>
+navigator.sendBeacon('/beacon', 'hello');
+</script>
--- /dev/null
+++ b/content/test/data/set_cookie.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Loading...</title>
+<script>
+  if (document.cookie == "A=B")
+    document.title = "cookie set";
+  else
+    document.title = "cookie not set";
+</script>
+</head>
+</html>
--- /dev/null
+++ b/content/test/data/set_document_cookie.html
@@ -0,0 +1,6 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Set a cookie string defined in the query string using document.cookie API</title>
+<script type="text/javascript">
+document.cookie = document.location.search.substring(1);
+</script>
--- /dev/null
+++ b/content/test/data/simple_database.html
@@ -0,0 +1,160 @@
+<html>
+<script>
+
+// Open a Web SQL database.
+var g_db = null;
+if (typeof window.openDatabase == "undefined") {
+  document.write("Error: Web SQL databases are not supported.");
+}
+try {
+  g_db = openDatabase("test", "1.0", "test database", 1024 * 1024);
+} catch(err) {
+  document.write("Error: cannot open database.");
+}
+
+// Creates a table named "table1" with one text column named "data".
+async function createTable() {
+  if (!g_db)
+    return;
+  try {
+    await new Promise((resolve, reject) => {
+      g_db.transaction(
+        function(tx) {
+          tx.executeSql("CREATE TABLE table1 (data TEXT)");
+        },
+        reject, resolve);
+    });
+    return "done";
+  } catch (error) {
+    return error;
+  }
+}
+
+// Inserts a record into the database.
+async function insertRecord(text) {
+  try {
+    await new Promise((resolve, reject) => {
+      g_db.transaction(
+      function(tx) {
+        tx.executeSql("INSERT INTO table1 VALUES (?)", [text]);
+      }, reject, resolve);
+    });
+    return "done";
+  } catch (error) {
+    return error;
+  }
+}
+
+
+// Updates a record at the given index with the given text. The indices are
+// 0-based and are ordered from oldest record, to newest record.
+async function updateRecord(index, text) {
+  var didUpdate = false;
+  try {
+    const rowId = await findId(index);
+    return new Promise((resolve, reject) => {
+      g_db.transaction(
+        function(tx) {
+          tx.executeSql(
+              "UPDATE table1 SET data=? WHERE ROWID=?",
+              [text, rowId],
+              function(tx, result) {
+                if (result.rowsAffected == 1)
+                  didUpdate = true;
+                else if (result.rowsAffected == 0)
+                  reject("could not update index: " + index);
+                else
+                  reject("multiple rows with index: " + index);
+              });
+        },
+        function(error) {
+          reject("update error: " + error);
+        },
+        function() {
+          if (didUpdate)
+            resolve("done");
+        });
+    });
+  } catch (error) {
+    return error;
+  }
+}
+
+// Deletes a record at the given index.
+async function deleteRecord(index) {
+  try {
+    const rowId = await findId(index);
+    await new Promise((resolve, reject) => {
+      g_db.transaction(
+        function(tx) {
+          tx.executeSql("DELETE FROM table1 WHERE ROWID=?", [rowId]);
+        },
+        function(error) {
+          reject("delete error: " + error);
+        }, resolve);
+      });
+    return "done";
+  } catch (error) {
+    return error;
+  }
+}
+
+// Gets all the records in the database, ordered by their age.
+async function getRecords() {
+  try {
+    const result = await new Promise((resolve, reject) => {
+      g_db.readTransaction(function(tx) {
+      tx.executeSql(
+          "SELECT data FROM table1 ORDER BY ROWID",
+          [],
+          function(tx, result) {
+            resolve(result);
+          },
+          function(tx, error) {
+            reject("getRecords error: " + error);
+          });
+      });
+    });
+    let items = "";
+    for (var i = 0; i < result.rows.length; i++) {
+      if (items != "")
+        items += ", ";
+      items += result.rows.item(i).data;
+    }
+    return items;
+  } catch (error) {
+    return error;
+  }
+}
+
+// Helper function that finds the ID for a record based on a given index.
+async function findId(index) {
+  return new Promise((resolve, reject) => {
+    g_db.readTransaction(function(tx) {
+      // |ROWID| is a special sqlite column. It is unique and is incremented
+      // automatically when a new record is created.
+      // |LIMIT| is a nonstandard clause supported by sqlite that lets us pick
+      // rows from the database by index. E.g., LIMIT 2,10 will give us 10 records
+      // starting at offset 2.
+      tx.executeSql(
+        "SELECT ROWID AS id FROM table1 ORDER BY ROWID LIMIT ?,1",
+        [index],
+        function(tx, result) {
+          if (result.rows.length >= 1)
+            resolve(result.rows.item(0).id);
+          else
+            reject("could not find row with index: " + index);
+        },
+        function(tx, error) {
+          reject("findId error: " + error);
+        });
+    });
+  });
+}
+
+</script>
+
+<body>
+This page is used for testing Web SQL databases.
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/simple_links.html
@@ -0,0 +1,87 @@
+<html>
+
+ <head><title>Simple links</title>
+ <script>
+  function simulateClick(target) {
+    var evt = document.createEvent("MouseEvents");
+    evt.initMouseEvent("click", true, true, window,
+                       0, 0, 0, 0, 0, false, false,
+                       false, false, 0, null);
+
+    return target.dispatchEvent(evt);
+  }
+
+  function setPortNumber(portNumber) {
+    var link = document.getElementById("cross_site_link");
+    link.setAttribute("href", "http://foo.com:" + portNumber + "/title2.html");
+
+    link = document.getElementById("cross_site_new_window_link");
+    link.setAttribute("href", "http://foo.com:" + portNumber + "/title2.html");
+
+    link = document.getElementById("cross_site_new_window_no_opener_link");
+    link.setAttribute("href", "http://foo.com:" + portNumber + "/title2.html");
+    return true;
+  }
+
+  function clickSameSiteLink() {
+    return simulateClick(document.getElementById("same_site_link"));
+  }
+
+  function clickSameSiteNewWindowLink() {
+    return simulateClick(document.getElementById("same_site_new_window_link"));
+  }
+
+  function clickSameSiteNewWindowWithNoopenerLink() {
+    return simulateClick(document.getElementById("same_site_new_window_with_noopener_link"));
+  }
+
+  function clickSameSiteNewWindowWithOpenerLink() {
+    return simulateClick(document.getElementById("same_site_new_window_with_opener_link"));
+  }
+
+  function clickCrossSiteLink() {
+    return simulateClick(document.getElementById("cross_site_link"));
+  }
+
+  function clickCrossSiteNewWindowLink() {
+    return simulateClick(document.getElementById("cross_site_new_window_link"));
+  }
+
+  function clickCrossSiteNewWindowNoOpenerLink() {
+    return simulateClick(document.getElementById("cross_site_new_window_no_opener_link"));
+  }
+
+  function clickViewSourceLink() {
+    return simulateClick(document.getElementById("view_source_link"));
+  }
+
+  function clickLinkToSelf() {
+    return simulateClick(document.getElementById("link_to_self_opener"));
+  }
+
+  function clickLinkToSelfNoOpener() {
+    return simulateClick(document.getElementById("link_to_self_no_opener"));
+  }
+
+  function clickGoogleChromeLink() {
+    return simulateClick(document.getElementById("google_chrome_link"));
+  }
+ </script>
+ </head>
+
+<a href="title2.html" id="same_site_link">same-site</a><br>
+<a href="http://foo.com/title2.html" id="cross_site_link">cross-site</a><br>
+<a href="view-source:about:blank" id="view_source_link">view-source:</a><br>
+<a href="title2.html" id="same_site_new_window_link" target="_blank">same-site new window</a>
+<a href="title2.html" id="same_site_new_window_with_noopener_link" rel="noopener" target="_blank">same-site new window with noopener</a>
+<a href="title2.html" id="same_site_new_window_with_opener_link" rel="opener" target="_blank">same-site new window with opener</a>
+<a href="http://foo.com/title2.html" id="cross_site_new_window_link" rel="opener" target="_blank">cross-site new window</a>
+<a href="http://foo.com/title2.html" id="cross_site_new_window_no_opener_link" rel="noopener" target="_blank">cross-site new window no opener</a>
+<a href="" id="link_to_self_opener" rel="opener" target="_blank">self new window_opener</a>
+<a href="" id="link_to_self_no_opener" rel="noopener" target="_blank">self new window_noopener</a>
+<script>
+  document.getElementById("link_to_self_opener").href = window.location.toString();
+  document.getElementById("link_to_self_no_opener").href = window.location.toString();
+</script>
+<a href="googlechrome://" id="google_chrome_link">googlechrome:</a></br>
+</html>
--- /dev/null
+++ b/content/test/data/simple_page.html
@@ -0,0 +1,6 @@
+<html>
+<head><title>OK</title></head>
+<body>
+Basic html test.
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/site_per_process_main.html
@@ -0,0 +1,8 @@
+<html>
+<title>site_per_process_main</title>
+<head></head>
+  <body>
+    <iframe id="test"></iframe>
+    <iframe id="child-2" src="frame_tree/1-3.html"></iframe>
+  </body>
+</html>
--- /dev/null
+++ b/content/test/data/stale-while-revalidate.html
@@ -0,0 +1,24 @@
+<script>
+function wait25ms() {
+  return new Promise(resolve => {
+    setTimeout(() => {
+      resolve();
+    }, 25);
+  });
+}
+
+// runTest() will be called by the browser test.
+async function runTest() {
+  const url = 'stale-script.js';
+  const response = await fetch(url).then(r => r.text());
+  const response2 = await fetch(url).then(r => r.text());
+  while(true) {
+    const revalidation_check = await fetch('stale-script.js');
+    if (performance.getEntriesByName(new URL(url, document.location)).length == 2) {
+      document.title = "Pass";
+      break;
+    }
+    await wait25ms();
+  }
+}
+</script>
--- /dev/null
+++ b/content/test/data/super_tall_page.html
@@ -0,0 +1,9 @@
+<html>
+  <head>
+  </head>
+  <body>
+    <div>This page is tall ...</div>
+    <div style="min-height: 30000px;"></div>
+    <div>... super tall!</div>
+  </body>
+</html>
--- /dev/null
+++ b/content/test/data/super_tall_parent.html
@@ -0,0 +1,9 @@
+<html>
+  <head>
+  </head>
+  <body>
+    <div>This page contains a super-tall iframe.</div>
+    <iframe style="min-height: 31000px; min-width: 500px" src="about:blank">
+    </iframe>
+  </body>
+</html>
--- /dev/null
+++ b/content/test/data/sync_xmlhttprequest.html
@@ -0,0 +1,32 @@
+<html>
+<head>
+<script>
+var request_success = false;
+
+function OnLoad() {
+  var request = new XMLHttpRequest();
+  request.open("GET", "title1.html", false);
+  request.send("");
+
+  var my_div = document.getElementById("my_div");
+  if (request.readyState == 4 && request.status == 200) {
+    request_success = true;
+    my_div.innerHTML = "Got a successful response: " + request.responseText;
+  } else {
+    my_div.innerHTML = "Request failed: ready state=" + request.readyState +
+                       " status=" + request.status;
+ }
+}
+
+function DidSyncRequestSucceed() {
+  return request_success;
+}
+
+</script>
+</head>
+<body onload="OnLoad();">
+This page sends a synchronous XMLHttpRequest.
+<br><br>
+<div id="my_div"></div>
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/sync_xmlhttprequest_cancelled.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<script>
+let errorCode = -1;
+
+function sendRequest() {
+  let status = document.getElementById('status');
+
+  let xhr = new XMLHttpRequest();
+  xhr.open("GET", "hung", false);
+
+  status.textContent = 'Sending a request.';
+  try {
+    xhr.send(null);
+  } catch (e) {
+    errorCode = e.code;
+  }
+  status.textContent = `Request completed. errorCode: ${errorCode}.`
+}
+
+function getErrorCode() {
+  return errorCode;
+}
+
+</script>
+<body onload="sendRequest()">
+This page sends a synchronous XMLHttpRequest that will get cancelled.<br>
+Status: <div id="status">Before sending the request.</div>
+</body>
--- /dev/null
+++ b/content/test/data/sync_xmlhttprequest_disallowed.html
@@ -0,0 +1,29 @@
+<html>
+<head>
+<script>
+var success = false;
+
+function OnLoad() {
+  try {
+    var request = new XMLHttpRequest();
+    request.open("GET", "file:///c:/foo.txt", false);
+    request.send(null);
+  } catch (e) {
+    success = true;
+  }
+  document.getElementById("console").appendChild(
+      document.createTextNode(success ? "SUCCESS" : "FAILURE"));
+}
+
+function DidSucceed() {
+  return success;
+}
+
+</script>
+</head>
+<body onload="OnLoad();">
+This page sends a synchronous XMLHttpRequest to fetch a local file, which
+should not be allowed.
+<div id="console"></div>
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/sync_xmlhttprequest_during_unload.html
@@ -0,0 +1,20 @@
+<html> 
+  <head>
+    <title>Loading...</title>
+  </head>
+  <body onload="attachUnloadListener()"> 
+    <script> 
+      function attachUnloadListener() {
+        window.addEventListener('unload', onUnload, false);
+        document.title = "sync xhr on unload";
+      }
+
+      var req = new XMLHttpRequest();
+      function onUnload() {
+        req.open('GET','download-test1.lib', false);
+        req.send(null);
+      }
+    </script> 
+    <p>Navigate to another page to trigger synch xmlhttprequest</p>
+  </body> 
+</html> 
--- /dev/null
+++ b/content/test/data/tall_page.html
@@ -0,0 +1,7 @@
+<html>
+<head></head>
+<body>
+  <div>This page is tall.</div>
+  <div style="min-height: 1000px">Hello</div>
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/tall_page_with_local_iframe.html
@@ -0,0 +1,9 @@
+<html>
+<head></head>
+<body>
+  <div>Tall w/ iframe.</div>
+  <iframe src="page_with_scrollable_div.html"
+          style="min-height: 800px; position: absolute; top: 28px; left: 8px"
+          id="nested_frame"></iframe>
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/target_text_link.html
@@ -0,0 +1,8 @@
+<html>
+  <head>
+    <meta name="viewport" content="width=device-width, minimum-scale=1.0">
+  </head>
+  <body>
+    <a id="link" href="/scrollable_page_with_content.html#:~:text=text">link</a>
+  </body>
+</html>
--- /dev/null
+++ b/content/test/data/theme_color.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <title>Test page</title>
+        <meta name="theme-color" content="#ff0000">
+    </head>
+    <body>
+        Theme color
+    </body>
+</html>
+
--- /dev/null
+++ b/content/test/data/title1.html
@@ -0,0 +1,4 @@
+<html>
+<head></head>
+<body>This page has no title.</body>
+</html>
--- /dev/null
+++ b/content/test/data/title2.html
@@ -0,0 +1,4 @@
+<html>
+<head><title>Title Of Awesomeness</title></head>
+<body>This page has a title.</body>
+</html>
--- /dev/null
+++ b/content/test/data/title3.html
@@ -0,0 +1,4 @@
+<html>
+<head><title>Title Of More Awesomeness</title></head>
+<body>This page has a title.</body>
+</html>
--- /dev/null
+++ b/content/test/data/title4.html
@@ -0,0 +1,4 @@
+<html>
+<head></head>
+<body>This page has no title.</body>
+</html>
--- /dev/null
+++ b/content/test/data/touch_selection.html
@@ -0,0 +1,42 @@
+<html>
+<style>
+/* Add space between div and textfield so that touch selects the text in div. */
+#textDiv {
+  margin-bottom: 30px;
+}
+</style>
+<body>
+<div id='dummy'>Dummy Padding Text</div>
+<div id='textDiv'>Some text we can select</div>
+<div id='dummy'>Dummy Padding Text</div>
+
+<input id='textfield' type="text" value="Text in a textfield">
+</body>
+<script>
+
+function focus_textfield() {
+  document.getElementById('textfield').focus();
+  // Focusing the textfiled selects its text. Collapse selection to a cursor.
+  window.getSelection().collapseToStart();
+}
+
+function get_point_inside(element) {
+  var rect = element.getBoundingClientRect();
+  var point = {
+    x: rect.left + 8,
+    y: rect.top + 8
+  };
+  return JSON.stringify(point);
+}
+
+function get_point_inside_text() {
+  return get_point_inside(document.getElementById('textDiv'));
+}
+
+function get_point_inside_textfield() {
+  return get_point_inside(document.getElementById('textfield'));
+}
+
+</script>
+
+</html>
--- /dev/null
+++ b/content/test/data/unload.html
@@ -0,0 +1,11 @@
+<html>
+  <head>
+    <title>Loading...</title>
+    <script>
+      window.onunload = function(e) { return 'foo'; };
+    </script>
+  </head>
+  <body>
+    <p>Navigate to another page to trigger unload handler.</p>
+  </body>
+</html>
--- /dev/null
+++ b/content/test/data/unload_handler_force_layout.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<body>
+  <div id="tall" style="height: 1000em">
+    This page will force a layout on unload
+  </div>
+</body>
+<script>
+window.addEventListener('unload', function(event) {
+  // Shrink the page.
+  document.getElementById('tall').style.height = '999em';
+  // Move the scroll position.
+  window.scroll(0, 100);
+  // Read the scroll position - this forces layout.
+  console.log(window.scrollY);
+  // Notify the opening window so tests can wait for this to run.
+  window.opener.postMessage('unloaded', '*');
+});
+// Notify the opening window that the page loaded, so tests can wait for it.
+window.opener.postMessage('loaded', '*');
+</script>
+</html>
--- /dev/null
+++ b/content/test/data/virtual-keyboard.html
@@ -0,0 +1,41 @@
+<html>
+<head>
+</head>
+<body>
+    <textarea id='txt1'
+              virtualkeyboardpolicy='manual'
+              onfocusin='FocusIn1()'
+              onkeydown='HideVKCalled()'></textarea>
+    <textarea id='txt2'
+              virtualkeyboardpolicy='manual'
+              onfocusin='FocusIn1()'
+              onkeydown='HideVKCalled()'></textarea>
+    <textarea id='txt3' virtualkeyboardpolicy='auto'></textarea>
+    <textarea id='txt4' virtualkeyboardpolicy='auto'></textarea>
+    <script>
+      txt4.focus();
+      let elemRect1 = txt1.getBoundingClientRect();
+      let elemRect2 = txt2.getBoundingClientRect();
+      let elemRect3 = txt3.getBoundingClientRect();
+      let elemRect4 = txt4.getBoundingClientRect();
+      let VKRect = navigator.virtualKeyboard.boundingRect, numEvents = 0;
+      navigator.virtualKeyboard.overlaysContent = true;
+      const editContext = new EditContext();
+      txt2.editContext = editContext;
+      editContext.inputPanelPolicy = "manual";
+
+      navigator.virtualKeyboard.addEventListener('geometrychange',
+       evt => {
+         numEvents++;
+         VKRect = navigator.virtualKeyboard.boundingRect
+       }, false);
+
+      function FocusIn1() {
+         navigator.virtualKeyboard.show();
+      }
+      function HideVKCalled() {
+         navigator.virtualKeyboard.hide();
+      }
+    </script>
+  </body>
+</html>
--- /dev/null
+++ b/content/test/data/web_ui_mojo_native.html
@@ -0,0 +1,4 @@
+<html>
+<head><script src="web_ui_mojo_native.js"></script></head>
+<body>hi</body>
+</html>
--- /dev/null
+++ b/content/test/data/web_ui_mojo_test.html
@@ -0,0 +1,8 @@
+<html>
+<head>
+  <script src="web_ui_mojo_test.js" type="module"></script>
+</head>
+<body>
+  x
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/wide_page.html
@@ -0,0 +1,7 @@
+<html>
+<head></head>
+<body>
+  <div>This page is wide.</div>
+  <div style="min-width: 1000px">Hello</div>
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/x-frame-options-deny.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>This page should NOT load in a frame</title>
+</head>
+<body>
+This page cannot be loaded in an iframe because its X-Frame-Options is set to DENY.
+</body>
+</html>
+
--- /dev/null
+++ b/content/test/data/fetch_event_octet_stream.js
@@ -0,0 +1,13 @@
+// Copyright 2018 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+self.addEventListener('fetch', e => {
+  const headers = new Headers();
+  const filename = 'octet-stream.abc';
+  headers.append('Content-Type', 'application/octet-stream; charset=UTF-8');
+  headers.append('Content-Disposition',
+                 'attachment; filename="' + filename +
+                 '"; filename*=UTF-8\'\'' + filename);
+  e.respondWith(new Response('This is a binary', {headers}));
+});
--- /dev/null
+++ b/content/test/data/fetch_event_passthrough.js
@@ -0,0 +1,5 @@
+// Copyright 2018 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+self.addEventListener('fetch', e => {});
--- /dev/null
+++ b/content/test/data/fetch_event_respond_with_fetch.js
@@ -0,0 +1,5 @@
+// Copyright 2018 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+self.addEventListener('fetch', e => { e.respondWith(fetch(e.request)) });
--- /dev/null
+++ b/content/test/data/result_queue.js
@@ -0,0 +1,44 @@
+// Copyright 2017 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Queue storing asynchronous results received from the Service Worker. Results
+// are sent to the test when requested.
+function ResultQueue() {
+  // Invariant: this.queue.length == 0 || this.pendingGets.length == 0
+  this.queue = [];
+  this.pendingGets = [];
+}
+
+// Adds a data item to the queue. Will be sent to the test if there are
+// pendingGets.
+ResultQueue.prototype.push = function(data) {
+  if (this.pendingGets.length > 0) {
+    const resolve = this.pendingGets.pop();
+    resolve(String(data));
+  } else {
+    this.queue.unshift(String(data));
+  }
+};
+
+// Called by native. Sends the next data item to the test if it is available.
+// Otherwise increments pendingGets so it will be delivered when received.
+ResultQueue.prototype.pop = function() {
+  return new Promise((resolve) => {
+    if (this.queue.length) {
+      resolve(this.queue.pop());
+    } else {
+      this.pendingGets.unshift(resolve);
+    }
+  });
+};
+
+// Called by native. Immediately sends the next data item to the test if it is
+// available, otherwise sends null.
+ResultQueue.prototype.popImmediately = function() {
+  return this.queue.length ? this.queue.pop() : null;
+};
+
+function formatError(error) {
+  return error.name + ' - ' + error.message;
+}
--- /dev/null
+++ b/content/test/data/stale-script.js
@@ -0,0 +1,5 @@
+// Copyright 2018 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// nothing to see here.
--- /dev/null
+++ b/content/test/data/tree_parser_util.js
@@ -0,0 +1,223 @@
+// 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.
+
+/**
+ * Parser for a simple grammar that describes a tree structure using a function-
+ * like "a(b(c,d))" syntax. Original intended usage: to have browsertests
+ * specify an arbitrary tree of iframes, loaded from various sites, without
+ * having to write a .html page for each level or do crazy feats of data: url
+ * escaping. But there's nothing really iframe-specific here. See below for some
+ * examples of the grammar and the parser output.
+ *
+ * @example <caption>Basic syntax: an identifier, optionally followed by a list
+ * of attributes, optionally followed by a list of children.</caption>
+ * // returns { value: 'abc', attributes: [], children: [] }
+ * TreeParserUtil.parse('abc {} ()');
+ *
+ * @example <caption>Both the attribute and child lists are optional. Dots and
+ * hyphens are legal in ids.</caption>
+ * // returns { value: 'example-b.com', attributes: [], children: [] }
+ * TreeParserUtil.parse('example-b.com');
+ *
+ * @example <caption>Attributes are identifiers as well, separated by commas.
+ * </caption>
+ * // returns { value: 'abc', attributes: ['attr-1', 'attr-2'], children: [] }
+ * TreeParserUtil.parse('abc {attr-1, attr-2}');
+ *
+ * @example <caption>Commas separate children in the child list.</caption>
+ * // returns { value: 'b', attributes: [], children: [
+ * //           { value: 'c', attributes: [], children: [] },
+ * //           { value: 'd', attributes: [], children: [] }
+ * //         ]}
+ * TreeParserUtil.parse('b (c, d)';
+ *
+ * @example <caption>Children can have children, and so on.</caption>
+ * // returns { value: 'e', attributes: [], children: [
+ * //           { value: 'f', attributes: [], children: [
+ * //             { value: 'g', attributes: [], children: [
+ * //               { value: 'h', attributes: [], children: [] },
+ * //               { value: 'i', attributes: [], children: [
+ * //                 { value: 'j', attributes: [], children: [] }
+ * //               ]},
+ * //             ]}
+ * //           ]}
+ * //         ]}
+ * TreeParserUtil.parse('e(f(g(h(),i(j))))';
+ *
+ * @example <caption>Attributes can be applied to children at any level of
+ * nesting.</caption>
+ * // returns { value: 'b', attributes: ['red', 'blue'], children: [
+ * //           { value: 'c', attributes: [], children: [] },
+ * //           { value: 'd', attributes: ['green'], children: [] }
+ * //         ]}
+ * TreeParserUtil.parse('b{red,blue}(c,d{green})';
+ *
+ * @example <caption>flatten() converts a [sub]tree back to a string.</caption>
+ * var tree = TreeParserUtil.parse('b.com (c.com(e.com), d.com)');
+ * TreeParserUtil.flatten(tree.children[0]);  // returns 'c.com(e.com())'
+ */
+var TreeParserUtil = (function() {
+  'use strict';
+
+  /**
+   * Parses an input string into a tree. See class comment for examples.
+   * @returns A tree of the form {value: <string>, children: <Array.<tree>>}.
+   */
+  function parse(input) {
+    var tokenStream = lex(input);
+
+    var result = takeIdAndChild(tokenStream);
+    if (tokenStream.length != 0)
+      throw new Error('Expected end of stream, but found "' +
+                      tokenStream[0] + '".')
+    return result;
+  }
+
+  /**
+   * Inverse of |parse|. Converts a parsed tree object into a string. Can be
+   * used to forward a subtree as an argument to a nested document.
+   */
+  function flatten(tree) {
+    var result = tree.value;
+    if (tree.attributes && tree.attributes.length)
+      result += '{' + tree.attributes.join(",") + "}";
+    return result + '(' + tree.children.map(flatten).join(',') + ')';
+  }
+
+  /**
+   * Lexer function to convert an input string into a token stream.  Splits the
+   * input along whitespace, parens and commas. Whitespace is removed, while
+   * parens and commas are preserved as standalone tokens.
+   *
+   * @param {string} input The input string.
+   * @return {Array.<string>} The resulting token stream.
+   */
+  function lex(input) {
+    return input.split(/(\s+|\(|\)|{|}|,)/).reduce(
+      function (resultArray, token) {
+        var trimmed = token.trim();
+        if (trimmed) {
+          resultArray.push(trimmed);
+        }
+        return resultArray;
+      }, []);
+  }
+
+  /**
+   * Consumes from the stream an identifier with optional attribute and child
+   * lists, returning its parsed representation.
+   */
+  function takeIdAndChild(tokenStream) {
+    return { value: takeIdentifier(tokenStream),
+             attributes: takeAttributeList(tokenStream),
+             children: takeChildList(tokenStream) };
+  }
+
+  /**
+   * Consumes from the stream an identifier, returning its value (as a string).
+   */
+  function takeIdentifier(tokenStream) {
+    if (tokenStream.length == 0)
+      throw new Error('Expected an identifier, but found end-of-stream.');
+    var token = tokenStream.shift();
+    // This regex includes slash, question mark, ampersand, and period in order
+    // to accommodate full URLs, which may be passed to
+    // cross_site_iframe_factory.html as leaf node locations. Colon is for port
+    // numbers, which may also be specified. Semicolon and equals are for cookie
+    // strings, which may sometimes be provided in URL query strings such as for
+    // the EmbeddedTestServer default handler /set-cookie. Percent is for
+    // allowing escaped squences in the URL, like %2F.
+    const re = new RegExp('^[a-zA-Z0-9\\/%_?&;:.=-]+$');
+    if (!token.match(re))
+      throw new Error('Expected an identifier, but found "' + token + '".');
+    return token;
+  }
+
+  /**
+   * Consumes an optional attribute list from the token stream, returning a list
+   * of the parsed attribute identifiers.
+   */
+  function takeAttributeList(tokenStream) {
+    // Remove the next token from the stream if it matches |token|.
+    function tryToEatA(token) {
+      if (tokenStream[0] == token) {
+        tokenStream.shift();
+        return true;
+      }
+      return false;
+    }
+
+    // Bare identifier case, as in 'b' in the input '(a (b, c))'
+    if (!tryToEatA('{'))
+      return [];
+
+    // Empty list case, as in 'b' in the input 'a (b {}, c)'.
+    if (tryToEatA('}')) {
+      return [];
+    }
+
+    // List with at least one entry.
+    var result = [ takeIdentifier(tokenStream) ];
+
+    // Additional entries allowed with comma.
+    while (tryToEatA(',')) {
+      result.push(takeIdentifier(tokenStream));
+    }
+
+    // End of list.
+    if (tryToEatA('}')) {
+      return result;
+    }
+    if (tokenStream.length == 0)
+      throw new Error('Expected "}" or ",", but found end-of-stream.');
+    throw new Error('Expected "}" or ",", but found "' + tokenStream[0] + '".');
+  }
+
+  /**
+   * Consumes an optional child list from the token stream, returning a list of
+   * the parsed children.
+   */
+  function takeChildList(tokenStream) {
+    // Remove the next token from the stream if it matches |token|.
+    function tryToEatA(token) {
+      if (tokenStream[0] == token) {
+        tokenStream.shift();
+        return true;
+      }
+      return false;
+    }
+
+    // Bare identifier case, as in 'b' in the input '(a (b, c))'
+    if (!tryToEatA('('))
+      return [];
+
+    // Empty list case, as in 'b' in the input 'a (b (), c)'.
+    if (tryToEatA(')')) {
+      return [];
+    }
+
+    // List with at least one entry.
+    var result = [ takeIdAndChild(tokenStream) ];
+
+    // Additional entries allowed with commas.
+    while (tryToEatA(',')) {
+      result.push(takeIdAndChild(tokenStream));
+    }
+
+    // End of list.
+    if (tryToEatA(')')) {
+      return result;
+    }
+    if (tokenStream.length == 0)
+      throw new Error('Expected ")" or ",", but found end-of-stream.');
+    throw new Error('Expected ")" or ",", but found "' + tokenStream[0] + '".');
+  }
+
+  return {
+    parse:   parse,
+    flatten: flatten
+  };
+})();
+
+document.scriptExecuted = true;
--- /dev/null
+++ b/content/test/data/web_ui_dedicated_worker.js
@@ -0,0 +1,9 @@
+// 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.
+
+self.onmessage = (e) => {
+  if (e.data === 'ping') {
+    self.postMessage('pong');
+  }
+};
--- /dev/null
+++ b/content/test/data/web_ui_mojo_native.js
@@ -0,0 +1,14 @@
+// Copyright 2017 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+'use strict';
+
+window.isNativeMojoAvailable = () => {
+  return 'Mojo' in self && 'MojoHandle' in self && 'MojoWatcher' in self &&
+      'createMessagePipe' in Mojo && 'writeMessage' in MojoHandle.prototype &&
+      'cancel' in MojoWatcher.prototype;
+};
+
+window.isChromeSendAvailable = () => {
+  return 'chrome' in self && 'send' in chrome;
+};
--- /dev/null
+++ b/content/test/data/web_ui_mojo_test.js
@@ -0,0 +1,46 @@
+// Copyright 2020 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {WebUIMojoTestCache} from './content/test/data/web_ui_test.test-mojom-webui.js';
+
+/** @type {{send: function(*)}} */
+Window.prototype.domAutomationController;
+
+const TEST_DATA = [
+  { url: 'https://google.com/', contents: 'i am in fact feeling lucky' },
+  { url: 'https://youtube.com/', contents: 'probably cat videos?' },
+  { url: 'https://example.com/', contents: 'internets wow' },
+];
+
+async function doTest() {
+  const cache = WebUIMojoTestCache.getRemote();
+  for (const entry of TEST_DATA) {
+    cache.put({ url: entry.url }, entry.contents);
+  }
+
+  const {items} = await cache.getAll();
+  if (items.length != TEST_DATA.length) {
+    return false;
+  }
+
+  const entries = {};
+  for (const item of items) {
+    entries[item.url.url] = item.contents;
+  }
+
+  for (const entry of TEST_DATA) {
+    if (!(entry.url in entries)) {
+      return false;
+    }
+    if (entries[entry.url] != entry.contents) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+window.runTest = async function() {
+  return doTest();
+}
--- /dev/null
+++ b/content/test/data/web_ui_shared_worker.js
@@ -0,0 +1,12 @@
+// Copyright 2020 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+onconnect = (event) => {
+  const port = event.ports[0];
+  port.onmessage = (e) => {
+    if (e.data === 'ping') {
+      port.postMessage('pong');
+    }
+  };
+};
--- /dev/null
+++ b/content/test/data/content-disposition-attachment.html
@@ -0,0 +1,3 @@
+<html>
+<body><p>A page with `content-Disposition: attachment`</p></body>
+</html>
--- /dev/null
+++ b/content/test/data/content-sniffer-test5.not-html
@@ -0,0 +1,5 @@
+<html>
+<head><title>Content Sniffer Test 5</title></head>
+<body>We should not sniff this content as HTML,
+    when read as a file URL.</body>
+</html>
--- /dev/null
+++ b/content/test/data/cross_site_scroll_into_view_factory.html
@@ -0,0 +1,297 @@
+<!DOCTYPE html>
+<html class="rootScroller">
+<!-- This file is based on cross_site_iframe_factory.html. See comments in that
+file as well as tree_parser_util.js for more details and syntax information.
+
+This page creates a nested, non-branching, frame tree with each child frame being
+fully outside of its embedder's initial viewport. The inner-most frame will
+create a <input> box, also outside the frame's initial viewport.
+
+See window.Attributes map for supported attributes.
+
+Usage Example:
+
+  cross_site_scroll_into_view_factory
+      .html?siteA{MobileViewport,RTL}(siteB(siteC{TouchActionNone}))
+
+ -->
+<head>
+<title>Cross-site scroll-into-view frame tree factory</title>
+<script>
+  window.Attributes = {
+    // Make's a frame's document "dir=rtl" to support testing right-to-left
+    // writing modes.
+    RTL: 0,
+    // Adds a viewport meta tag to a frame, making it "mobile friendly". This
+    // occurs for subframes as well but only has an effect in the root frame.
+    MobileViewport: 1,
+    // Adds a viewport meta tag to a frame, making it
+    // "mobile friendly" and also limiting minimum-zoom. This occurs for
+    // subframes as well but only has an effect in the root frame.
+    MobileViewportNoZoom: 2,
+    // Adds a `touch-action: none` style to the inner-most
+    // frame's <input> box. No-op in other frames.
+    TouchActionNone: 3,
+    // Uses a <fencedframe> element rather than an <iframe> for the child
+    // frame.
+    FencedFrame: 4,
+    // Puts the <input> box into a root scroller element.
+    RootScroller: 5,
+
+    // Keep this up to date with last enum.
+    MAX_VALUE: 5
+  }
+</script>
+<style>
+  html,body {
+    width: 100%;
+    height: 100%;
+  }
+  html {
+    overflow: hidden;
+  }
+  .rootScroller {
+    overflow: auto;
+  }
+  iframe, fencedframe {
+    position: absolute;
+    /* 4 screens worth of inset, to make sure this is off screen on Android
+     * where minimum scale on load is 0.25 */
+    inset-inline-start: 400%;
+    inset-block-start: 400%;
+    width: 60%;
+    height: 60%;
+  }
+  input {
+    position: absolute;
+    inset-inline-start: 400%;
+    inset-block-start: 400%;
+    width: 50%;
+  }
+  div.rootScroller {
+    position: fixed;
+    left: 0;
+    right: 0;
+    top: 0;
+    bottom: 0;
+    background-color: coral;
+  }
+  .spacer {
+    /* make the document scrollable */
+    position: absolute;
+    inset-inline-start: 0;
+    inset-block-start: 0;
+    width: 1200%;
+    height: 1200%;
+  }
+
+  .touchActionNone {
+    touch-action: none;
+  }
+
+  .layoutViewport {
+    /* Used to let the test measure the size of the layout viewport; needed when
+     * testing desktop page on mobile where ICB size doesn't match layout
+     * viewport size (i.e. the "shrinks viewport contents to fit" setting) */
+    position: fixed;
+    left: 0;
+    top: 0;
+    right: 0;
+    bottom: 0;
+    visibility: hidden;
+  }
+</style>
+</head>
+<body>
+<div class="layoutViewport"></div>
+<h2 id='siteNameHeading'></h2>
+<div class="spacer"></div>
+
+
+<script src='tree_parser_util.js'></script>
+<script>
+
+function backgroundColorForSite(site) {
+  var lightness = 0.75;
+
+  // The site names will be of the form siteA, siteB, etc so map the fifth
+  // character to an index. This could be negative, we don't really care.
+  var index = site.charCodeAt(4) - '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 isUrl(siteString) {
+  try {
+    var url = new URL(siteString);
+  } catch (e) {
+    if (e instanceof TypeError)
+      return false;
+  }
+  return true;
+}
+
+/**
+ * Extract the specified port number, if any. Returns empty string if not
+ * specified.
+ */
+function sitePortNumber(siteString) {
+  let index = siteString.indexOf(':');
+  if (index == -1)
+    return ""
+  return siteString.substring(index + 1);
+}
+
+/**
+ * Adds ".test" to an argument if it doesn't already have a top level domain.
+ * Converts "siteA" to "a.test" to match test host names defined in test SSL
+ * certificate.  Adds the specified port number, if any, or the default port
+ * otherwise.
+ */
+function canonicalizeSiteAndPort(siteString, defaultPort) {
+  var portNumber = sitePortNumber(siteString) || defaultPort;
+  var hostName = siteString.split(':')[0];
+  if (hostName !== "localhost" && hostName.indexOf('.') == -1)
+    hostName = hostName + '.test';
+
+  if (hostName.startsWith('site'))
+    hostName = hostName.substring('site'.length).toLowerCase();
+
+  return hostName + (portNumber ? ':' + portNumber : "");
+}
+
+function hasAttribute(attribute) {
+  if (!Number.isInteger(attribute) || attribute < 0
+      || attribute > Attributes.MAX_VALUE) {
+    throw new Error("hasAttribute parameter invalid: " + attribute);
+  }
+
+  for (var frame_attribute of frame_attributes) {
+    if (frame_attribute == attribute) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+function processFrameAttributes(frameTree) {
+  window.frame_attributes = [];
+  for (var attribute of frameTree.attributes) {
+    if (!Attributes.hasOwnProperty(attribute))
+      throw new Error("Invalid specified attribute: " + attribute);
+
+    frame_attributes.push(Attributes[attribute]);
+  }
+}
+
+function main() {
+  var goCrossSite = !window.location.protocol.startsWith('file');
+  var queryString = decodeURIComponent(window.location.search.substring(1));
+  var frameTree = TreeParserUtil.parse(queryString);
+  var currentSite = isUrl(frameTree.value)
+                      ? frameTree.value
+                      : canonicalizeSiteAndPort(frameTree.value, "");
+
+  processFrameAttributes(frameTree);
+
+  if (hasAttribute(Attributes.RTL)) {
+    document.documentElement.setAttribute('dir', 'rtl')
+  }
+
+  if (hasAttribute(Attributes.MobileViewport)) {
+    const meta = document.createElement('meta');
+    meta.setAttribute('name', 'viewport');
+    meta.setAttribute('content', 'width=device-width');
+    document.head.appendChild(meta);
+  }
+
+  if (hasAttribute(Attributes.MobileViewportNoZoom)) {
+    const meta = document.createElement('meta');
+    meta.setAttribute('name', 'viewport');
+    meta.setAttribute('content', 'width=device-width,minimum-scale=1');
+    document.head.appendChild(meta);
+  }
+
+  if (hasAttribute(Attributes.RootScroller)) {
+    const html = document.documentElement;
+    const kMaxOffset = 1000000;
+    html.scrollLeft = kMaxOffset;
+    html.scrollTop = kMaxOffset;
+
+    html.classList.remove('rootScroller');
+
+    const rootScroller = document.createElement('div');
+    rootScroller.classList.add('rootScroller');
+
+    const spacer = document.createElement('div');
+    spacer.classList.add('spacer');
+    spacer.innerText = "Root Scroller";
+    rootScroller.appendChild(spacer);
+
+    document.body.appendChild(rootScroller);
+  }
+
+  document.getElementById('siteNameHeading').appendChild(
+      document.createTextNode(currentSite));
+
+  // Apply style to the current document.
+  document.body.style.backgroundColor = backgroundColorForSite(currentSite);
+
+  if (frameTree.children.length == 0) {
+    let input = document.createElement('input');
+
+    if (hasAttribute(Attributes.TouchActionNone))
+      input.classList.add('touchActionNone');
+
+    if (document.documentElement.classList.contains('rootScroller'))
+      document.body.appendChild(input);
+    else
+      document.querySelector('.rootScroller').appendChild(input);
+
+    return;
+  }
+
+  // ScrollIntoView tests don't need multiple children and we don't support it.
+  console.assert(frameTree.children.length == 1);
+
+  // Compute the URL for this child frame.
+  let url = frameTree.children[0].value;
+  let siteAndPort = url;
+  if (!isUrl(url)) {
+    siteAndPort = canonicalizeSiteAndPort(url, window.location.port);
+    const subtreeString = TreeParserUtil.flatten(frameTree.children[0]);
+    url = '';
+    url += window.location.protocol + '//';              // scheme (preserved)
+    url += goCrossSite ? siteAndPort : window.location.host;    // host and port
+    url += window.location.pathname;                     // path (preserved)
+    url += '?' + encodeURIComponent(subtreeString);      // query
+  }
+
+  // Construct the child frame.
+  var elementType = hasAttribute(Attributes.FencedFrame) ? 'fencedframe' : 'iframe';
+  var frame = document.createElement(elementType);
+  if (elementType == "fencedframe") {
+    frame.config = new FencedFrameConfig(url);
+  } else {
+    frame.src = url;
+  }
+  frame.id = "childframe";
+
+  // Add the child frame to the document.
+  document.body.appendChild(frame);
+}
+
+main();
+</script>
+</body></html>
--- /dev/null
+++ b/content/test/data/file_input_webkitdirectory.html
@@ -0,0 +1 @@
+<input type="file" id="fileinput" webkitdirectory />
--- /dev/null
+++ b/content/test/data/page_with_credentialless_iframe.html
@@ -0,0 +1,5 @@
+<iframe credentialless
+        src="title1.html"
+        id="test_iframe"
+        name="test_iframe">
+</iframe>
--- /dev/null
+++ b/content/test/data/page_with_stop_iframe.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+<iframe src="stop.html"></iframe>
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/stop.html
@@ -0,0 +1,3 @@
+<html>
+<script>stop();</script>
+</html>
--- /dev/null
+++ b/content/test/data/web_ui_mojo_ts_test.html
@@ -0,0 +1,8 @@
+<html>
+<head>
+  <script src="web_ui_mojo_ts_test.js" type="module"></script>
+</head>
+<body>
+  x
+</body>
+</html>
--- /dev/null
+++ b/content/test/data/web_ui_ts_test.test-mojom
@@ -0,0 +1,13 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module content.mojom;
+
+import "content/test/data/web_ui_ts_test_types.test-mojom";
+import "url/mojom/url.mojom";
+
+interface WebUITsMojoTestCache {
+  Put(url.mojom.Url url, string contents);
+  GetAll() => (array<TsCacheItem> items);
+};
--- /dev/null
+++ b/content/test/data/web_ui_ts_test_types.test-mojom
@@ -0,0 +1,16 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module content.mojom;
+
+import "url/mojom/url.mojom";
+
+// This struct is imported by web_ui_ts_test.test-mojom and is used to provide
+// test coverage of non-shared WebUI mojom JS modules importing other
+// non-shared WebUI mojom JS modules.
+struct TsCacheItem {
+  url.mojom.Url url;
+  string contents;
+};
+
--- /dev/null
+++ b/content/test/data/web_ui_mojo_ts_test.ts
@@ -0,0 +1,45 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {WebUITsMojoTestCache} from './web_ui_ts_test.test-mojom-webui.js';
+
+const TEST_DATA: Array<{url: string, contents: string}> = [
+  { url: 'https://google.com/', contents: 'i am in fact feeling lucky' },
+  { url: 'https://youtube.com/', contents: 'probably cat videos?' },
+  { url: 'https://example.com/', contents: 'internets wow' },
+];
+
+async function doTest(): Promise<boolean> {
+  const cache = WebUITsMojoTestCache.getRemote();
+  for (const entry of TEST_DATA) {
+    cache.put({ url: entry.url }, entry.contents);
+  }
+
+  const {items} = await cache.getAll();
+  if (items.length != TEST_DATA.length) {
+    return false;
+  }
+
+  const entries: {[key: string]: string } = {};
+  for (const item of items) {
+    entries[item.url.url] = item.contents;
+  }
+
+  for (const entry of TEST_DATA) {
+    if (!(entry.url in entries)) {
+      return false;
+    }
+    if (entries[entry.url] != entry.contents) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+async function runTest(): Promise<boolean> {
+  return doTest();
+}
+
+Object.assign(window, {runTest});
--- /dev/null
+++ b/content/test/data/cacheable.js
@@ -0,0 +1,5 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/* empty */
--- /dev/null
+++ b/content/test/data/cacheable2.js
@@ -0,0 +1,5 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/* empty */
--- /dev/null
+++ b/content/test/data/web_ui_managed_interface_test.html
@@ -0,0 +1,10 @@
+<!--
+Copyright 2023 The Chromium Authors
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+
+<html>
+<title>Foo</title>
+<script src="/web_ui_managed_interface_test.js" type="module"></script>
+</html>
--- /dev/null
+++ b/content/test/data/web_ui_managed_interface_test.js
@@ -0,0 +1,9 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {BarCallbackRouter, BazCallbackRouter, FooRemote, TestWebUIJsBridge} from '/web_ui_managed_interface_test.test-mojom-webui.js';
+
+Object.assign(
+    window,
+    {BarCallbackRouter, BazCallbackRouter, FooRemote, TestWebUIJsBridge});
--- /dev/null
+++ b/content/test/data/web_ui_managed_interface_test.test-mojom
@@ -0,0 +1,32 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module content.mojom;
+
+interface Foo {
+  GetFoo() => (string value);
+};
+
+interface Bar {
+  GetBar() => (string value);
+};
+
+interface Baz {
+  GetBaz() => (string value);
+};
+
+// A bridge (aka Factory) interface that is used for binding other
+// interfaces.
+interface TestWebUIJsBridge {
+  // The WebUI calls this method to bind a Foo remote.
+  BindFoo(pending_receiver<Foo> foo);
+
+  // The WebUI calls this method to bind a Foo remote and provide the
+  // browser with a Bar remote.
+  BindFooBar(pending_receiver<Foo> foo,
+             pending_remote<Bar> bar);
+
+  // The WebUI calls this method to provide the browser with a Baz remote.
+  BindBaz(pending_remote<Baz> baz);
+};
