Bug 1320235 Test that the browser continues to function during timer floods. r=bz a=testonly
authorBen Kelly <ben@wanderview.com>
Wed, 30 Nov 2016 14:16:44 -0800
changeset 349418 3fa3896980f6726c3897ce1645a839996d5d5e8b
parent 349417 a065f231fb79a48683b37c4cdb5136d7f14fd560
child 349419 9611dbc3df67645063288db31517c267ebe320c1
push id10382
push userbkelly@mozilla.com
push dateWed, 30 Nov 2016 22:16:50 +0000
treeherdermozilla-aurora@3fa3896980f6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz, testonly
bugs1320235
milestone52.0a2
Bug 1320235 Test that the browser continues to function during timer floods. r=bz a=testonly
dom/base/test/file_timer_flood.html
dom/base/test/mochitest.ini
dom/base/test/test_timer_flood.html
new file mode 100644
--- /dev/null
+++ b/dom/base/test/file_timer_flood.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML>
+<html>
+<body>
+<script>
+let count = 0;
+function cb() {
+  count += 1;
+  // Notify our parent that we are ready once the timer flood has
+  // warmed up.
+  if (count === 10000) {
+    window.parent.postMessage('STARTED', '*');
+  }
+  setTimeout(cb, 0);
+  setTimeout(cb, 0);
+}
+addEventListener('load', cb);
+</script>
+</body>
+</html>
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -154,16 +154,17 @@ support-files =
   file_mozfiledataurl_audio.ogg
   file_mozfiledataurl_doc.html
   file_mozfiledataurl_img.jpg
   file_mozfiledataurl_inner.html
   file_mozfiledataurl_text.txt
   file_record_orientation.html
   file_restrictedEventSource.sjs
   file_simplecontentpolicy.js
+  file_timer_flood.html
   file_websocket_basic_wsh.py
   file_websocket_hello_wsh.py
   file_websocket_http_resource.txt
   file_websocket_permessage_deflate_wsh.py
   file_websocket_permessage_deflate_disabled_wsh.py
   file_websocket_permessage_deflate_rejected_wsh.py
   file_websocket_permessage_deflate_params_wsh.py
   file_websocket_wsh.py
@@ -760,16 +761,17 @@ skip-if = debug == false
 [test_settimeout_inner.html]
 [test_setTimeoutWith0.html]
 [test_setting_opener.html]
 [test_simplecontentpolicy.html]
 skip-if = e10s # Bug 1156489.
 [test_text_wholeText.html]
 [test_textnode_normalize_in_selection.html]
 [test_textnode_split_in_selection.html]
+[test_timer_flood.html]
 [test_title.html]
 [test_treewalker_nextsibling.xml]
 [test_user_select.html]
 skip-if = toolkit == 'android'
 [test_viewport_scroll.html]
 [test_viewsource_forbidden_in_object.html]
 [test_w3element_traversal.html]
 [test_w3element_traversal.xhtml]
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_timer_flood.html
@@ -0,0 +1,116 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="UTF-8">
+  <title>Test Behavior During Timer Flood</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+
+function onLoad() {
+  return new Promise(resolve => {
+    addEventListener('load', resolve, { once: true });
+  });
+}
+
+// Create a frame that executes a timer flood.  The frame signals
+// that is ready once the flood has had a chance to warm up.
+function withFloodFrame() {
+  return new Promise(resolve => {
+    let frame = document.createElement('iframe');
+    addEventListener('message', function onMsg(evt) {
+      if (evt.data === 'STARTED') {
+        removeEventListener('message', onMsg);
+        resolve(frame);
+      }
+    });
+    frame.src = 'file_timer_flood.html';
+    document.body.appendChild(frame);
+  });
+}
+
+// Test that we can load documents during a timer flood.
+function testFrameLoad() {
+  return new Promise(resolve => {
+    let frame = document.createElement('iframe');
+    frame.addEventListener('load', _ => {
+      frame.remove();
+      resolve();
+    }, { once: true });
+    document.body.appendChild(frame);
+  });
+}
+
+// Test that we can perform network requests while a timer flood
+// is occuring.
+function testFetch(url) {
+  return fetch(url).then(response => {
+    return response.text();
+  });
+}
+
+// Test that we can run animations for 5 seconds while a timer
+// flood is occuring.
+function testRequestAnimationFrame() {
+  return new Promise(resolve => {
+    let remainingFrames = 5 * 60;
+    function nextFrame() {
+      remainingFrames -= 1;
+      if (remainingFrames > 0) {
+        requestAnimationFrame(nextFrame);
+      } else {
+        resolve();
+      }
+    };
+    requestAnimationFrame(nextFrame);
+  });
+}
+
+let floodFrame;
+
+onLoad().then(_ => {
+  // Start a timer flood in a frame.
+  return withFloodFrame();
+}).then(frame => {
+  floodFrame = frame;
+
+  // Next we are going to start a bunch of asynchronous work that we
+  // expect to complete in spite of the timer flood.  The type of work
+  // is a bit arbitrary, but is chosen to reflect the kinds of things
+  // we would like the browser to be able to do even when pages are
+  // abusing timers.  Feel free to add more types of work here, but
+  // think carefully before removing anything.
+  let tests = [];
+
+  // Verify we can perform a variety of work while the timer flood
+  // is running.
+  for (let i = 0; i < 20; ++i) {
+    tests.push(testFrameLoad());
+    tests.push(testFetch('file_timer_flood.html'));
+  }
+  // Verify that animations still work while the timer flood is running.
+  // Note that we do one long run of animations instead of parallel runs
+  // like the other activities because of the way requestAnimationFrame()
+  // is scheduled.  Parallel animations would not result in any additional
+  // runnables be placed on the event queue.
+  tests.push(testRequestAnimationFrame());
+
+  // Wait for all tests to finish.  If we do not handle the timer flood
+  // well then this will likely time out.
+  return Promise.all(tests);
+}).then(_ => {
+  ok(true, 'completed tests without timing out');
+  floodFrame.remove();
+  SimpleTest.finish();
+});
+</script>
+</pre>
+</body>
+</html>