Bug 972110 Rewrite editor/libeditor/test_bug674770-1.html without setTimeout() for avoiding intermittent failure r?smaug draft
authorMasayuki Nakano <masayuki@d-toybox.com>
Thu, 22 Sep 2016 10:54:51 +0900
changeset 416408 a8ad974fb426aac8450f775346c3786aa8000573
parent 415495 62f79d676e0e11b3ad59a5425b3ebb3ec5bbefb5
child 531845 256ccdecabbf642f41336b987d845fd0d06ccf47
push id30134
push usermasayuki@d-toybox.com
push dateThu, 22 Sep 2016 01:55:59 +0000
reviewerssmaug
bugs972110, 674770
milestone52.0a1
Bug 972110 Rewrite editor/libeditor/test_bug674770-1.html without setTimeout() for avoiding intermittent failure r?smaug We can assume that if middle button's click event on a link isn't consumed by any event handlers including system event group's, it will cause open new tab. With this assumption, we can avoid using setTimeout which causes random orange. However, unfortunately, in e10s mode, the default is NOT consumed at window in bubbling phase but consumed at that time. So, when not working the link is expected, we cannot check Event.defaultPrevented. But fortunately, we can check if the page is loaded after that. Note that for testing this, the test needs to check if an event handler which is either in default group or system group consumed a click event. However, this runs as mochitest-plain. Therefore, Event.defaultPrevented returns false if the event is consumed only in the system group's event listener. For avoiding this issue, this patch adds defaultPreventedInAnyGroups() into SpecialPowers. In SpecialPowers, Event.defaultPrevented is accessed from chrome context. Therefore, we can get the result what this test needs. MozReview-Commit-ID: Cfn4lFR1dfI
editor/libeditor/tests/mochitest.ini
editor/libeditor/tests/test_bug674770-1.html
testing/specialpowers/content/specialpowersAPI.js
--- a/editor/libeditor/tests/mochitest.ini
+++ b/editor/libeditor/tests/mochitest.ini
@@ -114,17 +114,17 @@ skip-if = toolkit == 'android' #bug 9577
 [test_bug638596.html]
 [test_bug640321.html]
 skip-if = android_version == '18' # bug 1147989
 [test_bug641466.html]
 [test_bug645914.html]
 [test_bug668599.html]
 [test_bug674770-1.html]
 subsuite = clipboard
-skip-if = toolkit == 'android' || (os == 'linux' && e10s && (debug||asan)) # Bug 972110
+skip-if = toolkit == 'android'
 [test_bug674770-2.html]
 subsuite = clipboard
 skip-if = toolkit == 'android'
 [test_bug674861.html]
 [test_bug676401.html]
 [test_bug677752.html]
 [test_bug681229.html]
 subsuite = clipboard
--- a/editor/libeditor/tests/test_bug674770-1.html
+++ b/editor/libeditor/tests/test_bug674770-1.html
@@ -19,51 +19,67 @@ https://bugzilla.mozilla.org/show_bug.cg
 <a href="file_bug674770-1.html" id="link2">test</a>
 </div>
 </div>
 <pre id="test">
 <script type="application/javascript">
 
 SimpleTest.waitForExplicitFinish();
 SimpleTest.waitForFocus(function() {
-  SpecialPowers.pushPrefEnv({"set":[["middlemouse.paste", true]]}, startTest);
+  SpecialPowers.pushPrefEnv({"set":[["middlemouse.paste", true]]}, startTests);
 });
-function startTest() {
-  localStorage.removeItem("clicked");
-  window.linkWasClicked = false;
 
-  var link = document.querySelector("#link1");
-  addEventListener("storage", function(e) {
-    is(e.key, "clicked", "Correct event");
-    is(e.newValue, "true", "Correct value");
-    window.linkWasClicked = true;
-  }, false);
-  synthesizeMouseAtCenter(link, {button: 1});
-
-  hitEventLoop(function() {
-    ok(window.linkWasClicked, "The click operation worked successfully");
-    window.linkWasClicked = false;
+function startTests() {
+  var tests = [
+    { description: "Testing link in <div>: ",
+      target: function () { return document.querySelector("#link1"); },
+      linkShouldWork: true },
+    { description: "Testing link in <div contenteditable>: ",
+      target: function () { return document.querySelector("#link2"); },
+      linkShouldWork: false },
+  ];
+  var currentTest;
+  function runNextTest() {
+    localStorage.removeItem("clicked");
+    currentTest = tests.shift();
+    if (!currentTest) {
+      SimpleTest.finish();
+      return;
+    }
+    ok(true, currentTest.description + "Starting to test...");
+    synthesizeMouseAtCenter(currentTest.target(), { button: 1 });
+  }
 
-    link = document.querySelector("#link2");
-    localStorage.removeItem("clicked");
-    synthesizeMouseAtCenter(link, {button: 1});
-
-    hitEventLoop(function() {
-      ok(!window.linkWasClicked, "The click operation shouldn't work in the contenteditable area");
+  addEventListener("storage", function(e) {
+    is(e.key, "clicked", currentTest.description + "Key should always be 'clicked'");
+    is(e.newValue, "true", currentTest.description + "Value should always be 'true'");
+    ok(currentTest.linkShouldWork, currentTest.description + "The click operation on the link " + (currentTest.linkShouldWork ? "should work" : "shouldn't work"));
+    SimpleTest.executeSoon(runNextTest);
+  }, false);
 
-      localStorage.removeItem("clicked");
-      SimpleTest.finish();
-    }, 100);
-  }, 100);
-}
+  SpecialPowers.addSystemEventListener(window, "click", function (aEvent) {
+    // When the click event should cause default action, e.g., opening the link,
+    // the event shouldn't have been consumed except the link handler.
+    // However, in e10s mode, it's not consumed during propagating the event but
+    // in non-e10s mode, it's consumed during the propagation.  Therefore,
+    // we cannot check defaultPrevented value when the link should work as is
+    // if there is no way to detect if it's running in e10s mode or not.
+    // So, let's skip checking Event.defaultPrevented value when the link should
+    // work.  In such case, we should receive "storage" event later.
+    if (currentTest.linkShouldWork) {
+      return;
+    }
 
-function hitEventLoop(func, times) {
-  if (times > 0) {
-    setTimeout(hitEventLoop, 0, func, times - 1);
-  } else {
-    setTimeout(func, 0);
-  }
+    ok(SpecialPowers.defaultPreventedInAnyGroup(aEvent),
+       currentTest.description + "The default action should be consumed because the link should work as is");
+    if (SpecialPowers.defaultPreventedInAnyGroup(aEvent)) {
+      // In this case, "storage" event won't be fired.
+      SimpleTest.executeSoon(runNextTest);
+    }
+  }, false);
+
+  SimpleTest.executeSoon(runNextTest);
 }
 
 </script>
 </pre>
 </body>
 </html>
--- a/testing/specialpowers/content/specialpowersAPI.js
+++ b/testing/specialpowers/content/specialpowersAPI.js
@@ -1590,16 +1590,24 @@ SpecialPowersAPI.prototype = {
       addSystemEventListener(target, type, listener, useCapture);
   },
   removeSystemEventListener: function(target, type, listener, useCapture) {
     Cc["@mozilla.org/eventlistenerservice;1"].
       getService(Ci.nsIEventListenerService).
       removeSystemEventListener(target, type, listener, useCapture);
   },
 
+  // helper method to check if the event is consumed by either default group's
+  // event listener or system group's event listener.
+  defaultPreventedInAnyGroup: function(event) {
+    // FYI: Event.defaultPrevented returns false in content context if the
+    //      event is consumed only by system group's event listeners.
+    return event.defaultPrevented;
+  },
+
   getDOMRequestService: function() {
     var serv = Services.DOMRequest;
     var res = {};
     var props = ["createRequest", "createCursor", "fireError", "fireSuccess",
                  "fireDone", "fireDetailedError"];
     for (var i in props) {
       let prop = props[i];
       res[prop] = function() { return serv[prop].apply(serv, arguments) };