Bug 1099088 - Part 1: Add CHROMEUI SiteIdentity for about: pages r=liuche draft
authorAndrzej Hunt <ahunt@mozilla.com>
Tue, 12 Jan 2016 14:49:31 -0800
changeset 321167 2d97c4a4c339672126a48c27897bc97edb7a83af
parent 321161 b0083022f6c36e2fe0f28873882a41b16fc02cfe
child 321168 65346745c857f2e6ed0aca9e21666508dc916c54
push id9334
push userahunt@mozilla.com
push dateTue, 12 Jan 2016 23:29:21 +0000
reviewersliuche
bugs1099088
milestone46.0a1
Bug 1099088 - Part 1: Add CHROMEUI SiteIdentity for about: pages r=liuche This replicates desktop behaviour.
mobile/android/base/java/org/mozilla/gecko/SiteIdentity.java
mobile/android/base/java/org/mozilla/gecko/toolbar/SiteIdentityPopup.java
mobile/android/chrome/content/browser.js
mobile/android/tests/browser/chrome/chrome.ini
mobile/android/tests/browser/chrome/test_identity_mode.html
--- a/mobile/android/base/java/org/mozilla/gecko/SiteIdentity.java
+++ b/mobile/android/base/java/org/mozilla/gecko/SiteIdentity.java
@@ -23,31 +23,32 @@ public class SiteIdentity {
     private String mVerifier;
     private String mOrigin;
 
     // The order of the items here relate to image levels in
     // site_security_level.xml
     public enum SecurityMode {
         UNKNOWN("unknown"),
         IDENTIFIED("identified"),
-        VERIFIED("verified");
+        VERIFIED("verified"),
+        CHROMEUI("chromeUI");
 
         private final String mId;
 
         private SecurityMode(String id) {
             mId = id;
         }
 
         public static SecurityMode fromString(String id) {
             if (id == null) {
                 throw new IllegalArgumentException("Can't convert null String to SiteIdentity");
             }
 
             for (SecurityMode mode : SecurityMode.values()) {
-                if (TextUtils.equals(mode.mId, id.toLowerCase())) {
+                if (TextUtils.equals(mode.mId, id)) {
                     return mode;
                 }
             }
 
             throw new IllegalArgumentException("Could not convert String id to SiteIdentity");
         }
 
         @Override
@@ -175,20 +176,20 @@ public class SiteIdentity {
                 mSecurityMode = SecurityMode.fromString(mode.getString("identity"));
             } catch (Exception e) {
                 resetIdentity();
                 return;
             }
 
             try {
                 mOrigin = identityData.getString("origin");
-                mHost = identityData.getString("host");
+                mHost = identityData.optString("host", null);
                 mOwner = identityData.optString("owner", null);
                 mSupplemental = identityData.optString("supplemental", null);
-                mVerifier = identityData.getString("verifier");
+                mVerifier = identityData.optString("verifier", null);
                 mSecure = identityData.optBoolean("secure", false);
             } catch (Exception e) {
                 resetIdentity();
             }
         } catch (Exception e) {
             reset();
         }
     }
--- a/mobile/android/base/java/org/mozilla/gecko/toolbar/SiteIdentityPopup.java
+++ b/mobile/android/base/java/org/mozilla/gecko/toolbar/SiteIdentityPopup.java
@@ -135,17 +135,18 @@ public class SiteIdentityPopup extends A
         mSiteSettingsLink = (TextView) mIdentity.findViewById(R.id.site_settings_link);
     }
 
     private void updateIdentity(final SiteIdentity siteIdentity) {
         if (!mInflated) {
             init();
         }
 
-        final boolean isIdentityKnown = (siteIdentity.getSecurityMode() != SecurityMode.UNKNOWN);
+        final boolean isIdentityKnown = (siteIdentity.getSecurityMode() == SecurityMode.IDENTIFIED ||
+                                         siteIdentity.getSecurityMode() == SecurityMode.VERIFIED);
         updateConnectionState(siteIdentity);
         toggleIdentityKnownContainerVisibility(isIdentityKnown);
 
         if (isIdentityKnown) {
             updateIdentityInformation(siteIdentity);
         }
 
         GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent(
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -6314,16 +6314,19 @@ var IdentityHandler = {
   IDENTITY_MODE_UNKNOWN: "unknown",
 
   // Domain-Validation SSL CA-signed domain verification (DV).
   IDENTITY_MODE_IDENTIFIED: "identified",
 
   // Extended-Validation SSL CA-signed identity information (EV). A more rigorous validation process.
   IDENTITY_MODE_VERIFIED: "verified",
 
+  // Part of the product's UI (built in about: pages)
+  IDENTITY_MODE_CHROMEUI: "chromeUI",
+
   // The following mixed content modes are only used if "security.mixed_content.block_active_content"
   // is enabled. Our Java frontend coalesces them into one indicator.
 
   // No mixed content information. No mixed content icon is shown.
   MIXED_MODE_UNKNOWN: "unknown",
 
   // Blocked active mixed content.
   MIXED_MODE_CONTENT_BLOCKED: "blocked",
@@ -6378,25 +6381,31 @@ var IdentityHandler = {
     result.cert = cert;
 
     return result;
   },
 
   /**
    * Determines the identity mode corresponding to the icon we show in the urlbar.
    */
-  getIdentityMode: function getIdentityMode(aState) {
+  getIdentityMode: function getIdentityMode(aState, uri) {
     if (aState & Ci.nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL) {
       return this.IDENTITY_MODE_VERIFIED;
     }
 
     if (aState & Ci.nsIWebProgressListener.STATE_IS_SECURE) {
       return this.IDENTITY_MODE_IDENTIFIED;
     }
 
+    // We also allow "about:" by allowing the selector to be empty (i.e. '(|.....|...|...)'
+    let whitelist = /^about:($|about|accounts|addons|buildconfig|cache|config|crashes|devices|downloads|fennec|firefox|feedback|healthreport|license|logins|logo|memory|mozilla|networking|plugins|privatebrowsing|rights|serviceworkers|support|telemetry|webrtc)($|\?)/i;
+    if (uri.schemeIs("about") && whitelist.test(uri.spec)) {
+        return this.IDENTITY_MODE_CHROMEUI;
+    }
+
     return this.IDENTITY_MODE_UNKNOWN;
   },
 
   getMixedDisplayMode: function getMixedDisplayMode(aState) {
     if (aState & Ci.nsIWebProgressListener.STATE_LOADED_MIXED_DISPLAY_CONTENT) {
         return this.MIXED_MODE_CONTENT_LOADED;
     }
 
@@ -6481,33 +6490,43 @@ var IdentityHandler = {
       locationObj.origin = location.origin;
     } catch (ex) {
       // Can sometimes throw if the URL being visited has no host/hostname,
       // e.g. about:blank. The _state for these pages means we won't need these
       // properties anyways, though.
     }
     this._lastLocation = locationObj;
 
-    let identityMode = this.getIdentityMode(aState);
+    let uri = aBrowser.currentURI;
+    try {
+      uri = Services.uriFixup.createExposableURI(uri);
+    } catch (e) {}
+
+    let identityMode = this.getIdentityMode(aState, uri);
     let mixedDisplay = this.getMixedDisplayMode(aState);
     let mixedActive = this.getMixedActiveMode(aState);
     let trackingMode = this.getTrackingMode(aState, aBrowser);
     let result = {
       origin: locationObj.origin,
       mode: {
         identity: identityMode,
         mixed_display: mixedDisplay,
         mixed_active: mixedActive,
         tracking: trackingMode
       }
     };
 
     // Don't show identity data for pages with an unknown identity or if any
     // mixed content is loaded (mixed display content is loaded by default).
-    if (identityMode == this.IDENTITY_MODE_UNKNOWN || aState & Ci.nsIWebProgressListener.STATE_IS_BROKEN) {
+    // We also return for CHROMEUI pages since they don't have any certificate
+    // information to load either. result.secure specifically refers to connection
+    // security, which is irrelevant for about: pages, as they're loaded locally.
+    if (identityMode == this.IDENTITY_MODE_UNKNOWN ||
+        identityMode == this.IDENTITY_MODE_CHROMEUI ||
+        aState & Ci.nsIWebProgressListener.STATE_IS_BROKEN) {
       result.secure = false;
       return result;
     }
 
     result.secure = true;
 
     result.host = this.getEffectiveHost();
 
--- a/mobile/android/tests/browser/chrome/chrome.ini
+++ b/mobile/android/tests/browser/chrome/chrome.ini
@@ -15,16 +15,17 @@ support-files =
 [test_accounts.html]
 [test_android_log.html]
 [test_app_constants.html]
 [test_debugger_server.html]
 [test_desktop_useragent.html]
 [test_device_search_engine.html]
 [test_get_last_visited.html]
 [test_home_provider.html]
+[test_identity_mode.html]
 [test_java_addons.html]
 [test_jni.html]
 [test_migrate_ui.html]
 [test_network_manager.html]
 [test_offline_page.html]
 [test_reader_view.html]
 [test_resource_substitutions.html]
 [test_restricted_profiles.html]
new file mode 100644
--- /dev/null
+++ b/mobile/android/tests/browser/chrome/test_identity_mode.html
@@ -0,0 +1,58 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1099088
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for getIdentityMode</title>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SpawnTask.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://global/skin"/>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="head.js"></script>
+  <script type="application/javascript;version=1.7">
+
+  "use strict";
+
+  const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+
+  Cu.import("resource://gre/modules/Services.jsm");
+
+  let chromeWin = Services.wm.getMostRecentWindow("navigator:browser");
+  let IdentityHandler = chromeWin.IdentityHandler;
+
+  ok(IdentityHandler.getIdentityMode(0, Services.io.newURI("about:", null, null)) == IdentityHandler.IDENTITY_MODE_CHROMEUI,
+     "'about:' is a verified internal page");
+  ok(IdentityHandler.getIdentityMode(0, Services.io.newURI("about:config", null, null)) == IdentityHandler.IDENTITY_MODE_CHROMEUI,
+     "'about:config' is a verified internal page");
+  ok(IdentityHandler.getIdentityMode(0, Services.io.newURI("about:accounts", null, null)) == IdentityHandler.IDENTITY_MODE_CHROMEUI,
+     "'about:accounts is a verified internal page");
+  ok(IdentityHandler.getIdentityMode(0, Services.io.newURI("about:addonss", null, null)) == IdentityHandler.IDENTITY_MODE_UNKNOWN,
+     "'about:addonss is not a verified internal page");
+  ok(IdentityHandler.getIdentityMode(0, Services.io.newURI("about:accountss", null, null)) == IdentityHandler.IDENTITY_MODE_UNKNOWN,
+     "'about:accountss is not a verified internal page");
+  ok(IdentityHandler.getIdentityMode(0, Services.io.newURI("about:accounts?action=signup", null, null)) == IdentityHandler.IDENTITY_MODE_CHROMEUI,
+     "'about:accounts?action=signup is a verified internal page");
+  ok(IdentityHandler.getIdentityMode(0, Services.io.newURI("about:evil_extension_page", null, null)) == IdentityHandler.IDENTITY_MODE_UNKNOWN,
+     "'about:evil_extension_page' is not a verified internal page");
+
+  ok(IdentityHandler.getIdentityMode(0, Services.io.newURI("http://mozilla.com", null, null)) == IdentityHandler.IDENTITY_MODE_UNKNOWN,
+     "http://mozilla.com is an unknown page");
+  ok(IdentityHandler.getIdentityMode(0, Services.io.newURI("https://mozilla.com", null, null)) == IdentityHandler.IDENTITY_MODE_UNKNOWN,
+     "https://mozilla.com over an insecure connection is an unknown page");
+  ok(IdentityHandler.getIdentityMode(Ci.nsIWebProgressListener.STATE_IS_SECURE, Services.io.newURI("https://mozilla.com", null, null)) == IdentityHandler.IDENTITY_MODE_IDENTIFIED,
+     "https://mozilla.com over a secure connection is a verified page");
+
+  </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1099088">Mozilla Bug 1099088</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>