Bug 1320235 Test that the browser continues to function during timer floods. r=bz
authorBen Kelly <ben@wanderview.com>
Mon, 28 Nov 2016 10:49:40 -0800
changeset 324426 9907f708f3fb952415a5b7701558fbb1c5dc69e7
parent 324425 b7af3fd1de51ec375508c7cc43f782138a0f9dff
child 324427 f7bf172373ff87d87ea8dfd81c08a7756667fa77
push id84401
push userbkelly@mozilla.com
push dateMon, 28 Nov 2016 18:50:18 +0000
treeherdermozilla-inbound@9907f708f3fb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz
bugs1320235
milestone53.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1320235 Test that the browser continues to function during timer floods. r=bz
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
@@ -759,16 +760,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>