Bug 1214333 - Signal to users when they are on an insecure page with a password field. r=margaret
authorChenxia Liu <liuche@mozilla.com>
Tue, 27 Oct 2015 18:23:02 -0700
changeset 270041 be3b67884f6e8227317c99e90c6a014b5598a079
parent 270040 10e8f85d539b3666037f06183cfb025991837aa7
child 270042 ec9458c922de0bf61e58021d295e4459c4294897
push id15963
push usercliu@mozilla.com
push dateThu, 29 Oct 2015 00:56:46 +0000
treeherderfx-team@be3b67884f6e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmargaret
bugs1214333
milestone44.0a1
Bug 1214333 - Signal to users when they are on an insecure page with a password field. r=margaret
mobile/android/base/SiteIdentity.java
mobile/android/base/Tab.java
mobile/android/base/Tabs.java
mobile/android/base/locales/en-US/android_strings.dtd
mobile/android/base/strings.xml.in
mobile/android/base/toolbar/SiteIdentityPopup.java
mobile/android/base/toolbar/ToolbarDisplayLayout.java
mobile/android/chrome/content/browser.js
--- a/mobile/android/base/SiteIdentity.java
+++ b/mobile/android/base/SiteIdentity.java
@@ -1,24 +1,24 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko;
 
-import android.util.Log;
 import org.json.JSONObject;
 
 import android.text.TextUtils;
 
 public class SiteIdentity {
     private final String LOGTAG = "GeckoSiteIdentity";
     private SecurityMode mSecurityMode;
     private boolean mSecure;
+    private boolean mLoginInsecure;
     private MixedMode mMixedModeActive;
     private MixedMode mMixedModeDisplay;
     private TrackingMode mTrackingMode;
     private String mHost;
     private String mOwner;
     private String mSupplemental;
     private String mVerifier;
     private String mOrigin;
@@ -129,16 +129,17 @@ public class SiteIdentity {
     public void resetIdentity() {
         mSecurityMode = SecurityMode.UNKNOWN;
         mOrigin = null;
         mHost = null;
         mOwner = null;
         mSupplemental = null;
         mVerifier = null;
         mSecure = false;
+        mLoginInsecure = false;
     }
 
     public void reset() {
         resetIdentity();
         mMixedModeActive = MixedMode.UNKNOWN;
         mMixedModeDisplay = MixedMode.UNKNOWN;
         mTrackingMode = TrackingMode.UNKNOWN;
     }
@@ -215,16 +216,24 @@ public class SiteIdentity {
     public String getVerifier() {
         return mVerifier;
     }
 
     public boolean isSecure() {
         return mSecure;
     }
 
+    public void setLoginInsecure(boolean isInsecure) {
+        mLoginInsecure = isInsecure;
+    }
+
+    public boolean loginInsecure() {
+        return mLoginInsecure;
+    }
+
     public MixedMode getMixedModeActive() {
         return mMixedModeActive;
     }
 
     public MixedMode getMixedModeDisplay() {
         return mMixedModeDisplay;
     }
 
--- a/mobile/android/base/Tab.java
+++ b/mobile/android/base/Tab.java
@@ -31,17 +31,16 @@ import android.content.ContentResolver;
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.Color;
 import android.graphics.drawable.BitmapDrawable;
 import android.os.Build;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.View;
-import android.widget.Toast;
 import org.mozilla.gecko.widget.SiteLogins;
 
 public class Tab {
     private static final String LOGTAG = "GeckoTab";
 
     private static Pattern sColorPattern;
     private final int mId;
     private final BrowserDB mDB;
@@ -503,16 +502,20 @@ public class Tab {
     public void setHasOpenSearch(boolean hasOpenSearch) {
         mHasOpenSearch = hasOpenSearch;
     }
 
     public void updateIdentityData(JSONObject identityData) {
         mSiteIdentity.update(identityData);
     }
 
+    public void setLoginInsecure(boolean isInsecure) {
+        mSiteIdentity.setLoginInsecure(isInsecure);
+    }
+
     public void setSiteLogins(SiteLogins siteLogins) {
         mSiteLogins = siteLogins;
     }
 
     void updateBookmark() {
         if (getURL() == null) {
             return;
         }
--- a/mobile/android/base/Tabs.java
+++ b/mobile/android/base/Tabs.java
@@ -94,16 +94,17 @@ public class Tabs implements GeckoEventL
     };
 
     private Tabs() {
         EventDispatcher.getInstance().registerGeckoThreadListener(this,
             "Tab:Added",
             "Tab:Close",
             "Tab:Select",
             "Content:LocationChange",
+            "Content:LoginInsecure",
             "Content:SecurityChange",
             "Content:StateChange",
             "Content:LoadError",
             "Content:PageShow",
             "DOMContentLoaded",
             "DOMTitleChanged",
             "Link:Favicon",
             "Link:Feed",
@@ -505,16 +506,19 @@ public class Tabs implements GeckoEventL
                 closeTab(tab);
             } else if (event.equals("Tab:Select")) {
                 selectTab(tab.getId());
             } else if (event.equals("Content:LocationChange")) {
                 tab.handleLocationChange(message);
             } else if (event.equals("Content:SecurityChange")) {
                 tab.updateIdentityData(message.getJSONObject("identity"));
                 notifyListeners(tab, TabEvents.SECURITY_CHANGE);
+            } else if (event.equals("Content:LoginInsecure")) {
+                tab.setLoginInsecure(true);
+                notifyListeners(tab, TabEvents.SECURITY_CHANGE);
             } else if (event.equals("Content:StateChange")) {
                 int state = message.getInt("state");
                 if ((state & GeckoAppShell.WPL_STATE_IS_NETWORK) != 0) {
                     if ((state & GeckoAppShell.WPL_STATE_START) != 0) {
                         boolean restoring = message.getBoolean("restoring");
                         tab.handleDocumentStart(restoring, message.getString("uri"));
                         notifyListeners(tab, Tabs.TabEvents.START);
                     } else if ((state & GeckoAppShell.WPL_STATE_STOP) != 0) {
--- a/mobile/android/base/locales/en-US/android_strings.dtd
+++ b/mobile/android/base/locales/en-US/android_strings.dtd
@@ -584,16 +584,17 @@ which is run by
 Example Enterprises, Inc.
 
 The layout of the identity dialog prevents combining this into a single string with
 substitution variables.  If it is difficult to translate the sense of the string
 with that structure, consider a translation which ignores the preceding domain and
 just addresses the organization to follow, e.g. "This site is run by " -->
 <!ENTITY identity_connection_secure "Secure Connection">
 <!ENTITY identity_connection_insecure "Insecure connection">
+<!ENTITY identity_login_insecure "This page is not secure and your login could be vulnerable.">
 
 <!-- Mixed content notifications in site identity popup -->
 <!ENTITY mixed_content_blocked_all1 "&brandShortName; has blocked insecure content on this page.">
 <!ENTITY mixed_content_blocked_some1 "&brandShortName; has blocked some of the insecure content on this page.">
 <!ENTITY mixed_content_display_loaded1 "Parts of this page are not secure (such as images).">
 <!ENTITY mixed_content_protection_disabled1 "You have disabled protection from insecure content.">
 
 <!-- Tracking content notifications in site identity popup -->
--- a/mobile/android/base/strings.xml.in
+++ b/mobile/android/base/strings.xml.in
@@ -489,16 +489,17 @@
   <string name="bookmarkdefaults_url_restricted_webmaker">https://webmaker.org/</string>
 
   <string name="bookmarkdefaults_title_restricted_support">&bookmarks_restricted_support2;</string>
   <string name="bookmarkdefaults_url_restricted_support">https://support.mozilla.org/kb/controlledaccess</string>
 
   <!-- Site identity popup -->
   <string name="identity_connection_secure">&identity_connection_secure;</string>
   <string name="identity_connection_insecure">&identity_connection_insecure;</string>
+  <string name="identity_login_insecure">&identity_login_insecure;</string>
 
   <string name="mixed_content_blocked_all">&mixed_content_blocked_all1;</string>
   <string name="mixed_content_blocked_some">&mixed_content_blocked_some1;</string>
   <string name="mixed_content_display_loaded">&mixed_content_display_loaded1;</string>
   <string name="mixed_content_protection_disabled">&mixed_content_protection_disabled1;</string>
 
   <string name="doorhanger_tracking_title">&doorhanger_tracking_title;</string>
   <string name="doorhanger_tracking_state_enabled">&doorhanger_tracking_state_enabled;</string>
--- a/mobile/android/base/toolbar/SiteIdentityPopup.java
+++ b/mobile/android/base/toolbar/SiteIdentityPopup.java
@@ -43,16 +43,18 @@ import android.view.View;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 import org.mozilla.gecko.widget.DoorhangerConfig;
 import org.mozilla.gecko.widget.SiteLogins;
 
 /**
  * SiteIdentityPopup is a singleton class that displays site identity data in
  * an arrow panel popup hanging from the lock icon in the browser toolbar.
+ *
+ * A site identity icon may be displayed in the url, and is set in <code>ToolbarDisplayLayout</code>.
  */
 public class SiteIdentityPopup extends AnchoredPopup implements GeckoEventListener {
 
     public static enum ButtonType { DISABLE, ENABLE, KEEP_BLOCKING, CANCEL, COPY }
 
     private static final String LOGTAG = "GeckoSiteIdentityPopup";
 
     private static final String MIXED_CONTENT_SUPPORT_URL =
@@ -304,17 +306,24 @@ public class SiteIdentityPopup extends A
      * a) Connection encryption
      * b) Mixed Content state (Active/Display Mixed content, loaded, blocked, none, etc)
      * and update the icons and strings to inform the user of that state.
      *
      * @param siteIdentity SiteIdentity information about the connection.
      */
     private void updateConnectionState(final SiteIdentity siteIdentity) {
         if (!siteIdentity.isSecure()) {
-            if (siteIdentity.getMixedModeActive() == MixedMode.MIXED_CONTENT_LOADED) {
+            if (siteIdentity.loginInsecure()) {
+                // Login detected on an insecure page.
+                mIcon.setImageResource(R.drawable.lock_disabled);
+                clearSecurityStateIcon();
+
+                mMixedContentActivity.setVisibility(View.VISIBLE);
+                mMixedContentActivity.setText(R.string.identity_login_insecure);
+            } else if (siteIdentity.getMixedModeActive() == MixedMode.MIXED_CONTENT_LOADED) {
                 // Active Mixed Content loaded because user has disabled blocking.
                 mIcon.setImageResource(R.drawable.lock_disabled);
                 clearSecurityStateIcon();
                 mMixedContentActivity.setVisibility(View.VISIBLE);
                 mMixedContentActivity.setText(R.string.mixed_content_protection_disabled);
 
                 mLink.setVisibility(View.VISIBLE);
             } else if (siteIdentity.getMixedModeDisplay() == MixedMode.MIXED_CONTENT_LOADED) {
--- a/mobile/android/base/toolbar/ToolbarDisplayLayout.java
+++ b/mobile/android/base/toolbar/ToolbarDisplayLayout.java
@@ -479,34 +479,39 @@ public class ToolbarDisplayLayout extend
         }
 
         mSiteIdentityPopup.setSiteIdentity(siteIdentity);
 
         final SecurityMode securityMode;
         final MixedMode activeMixedMode;
         final MixedMode displayMixedMode;
         final TrackingMode trackingMode;
+        final boolean loginInsecure;
         if (siteIdentity == null) {
             securityMode = SecurityMode.UNKNOWN;
             activeMixedMode = MixedMode.UNKNOWN;
             displayMixedMode = MixedMode.UNKNOWN;
             trackingMode = TrackingMode.UNKNOWN;
+            loginInsecure = false;
         } else {
             securityMode = siteIdentity.getSecurityMode();
             activeMixedMode = siteIdentity.getMixedModeActive();
             displayMixedMode = siteIdentity.getMixedModeDisplay();
             trackingMode = siteIdentity.getTrackingMode();
+            loginInsecure = siteIdentity.loginInsecure();
         }
 
         // This is a bit tricky, but we have one icon and three potential indicators.
         // Default to the identity level
         int imageLevel = securityMode.ordinal();
 
         // Check to see if any protection was overridden first
-        if (trackingMode == TrackingMode.TRACKING_CONTENT_LOADED) {
+        if (loginInsecure) {
+            imageLevel = LEVEL_LOCK_DISABLED;
+        } else if (trackingMode == TrackingMode.TRACKING_CONTENT_LOADED) {
             imageLevel = LEVEL_SHIELD_DISABLED;
         } else if (trackingMode == TrackingMode.TRACKING_CONTENT_BLOCKED) {
             imageLevel = LEVEL_SHIELD_ENABLED;
         } else if (activeMixedMode == MixedMode.MIXED_CONTENT_LOADED) {
             imageLevel = LEVEL_LOCK_DISABLED;
         } else if (displayMixedMode == MixedMode.MIXED_CONTENT_LOADED) {
             imageLevel = LEVEL_WARNING_MINOR;
         }
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -4758,16 +4758,17 @@ var BrowserEventHandler = {
     Services.obs.addObserver(this, "dom-touch-listener-added", false);
 
     BrowserApp.deck.addEventListener("DOMUpdatePageReport", PopupBlockerObserver.onUpdatePageReport, false);
     BrowserApp.deck.addEventListener("touchstart", this, true);
     BrowserApp.deck.addEventListener("MozMouseHittest", this, true);
 
     InitLater(() => BrowserApp.deck.addEventListener("click", InputWidgetHelper, true));
     InitLater(() => BrowserApp.deck.addEventListener("click", SelectHelper, true));
+    InitLater(() => BrowserApp.deck.addEventListener("InsecureLoginFormsStateChange", IdentityHandler.sendLoginInsecure, true));
 
     // ReaderViews support backPress listeners.
     Messaging.addListener(() => {
       return Reader.onBackPress(BrowserApp.selectedTab.id);
     }, "Browser:OnBackPressed");
   },
 
   handleEvent: function(aEvent) {
@@ -6612,16 +6613,28 @@ var IdentityHandler = {
       this.shieldHistogramAdd(aBrowser, 1);
       return this.TRACKING_MODE_CONTENT_LOADED;
     }
 
     this.shieldHistogramAdd(aBrowser, 0);
     return this.TRACKING_MODE_UNKNOWN;
   },
 
+  sendLoginInsecure: function sendLoginInsecure() {
+    let loginInsecure = LoginManagerParent.hasInsecureLoginForms(BrowserApp.selectedBrowser);
+        if (loginInsecure) {
+          let message = {
+            type: "Content:LoginInsecure",
+            tabID: BrowserApp.selectedTab.id
+          };
+          Messaging.sendRequest(message);
+        }
+    },
+
+
   shieldHistogramAdd: function(browser, value) {
     if (PrivateBrowsingUtils.isBrowserPrivate(browser)) {
       return;
     }
     Telemetry.addData("TRACKING_PROTECTION_SHIELD", value);
   },
 
   /**