Bug 454520 - fix caret browsing dialog to default to no and remember choices, r=jaws,f=dbolter
authorGijs Kruitbosch <gijskruitbosch@gmail.com>
Mon, 26 May 2014 14:54:03 +0100
changeset 205549 81d79924f919ff293cedd87e80d2376cc7d2f51c
parent 205548 9063b60fe526d53d714c17cef0b2dc91810b654e
child 205550 509dcb2fd26b551deaf8f3ad513e42e660467b5e
push id3741
push userasasaki@mozilla.com
push dateMon, 21 Jul 2014 20:25:18 +0000
treeherdermozilla-beta@4d6f46f5af68 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjaws
bugs454520
milestone32.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 454520 - fix caret browsing dialog to default to no and remember choices, r=jaws,f=dbolter
toolkit/content/tests/browser/browser.ini
toolkit/content/tests/browser/browser_f7_caret_browsing.js
toolkit/content/widgets/browser.xml
--- a/toolkit/content/tests/browser/browser.ini
+++ b/toolkit/content/tests/browser/browser.ini
@@ -5,16 +5,17 @@ skip-if = e10s # Bug ?????? - test touch
 [browser_browserDrop.js]
 [browser_bug295977_autoscroll_overflow.js]
 skip-if = e10s # Bug 921935 - focusmanager issues with e10s
 [browser_bug594509.js]
 skip-if = e10s # Bug ?????? - intermittent crash of child process reported when run under e10s
 [browser_bug982298.js]
 [browser_default_image_filename.js]
 skip-if = e10s # Bug 933103 - mochitest's EventUtils.synthesizeMouse functions not e10s friendly
+[browser_f7_caret_browsing.js]
 [browser_findbar.js]
 skip-if = e10s # Disabled for e10s: Bug ?????? - seems to be a timing issue with RemoteFinder.jsm messages coming later than the tests expect.
 [browser_input_file_tooltips.js]
 skip-if = e10s # Bug ?????? - test directly manipulates content (TypeError: doc.createElement is not a function)
 [browser_keyevents_during_autoscrolling.js]
 skip-if = e10s # Bug 921935 - focusmanager issues with e10s
 [browser_save_resend_postdata.js]
 support-files =
new file mode 100644
--- /dev/null
+++ b/toolkit/content/tests/browser/browser_f7_caret_browsing.js
@@ -0,0 +1,279 @@
+XPCOMUtils.defineLazyModuleGetter(this, "Promise",
+  "resource://gre/modules/Promise.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Task",
+  "resource://gre/modules/Task.jsm");
+
+let gTab = null;
+let gListener = null;
+const kURL = "data:text/html;charset=utf-8,Caret browsing is fun.<input id='in'>";
+
+const kPrefShortcutEnabled = "accessibility.browsewithcaret_shortcut.enabled";
+const kPrefWarnOnEnable    = "accessibility.warn_on_browsewithcaret";
+const kPrefCaretBrowsingOn = "accessibility.browsewithcaret";
+
+let oldPrefs = {};
+for (let pref of [kPrefShortcutEnabled, kPrefWarnOnEnable, kPrefCaretBrowsingOn]) {
+  oldPrefs[pref] = Services.prefs.getBoolPref(pref);
+}
+
+Services.prefs.setBoolPref(kPrefShortcutEnabled, true);
+Services.prefs.setBoolPref(kPrefWarnOnEnable, true);
+Services.prefs.setBoolPref(kPrefCaretBrowsingOn, false);
+
+registerCleanupFunction(function() {
+  if (gTab)
+    gBrowser.removeTab(gTab);
+  if (gListener)
+    Services.wm.removeListener(gListener);
+
+  for (let pref of [kPrefShortcutEnabled, kPrefWarnOnEnable, kPrefCaretBrowsingOn]) {
+    Services.prefs.setBoolPref(pref, oldPrefs[pref]);
+  }
+});
+
+function waitForCondition(aConditionFn, aMaxTries=50, aCheckInterval=100) {
+  function tryNow() {
+    tries++;
+    if (aConditionFn()) {
+      deferred.resolve();
+    } else if (tries < aMaxTries) {
+      tryAgain();
+    } else {
+      deferred.reject("Condition timed out: " + aConditionFn.toSource());
+    }
+  }
+  function tryAgain() {
+    setTimeout(tryNow, aCheckInterval);
+  }
+  let deferred = Promise.defer();
+  let tries = 0;
+  tryAgain();
+  return deferred.promise;
+}
+
+function promiseWaitForDialogUnload(dialog) {
+  let deferred = Promise.defer();
+  dialog.addEventListener("unload", function listener() {
+    dialog.removeEventListener("unload", listener, false);
+    deferred.resolve();
+  }, false);
+  return deferred.promise;
+}
+
+function promiseWaitForFocusEvent(el) {
+  if (el.ownerDocument.activeElement == el) {
+    return true;
+  }
+  let deferred = Promise.defer();
+  el.addEventListener("focus", function listener() {
+    el.removeEventListener("focus", listener, false);
+    deferred.resolve();
+  }, false);
+  return deferred.promise;
+}
+
+function promiseTestPageLoad() {
+  let deferred = Promise.defer();
+  info("Waiting for test page to load.");
+
+  gTab = gBrowser.selectedTab = gBrowser.addTab(kURL);
+  let browser = gBrowser.selectedBrowser;
+  browser.addEventListener("load", function listener() {
+    if (browser.currentURI.spec == "about:blank")
+      return;
+    info("Page loaded: " + browser.currentURI.spec);
+    browser.removeEventListener("load", listener, true);
+
+    deferred.resolve();
+  }, true);
+
+  return deferred.promise;
+}
+
+function promiseCaretPromptOpened() {
+  let deferred = Promise.defer();
+  if (gListener) {
+    console.trace();
+    ok(false, "Should not be waiting for another prompt right now.");
+    return false;
+  }
+  info("Waiting for caret prompt to open");
+  gListener = {
+    onOpenWindow: function(win) {
+      let window = win.QueryInterface(Ci.nsIInterfaceRequestor)
+                      .getInterface(Ci.nsIDOMWindow);
+      window.addEventListener("load", function listener() {
+        window.removeEventListener("load", listener);
+        if (window.location.href == "chrome://global/content/commonDialog.xul") {
+          info("Caret prompt opened, removing listener and focusing");
+          Services.wm.removeListener(gListener);
+          gListener = null;
+          deferred.resolve(window);
+        }
+      });
+    },
+    onCloseWindow: function() {},
+  };
+  Services.wm.addListener(gListener);
+  return deferred.promise;
+}
+
+function hitF7(async = true) {
+  let f7 = () => EventUtils.sendKey("F7", window.content);
+  // Need to not stop execution inside this task:
+  if (async) {
+    executeSoon(f7);
+  } else {
+    f7();
+  }
+}
+
+function syncToggleCaretNoDialog(expected) {
+  let openedDialog = false;
+  promiseCaretPromptOpened().then(function(win) {
+    openedDialog = true;
+    win.close(); // This will eventually return focus here and allow the test to continue...
+  });
+  // Cause the dialog to appear sync, if it still does.
+  hitF7(false);
+  if (gListener) {
+    Services.wm.removeListener(gListener);
+    gListener = null;
+  }
+  let expectedStr = expected ? "on." : "off.";
+  ok(!openedDialog, "Shouldn't open a dialog to turn caret browsing " + expectedStr);
+  let prefVal = Services.prefs.getBoolPref(kPrefCaretBrowsingOn);
+  is(prefVal, expected, "Caret browsing should now be " + expectedStr);
+}
+
+add_task(function* checkTogglingCaretBrowsing() {
+  yield promiseTestPageLoad();
+  let textEl = window.content.document.getElementById("in");
+  textEl.focus();
+
+  let promiseGotKey = promiseCaretPromptOpened();
+  hitF7();
+  let prompt = yield promiseGotKey;
+  let doc = prompt.document;
+  is(doc.documentElement.defaultButton, "cancel", "No button should be the default");
+  ok(!doc.getElementById("checkbox").checked, "Checkbox shouldn't be checked by default.");
+  let promiseDialogUnloaded = promiseWaitForDialogUnload(prompt);
+  doc.documentElement.cancelDialog();
+  yield promiseDialogUnloaded;
+  yield waitForCondition(() => textEl.ownerDocument.activeElement == textEl);
+  ok(!Services.prefs.getBoolPref(kPrefCaretBrowsingOn), "Caret browsing should still be off after cancelling the dialog.");
+
+  promiseGotKey = promiseCaretPromptOpened();
+  hitF7();
+  prompt = yield promiseGotKey;
+
+  doc = prompt.document;
+  is(doc.documentElement.defaultButton, "cancel", "No button should be the default");
+  ok(!doc.getElementById("checkbox").checked, "Checkbox shouldn't be checked by default.");
+  promiseDialogUnloaded = promiseWaitForDialogUnload(prompt);
+  doc.documentElement.acceptDialog();
+  yield promiseDialogUnloaded;
+  yield waitForCondition(() => textEl.ownerDocument.activeElement == textEl);
+  ok(Services.prefs.getBoolPref(kPrefCaretBrowsingOn), "Caret browsing should be on after accepting the dialog.");
+
+  syncToggleCaretNoDialog(false);
+
+  promiseGotKey = promiseCaretPromptOpened();
+  hitF7();
+  prompt = yield promiseGotKey;
+  doc = prompt.document;
+
+  is(doc.documentElement.defaultButton, "cancel", "No button should be the default");
+  ok(!doc.getElementById("checkbox").checked, "Checkbox shouldn't be checked by default.");
+
+  promiseDialogUnloaded = promiseWaitForDialogUnload(prompt);
+  doc.documentElement.cancelDialog();
+  yield promiseDialogUnloaded;
+  yield waitForCondition(() => textEl.ownerDocument.activeElement == textEl);
+
+  ok(!Services.prefs.getBoolPref(kPrefCaretBrowsingOn), "Caret browsing should still be off after cancelling the dialog.");
+
+  Services.prefs.setBoolPref(kPrefShortcutEnabled, true);
+  Services.prefs.setBoolPref(kPrefWarnOnEnable, true);
+  Services.prefs.setBoolPref(kPrefCaretBrowsingOn, false);
+
+  gBrowser.removeTab(gTab);
+  gTab = null;
+});
+
+add_task(function* toggleCheckboxNoCaretBrowsing() {
+  yield promiseTestPageLoad();
+  let textEl = window.content.document.getElementById("in");
+  textEl.focus();
+
+  let promiseGotKey = promiseCaretPromptOpened();
+  hitF7();
+  let prompt = yield promiseGotKey;
+  let doc = prompt.document;
+  is(doc.documentElement.defaultButton, "cancel", "No button should be the default");
+  let checkbox = doc.getElementById("checkbox");
+  ok(!checkbox.checked, "Checkbox shouldn't be checked by default.");
+
+  // Check the box:
+  checkbox.click();
+  let promiseDialogUnloaded = promiseWaitForDialogUnload(prompt);
+  // Say no:
+  doc.documentElement.getButton("cancel").click();
+  yield promiseDialogUnloaded;
+  yield waitForCondition(() => textEl.ownerDocument.activeElement == textEl);
+  ok(!Services.prefs.getBoolPref(kPrefCaretBrowsingOn), "Caret browsing should still be off.");
+
+  ok(!Services.prefs.getBoolPref(kPrefShortcutEnabled), "Shortcut should now be disabled.");
+
+  syncToggleCaretNoDialog(false);
+  ok(!Services.prefs.getBoolPref(kPrefShortcutEnabled), "Shortcut should still be disabled.");
+
+  Services.prefs.setBoolPref(kPrefShortcutEnabled, true);
+  Services.prefs.setBoolPref(kPrefWarnOnEnable, true);
+  Services.prefs.setBoolPref(kPrefCaretBrowsingOn, false);
+
+  gBrowser.removeTab(gTab);
+  gTab = null;
+});
+
+
+add_task(function* toggleCheckboxWantCaretBrowsing() {
+  yield promiseTestPageLoad();
+  let textEl = window.content.document.getElementById("in");
+  textEl.focus();
+
+  let promiseGotKey = promiseCaretPromptOpened();
+  hitF7();
+  let prompt = yield promiseGotKey;
+  let doc = prompt.document;
+  is(doc.documentElement.defaultButton, "cancel", "No button should be the default");
+  let checkbox = doc.getElementById("checkbox");
+  ok(!checkbox.checked, "Checkbox shouldn't be checked by default.");
+
+  // Check the box:
+  checkbox.click();
+  let promiseDialogUnloaded = promiseWaitForDialogUnload(prompt);
+  // Say yes:
+  doc.documentElement.acceptDialog();
+  yield promiseDialogUnloaded;
+  yield waitForCondition(() => textEl.ownerDocument.activeElement == textEl);
+  ok(Services.prefs.getBoolPref(kPrefCaretBrowsingOn), "Caret browsing should now be on.");
+  ok(Services.prefs.getBoolPref(kPrefShortcutEnabled), "Shortcut should still be enabled.");
+  ok(!Services.prefs.getBoolPref(kPrefWarnOnEnable), "Should no longer warn when enabling.");
+
+
+  syncToggleCaretNoDialog(false);
+  syncToggleCaretNoDialog(true);
+  syncToggleCaretNoDialog(false);
+  
+  Services.prefs.setBoolPref(kPrefShortcutEnabled, true);
+  Services.prefs.setBoolPref(kPrefWarnOnEnable, true);
+  Services.prefs.setBoolPref(kPrefCaretBrowsingOn, false);
+
+  gBrowser.removeTab(gTab);
+  gTab = null;
+});
+
+
+
+
--- a/toolkit/content/widgets/browser.xml
+++ b/toolkit/content/widgets/browser.xml
@@ -1095,58 +1095,70 @@
     </implementation>
 
     <handlers>
       <handler event="keypress" keycode="VK_F7" group="system">
         <![CDATA[
           if (event.defaultPrevented || !event.isTrusted)
             return;
 
-          var isEnabled = this.mPrefs.getBoolPref("accessibility.browsewithcaret_shortcut.enabled");
+          const kPrefShortcutEnabled = "accessibility.browsewithcaret_shortcut.enabled";
+          const kPrefWarnOnEnable    = "accessibility.warn_on_browsewithcaret";
+          const kPrefCaretBrowsingOn = "accessibility.browsewithcaret";
+
+          var isEnabled = this.mPrefs.getBoolPref(kPrefShortcutEnabled);
           if (!isEnabled)
             return;
 
           // Toggle browse with caret mode
           var browseWithCaretOn = false;
           var warn = true;
 
           try {
-            warn = this.mPrefs.getBoolPref("accessibility.warn_on_browsewithcaret");
+            warn = this.mPrefs.getBoolPref(kPrefWarnOnEnable);
           } catch (ex) {
           }
 
           try {
-            browseWithCaretOn = this.mPrefs.getBoolPref("accessibility.browsewithcaret");
+            browseWithCaretOn = this.mPrefs.getBoolPref(kPrefCaretBrowsingOn);
           } catch (ex) {
           }
           if (warn && !browseWithCaretOn) {
             var checkValue = {value:false};
             var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
                                           .getService(Components.interfaces.nsIPromptService);
 
             var buttonPressed = promptService.confirmEx(window,
               this.mStrBundle.GetStringFromName('browsewithcaret.checkWindowTitle'),
               this.mStrBundle.GetStringFromName('browsewithcaret.checkLabel'),
-              promptService.STD_YES_NO_BUTTONS,
+              // Make "No" the default:
+              promptService.STD_YES_NO_BUTTONS | promptService.BUTTON_POS_1_DEFAULT,
               null, null, null, this.mStrBundle.GetStringFromName('browsewithcaret.checkMsg'),
               checkValue);
-            if (buttonPressed != 0)
+            if (buttonPressed != 0) {
+              if (checkValue.value) {
+                try {
+                  this.mPrefs.setBoolPref(kPrefShortcutEnabled, false);
+                } catch (ex) {
+                }
+              }
               return;
+            }
             if (checkValue.value) {
               try {
-                this.mPrefs.setBoolPref("accessibility.warn_on_browsewithcaret", false);
+                this.mPrefs.setBoolPref(kPrefWarnOnEnable, false);
               }
               catch (ex) {
               }
             }
           }
 
           // Toggle the pref
           try {
-            this.mPrefs.setBoolPref("accessibility.browsewithcaret",!browseWithCaretOn);
+            this.mPrefs.setBoolPref(kPrefCaretBrowsingOn, !browseWithCaretOn);
           } catch (ex) {
           }
         ]]>
       </handler>
       <handler event="dragover" group="system">
       <![CDATA[
         if (!this.droppedLinkHandler || event.defaultPrevented)
           return;