Bug 652186 - Implement URL Standard's backslash replacement r?mcmanus draft
authorValentin Gosu <valentin.gosu@gmail.com>
Thu, 03 Mar 2016 15:50:16 +0100
changeset 336518 83a62ad1797c4140a903c4dff5493441612e6f8c
parent 336513 758fcef0c6a18aa306c090a5e0a702d92a6da17c
child 336519 ef1f7da92c12e3fe5c55ab8adc83caddba46522c
push id12097
push uservalentin.gosu@gmail.com
push dateThu, 03 Mar 2016 14:55:06 +0000
reviewersmcmanus
bugs652186
milestone47.0a1
Bug 652186 - Implement URL Standard's backslash replacement r?mcmanus * * * [mq]: test MozReview-Commit-ID: JAZ36DstjFs
netwerk/base/nsStandardURL.cpp
netwerk/test/unit/test_standardurl.js
--- a/netwerk/base/nsStandardURL.cpp
+++ b/netwerk/base/nsStandardURL.cpp
@@ -1182,16 +1182,39 @@ nsStandardURL::GetOriginCharset(nsACStri
 {
     if (mOriginCharset.IsEmpty())
         result.AssignLiteral("UTF-8");
     else
         result = mOriginCharset;
     return NS_OK;
 }
 
+static bool
+IsSpecialProtocol(const nsACString &input)
+{
+    nsACString::const_iterator start, end;
+    input.BeginReading(start);
+    nsACString::const_iterator iterator(start);
+    input.EndReading(end);
+
+    while (iterator != end && *iterator != ':') {
+        iterator++;
+    }
+
+    nsAutoCString protocol(nsDependentCSubstring(start.get(), iterator.get()));
+
+    return protocol.LowerCaseEqualsLiteral("http") ||
+           protocol.LowerCaseEqualsLiteral("https") ||
+           protocol.LowerCaseEqualsLiteral("ftp") ||
+           protocol.LowerCaseEqualsLiteral("ws") ||
+           protocol.LowerCaseEqualsLiteral("wss") ||
+           protocol.LowerCaseEqualsLiteral("file") ||
+           protocol.LowerCaseEqualsLiteral("gopher");
+}
+
 NS_IMETHODIMP
 nsStandardURL::SetSpec(const nsACString &input)
 {
     ENSURE_MUTABLE();
 
     const nsPromiseFlatCString &flat = PromiseFlatCString(input);
     const char *spec = flat.get();
     int32_t specLength = flat.Length();
@@ -1212,22 +1235,45 @@ nsStandardURL::SetSpec(const nsACString 
     }
 
     // Make a backup of the curent URL
     nsStandardURL prevURL(false,false);
     prevURL.CopyMembers(this, eHonorRef);
     Clear();
 
     // filter out unexpected chars "\r\n\t" if necessary
-    nsAutoCString buf1;
-    if (net_FilterURIString(spec, buf1)) {
-        spec = buf1.get();
-        specLength = buf1.Length();
+    nsAutoCString filteredURI;
+    if (!net_FilterURIString(spec, filteredURI)) {
+        // Copy the content into filteredURI even if no whitespace was stripped.
+        // We need a non-const buffer to perform backslash replacement.
+        filteredURI = input;
     }
 
+    if (IsSpecialProtocol(filteredURI)) {
+        // Bug 652186: Replace all backslashes with slashes when parsing paths
+        // Stop when we reach the query or the hash.
+        nsAutoCString::iterator start;
+        nsAutoCString::iterator end;
+        filteredURI.BeginWriting(start);
+        filteredURI.EndWriting(end);
+        while (start != end) {
+            if (*start == '?' || *start == '#') {
+                break;
+            }
+            if (*start == '\\') {
+                *start = '/';
+            }
+            start++;
+        }
+    }
+
+    spec = filteredURI.get();
+    specLength = filteredURI.Length();
+
+
     // parse the given URL...
     nsresult rv = ParseURL(spec, specLength);
     if (NS_SUCCEEDED(rv)) {
         // finally, use the URLSegment member variables to build a normalized
         // copy of |spec|
         rv = BuildNormalizedSpec(spec);
     }
 
@@ -1999,22 +2045,25 @@ NS_IMETHODIMP
 nsStandardURL::Resolve(const nsACString &in, nsACString &out)
 {
     const nsPromiseFlatCString &flat = PromiseFlatCString(in);
     const char *relpath = flat.get();
 
     // filter out unexpected chars "\r\n\t" if necessary
     nsAutoCString buf;
     int32_t relpathLen;
-    if (net_FilterURIString(relpath, buf)) {
-        relpath = buf.get();
-        relpathLen = buf.Length();
-    } else
-        relpathLen = flat.Length();
-    
+    if (!net_FilterURIString(relpath, buf)) {
+        // Copy the content into filteredURI even if no whitespace was stripped.
+        // We need a non-const buffer to perform backslash replacement.
+        buf = in;
+    }
+
+    relpath = buf.get();
+    relpathLen = buf.Length();
+
     char *result = nullptr;
 
     LOG(("nsStandardURL::Resolve [this=%p spec=%s relpath=%s]\n",
         this, mSpec.get(), relpath));
 
     NS_ASSERTION(mParser, "no parser: unitialized");
 
     // NOTE: there is no need for this function to produce normalized
@@ -2041,16 +2090,40 @@ nsStandardURL::Resolve(const nsACString 
                            &scheme.mPos, &scheme.mLen,
                            nullptr, nullptr,
                            nullptr, nullptr);
 
     // if the parser fails (for example because there is no valid scheme)
     // reset the scheme and assume a relative url
     if (NS_FAILED(rv)) scheme.Reset(); 
 
+    nsAutoCString protocol(Segment(scheme));
+    nsAutoCString baseProtocol(Scheme());
+
+    // We need to do backslash replacement for the following cases:
+    // 1. The input is an absolute path with a http/https/ftp scheme
+    // 2. The input is a relative path, and the base URL has a http/https/ftp scheme
+    if ((protocol.IsEmpty() && IsSpecialProtocol(baseProtocol)) ||
+         IsSpecialProtocol(protocol)) {
+
+        nsAutoCString::iterator start;
+        nsAutoCString::iterator end;
+        buf.BeginWriting(start);
+        buf.EndWriting(end);
+        while (start != end) {
+            if (*start == '?' || *start == '#') {
+                break;
+            }
+            if (*start == '\\') {
+                *start = '/';
+            }
+            start++;
+        }
+    }
+
     if (scheme.mLen >= 0) {
         // add some flags to coalesceFlag if it is an ftp-url
         // need this later on when coalescing the resulting URL
         if (SegmentIs(relpath, scheme, "ftp", true)) {
             coalesceFlag = (netCoalesceFlags) (coalesceFlag 
                                         | NET_COALESCE_ALLOW_RELATIVE_ROOT
                                         | NET_COALESCE_DOUBLE_SLASH_IS_ROOT);
 
--- a/netwerk/test/unit/test_standardurl.js
+++ b/netwerk/test/unit/test_standardurl.js
@@ -328,8 +328,21 @@ add_test(function test_pathPercentEncode
 });
 
 add_test(function test_filterWhitespace()
 {
   var url = stringToURL(" \r\n\th\nt\rt\tp://ex\r\n\tample.com/path\r\n\t/\r\n\tto the/fil\r\n\te.e\r\n\txt?que\r\n\try#ha\r\n\tsh \r\n\t ");
   do_check_eq(url.spec, "http://example.com/path/to%20the/file.ext?query#hash");
   run_next_test();
 });
+
+add_test(function test_backslashReplacement()
+{
+  var url = stringToURL("http:\\\\test.com\\path/to\\file?query\\backslash#hash\\");
+  do_check_eq(url.spec, "http://test.com/path/to/file?query\\backslash#hash\\");
+
+  url = stringToURL("http:\\\\test.com\\example.org/path\\to/file");
+  do_check_eq(url.spec, "http://test.com/example.org/path/to/file");
+  do_check_eq(url.host, "test.com");
+  do_check_eq(url.path, "/example.org/path/to/file");
+
+  run_next_test();
+});