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
Treeherderresults
reviewersbz
bugs935377
milestone28.0a1
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