FileURLToFilePath:  Don't unescape '/' and '\\'.

GURL leaves these escaped, and unescaping them in paths changes the
meaning of the path.

Added two values to the UnescapeRule enumeration:
PATH_SEPARATORS and URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS.

In followup CLs, I intend to replace all uses of URL_SPECIAL_CHARS,
in favor of one or both the two new values, and eventually remove
the value, as it's easily to use in an unsafe manner.

BUG=586657

Review URL: https://codereview.chromium.org/1704163003

Cr-Commit-Position: refs/heads/master@{#377013}
diff --git a/net/base/escape.cc b/net/base/escape.cc
index 15de5e1..fc7d5cf0 100644
--- a/net/base/escape.cc
+++ b/net/base/escape.cc
@@ -290,7 +290,10 @@
            (first_byte == ' ' && (rules & UnescapeRule::SPACES)) ||
            // Allow any of the prohibited but non-control characters when
            // we're doing "special" chars.
-           (first_byte > ' ' && (rules & UnescapeRule::URL_SPECIAL_CHARS)) ||
+           ((first_byte == '/' || first_byte == '\\') &&
+            (rules & UnescapeRule::PATH_SEPARATORS)) ||
+           (first_byte > ' ' && first_byte != '/' && first_byte != '\\' &&
+            (rules & UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS)) ||
            // Additionally allow non-display characters if requested.
            (first_byte < ' ' &&
             (rules & UnescapeRule::SPOOFING_AND_CONTROL_CHARS)))) {
diff --git a/net/base/escape.h b/net/base/escape.h
index c31dcf9..759631a 100644
--- a/net/base/escape.h
+++ b/net/base/escape.h
@@ -78,20 +78,36 @@
     // just the absence of them). All other unescape rules imply "normal" in
     // addition to their special meaning. Things like escaped letters, digits,
     // and most symbols will get unescaped with this mode.
-    NORMAL = 1,
+    NORMAL = 1 << 0,
 
     // Convert %20 to spaces. In some places where we're showing URLs, we may
     // want this. In places where the URL may be copied and pasted out, then
     // you wouldn't want this since it might not be interpreted in one piece
     // by other applications.
-    SPACES = 2,
+    SPACES = 1 << 1,
+
+    // Unescapes '/' and '\\'. If these characters were unescaped, the resulting
+    // URL won't be the same as the source one. Moreover, they are dangerous to
+    // unescape in strings that will be used as file paths or names. This value
+    // should only be used when slashes don't have special meaning, like data
+    // URLs.
+    PATH_SEPARATORS = 1 << 2,
 
     // Unescapes various characters that will change the meaning of URLs,
-    // including '%', '+', '&', '/', '#'. If we unescaped these characters, the
-    // resulting URL won't be the same as the source one. This flag is used when
-    // generating final output like filenames for URLs where we won't be
-    // interpreting as a URL and want to do as much unescaping as possible.
-    URL_SPECIAL_CHARS = 4,
+    // including '%', '+', '&', '#'. Does not unescape path separators.
+    // If these characters were unescaped, the resulting URL won't be the same
+    // as the source one. This flag is used when generating final output like
+    // filenames for URLs where we won't be interpreting as a URL and want to do
+    // as much unescaping as possible.
+    URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS = 1 << 3,
+
+    // A combination of URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS and
+    // PATH_SEPARATORS. Warning about the use of PATH_SEPARATORS also apply
+    // here.
+    // TODO(mmenke):  Audit all uses of this and replace with the above values,
+    // as needed.
+    URL_SPECIAL_CHARS =
+        PATH_SEPARATORS | URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS,
 
     // Unescapes characters that can be used in spoofing attempts (such as LOCK)
     // and control characters (such as BiDi control characters and %01).  This
@@ -100,10 +116,10 @@
     //
     // DO NOT use SPOOFING_AND_CONTROL_CHARS if the URL is going to be displayed
     // in the UI for security reasons.
-    SPOOFING_AND_CONTROL_CHARS = 8,
+    SPOOFING_AND_CONTROL_CHARS = 1 << 4,
 
     // URL queries use "+" for space. This flag controls that replacement.
-    REPLACE_PLUS_WITH_SPACE = 16,
+    REPLACE_PLUS_WITH_SPACE = 1 << 5,
   };
 };
 
diff --git a/net/base/escape_unittest.cc b/net/base/escape_unittest.cc
index cec7f32..b828d80b 100644
--- a/net/base/escape_unittest.cc
+++ b/net/base/escape_unittest.cc
@@ -150,43 +150,55 @@
 
 TEST(EscapeTest, UnescapeURLComponentASCII) {
   const UnescapeURLCaseASCII unescape_cases[] = {
-    {"", UnescapeRule::NORMAL, ""},
-    {"%2", UnescapeRule::NORMAL, "%2"},
-    {"%%%%%%", UnescapeRule::NORMAL, "%%%%%%"},
-    {"Don't escape anything", UnescapeRule::NORMAL, "Don't escape anything"},
-    {"Invalid %escape %2", UnescapeRule::NORMAL, "Invalid %escape %2"},
-    {"Some%20random text %25%2dOK", UnescapeRule::NONE,
-     "Some%20random text %25%2dOK"},
-    {"Some%20random text %25%2dOK", UnescapeRule::NORMAL,
-     "Some%20random text %25-OK"},
-    {"Some%20random text %25%2dOK", UnescapeRule::SPACES,
-     "Some random text %25-OK"},
-    {"Some%20random text %25%2dOK", UnescapeRule::URL_SPECIAL_CHARS,
-     "Some%20random text %-OK"},
-    {"Some%20random text %25%2dOK",
-     UnescapeRule::SPACES | UnescapeRule::URL_SPECIAL_CHARS,
-     "Some random text %-OK"},
-    {"%A0%B1%C2%D3%E4%F5", UnescapeRule::NORMAL, "\xA0\xB1\xC2\xD3\xE4\xF5"},
-    {"%Aa%Bb%Cc%Dd%Ee%Ff", UnescapeRule::NORMAL, "\xAa\xBb\xCc\xDd\xEe\xFf"},
-    // Certain URL-sensitive characters should not be unescaped unless asked.
-    {"Hello%20%13%10world %23# %3F? %3D= %26& %25% %2B+", UnescapeRule::SPACES,
-     "Hello %13%10world %23# %3F? %3D= %26& %25% %2B+"},
-    {"Hello%20%13%10world %23# %3F? %3D= %26& %25% %2B+",
-     UnescapeRule::URL_SPECIAL_CHARS,
-     "Hello%20%13%10world ## ?? == && %% ++"},
-    // We can neither escape nor unescape '@' since some websites expect it to
-    // be preserved as either '@' or "%40".
-    // See http://b/996720 and http://crbug.com/23933 .
-    {"me@my%40example", UnescapeRule::NORMAL, "me@my%40example"},
-    // Control characters.
-    {"%01%02%03%04%05%06%07%08%09 %25", UnescapeRule::URL_SPECIAL_CHARS,
-     "%01%02%03%04%05%06%07%08%09 %"},
-    {"%01%02%03%04%05%06%07%08%09 %25",
-     UnescapeRule::SPOOFING_AND_CONTROL_CHARS,
-     "\x01\x02\x03\x04\x05\x06\x07\x08\x09 %25"},
-    {"Hello%20%13%10%02", UnescapeRule::SPACES, "Hello %13%10%02"},
-    {"Hello%20%13%10%02", UnescapeRule::SPOOFING_AND_CONTROL_CHARS,
-     "Hello%20\x13\x10\x02"},
+      {"", UnescapeRule::NORMAL, ""},
+      {"%2", UnescapeRule::NORMAL, "%2"},
+      {"%%%%%%", UnescapeRule::NORMAL, "%%%%%%"},
+      {"Don't escape anything", UnescapeRule::NORMAL, "Don't escape anything"},
+      {"Invalid %escape %2", UnescapeRule::NORMAL, "Invalid %escape %2"},
+      {"Some%20random text %25%2dOK", UnescapeRule::NONE,
+       "Some%20random text %25%2dOK"},
+      {"Some%20random text %25%2dOK", UnescapeRule::NORMAL,
+       "Some%20random text %25-OK"},
+      {"Some%20random text %25%2dOK", UnescapeRule::SPACES,
+       "Some random text %25-OK"},
+      {"Some%20random text %25%2dOK", UnescapeRule::PATH_SEPARATORS,
+       "Some%20random text %25-OK"},
+      {"Some%20random text %25%2dOK",
+       UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS,
+       "Some%20random text %-OK"},
+      {"Some%20random text %25%2dOK",
+       UnescapeRule::SPACES |
+           UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS,
+       "Some random text %-OK"},
+      {"%A0%B1%C2%D3%E4%F5", UnescapeRule::NORMAL, "\xA0\xB1\xC2\xD3\xE4\xF5"},
+      {"%Aa%Bb%Cc%Dd%Ee%Ff", UnescapeRule::NORMAL, "\xAa\xBb\xCc\xDd\xEe\xFf"},
+      // Certain URL-sensitive characters should not be unescaped unless asked.
+      {"Hello%20%13%10world %23# %3F? %3D= %26& %25% %2B+",
+       UnescapeRule::SPACES, "Hello %13%10world %23# %3F? %3D= %26& %25% %2B+"},
+      {"Hello%20%13%10world %23# %3F? %3D= %26& %25% %2B+",
+       UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS,
+       "Hello%20%13%10world ## ?? == && %% ++"},
+      // We can neither escape nor unescape '@' since some websites expect it to
+      // be preserved as either '@' or "%40".
+      // See http://b/996720 and http://crbug.com/23933 .
+      {"me@my%40example", UnescapeRule::NORMAL, "me@my%40example"},
+      // Control characters.
+      {"%01%02%03%04%05%06%07%08%09 %25",
+       UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS,
+       "%01%02%03%04%05%06%07%08%09 %"},
+      {"%01%02%03%04%05%06%07%08%09 %25",
+       UnescapeRule::SPOOFING_AND_CONTROL_CHARS,
+       "\x01\x02\x03\x04\x05\x06\x07\x08\x09 %25"},
+      {"Hello%20%13%10%02", UnescapeRule::SPACES, "Hello %13%10%02"},
+      {"Hello%20%13%10%02", UnescapeRule::SPOOFING_AND_CONTROL_CHARS,
+       "Hello%20\x13\x10\x02"},
+
+      // '/' and '\\' should only be unescaped by PATH_SEPARATORS.
+      {"%2F%5C", UnescapeRule::PATH_SEPARATORS, "/\\"},
+      {"%2F%5C", UnescapeRule::SPACES |
+                     UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS |
+                     UnescapeRule::SPOOFING_AND_CONTROL_CHARS,
+       "%2F%5C"},
   };
 
   for (size_t i = 0; i < arraysize(unescape_cases); i++) {
@@ -218,126 +230,135 @@
 
 TEST(EscapeTest, UnescapeURLComponent) {
   const UnescapeURLCase unescape_cases[] = {
-    {L"", UnescapeRule::NORMAL, L""},
-    {L"%2", UnescapeRule::NORMAL, L"%2"},
-    {L"%%%%%%", UnescapeRule::NORMAL, L"%%%%%%"},
-    {L"Don't escape anything", UnescapeRule::NORMAL, L"Don't escape anything"},
-    {L"Invalid %escape %2", UnescapeRule::NORMAL, L"Invalid %escape %2"},
-    {L"Some%20random text %25%2dOK", UnescapeRule::NONE,
-     L"Some%20random text %25%2dOK"},
-    {L"Some%20random text %25%2dOK", UnescapeRule::NORMAL,
-     L"Some%20random text %25-OK"},
-    {L"Some%20random text %25%E2%80", UnescapeRule::NORMAL,
-     L"Some%20random text %25\xE2\x80"},
-    {L"Some%20random text %25%E2%80OK", UnescapeRule::NORMAL,
-     L"Some%20random text %25\xE2\x80OK"},
-    {L"Some%20random text %25%E2%80%84OK", UnescapeRule::NORMAL,
-     L"Some%20random text %25\xE2\x80\x84OK"},
+      {L"", UnescapeRule::NORMAL, L""},
+      {L"%2", UnescapeRule::NORMAL, L"%2"},
+      {L"%%%%%%", UnescapeRule::NORMAL, L"%%%%%%"},
+      {L"Don't escape anything", UnescapeRule::NORMAL,
+       L"Don't escape anything"},
+      {L"Invalid %escape %2", UnescapeRule::NORMAL, L"Invalid %escape %2"},
+      {L"Some%20random text %25%2dOK", UnescapeRule::NONE,
+       L"Some%20random text %25%2dOK"},
+      {L"Some%20random text %25%2dOK", UnescapeRule::NORMAL,
+       L"Some%20random text %25-OK"},
+      {L"Some%20random text %25%E2%80", UnescapeRule::NORMAL,
+       L"Some%20random text %25\xE2\x80"},
+      {L"Some%20random text %25%E2%80OK", UnescapeRule::NORMAL,
+       L"Some%20random text %25\xE2\x80OK"},
+      {L"Some%20random text %25%E2%80%84OK", UnescapeRule::NORMAL,
+       L"Some%20random text %25\xE2\x80\x84OK"},
 
-    // BiDi Control characters should not be unescaped unless explicity told to
-    // do so with UnescapeRule::SPOOFING_AND_CONTROL_CHARS
-    {L"Some%20random text %25%D8%9COK", UnescapeRule::NORMAL,
-     L"Some%20random text %25%D8%9COK"},
-    {L"Some%20random text %25%E2%80%8EOK", UnescapeRule::NORMAL,
-     L"Some%20random text %25%E2%80%8EOK"},
-    {L"Some%20random text %25%E2%80%8FOK", UnescapeRule::NORMAL,
-     L"Some%20random text %25%E2%80%8FOK"},
-    {L"Some%20random text %25%E2%80%AAOK", UnescapeRule::NORMAL,
-     L"Some%20random text %25%E2%80%AAOK"},
-    {L"Some%20random text %25%E2%80%ABOK", UnescapeRule::NORMAL,
-     L"Some%20random text %25%E2%80%ABOK"},
-    {L"Some%20random text %25%E2%80%AEOK", UnescapeRule::NORMAL,
-     L"Some%20random text %25%E2%80%AEOK"},
-    {L"Some%20random text %25%E2%81%A6OK", UnescapeRule::NORMAL,
-     L"Some%20random text %25%E2%81%A6OK"},
-    {L"Some%20random text %25%E2%81%A9OK", UnescapeRule::NORMAL,
-     L"Some%20random text %25%E2%81%A9OK"},
-    // UnescapeRule::SPOOFING_AND_CONTROL_CHARS should unescape BiDi Control
-    // characters.
-    {L"Some%20random text %25%D8%9COK",
-     UnescapeRule::NORMAL | UnescapeRule::SPOOFING_AND_CONTROL_CHARS,
-     L"Some%20random text %25\xD8\x9COK"},
-    {L"Some%20random text %25%E2%80%8EOK",
-     UnescapeRule::NORMAL | UnescapeRule::SPOOFING_AND_CONTROL_CHARS,
-     L"Some%20random text %25\xE2\x80\x8EOK"},
-    {L"Some%20random text %25%E2%80%8FOK",
-     UnescapeRule::NORMAL | UnescapeRule::SPOOFING_AND_CONTROL_CHARS,
-     L"Some%20random text %25\xE2\x80\x8FOK"},
-    {L"Some%20random text %25%E2%80%AAOK",
-     UnescapeRule::NORMAL | UnescapeRule::SPOOFING_AND_CONTROL_CHARS,
-     L"Some%20random text %25\xE2\x80\xAAOK"},
-    {L"Some%20random text %25%E2%80%ABOK",
-     UnescapeRule::NORMAL | UnescapeRule::SPOOFING_AND_CONTROL_CHARS,
-     L"Some%20random text %25\xE2\x80\xABOK"},
-    {L"Some%20random text %25%E2%80%AEOK",
-     UnescapeRule::NORMAL | UnescapeRule::SPOOFING_AND_CONTROL_CHARS,
-     L"Some%20random text %25\xE2\x80\xAEOK"},
-    {L"Some%20random text %25%E2%81%A6OK",
-     UnescapeRule::NORMAL | UnescapeRule::SPOOFING_AND_CONTROL_CHARS,
-     L"Some%20random text %25\xE2\x81\xA6OK"},
-    {L"Some%20random text %25%E2%81%A9OK",
-     UnescapeRule::NORMAL | UnescapeRule::SPOOFING_AND_CONTROL_CHARS,
-     L"Some%20random text %25\xE2\x81\xA9OK"},
+      // BiDi Control characters should not be unescaped unless explicity told
+      // to
+      // do so with UnescapeRule::SPOOFING_AND_CONTROL_CHARS
+      {L"Some%20random text %25%D8%9COK", UnescapeRule::NORMAL,
+       L"Some%20random text %25%D8%9COK"},
+      {L"Some%20random text %25%E2%80%8EOK", UnescapeRule::NORMAL,
+       L"Some%20random text %25%E2%80%8EOK"},
+      {L"Some%20random text %25%E2%80%8FOK", UnescapeRule::NORMAL,
+       L"Some%20random text %25%E2%80%8FOK"},
+      {L"Some%20random text %25%E2%80%AAOK", UnescapeRule::NORMAL,
+       L"Some%20random text %25%E2%80%AAOK"},
+      {L"Some%20random text %25%E2%80%ABOK", UnescapeRule::NORMAL,
+       L"Some%20random text %25%E2%80%ABOK"},
+      {L"Some%20random text %25%E2%80%AEOK", UnescapeRule::NORMAL,
+       L"Some%20random text %25%E2%80%AEOK"},
+      {L"Some%20random text %25%E2%81%A6OK", UnescapeRule::NORMAL,
+       L"Some%20random text %25%E2%81%A6OK"},
+      {L"Some%20random text %25%E2%81%A9OK", UnescapeRule::NORMAL,
+       L"Some%20random text %25%E2%81%A9OK"},
+      // UnescapeRule::SPOOFING_AND_CONTROL_CHARS should unescape BiDi Control
+      // characters.
+      {L"Some%20random text %25%D8%9COK",
+       UnescapeRule::NORMAL | UnescapeRule::SPOOFING_AND_CONTROL_CHARS,
+       L"Some%20random text %25\xD8\x9COK"},
+      {L"Some%20random text %25%E2%80%8EOK",
+       UnescapeRule::NORMAL | UnescapeRule::SPOOFING_AND_CONTROL_CHARS,
+       L"Some%20random text %25\xE2\x80\x8EOK"},
+      {L"Some%20random text %25%E2%80%8FOK",
+       UnescapeRule::NORMAL | UnescapeRule::SPOOFING_AND_CONTROL_CHARS,
+       L"Some%20random text %25\xE2\x80\x8FOK"},
+      {L"Some%20random text %25%E2%80%AAOK",
+       UnescapeRule::NORMAL | UnescapeRule::SPOOFING_AND_CONTROL_CHARS,
+       L"Some%20random text %25\xE2\x80\xAAOK"},
+      {L"Some%20random text %25%E2%80%ABOK",
+       UnescapeRule::NORMAL | UnescapeRule::SPOOFING_AND_CONTROL_CHARS,
+       L"Some%20random text %25\xE2\x80\xABOK"},
+      {L"Some%20random text %25%E2%80%AEOK",
+       UnescapeRule::NORMAL | UnescapeRule::SPOOFING_AND_CONTROL_CHARS,
+       L"Some%20random text %25\xE2\x80\xAEOK"},
+      {L"Some%20random text %25%E2%81%A6OK",
+       UnescapeRule::NORMAL | UnescapeRule::SPOOFING_AND_CONTROL_CHARS,
+       L"Some%20random text %25\xE2\x81\xA6OK"},
+      {L"Some%20random text %25%E2%81%A9OK",
+       UnescapeRule::NORMAL | UnescapeRule::SPOOFING_AND_CONTROL_CHARS,
+       L"Some%20random text %25\xE2\x81\xA9OK"},
 
-    // Certain banned characters should not be unescaped unless explicitly told
-    // to do so with UnescapeRule::SPOOFING_AND_CONTROL_CHARS.
-    // U+1F50F LOCK WITH INK PEN
-    {L"Some%20random text %25%F0%9F%94%8FOK", UnescapeRule::NORMAL,
-     L"Some%20random text %25%F0%9F%94%8FOK"},
-    // U+1F510 CLOSED LOCK WITH KEY
-    {L"Some%20random text %25%F0%9F%94%90OK", UnescapeRule::NORMAL,
-     L"Some%20random text %25%F0%9F%94%90OK"},
-    // U+1F512 LOCK
-    {L"Some%20random text %25%F0%9F%94%92OK", UnescapeRule::NORMAL,
-     L"Some%20random text %25%F0%9F%94%92OK"},
-    // U+1F513 OPEN LOCK
-    {L"Some%20random text %25%F0%9F%94%93OK", UnescapeRule::NORMAL,
-     L"Some%20random text %25%F0%9F%94%93OK"},
-    // UnescapeRule::SPOOFING_AND_CONTROL_CHARS should unescape banned
-    // characters.
-    {L"Some%20random text %25%F0%9F%94%8FOK",
-     UnescapeRule::NORMAL | UnescapeRule::SPOOFING_AND_CONTROL_CHARS,
-     L"Some%20random text %25\xF0\x9F\x94\x8FOK"},
-    {L"Some%20random text %25%F0%9F%94%90OK",
-     UnescapeRule::NORMAL | UnescapeRule::SPOOFING_AND_CONTROL_CHARS,
-     L"Some%20random text %25\xF0\x9F\x94\x90OK"},
-    {L"Some%20random text %25%F0%9F%94%92OK",
-     UnescapeRule::NORMAL | UnescapeRule::SPOOFING_AND_CONTROL_CHARS,
-     L"Some%20random text %25\xF0\x9F\x94\x92OK"},
-    {L"Some%20random text %25%F0%9F%94%93OK",
-     UnescapeRule::NORMAL | UnescapeRule::SPOOFING_AND_CONTROL_CHARS,
-     L"Some%20random text %25\xF0\x9F\x94\x93OK"},
+      // Certain banned characters should not be unescaped unless explicitly
+      // told
+      // to do so with UnescapeRule::SPOOFING_AND_CONTROL_CHARS.
+      // U+1F50F LOCK WITH INK PEN
+      {L"Some%20random text %25%F0%9F%94%8FOK", UnescapeRule::NORMAL,
+       L"Some%20random text %25%F0%9F%94%8FOK"},
+      // U+1F510 CLOSED LOCK WITH KEY
+      {L"Some%20random text %25%F0%9F%94%90OK", UnescapeRule::NORMAL,
+       L"Some%20random text %25%F0%9F%94%90OK"},
+      // U+1F512 LOCK
+      {L"Some%20random text %25%F0%9F%94%92OK", UnescapeRule::NORMAL,
+       L"Some%20random text %25%F0%9F%94%92OK"},
+      // U+1F513 OPEN LOCK
+      {L"Some%20random text %25%F0%9F%94%93OK", UnescapeRule::NORMAL,
+       L"Some%20random text %25%F0%9F%94%93OK"},
+      // UnescapeRule::SPOOFING_AND_CONTROL_CHARS should unescape banned
+      // characters.
+      {L"Some%20random text %25%F0%9F%94%8FOK",
+       UnescapeRule::NORMAL | UnescapeRule::SPOOFING_AND_CONTROL_CHARS,
+       L"Some%20random text %25\xF0\x9F\x94\x8FOK"},
+      {L"Some%20random text %25%F0%9F%94%90OK",
+       UnescapeRule::NORMAL | UnescapeRule::SPOOFING_AND_CONTROL_CHARS,
+       L"Some%20random text %25\xF0\x9F\x94\x90OK"},
+      {L"Some%20random text %25%F0%9F%94%92OK",
+       UnescapeRule::NORMAL | UnescapeRule::SPOOFING_AND_CONTROL_CHARS,
+       L"Some%20random text %25\xF0\x9F\x94\x92OK"},
+      {L"Some%20random text %25%F0%9F%94%93OK",
+       UnescapeRule::NORMAL | UnescapeRule::SPOOFING_AND_CONTROL_CHARS,
+       L"Some%20random text %25\xF0\x9F\x94\x93OK"},
 
-    {L"Some%20random text %25%2dOK", UnescapeRule::SPACES,
-     L"Some random text %25-OK"},
-    {L"Some%20random text %25%2dOK", UnescapeRule::URL_SPECIAL_CHARS,
-     L"Some%20random text %-OK"},
-    {L"Some%20random text %25%2dOK",
-     UnescapeRule::SPACES | UnescapeRule::URL_SPECIAL_CHARS,
-     L"Some random text %-OK"},
-    {L"%A0%B1%C2%D3%E4%F5", UnescapeRule::NORMAL, L"\xA0\xB1\xC2\xD3\xE4\xF5"},
-    {L"%Aa%Bb%Cc%Dd%Ee%Ff", UnescapeRule::NORMAL, L"\xAa\xBb\xCc\xDd\xEe\xFf"},
-    // Certain URL-sensitive characters should not be unescaped unless asked.
-    {L"Hello%20%13%10world %23# %3F? %3D= %26& %25% %2B+", UnescapeRule::SPACES,
-     L"Hello %13%10world %23# %3F? %3D= %26& %25% %2B+"},
-    {L"Hello%20%13%10world %23# %3F? %3D= %26& %25% %2B+",
-     UnescapeRule::URL_SPECIAL_CHARS,
-     L"Hello%20%13%10world ## ?? == && %% ++"},
-    // We can neither escape nor unescape '@' since some websites expect it to
-    // be preserved as either '@' or "%40".
-    // See http://b/996720 and http://crbug.com/23933 .
-    {L"me@my%40example", UnescapeRule::NORMAL, L"me@my%40example"},
-    // Control characters.
-    {L"%01%02%03%04%05%06%07%08%09 %25", UnescapeRule::URL_SPECIAL_CHARS,
-     L"%01%02%03%04%05%06%07%08%09 %"},
-    {L"%01%02%03%04%05%06%07%08%09 %25",
-     UnescapeRule::SPOOFING_AND_CONTROL_CHARS,
-     L"\x01\x02\x03\x04\x05\x06\x07\x08\x09 %25"},
-    {L"Hello%20%13%10%02", UnescapeRule::SPACES, L"Hello %13%10%02"},
-    {L"Hello%20%13%10%02", UnescapeRule::SPOOFING_AND_CONTROL_CHARS,
-     L"Hello%20\x13\x10\x02"},
-    {L"Hello\x9824\x9827", UnescapeRule::SPOOFING_AND_CONTROL_CHARS,
-     L"Hello\x9824\x9827"},
+      {L"Some%20random text %25%2dOK", UnescapeRule::SPACES,
+       L"Some random text %25-OK"},
+      {L"Some%20random text %25%2dOK",
+       UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS,
+       L"Some%20random text %-OK"},
+      {L"Some%20random text %25%2dOK",
+       UnescapeRule::SPACES |
+           UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS,
+       L"Some random text %-OK"},
+      {L"%A0%B1%C2%D3%E4%F5", UnescapeRule::NORMAL,
+       L"\xA0\xB1\xC2\xD3\xE4\xF5"},
+      {L"%Aa%Bb%Cc%Dd%Ee%Ff", UnescapeRule::NORMAL,
+       L"\xAa\xBb\xCc\xDd\xEe\xFf"},
+      // Certain URL-sensitive characters should not be unescaped unless asked.
+      {L"Hello%20%13%10world %23# %3F? %3D= %26& %25% %2B+",
+       UnescapeRule::SPACES,
+       L"Hello %13%10world %23# %3F? %3D= %26& %25% %2B+"},
+      {L"Hello%20%13%10world %23# %3F? %3D= %26& %25% %2B+",
+       UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS,
+       L"Hello%20%13%10world ## ?? == && %% ++"},
+      // We can neither escape nor unescape '@' since some websites expect it to
+      // be preserved as either '@' or "%40".
+      // See http://b/996720 and http://crbug.com/23933 .
+      {L"me@my%40example", UnescapeRule::NORMAL, L"me@my%40example"},
+      // Control characters.
+      {L"%01%02%03%04%05%06%07%08%09 %25",
+       UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS,
+       L"%01%02%03%04%05%06%07%08%09 %"},
+      {L"%01%02%03%04%05%06%07%08%09 %25",
+       UnescapeRule::SPOOFING_AND_CONTROL_CHARS,
+       L"\x01\x02\x03\x04\x05\x06\x07\x08\x09 %25"},
+      {L"Hello%20%13%10%02", UnescapeRule::SPACES, L"Hello %13%10%02"},
+      {L"Hello%20%13%10%02", UnescapeRule::SPOOFING_AND_CONTROL_CHARS,
+       L"Hello%20\x13\x10\x02"},
+      {L"Hello\x9824\x9827", UnescapeRule::SPOOFING_AND_CONTROL_CHARS,
+       L"Hello\x9824\x9827"},
   };
 
   for (size_t i = 0; i < arraysize(unescape_cases); i++) {
diff --git a/net/base/filename_util.cc b/net/base/filename_util.cc
index 0969f715..153ce43 100644
--- a/net/base/filename_util.cc
+++ b/net/base/filename_util.cc
@@ -98,7 +98,8 @@
 
   // GURL stores strings as percent-encoded 8-bit, this will undo if possible.
   path = UnescapeURLComponent(
-      path, UnescapeRule::SPACES | UnescapeRule::URL_SPECIAL_CHARS);
+      path, UnescapeRule::SPACES |
+                UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS);
 
 #if defined(OS_WIN)
   if (base::IsStringUTF8(path)) {
diff --git a/net/base/filename_util_unittest.cc b/net/base/filename_util_unittest.cc
index 5e3fb7e1..4ca7f02 100644
--- a/net/base/filename_util_unittest.cc
+++ b/net/base/filename_util_unittest.cc
@@ -239,6 +239,9 @@
     {L"\\\\foo\\bar.txt", "file:/foo/bar.txt"},
     {L"\\\\foo\\bar.txt", "file://foo\\bar.txt"},
     {L"C:\\foo\\bar.txt", "file:\\\\\\c:/foo/bar.txt"},
+    // %2f ('/') and %5c ('\\') are left alone by both GURL and
+    // FileURLToFilePath.
+    {L"C:\\foo%2f..%5cbar", "file:///C:\\foo%2f..%5cbar"},
 #elif defined(OS_POSIX)
     {L"/c:/foo/bar.txt", "file:/c:/foo/bar.txt"},
     {L"/c:/foo/bar.txt", "file:///c:/foo/bar.txt"},
@@ -253,6 +256,9 @@
     {L"/foo/bar.txt", "file:////foo////bar.txt"},
     {L"/c:/foo/bar.txt", "file:\\\\\\c:/foo/bar.txt"},
     {L"/c:/foo/bar.txt", "file:c:/foo/bar.txt"},
+    // %2f ('/') and %5c ('\\') are left alone by both GURL and
+    // FileURLToFilePath.
+    {L"/foo%2f..%5cbar", "file:///foo%2f..%5cbar"},
 //  We get these wrong because GURL turns back slashes into forward
 //  slashes.
 //  {L"/foo%5Cbar.txt", "file://foo\\bar.txt"},