Bug 860581 - Add support for Mixed Content Blocking. r=wesj
authorMargaret Leibovic <margaret.leibovic@gmail.com>
Fri, 21 Jun 2013 20:36:38 -0700
changeset 136074 e9b1e686b98389ccd7e5e08da025efd76b49d8c3
parent 136073 4f98cdea7ea41b8295df48a0ea68041dbc5c2e2f
child 136075 ec6f95743c5d777906bc4743d671500956eeacd2
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewerswesj
bugs860581
milestone24.0a1
Bug 860581 - Add support for Mixed Content Blocking. r=wesj
mobile/android/app/mobile.js
mobile/android/base/BrowserToolbar.java
mobile/android/base/Makefile.in
mobile/android/base/SiteIdentityPopup.java
mobile/android/base/locales/en-US/android_strings.dtd
mobile/android/base/resources/drawable-hdpi/site_security_blocked_mixed_content.png
mobile/android/base/resources/drawable-hdpi/site_security_loaded_mixed_content.png
mobile/android/base/resources/drawable-mdpi/site_security_blocked_mixed_content.png
mobile/android/base/resources/drawable-mdpi/site_security_loaded_mixed_content.png
mobile/android/base/resources/drawable-xhdpi/site_security_blocked_mixed_content.png
mobile/android/base/resources/drawable-xhdpi/site_security_loaded_mixed_content.png
mobile/android/base/resources/drawable/site_security_level.xml
mobile/android/base/resources/values/colors.xml
mobile/android/base/strings.xml.in
mobile/android/chrome/content/browser.js
mobile/android/locales/en-US/chrome/browser.properties
--- a/mobile/android/app/mobile.js
+++ b/mobile/android/app/mobile.js
@@ -471,16 +471,21 @@ pref("app.faqURL", "http://www.mozilla.c
 #endif
 pref("app.marketplaceURL", "https://marketplace.mozilla.org/");
 
 // Name of alternate about: page for certificate errors (when undefined, defaults to about:neterror)
 pref("security.alternate_certificate_error_page", "certerror");
 
 pref("security.warn_viewing_mixed", false); // Warning is disabled.  See Bug 616712.
 
+#ifdef NIGHTLY_BUILD
+// Block insecure active content on https pages
+pref("security.mixed_content.block_active_content", true);
+#endif
+
 // Override some named colors to avoid inverse OS themes
 pref("ui.-moz-dialog", "#efebe7");
 pref("ui.-moz-dialogtext", "#101010");
 pref("ui.-moz-field", "#fff");
 pref("ui.-moz-fieldtext", "#1a1a1a");
 pref("ui.-moz-buttonhoverface", "#f3f0ed");
 pref("ui.-moz-buttonhovertext", "#101010");
 pref("ui.-moz-combobox", "#fff");
--- a/mobile/android/base/BrowserToolbar.java
+++ b/mobile/android/base/BrowserToolbar.java
@@ -955,26 +955,19 @@ public class BrowserToolbar implements T
             image = Bitmap.createScaledBitmap(image, mFaviconSize, mFaviconSize, false);
             mFavicon.setImageBitmap(image);
         } else {
             mFavicon.setImageResource(R.drawable.favicon);
         }
     }
     
     private void setSecurityMode(String mode) {
-        mShowSiteSecurity = true;
-
-        if (mode.equals(SiteIdentityPopup.IDENTIFIED)) {
-            mSiteSecurity.setImageLevel(1);
-        } else if (mode.equals(SiteIdentityPopup.VERIFIED)) {
-            mSiteSecurity.setImageLevel(2);
-        } else {
-            mSiteSecurity.setImageLevel(0);
-            mShowSiteSecurity = false;
-        }
+        int imageLevel = SiteIdentityPopup.getSecurityImageLevel(mode);
+        mSiteSecurity.setImageLevel(imageLevel);
+        mShowSiteSecurity = (imageLevel != SiteIdentityPopup.LEVEL_UKNOWN);
 
         setPageActionVisibility(mStop.getVisibility() == View.VISIBLE);
     }
 
     private void setReaderMode(boolean showReader) {
         mShowReader = showReader;
         setPageActionVisibility(mStop.getVisibility() == View.VISIBLE);
     }
--- a/mobile/android/base/Makefile.in
+++ b/mobile/android/base/Makefile.in
@@ -683,16 +683,18 @@ RES_DRAWABLE_MDPI = \
   res/drawable-mdpi/menu_pb.png \
   res/drawable-mdpi/menu_panel_bg.9.png \
   res/drawable-mdpi/menu_popup_bg.9.png \
   res/drawable-mdpi/menu_popup_arrow_bottom.png \
   res/drawable-mdpi/menu_popup_arrow_top.png \
   res/drawable-mdpi/menu_item_check.png \
   res/drawable-mdpi/menu_item_more.png \
   res/drawable-mdpi/menu_item_uncheck.png \
+  res/drawable-mdpi/site_security_blocked_mixed_content.png \
+  res/drawable-mdpi/site_security_loaded_mixed_content.png \
   res/drawable-mdpi/site_security_identified.png \
   res/drawable-mdpi/site_security_verified.png \
   res/drawable-mdpi/tabs_normal.png \
   res/drawable-mdpi/tabs_private.png \
   res/drawable-mdpi/tabs_synced.png \
   res/drawable-mdpi/urlbar_stop.png \
   res/drawable-mdpi/reader.png \
   res/drawable-mdpi/reader_active.png \
@@ -787,16 +789,18 @@ RES_DRAWABLE_HDPI = \
   res/drawable-hdpi/menu_pb.png \
   res/drawable-hdpi/menu_panel_bg.9.png \
   res/drawable-hdpi/menu_popup_bg.9.png \
   res/drawable-hdpi/menu_popup_arrow_bottom.png \
   res/drawable-hdpi/menu_popup_arrow_top.png \
   res/drawable-hdpi/menu_item_check.png \
   res/drawable-hdpi/menu_item_more.png \
   res/drawable-hdpi/menu_item_uncheck.png \
+  res/drawable-hdpi/site_security_blocked_mixed_content.png \
+  res/drawable-hdpi/site_security_loaded_mixed_content.png \
   res/drawable-hdpi/site_security_identified.png \
   res/drawable-hdpi/site_security_verified.png \
   res/drawable-hdpi/tabs_normal.png \
   res/drawable-hdpi/tabs_private.png \
   res/drawable-hdpi/tabs_synced.png \
   res/drawable-hdpi/urlbar_stop.png \
   res/drawable-hdpi/reader.png \
   res/drawable-hdpi/reader_active.png \
@@ -883,16 +887,18 @@ RES_DRAWABLE_XHDPI = \
   res/drawable-xhdpi/menu_popup_arrow_bottom.png \
   res/drawable-xhdpi/menu_popup_arrow_top.png \
   res/drawable-xhdpi/menu_item_check.png \
   res/drawable-xhdpi/menu_item_more.png \
   res/drawable-xhdpi/menu_item_uncheck.png \
   res/drawable-xhdpi/tab_indicator_divider.9.png \
   res/drawable-xhdpi/tab_indicator_selected.9.png \
   res/drawable-xhdpi/tab_indicator_selected_focused.9.png \
+  res/drawable-xhdpi/site_security_blocked_mixed_content.png \
+  res/drawable-xhdpi/site_security_loaded_mixed_content.png \
   res/drawable-xhdpi/site_security_identified.png \
   res/drawable-xhdpi/site_security_verified.png \
   res/drawable-xhdpi/tabs_normal.png \
   res/drawable-xhdpi/tabs_private.png \
   res/drawable-xhdpi/tabs_synced.png \
   res/drawable-xhdpi/validation_arrow.png \
   res/drawable-xhdpi/validation_arrow_inverted.png \
   res/drawable-xhdpi/validation_bg.9.png \
--- a/mobile/android/base/SiteIdentityPopup.java
+++ b/mobile/android/base/SiteIdentityPopup.java
@@ -16,38 +16,70 @@ import android.view.View;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
 /**
  * SiteIdentityPopup is a singleton class that displays site identity data in
  * an arrow panel popup hanging from the lock icon in the browser toolbar.
  */
-public class SiteIdentityPopup extends ArrowPopup {
+public class SiteIdentityPopup extends ArrowPopup
+                               implements DoorHanger.OnButtonClickListener {
     private static final String LOGTAG = "GeckoSiteIdentityPopup";
 
     public static final String UNKNOWN = "unknown";
     public static final String VERIFIED = "verified";
     public static final String IDENTIFIED = "identified";
+    public static final String MIXED_CONTENT_BLOCKED = "mixed_content_blocked";
+    public static final String MIXED_CONTENT_LOADED = "mixed_content_loaded";
+
+    // Security states corresponding to image levels in site_security_level.xml
+    public static final int LEVEL_UKNOWN = 0;
+    public static final int LEVEL_IDENTIFIED = 1;
+    public static final int LEVEL_VERIFIED = 2;
+    public static final int LEVEL_MIXED_CONTENT_BLOCKED = 3;
+    public static final int LEVEL_MIXED_CONTENT_LOADED = 4;
+
+    // FIXME: Update this URL for mobile. See bug 885923.
+    private static final String MIXED_CONTENT_SUPPORT_URL =
+        "https://support.mozilla.org/kb/how-does-content-isnt-secure-affect-my-safety";
 
     private Resources mResources;
 
     private TextView mHost;
     private TextView mOwner;
     private TextView mSupplemental;
     private TextView mVerifier;
     private TextView mEncrypted;
     private ImageView mLarry;
 
+    private DoorHanger mMixedContentNotification;
+
     SiteIdentityPopup(BrowserApp aActivity) {
         super(aActivity, null);
 
         mResources = aActivity.getResources();
     }
 
+    public static int getSecurityImageLevel(String mode) {
+        if (IDENTIFIED.equals(mode)) {
+            return LEVEL_IDENTIFIED;
+        }
+        if (VERIFIED.equals(mode)) {
+            return LEVEL_VERIFIED;
+        }
+        if (MIXED_CONTENT_BLOCKED.equals(mode)) {
+            return LEVEL_MIXED_CONTENT_BLOCKED;
+        }
+        if (MIXED_CONTENT_LOADED.equals(mode)) {
+            return LEVEL_MIXED_CONTENT_LOADED;
+        }
+        return LEVEL_UKNOWN;
+    }
+
     @Override
     protected void init() {
         super.init();
 
         // Make the popup focusable so it doesn't inadvertently trigger click events elsewhere
         // which may reshow the popup (see bug 785156)
         setFocusable(true);
 
@@ -56,36 +88,17 @@ public class SiteIdentityPopup extends A
         mContent.addView(layout);
 
         mHost = (TextView) layout.findViewById(R.id.host);
         mOwner = (TextView) layout.findViewById(R.id.owner);
         mVerifier = (TextView) layout.findViewById(R.id.verifier);
         mLarry = (ImageView) layout.findViewById(R.id.larry);
     }
 
-    /*
-     * @param identityData A JSONObject that holds the current tab's identity data.
-     */
-    public void updateIdentity(JSONObject identityData) {
-        String mode;
-        try {
-            mode = identityData.getString("mode");
-        } catch (JSONException e) {
-            Log.e(LOGTAG, "Exception trying to get identity mode", e);
-            return;
-        }
-
-        if (!mode.equals(VERIFIED) && !mode.equals(IDENTIFIED)) {
-            Log.e(LOGTAG, "Can't show site identity popup in non-identified state");
-            return;
-        }
-
-        if (!mInflated)
-            init();
-
+    private void setIdentity(JSONObject identityData) {
         try {
             String host = identityData.getString("host");
             mHost.setText(host);
 
             String owner = identityData.getString("owner");
 
             try {
                 String supplemental = identityData.getString("supplemental");
@@ -94,24 +107,115 @@ public class SiteIdentityPopup extends A
 
             mOwner.setText(owner);
 
             String verifier = identityData.getString("verifier");
             String encrypted = identityData.getString("encrypted");
             mVerifier.setText(verifier + "\n" + encrypted);
         } catch (JSONException e) {
             Log.e(LOGTAG, "Exception trying to get identity data", e);
+        }
+    }
+
+    @Override
+    public void onButtonClick(DoorHanger dh, String tag) {
+        if (tag.equals("disable")) {
+            // To disable mixed content blocking, reload the page with a flag to load mixed content.
+            try {
+                JSONObject data = new JSONObject();
+                data.put("allowMixedContent", true);
+                GeckoEvent e = GeckoEvent.createBroadcastEvent("Session:Reload", data.toString());
+                GeckoAppShell.sendEventToGecko(e);
+            } catch (JSONException e) {
+                Log.e(LOGTAG, "Exception creating message to allow mixed content", e);
+            }
+        } else if (tag.equals("enable")) {
+            // To enable mixed content blocking, reload the page without any flags.
+            GeckoEvent e = GeckoEvent.createBroadcastEvent("Session:Reload", "");
+            GeckoAppShell.sendEventToGecko(e);
+        }
+
+        dismiss();
+    }
+
+    private void addMixedContentNotification(boolean blocked) {
+        // Remove any exixting mixed content notification.
+        removeMixedContentNotification();
+        mMixedContentNotification = new DoorHanger(mActivity);
+
+        String message;
+        if (blocked) {
+            message = mActivity.getString(R.string.blocked_mixed_content_message_top) + "\n\n" +
+                      mActivity.getString(R.string.blocked_mixed_content_message_bottom);
+        } else {
+            message = mActivity.getString(R.string.loaded_mixed_content_message);
+        }
+        mMixedContentNotification.setMessage(message);
+        mMixedContentNotification.addLink(mActivity.getString(R.string.learn_more), MIXED_CONTENT_SUPPORT_URL, "\n\n");
+
+        if (blocked) {
+            mMixedContentNotification.addButton(mActivity.getString(R.string.disable_protection), "disable", this);
+            mMixedContentNotification.addButton(mActivity.getString(R.string.keep_blocking), "keepBlocking", this);
+        } else {
+            mMixedContentNotification.addButton(mActivity.getString(R.string.enable_protection), "enable", this);
+        }
+        mMixedContentNotification.hideDivider();
+        mMixedContentNotification.setBackgroundColor(0xFFDDE4EA);
+
+        mContent.addView(mMixedContentNotification);
+    }
+
+    private void removeMixedContentNotification() {
+        if (mMixedContentNotification != null) {
+            mContent.removeView(mMixedContentNotification);
+            mMixedContentNotification = null;
+        }
+    }
+
+    /*
+     * @param identityData A JSONObject that holds the current tab's identity data.
+     */
+    public void updateIdentity(JSONObject identityData) {
+        String mode;
+        try {
+            mode = identityData.getString("mode");
+        } catch (JSONException e) {
+            Log.e(LOGTAG, "Exception trying to get identity mode", e);
             return;
         }
 
-        if (mode.equals(VERIFIED)) {
+        if (UNKNOWN.equals(mode)) {
+            Log.e(LOGTAG, "Can't show site identity popup in non-identified state");
+            return;
+        }
+
+        if (!mInflated)
+            init();
+
+        setIdentity(identityData);
+
+        if (VERIFIED.equals(mode)) {
             // Use a blue theme for SSL
             mLarry.setImageResource(R.drawable.larry_blue);
             mHost.setTextColor(mResources.getColor(R.color.identity_verified));
             mOwner.setTextColor(mResources.getColor(R.color.identity_verified));
-        } else {
+        } else if (IDENTIFIED.equals(mode)) {
             // Use a green theme for EV
             mLarry.setImageResource(R.drawable.larry_green);
             mHost.setTextColor(mResources.getColor(R.color.identity_identified));
             mOwner.setTextColor(mResources.getColor(R.color.identity_identified));
+        } else {
+            // Use a gray theme for sites with mixed content
+            // FIXME: Get a gray larry
+            mLarry.setImageResource(R.drawable.larry_blue);
+            mHost.setTextColor(mResources.getColor(R.color.identity_mixed_content));
+            mOwner.setTextColor(mResources.getColor(R.color.identity_mixed_content));
+
+            addMixedContentNotification(MIXED_CONTENT_BLOCKED.equals(mode));
         }
     }
+
+    @Override
+    public void dismiss() {
+        super.dismiss();
+        removeMixedContentNotification();
+    }
 }
--- a/mobile/android/base/locales/en-US/android_strings.dtd
+++ b/mobile/android/base/locales/en-US/android_strings.dtd
@@ -283,16 +283,25 @@ 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_run_by "which is run by">
 
+<!-- Mixed content notifications in site identity popup -->
+<!ENTITY loaded_mixed_content_message "This page is displaying content that isn\'t secure.">
+<!ENTITY blocked_mixed_content_message_top "&brandShortName; has blocked content that isn\'t secure.">
+<!ENTITY blocked_mixed_content_message_bottom "Most websites will still work properly even when this content is blocked.">
+<!ENTITY learn_more "Learn More">
+<!ENTITY enable_protection "Enable protection">
+<!ENTITY disable_protection "Disable protection">
+<!ENTITY keep_blocking "Keep blocking">
+
 <!ENTITY private_data_success "Private data cleared">
 <!ENTITY private_data_fail "Some private data could not be cleared">
 
 <!ENTITY bookmarkhistory_button_import "Import">
 <!ENTITY bookmarkhistory_import_both "Importing bookmarks and history
                                       from Android">
 <!ENTITY bookmarkhistory_import_bookmarks "Importing bookmarks
                                            from Android">
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..c2e49b3a7fb616bf5b84f1e99575e8d1658d2321
GIT binary patch
literal 691
zc$@*V0!;mhP)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F80007dNkl<Zc-rmP
zze`nd7{~DkB_$<n5X4o2hN5Wb*T0|;2qL%y4$&6UQdGnB`ukU2f9q1VxD`PVQITT=
z5tM>rdLbeLn<6X3>eG98gv*z6`d(3}q2aZB&+{DK)8Tte(qHxO2h7aOBsq&5R$R`{
zEU^24>FMch6}W_DD8;hRRKTz&0cE&~Pf#&@_6o{&6)-h5Rgbq&f(<-(8&Jr1>vtS5
zIXPK{+fd*a9^xFzVXoZI9{P*|w@_770E4HIhXRjr6y{2B2o^Z%vkK&KIvOxBF_DJ~
zd_^nF)nORx5G+Nj@2WsP8ZbUS{sM9zaRP=4+{6}Cz|#2cN65X128@l3-Gy8;40Tw5
z5?Dkt<nBcSMn^|;kh=gw9!g;m7a=zi4Hy|2xeU213@cCyi^xLmax@^9%QZo67KUd~
z3X8Y{xu$5q@bK_S$j!qr3#G7#dB~lL1`G`i)j)0yhAfoABGw>R6A1tY2M0ex*g`qZ
zLn$nc@BV=BrRY!bz`(#u$Q{EOD21i*UAb392lV&%&q1yQ2k;Y0U}=0;Zm#HnzP`R|
zkn4b90ZL$Ld{^#z(E+`^y$z6i3PT1;V1Mugat%cX^z`)9K<+yZVjpr)z*1D<8{}$=
z3vk`t-HVWWfGQXo@eXp9pc3~Xx47dQQCC-2KU81^$6=_%4XncgNAM=t9o%t1XJ=<E
zR-po$XvcmSDq(IvGCrdMtEi0xB<twtsKzrW@BwY8bZvMK1?Ew`t6On4n=QpPY(jx`
zr@*G~l<xXk-rnB6?Jyoe5zH{_dwUTOYQz#0Sn_A)_9h^c$s{R33tqbxKeIse{r~@t
Ze*-iXDJC4w(s2L)002ovPDHLkV1f@5Ej$1K
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..83ba37c2461fce9e9b0bac4a63bbfc9336263f1e
GIT binary patch
literal 679
zc$@*J0$BZtP)<h;3K|Lk000e1NJLTq001BW000mO1^@s6cL04^0007RNkl<Zc-pPi
z-Ahw(7zgm*IVat84P1&e4U4ix_)>}&1!XsO5hZn%MBaAORsVx2m01K8Ip3x%m+2B;
zf|9(E2<s2%GKh#Q=;C+OZB5<N_hG;2cy=6T)Wrv$^StbQx0?wf`Zw>YoM_;I08_Q*
zw7cU6o3qD*@KLEQFhEgi&fg@*v`-9m3-<~OFmr4eNQo2>K!bo8HD+W)5JNr3J<wbQ
zFsB<sumQwWeZ*ye;^efiO%91oAY~K1vpjPbAYMNX1lQq)s$(t!Xt2i|F<!|5L2J?C
zTWisxKCaPw%`?#Q1~4b<>R<~t)w2PldJXvl;M-66&dGkU#qfM6v$Qvan)o?6S$ZBl
zPyPTCN2h?4G6(=Eaoc|Gw-4)!^06S6o)6Tg=gS(nW<0}AIcSan%vh)iGC<0Hr~zVX
z*1SUiPa;-&Q9cwIAeM^zPs1s$l?-++@vd`#r?qoH7L8DA>%DXcph3C$NMDi<1pSd5
z_qC^<?{2g9u=CdXEBt-=*ufJ(Hm%U$&_8Jl;7N>?T$c9)StX*S*HRPyj-38zNe6Dc
z+XF-nEC9Lm#u7k>3iH0cEPDmHf!+6f1A6x5!!@lyHeVnFhvBos{EQ0VO$>QE<!wRR
z^lINz&;0Fh0rfZge0|W{$+y`6gZmbNY({vd=3$5Vf(j5Y@8}<7k02|v&zQxmimmn;
zFLLI+`ZC{UUl6V~foxWw1)AZj!+cEzsF2-aUEUP5O}F+l8S!0Bj{2^k{*gNu3BJt+
z=nr<mFCdrF(4zwQSMTn6BD+Ky$fXV34Djv$_&YYh50wyfsZ!k-zX7A*-Ro;sLOuWh
N002ovPDHLkV1oZgJv0CS
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..7cf33ec4c5acc81330208f5f5ac45fa5435c3d1a
GIT binary patch
literal 346
zc$@)R0j2(lP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0003YNkl<Zc-p1Y
zu}Z^G6o6rI6kNJ=3;GC7f|I3C+AcnXu6+Wh#1KLVgcuT%!P(7@(kBqi0~nvcC=S}m
z2K4w3XK1+54jn!?xhMQta(6^zE1ic$QCM(L;Ynv}V<6A-9#S+AeBwjrMt8l=K$c|_
zyg;zP7<<^&c>zI<i4jQC^c5?d;}Ey_TK}5vR;Z0Yk|ZVWaEc`aqI&@PPH6<<IG*DX
z7Z7CxI4dzX0#Ovjs4<2pBcShG8-XwkXAm4gR4c&qJ_Iu(5Cp*xf)gxtZUuNghG1v}
zeBbXwaEU9OTLIk(`t5-7Jg>q#MmWRs+HRz~>d!yXb=?8p@Piv1z|#3!cZ2SLavaCP
sJp_#sJ+n4nCS}|96c1|p-)-E=FZ@fcYF)+4`v3p{07*qoM6N<$f<M=eoB#j-
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..83ba37c2461fce9e9b0bac4a63bbfc9336263f1e
GIT binary patch
literal 679
zc$@*J0$BZtP)<h;3K|Lk000e1NJLTq001BW000mO1^@s6cL04^0007RNkl<Zc-pPi
z-Ahw(7zgm*IVat84P1&e4U4ix_)>}&1!XsO5hZn%MBaAORsVx2m01K8Ip3x%m+2B;
zf|9(E2<s2%GKh#Q=;C+OZB5<N_hG;2cy=6T)Wrv$^StbQx0?wf`Zw>YoM_;I08_Q*
zw7cU6o3qD*@KLEQFhEgi&fg@*v`-9m3-<~OFmr4eNQo2>K!bo8HD+W)5JNr3J<wbQ
zFsB<sumQwWeZ*ye;^efiO%91oAY~K1vpjPbAYMNX1lQq)s$(t!Xt2i|F<!|5L2J?C
zTWisxKCaPw%`?#Q1~4b<>R<~t)w2PldJXvl;M-66&dGkU#qfM6v$Qvan)o?6S$ZBl
zPyPTCN2h?4G6(=Eaoc|Gw-4)!^06S6o)6Tg=gS(nW<0}AIcSan%vh)iGC<0Hr~zVX
z*1SUiPa;-&Q9cwIAeM^zPs1s$l?-++@vd`#r?qoH7L8DA>%DXcph3C$NMDi<1pSd5
z_qC^<?{2g9u=CdXEBt-=*ufJ(Hm%U$&_8Jl;7N>?T$c9)StX*S*HRPyj-38zNe6Dc
z+XF-nEC9Lm#u7k>3iH0cEPDmHf!+6f1A6x5!!@lyHeVnFhvBos{EQ0VO$>QE<!wRR
z^lINz&;0Fh0rfZge0|W{$+y`6gZmbNY({vd=3$5Vf(j5Y@8}<7k02|v&zQxmimmn;
zFLLI+`ZC{UUl6V~foxWw1)AZj!+cEzsF2-aUEUP5O}F+l8S!0Bj{2^k{*gNu3BJt+
z=nr<mFCdrF(4zwQSMTn6BD+Ky$fXV34Djv$_&YYh50wyfsZ!k-zX7A*-Ro;sLOuWh
N002ovPDHLkV1oZgJv0CS
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..cac4415140767c17f9132e79e68805a959f14591
GIT binary patch
literal 2063
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
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..83ba37c2461fce9e9b0bac4a63bbfc9336263f1e
GIT binary patch
literal 679
zc$@*J0$BZtP)<h;3K|Lk000e1NJLTq001BW000mO1^@s6cL04^0007RNkl<Zc-pPi
z-Ahw(7zgm*IVat84P1&e4U4ix_)>}&1!XsO5hZn%MBaAORsVx2m01K8Ip3x%m+2B;
zf|9(E2<s2%GKh#Q=;C+OZB5<N_hG;2cy=6T)Wrv$^StbQx0?wf`Zw>YoM_;I08_Q*
zw7cU6o3qD*@KLEQFhEgi&fg@*v`-9m3-<~OFmr4eNQo2>K!bo8HD+W)5JNr3J<wbQ
zFsB<sumQwWeZ*ye;^efiO%91oAY~K1vpjPbAYMNX1lQq)s$(t!Xt2i|F<!|5L2J?C
zTWisxKCaPw%`?#Q1~4b<>R<~t)w2PldJXvl;M-66&dGkU#qfM6v$Qvan)o?6S$ZBl
zPyPTCN2h?4G6(=Eaoc|Gw-4)!^06S6o)6Tg=gS(nW<0}AIcSan%vh)iGC<0Hr~zVX
z*1SUiPa;-&Q9cwIAeM^zPs1s$l?-++@vd`#r?qoH7L8DA>%DXcph3C$NMDi<1pSd5
z_qC^<?{2g9u=CdXEBt-=*ufJ(Hm%U$&_8Jl;7N>?T$c9)StX*S*HRPyj-38zNe6Dc
z+XF-nEC9Lm#u7k>3iH0cEPDmHf!+6f1A6x5!!@lyHeVnFhvBos{EQ0VO$>QE<!wRR
z^lINz&;0Fh0rfZge0|W{$+y`6gZmbNY({vd=3$5Vf(j5Y@8}<7k02|v&zQxmimmn;
zFLLI+`ZC{UUl6V~foxWw1)AZj!+cEzsF2-aUEUP5O}F+l8S!0Bj{2^k{*gNu3BJt+
z=nr<mFCdrF(4zwQSMTn6BD+Ky$fXV34Djv$_&YYh50wyfsZ!k-zX7A*-Ro;sLOuWh
N002ovPDHLkV1oZgJv0CS
--- a/mobile/android/base/resources/drawable/site_security_level.xml
+++ b/mobile/android/base/resources/drawable/site_security_level.xml
@@ -3,10 +3,12 @@
    - 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/. -->
 
 <level-list xmlns:android="http://schemas.android.com/apk/res/android">
 
     <item android:maxLevel="0" android:drawable="@android:color/transparent"/>
     <item android:maxLevel="1" android:drawable="@drawable/site_security_identified"/>
     <item android:maxLevel="2" android:drawable="@drawable/site_security_verified"/>
+    <item android:maxLevel="3" android:drawable="@drawable/site_security_blocked_mixed_content"/>
+    <item android:maxLevel="4" android:drawable="@drawable/site_security_loaded_mixed_content"/>
 
 </level-list>
--- a/mobile/android/base/resources/values/colors.xml
+++ b/mobile/android/base/resources/values/colors.xml
@@ -63,16 +63,17 @@
   <color name="splash_msgfont">#ffffff</color>
   <color name="splash_urlfont">#000000</color>
   <color name="splash_content">#ffffff</color>
   <color name="doorhanger_text">#FF222222</color>
   <color name="doorhanger_link">#ACC4D5</color>
   <color name="validation_message_text">#ffffff</color>
   <color name="identity_verified">#FF3298FF</color>
   <color name="identity_identified">#FF89C450</color>
+  <color name="identity_mixed_content">#FF000000</color>
   <color name="url_bar_text_highlight">#FFFF9500</color>
   <color name="url_bar_text_highlight_pb">#FFD06BFF</color>
   <color name="suggestion_primary">#dddddd</color>
   <color name="suggestion_pressed">#bbbbbb</color>
   <color name="abouthome_thumbnail_bg">#5FFF</color>
   <color name="abouthome_topsite_shadow">#1000</color>
   <color name="tab_row_pressed">#4D000000</color>
   <color name="abouthome_topsite_pin">#55000000</color>
--- a/mobile/android/base/strings.xml.in
+++ b/mobile/android/base/strings.xml.in
@@ -257,16 +257,23 @@
   <string name="bookmarkdefaults_favicon_abouthome">chrome/chrome/content/branding/favicon64.png</string>
 
   <string name="bookmarkdefaults_title_abouthome">@bookmarks_aboutHome@</string>
   <string name="bookmarkdefaults_url_abouthome">about:home</string>
 
   <!-- Site identity popup -->
   <string name="identity_connected_to">&identity_connected_to;</string>
   <string name="identity_run_by">&identity_run_by;</string>
+  <string name="loaded_mixed_content_message">&loaded_mixed_content_message;</string>
+  <string name="blocked_mixed_content_message_top">&blocked_mixed_content_message_top;</string>
+  <string name="blocked_mixed_content_message_bottom">&blocked_mixed_content_message_bottom;</string>
+  <string name="learn_more">&learn_more;</string>
+  <string name="enable_protection">&enable_protection;</string>
+  <string name="disable_protection">&disable_protection;</string>
+  <string name="keep_blocking">&keep_blocking;</string>
 
   <!-- Clear private data -->
   <string name="private_data_success">&private_data_success;</string>
   <string name="private_data_fail">&private_data_fail;</string>
 
   <!-- Bookmark import/export -->
   <string name="bookmarkhistory_button_import">&bookmarkhistory_button_import;</string>
   <string name="bookmarkhistory_import_both">&bookmarkhistory_import_both;</string>
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -1311,38 +1311,47 @@ var BrowserApp = {
       case "Session:Back":
         browser.goBack();
         break;
 
       case "Session:Forward":
         browser.goForward();
         break;
 
-      case "Session:Reload":
+      case "Session:Reload": {
+        let allowMixedContent = false;
+        if (aData) {
+            let data = JSON.parse(aData);
+            allowMixedContent = data.allowMixedContent;
+        }
+
         // Try to use the session history to reload so that framesets are
         // handled properly. If the window has no session history, fall back
         // to using the web navigation's reload method.
-        let flags = Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_PROXY | Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE;
+        let flags = allowMixedContent ? Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_MIXED_CONTENT :
+                    Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_PROXY | Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE;
         let webNav = browser.webNavigation;
         try {
           let sh = webNav.sessionHistory;
           if (sh)
             webNav = sh.QueryInterface(Ci.nsIWebNavigation);
         } catch (e) {}
         webNav.reload(flags);
         break;
+      }
 
       case "Session:Stop":
         browser.stop();
         break;
 
-      case "Session:ShowHistory":
+      case "Session:ShowHistory": {
         let data = JSON.parse(aData);
         this.showHistory(data.fromIndex, data.toIndex, data.selIndex);
         break;
+      }
 
       case "Tab:Load": {
         let data = JSON.parse(aData);
 
         // Pass LOAD_FLAGS_DISALLOW_INHERIT_OWNER to prevent any loads from
         // inheriting the currently loaded document's principal.
         let flags = Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
         if (data.userEntered) {
@@ -6029,20 +6038,34 @@ var CharacterEncoding = {
     let browser = BrowserApp.selectedBrowser;
     browser.docShell.gatherCharsetMenuTelemetry();
     browser.docShell.charset = aEncoding;
     browser.reload(Ci.nsIWebNavigation.LOAD_FLAGS_CHARSET_CHANGE);
   }
 };
 
 var IdentityHandler = {
-  // Mode strings used to control CSS display
-  IDENTITY_MODE_IDENTIFIED       : "identified", // High-quality identity information
-  IDENTITY_MODE_DOMAIN_VERIFIED  : "verified",   // Minimal SSL CA-signed domain verification
-  IDENTITY_MODE_UNKNOWN          : "unknown",  // No trusted identity information
+  // No trusted identity information. No site identity icon is shown.
+  IDENTITY_MODE_UNKNOWN: "unknown",
+
+  // Minimal SSL CA-signed domain verification. Blue lock icon is shown.
+  IDENTITY_MODE_DOMAIN_VERIFIED: "verified",
+
+  // High-quality identity information. Green lock icon is shown.
+  IDENTITY_MODE_IDENTIFIED: "identified",
+
+  // The following mixed content modes are only used if "security.mixed_content.block_active_content"
+  // is enabled. Even though the mixed content state and identitity state are orthogonal,
+  // our Java frontend coalesces them into one indicator.
+
+  // Blocked active mixed content. Shield icon is shown, with a popup option to load content.
+  IDENTITY_MODE_MIXED_CONTENT_BLOCKED: "mixed_content_blocked",
+
+  // Loaded active mixed content. Yellow triangle icon is shown.
+  IDENTITY_MODE_MIXED_CONTENT_LOADED: "mixed_content_loaded",
 
   // Cache the most recent SSLStatus and Location seen in getIdentityStrings
   _lastStatus : null,
   _lastLocation : null,
 
   /**
    * Helper to parse out the important parts of _lastStatus (of the SSL cert in
    * particular) for use in constructing identity UI strings
@@ -6072,16 +6095,24 @@ var IdentityHandler = {
     // Human readable name of Certificate Authority
     result.caOrg =  cert.issuerOrganization || cert.issuerCommonName;
     result.cert = cert;
 
     return result;
   },
 
   getIdentityMode: function getIdentityMode(aState) {
+    if (aState & Ci.nsIWebProgressListener.STATE_BLOCKED_MIXED_ACTIVE_CONTENT)
+      return this.IDENTITY_MODE_MIXED_CONTENT_BLOCKED;
+
+    // Only show an indicator for loaded mixed content if the pref to block it is enabled
+    if ((aState & Ci.nsIWebProgressListener.STATE_LOADED_MIXED_ACTIVE_CONTENT) &&
+         Services.prefs.getBoolPref("security.mixed_content.block_active_content"))
+      return this.IDENTITY_MODE_MIXED_CONTENT_LOADED;
+
     if (aState & Ci.nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL)
       return this.IDENTITY_MODE_IDENTIFIED;
 
     if (aState & Ci.nsIWebProgressListener.STATE_IS_SECURE)
       return this.IDENTITY_MODE_DOMAIN_VERIFIED;
 
     return this.IDENTITY_MODE_UNKNOWN;
   },
@@ -6121,17 +6152,17 @@ var IdentityHandler = {
     // Ideally we'd just make this a Java string
     result.encrypted = Strings.browser.GetStringFromName("identity.encrypted2");
     result.host = this.getEffectiveHost();
 
     let iData = this.getIdentityData();
     result.verifier = Strings.browser.formatStringFromName("identity.identified.verifier", [iData.caOrg], 1);
 
     // If the cert is identified, then we can populate the results with credentials
-    if (mode == this.IDENTITY_MODE_IDENTIFIED) {
+    if (aState & Ci.nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL) {
       result.owner = iData.subjectOrg;
 
       // Build an appropriate supplemental block out of whatever location data we have
       let supplemental = "";
       if (iData.city)
         supplemental += iData.city + "\n";
       if (iData.state && iData.country)
         supplemental += Strings.browser.formatStringFromName("identity.identified.state_and_country", [iData.state, iData.country], 2);
@@ -6140,17 +6171,17 @@ var IdentityHandler = {
       else if (iData.country) // Country only
         supplemental += iData.country;
       result.supplemental = supplemental;
 
       return result;
     }
     
     // Otherwise, we don't know the cert owner
-    result.owner = Strings.browser.GetStringFromName("identity.ownerUnknown2");
+    result.owner = Strings.browser.GetStringFromName("identity.ownerUnknown3");
 
     // Cache the override service the first time we need to check it
     if (!this._overrideService)
       this._overrideService = Cc["@mozilla.org/security/certoverride;1"].getService(Ci.nsICertOverrideService);
 
     // Check whether this site is a security exception. XPConnect does the right
     // thing here in terms of converting _lastLocation.port from string to int, but
     // the overrideService doesn't like undefined ports, so make sure we have
--- a/mobile/android/locales/en-US/chrome/browser.properties
+++ b/mobile/android/locales/en-US/chrome/browser.properties
@@ -67,17 +67,17 @@ xpinstallDisabledMessage2=Software insta
 xpinstallDisabledButton=Enable
 
 # Site Identity
 identity.identified.verifier=Verified by: %S
 identity.identified.verified_by_you=You have added a security exception for this site
 identity.identified.state_and_country=%S, %S
 identity.identified.title_with_country=%S (%S)
 identity.encrypted2=Encrypted
-identity.ownerUnknown2=(unknown)
+identity.ownerUnknown3=unknown
 
 # Geolocation UI
 geolocation.allow=Share
 geolocation.dontAllow=Don't share
 geolocation.ask=Share your location with %S?
 # LOCALIZATION NOTE (geolocation.shareLocation): Label that will be used in
 # site settings dialog.
 geolocation.shareLocation=Share Location