Percent-encode illegal characters in Android page info popup URL

This prevents whitespace and non-ASCII characters from being displayed in
the url, stopping attacks where a carefully crafted URL can be used
to display a message in the popup.

BUG=466351

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

Cr-Commit-Position: refs/heads/master@{#322296}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/WebsiteSettingsPopup.java b/chrome/android/java/src/org/chromium/chrome/browser/WebsiteSettingsPopup.java
index 6b6d07a..8f456d0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/WebsiteSettingsPopup.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/WebsiteSettingsPopup.java
@@ -9,6 +9,7 @@
 import android.content.DialogInterface;
 import android.graphics.Color;
 import android.graphics.drawable.ColorDrawable;
+import android.net.Uri;
 import android.text.Layout;
 import android.text.Spannable;
 import android.text.SpannableStringBuilder;
@@ -164,6 +165,10 @@
 
     private static final int MAX_TABLET_DIALOG_WIDTH_DP = 400;
 
+    private static final char FIRST_UNICODE_WHITESPACE = '\u2000';
+    private static final char FINAL_UNICODE_WHITESPACE = '\u200F';
+    private static final char UNICODE_NBSP = '\u00A0';
+
     private final Context mContext;
     private final Profile mProfile;
     private final WebContents mWebContents;
@@ -289,7 +294,8 @@
         }
         mSecurityLevel = ToolbarModel.getSecurityLevelForWebContents(mWebContents);
 
-        SpannableStringBuilder urlBuilder = new SpannableStringBuilder(mFullUrl);
+        String displayUrl = prepareUrlForDisplay(mFullUrl);
+        SpannableStringBuilder urlBuilder = new SpannableStringBuilder(displayUrl);
         OmniboxUrlEmphasizer.emphasizeUrl(urlBuilder, mContext.getResources(), mProfile,
                 mSecurityLevel, mIsInternalPage, true);
         mUrlTitle.setText(urlBuilder);
@@ -300,6 +306,26 @@
     }
 
     /**
+     * Percent-encodes suspicious Unicode whitespace characters in a URL so that it can be safely
+     * displayed.
+     */
+    public static String prepareUrlForDisplay(String urlStr) {
+        StringBuilder urlBuilder = new StringBuilder();
+        for (int i = 0; i < urlStr.length(); i++) {
+            char c = urlStr.charAt(i);
+            if ((c >= FIRST_UNICODE_WHITESPACE
+                        && c <= FINAL_UNICODE_WHITESPACE)
+                    || c == ' '
+                    || c == UNICODE_NBSP) {
+                urlBuilder.append(Uri.encode(Character.toString(c)));
+            } else {
+                urlBuilder.append(c);
+            }
+        }
+        return urlBuilder.toString();
+    }
+
+    /**
      * Sets the visibility of the lower area of the dialog (containing the permissions and 'Site
      * Settings' button).
      *
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/WebsiteSettingsPopupTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/WebsiteSettingsPopupTest.java
new file mode 100644
index 0000000..e1f605b
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/WebsiteSettingsPopupTest.java
@@ -0,0 +1,40 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for WebsiteSettingsPopup
+ */
+public class WebsiteSettingsPopupTest extends TestCase {
+    @SmallTest
+    public void testPrepareUrlForDisplay() {
+        assertEquals("Encode suspicious message",
+                WebsiteSettingsPopup.prepareUrlForDisplay(
+                        "http://example.com/#  WARNING  \u00A0Chrome has detected malware on your"
+                        + " device!"),
+                "http://example.com/#%20%20WARNING%20%20%C2%A0Chrome%20has%20detected%20malware%20"
+                        + "on%20your%20device!");
+        assertEquals("Do not encode valid Unicode fragment",
+                WebsiteSettingsPopup.prepareUrlForDisplay("http://example.com/#Düsseldorf"),
+                "http://example.com/#Düsseldorf");
+        assertEquals("Encode fragment with spaces",
+                WebsiteSettingsPopup.prepareUrlForDisplay("http://example.com/#hi how are you"),
+                "http://example.com/#hi%20how%20are%20you");
+        assertEquals("Encode fragment with Unicode whitespace",
+                WebsiteSettingsPopup.prepareUrlForDisplay("http://example.com/#em\u2003space"),
+                "http://example.com/#em%E2%80%83space");
+        assertEquals("Do not encode reserved URI characters or valid Unicode",
+                WebsiteSettingsPopup.prepareUrlForDisplay("http://example.com/?q=a#Düsseldorf,"
+                        + " Germany"),
+                "http://example.com/?q=a#Düsseldorf,%20Germany");
+        assertEquals("Preserve characters from supplementary Unicode planes",
+                WebsiteSettingsPopup.prepareUrlForDisplay("http://example.com/#\uD835\uDC9Cstral"),
+                "http://example.com/#\uD835\uDC9Cstral");
+    }
+}