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 270055 be3b67884f6e8227317c99e90c6a014b5598a079
parent 270054 10e8f85d539b3666037f06183cfb025991837aa7
child 270056 ec9458c922de0bf61e58021d295e4459c4294897
push id29598
push usercbook@mozilla.com
push dateThu, 29 Oct 2015 10:48:16 +0000
treeherdermozilla-central@f3754dcad86e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmargaret
bugs1214333
milestone44.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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);
   },
 
   /**