Bug 935377 - Firefox should fix common scheme typos, r=bz
authorChristian Legnitto <christian@legnitto.com>
Sun, 17 Nov 2013 12:16:45 -0800
changeset 155017 604bf3977dc5
parent 155016 e3b4cb5a0c61
child 155018 d58ab6f6ca0a
child 156002 fc1d24a70418
push id25660
push userchristian@legnitto.com
push date2013-11-17 20:19 +0000
treeherdermozilla-central@604bf3977dc5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz
bugs935377
milestone28.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 935377 - Firefox should fix common scheme typos, r=bz
docshell/base/nsDefaultURIFixup.cpp
docshell/base/nsIURIFixup.idl
docshell/test/unit/test_nsDefaultURIFixup.js
docshell/test/unit/xpcshell.ini
modules/libpref/src/init/all.js
--- a/docshell/base/nsDefaultURIFixup.cpp
+++ b/docshell/base/nsDefaultURIFixup.cpp
@@ -24,16 +24,18 @@
 #include "nsIObserverService.h"
 #include "nsXULAppAPI.h"
 
 using namespace mozilla;
 
 /* Implementation file */
 NS_IMPL_ISUPPORTS1(nsDefaultURIFixup, nsIURIFixup)
 
+static bool sFixTypos = true;
+
 nsDefaultURIFixup::nsDefaultURIFixup()
 {
   /* member initializers and constructor code */
 }
 
 
 nsDefaultURIFixup::~nsDefaultURIFixup()
 {
@@ -209,16 +211,60 @@ nsDefaultURIFixup::CreateFixupURI(const 
     bool bUseNonDefaultCharsetForURI =
                         !bAsciiURI && !useUTF8 &&
                         (scheme.IsEmpty() ||
                          scheme.LowerCaseEqualsLiteral("http") ||
                          scheme.LowerCaseEqualsLiteral("https") ||
                          scheme.LowerCaseEqualsLiteral("ftp") ||
                          scheme.LowerCaseEqualsLiteral("file"));
 
+    // Check if we want to fix up common scheme typos.
+    rv = Preferences::AddBoolVarCache(&sFixTypos,
+                                      "browser.fixup.typo.scheme",
+                                      sFixTypos);
+    MOZ_ASSERT(NS_SUCCEEDED(rv),
+              "Failed to observe \"browser.fixup.typo.scheme\"");
+
+    // Fix up common scheme typos.
+    if (sFixTypos && (aFixupFlags & FIXUP_FLAG_FIX_SCHEME_TYPOS)) {
+
+        // Fast-path for common cases.
+        if (scheme.IsEmpty() ||
+            scheme.LowerCaseEqualsLiteral("http") ||
+            scheme.LowerCaseEqualsLiteral("https") ||
+            scheme.LowerCaseEqualsLiteral("ftp") ||
+            scheme.LowerCaseEqualsLiteral("file")) {
+            // Do nothing.
+        } else if (scheme.LowerCaseEqualsLiteral("ttp")) {
+            // ttp -> http.
+            uriString.Replace(0, 3, "http");
+            scheme.AssignLiteral("http");
+        } else if (scheme.LowerCaseEqualsLiteral("ttps")) {
+            // ttps -> https.
+            uriString.Replace(0, 4, "https");
+            scheme.AssignLiteral("https");
+        } else if (scheme.LowerCaseEqualsLiteral("tps")) {
+            // tps -> https.
+            uriString.Replace(0, 3, "https");
+            scheme.AssignLiteral("https");
+        } else if (scheme.LowerCaseEqualsLiteral("ps")) {
+            // ps -> https.
+            uriString.Replace(0, 2, "https");
+            scheme.AssignLiteral("https");
+        } else if (scheme.LowerCaseEqualsLiteral("ile")) {
+            // ile -> file.
+            uriString.Replace(0, 3, "file");
+            scheme.AssignLiteral("file");
+        } else if (scheme.LowerCaseEqualsLiteral("le")) {
+            // le -> file.
+            uriString.Replace(0, 2, "file");
+            scheme.AssignLiteral("file");
+        }
+    }
+
     // Now we need to check whether "scheme" is something we don't
     // really know about.
     nsCOMPtr<nsIProtocolHandler> ourHandler, extHandler;
     
     ioService->GetProtocolHandler(scheme.get(), getter_AddRefs(ourHandler));
     extHandler = do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX"default");
     
     if (ourHandler != extHandler || !PossiblyHostPortUrl(uriString)) {
--- a/docshell/base/nsIURIFixup.idl
+++ b/docshell/base/nsIURIFixup.idl
@@ -7,17 +7,17 @@
 #include "nsISupports.idl"
 
 interface nsIURI;
 interface nsIInputStream;
 
 /**
  * Interface implemented by objects capable of fixing up strings into URIs
  */
-[scriptable, uuid(552a23bb-c1b2-426e-a801-d346c6a98f1d)]
+[scriptable, uuid(731877f8-973b-414c-b772-9ca1f3fffb7e)]
 interface nsIURIFixup : nsISupports
 {
     /** No fixup flags. */
     const unsigned long FIXUP_FLAG_NONE = 0;
 
     /**
      * Allow the fixup to use a keyword lookup service to complete the URI.
      * The fixup object implementer should honour this flag and only perform
@@ -32,16 +32,21 @@ interface nsIURIFixup : nsISupports
     const unsigned long FIXUP_FLAGS_MAKE_ALTERNATE_URI = 2;
 
     /**
      * Use UTF-8 to encode the URI instead of platform charset.
      */
     const unsigned long FIXUP_FLAG_USE_UTF8 = 4;
 
     /**
+     * Fix common scheme typos.
+     */
+    const unsigned long FIXUP_FLAG_FIX_SCHEME_TYPOS = 8;
+
+    /**
      * Converts an internal URI (e.g. a wyciwyg URI) into one which we can
      * expose to the user, for example on the URL bar.
      *
      * @param  aURI       The URI to be converted
      * @return nsIURI     The converted, exposable URI
      * @throws NS_ERROR_MALFORMED_URI when the exposable portion of aURI is malformed
      * @throws NS_ERROR_UNKNOWN_PROTOCOL when we can't get a protocol handler service
      *         for the URI scheme.
new file mode 100644
--- /dev/null
+++ b/docshell/test/unit/test_nsDefaultURIFixup.js
@@ -0,0 +1,93 @@
+let urifixup = Cc["@mozilla.org/docshell/urifixup;1"].
+               getService(Ci.nsIURIFixup);
+let prefs = Cc["@mozilla.org/preferences-service;1"].
+            getService(Ci.nsIPrefBranch);
+
+let pref = "browser.fixup.typo.scheme";
+
+let data = [
+  {
+    // ttp -> http.
+    wrong: 'ttp://www.example.com/',
+    fixed: 'http://www.example.com/',
+  },
+  {
+    // ttps -> https.
+    wrong: 'ttps://www.example.com/',
+    fixed: 'https://www.example.com/',
+  },
+  {
+    // tps -> https.
+    wrong: 'tps://www.example.com/',
+    fixed: 'https://www.example.com/',
+  },
+  {
+    // ps -> https.
+    wrong: 'ps://www.example.com/',
+    fixed: 'https://www.example.com/',
+  },
+  {
+    // ile -> file.
+    wrong: 'ile:///this/is/a/test.html',
+    fixed: 'file:///this/is/a/test.html',
+  },
+  {
+    // le -> file.
+    wrong: 'le:///this/is/a/test.html',
+    fixed: 'file:///this/is/a/test.html',
+  },
+  {
+    // Valid should not be changed.
+    wrong: 'https://example.com/this/is/a/test.html',
+    fixed: 'https://example.com/this/is/a/test.html',
+  },
+  {
+    // Unmatched should not be changed.
+    wrong: 'whatever://this/is/a/test.html',
+    fixed: 'whatever://this/is/a/test.html',
+  },
+];
+
+let len = data.length;
+
+function run_test() {
+  run_next_test();
+}
+
+// Make sure we fix what needs fixing when there is no pref set.
+add_task(function test_unset_pref_fixes_typos() {
+  prefs.clearUserPref(pref);
+  for (let i = 0; i < len; ++i) {
+    let item = data[i];
+    let result =
+      urifixup.createFixupURI(item.wrong,
+                              urifixup.FIXUP_FLAG_FIX_SCHEME_TYPOS).spec;
+    do_check_eq(result, item.fixed);
+  }
+});
+  
+// Make sure we don't do anything when the pref is explicitly
+// set to false.
+add_task(function test_false_pref_keeps_typos() {
+  prefs.setBoolPref(pref, false);
+  for (let i = 0; i < len; ++i) {
+    let item = data[i];
+    let result =
+      urifixup.createFixupURI(item.wrong,
+                              urifixup.FIXUP_FLAG_FIX_SCHEME_TYPOS).spec;
+    do_check_eq(result, item.wrong);
+  }
+});
+
+// Finally, make sure we still fix what needs fixing if the pref is
+// explicitly set to true.
+add_task(function test_true_pref_fixes_typos() {
+  prefs.setBoolPref(pref, true);
+  for (let i = 0; i < len; ++i) {
+    let item = data[i];
+    let result =
+        urifixup.createFixupURI(item.wrong,
+                                urifixup.FIXUP_FLAG_FIX_SCHEME_TYPOS).spec;
+    do_check_eq(result, item.fixed);
+  }
+});
--- a/docshell/test/unit/xpcshell.ini
+++ b/docshell/test/unit/xpcshell.ini
@@ -1,11 +1,12 @@
 [DEFAULT]
 head = head_docshell.js
 tail = 
 
 [test_bug414201_jfif.js]
 [test_bug442584.js]
+[test_nsDefaultURIFixup.js]
 [test_nsIDownloadHistory.js]
 [test_pb_notification.js]
 # Bug 751575: unrelated JS changes cause timeouts on random platforms
 skip-if = true
 [test_privacy_transition.js]
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -964,16 +964,24 @@ pref("network.protocol-handler.external.
 #ifdef XP_MACOSX
 pref("network.protocol-handler.external.help", false);
 #endif
 pref("network.protocol-handler.external.disk", false);
 pref("network.protocol-handler.external.disks", false);
 pref("network.protocol-handler.external.afp", false);
 pref("network.protocol-handler.external.moz-icon", false);
 
+// Don't allow  external protocol handlers for common typos
+pref("network.protocol-handler.external.ttp", false);  // http
+pref("network.protocol-handler.external.ttps", false); // https
+pref("network.protocol-handler.external.tps", false);  // https
+pref("network.protocol-handler.external.ps", false);   // https
+pref("network.protocol-handler.external.ile", false);  // file
+pref("network.protocol-handler.external.le", false);   // file
+
 // An exposed protocol handler is one that can be used in all contexts.  A
 // non-exposed protocol handler is one that can only be used internally by the
 // application.  For example, a non-exposed protocol would not be loaded by the
 // application in response to a link click or a X-remote openURL command.
 // Instead, it would be deferred to the system's external protocol handler.
 // Only internal/built-in protocol handlers can be marked as exposed.
 
 // This pref controls the default settings.  Per protocol settings can be used