Bug 945212 - Encapsulate identity data/security mode behind a type-safe API (r=margaret)
authorLucas Rocha <lucasr@mozilla.com>
Thu, 12 Dec 2013 00:28:15 +0000
changeset 160132 e61d36bd385f0efc23ae3f11fd0ffcb90078cf7a
parent 160131 6812364ddbaeab5d4427452be73bf5428046a728
child 160133 5fba9c37392d00e5adcd7a20cfd6549fcfaf19d4
push id1672
push userkvijayan@mozilla.com
push dateThu, 12 Dec 2013 20:08:00 +0000
reviewersmargaret
bugs945212
milestone29.0a1
Bug 945212 - Encapsulate identity data/security mode behind a type-safe API (r=margaret)
mobile/android/base/SiteIdentity.java
mobile/android/base/Tab.java
mobile/android/base/moz.build
mobile/android/base/toolbar/BrowserToolbar.java
mobile/android/base/toolbar/SiteIdentityPopup.java
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/SiteIdentity.java
@@ -0,0 +1,110 @@
+/* -*- 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 org.json.JSONObject;
+
+import android.text.TextUtils;
+
+public class SiteIdentity {
+    private SecurityMode mSecurityMode;
+    private String mHost;
+    private String mOwner;
+    private String mSupplemental;
+    private String mVerifier;
+    private String mEncrypted;
+
+    // The order of the items here correspond to image
+    // levels in site_security_level.xml
+    public enum SecurityMode {
+        UNKNOWN("unknown"),
+        VERIFIED("verified"),
+        IDENTIFIED("identified"),
+        MIXED_CONTENT_BLOCKED("mixed_content_blocked"),
+        MIXED_CONTENT_LOADED("mixed_content_loaded");
+
+        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())) {
+                    return mode;
+                }
+            }
+
+            throw new IllegalArgumentException("Could not convert String id to SiteIdentity");
+        }
+
+        @Override
+        public String toString() {
+            return mId;
+        }
+    }
+
+    public SiteIdentity() {
+        reset(SecurityMode.UNKNOWN);
+    }
+
+    private void reset(SecurityMode securityMode) {
+        mSecurityMode = securityMode;
+        mHost = null;
+        mOwner = null;
+        mSupplemental = null;
+        mVerifier = null;
+        mEncrypted = null;
+    }
+
+    void update(JSONObject identityData) {
+        try {
+            mSecurityMode = SecurityMode.fromString(identityData.getString("mode"));
+        } catch (Exception e) {
+            reset(SecurityMode.UNKNOWN);
+            return;
+        }
+
+        try {
+            mHost = identityData.getString("host");
+            mOwner = identityData.getString("owner");
+            mSupplemental = identityData.optString("supplemental", null);
+            mVerifier = identityData.getString("verifier");
+            mEncrypted = identityData.getString("encrypted");
+        } catch (Exception e) {
+            reset(mSecurityMode);
+        }
+    }
+
+    public SecurityMode getSecurityMode() {
+        return mSecurityMode;
+    }
+
+    public String getHost() {
+        return mHost;
+    }
+
+    public String getOwner() {
+        return mOwner;
+    }
+
+    public String getSupplemental() {
+        return mSupplemental;
+    }
+
+    public String getVerifier() {
+        return mVerifier;
+    }
+
+    public String getEncrypted() {
+        return mEncrypted;
+    }
+}
\ No newline at end of file
--- a/mobile/android/base/Tab.java
+++ b/mobile/android/base/Tab.java
@@ -1,19 +1,19 @@
 /* -*- 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 org.mozilla.gecko.SiteIdentity.SecurityMode;
 import org.mozilla.gecko.db.BrowserDB;
 import org.mozilla.gecko.gfx.Layer;
 import org.mozilla.gecko.home.HomePager;
-import org.mozilla.gecko.toolbar.SiteIdentityPopup;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import org.json.JSONException;
 import org.json.JSONObject;
 
 import android.content.ContentResolver;
 import android.content.Context;
 import android.graphics.Bitmap;
@@ -41,17 +41,17 @@ public class Tab {
     private String mBaseDomain;
     private String mUserSearch;
     private String mTitle;
     private Bitmap mFavicon;
     private String mFaviconUrl;
     private int mFaviconSize;
     private boolean mHasFeeds;
     private boolean mHasOpenSearch;
-    private JSONObject mIdentityData;
+    private SiteIdentity mSiteIdentity;
     private boolean mReaderEnabled;
     private BitmapDrawable mThumbnail;
     private int mHistoryIndex;
     private int mHistorySize;
     private int mParentId;
     private HomePager.Page mAboutHomePage;
     private boolean mExternal;
     private boolean mBookmark;
@@ -97,17 +97,17 @@ public class Tab {
         mParentId = parentId;
         mAboutHomePage = HomePager.Page.TOP_SITES;
         mTitle = title == null ? "" : title;
         mFavicon = null;
         mFaviconUrl = null;
         mFaviconSize = 0;
         mHasFeeds = false;
         mHasOpenSearch = false;
-        mIdentityData = null;
+        mSiteIdentity = new SiteIdentity();
         mReaderEnabled = false;
         mEnteringReaderMode = false;
         mThumbnail = null;
         mHistoryIndex = -1;
         mHistorySize = 0;
         mBookmark = false;
         mReadingListItem = false;
         mFaviconLoadId = 0;
@@ -242,27 +242,22 @@ public class Tab {
     public boolean hasFeeds() {
         return mHasFeeds;
     }
 
     public boolean hasOpenSearch() {
         return mHasOpenSearch;
     }
 
-    public String getSecurityMode() {
-        try {
-            return mIdentityData.getString("mode");
-        } catch (Exception e) {
-            // If mIdentityData is null, or we get a JSONException
-            return SiteIdentityPopup.UNKNOWN;
-        }
+    public SecurityMode getSecurityMode() {
+        return mSiteIdentity.getSecurityMode();
     }
 
-    public JSONObject getIdentityData() {
-        return mIdentityData;
+    public SiteIdentity getSiteIdentity() {
+        return mSiteIdentity;
     }
 
     public boolean getReaderEnabled() {
         return mReaderEnabled;
     }
 
     public boolean isBookmark() {
         return mBookmark;
@@ -411,17 +406,17 @@ public class Tab {
         mHasFeeds = hasFeeds;
     }
 
     public void setHasOpenSearch(boolean hasOpenSearch) {
         mHasOpenSearch = hasOpenSearch;
     }
 
     public void updateIdentityData(JSONObject identityData) {
-        mIdentityData = identityData;
+        mSiteIdentity.update(identityData);
     }
 
     public void setReaderEnabled(boolean readerEnabled) {
         mReaderEnabled = readerEnabled;
         Tabs.getInstance().notifyListeners(this, Tabs.TabEvents.MENU_UPDATED);
     }
 
     void updateBookmark() {
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -273,16 +273,17 @@ gbjar.sources += [
     'ReaderModeUtils.java',
     'ReferrerReceiver.java',
     'RemoteTabs.java',
     'Restarter.java',
     'ScrollAnimator.java',
     'ServiceNotificationClient.java',
     'SessionParser.java',
     'SharedPreferencesHelper.java',
+    'SiteIdentity.java',
     'SmsManager.java',
     'sqlite/ByteBufferInputStream.java',
     'sqlite/MatrixBlobCursor.java',
     'sqlite/SQLiteBridge.java',
     'sqlite/SQLiteBridgeException.java',
     'SurfaceBits.java',
     'Tab.java',
     'Tabs.java',
--- a/mobile/android/base/toolbar/BrowserToolbar.java
+++ b/mobile/android/base/toolbar/BrowserToolbar.java
@@ -7,16 +7,18 @@ package org.mozilla.gecko.toolbar;
 
 import org.mozilla.gecko.AboutPages;
 import org.mozilla.gecko.BrowserApp;
 import org.mozilla.gecko.GeckoApplication;
 import org.mozilla.gecko.GeckoAppShell;
 import org.mozilla.gecko.GeckoProfile;
 import org.mozilla.gecko.LightweightTheme;
 import org.mozilla.gecko.R;
+import org.mozilla.gecko.SiteIdentity;
+import org.mozilla.gecko.SiteIdentity.SecurityMode;
 import org.mozilla.gecko.Tab;
 import org.mozilla.gecko.Tabs;
 import org.mozilla.gecko.animation.PropertyAnimator;
 import org.mozilla.gecko.animation.PropertyAnimator.PropertyAnimationListener;
 import org.mozilla.gecko.animation.ViewHelper;
 import org.mozilla.gecko.menu.GeckoMenu;
 import org.mozilla.gecko.menu.MenuPopup;
 import org.mozilla.gecko.PrefsHelper;
@@ -423,23 +425,25 @@ public class BrowserToolbar extends Geck
         });
 
         Button.OnClickListener faviconListener = new Button.OnClickListener() {
             @Override
             public void onClick(View view) {
                 if (mSiteSecurity.getVisibility() != View.VISIBLE)
                     return;
 
-                JSONObject identityData = Tabs.getInstance().getSelectedTab().getIdentityData();
-                if (identityData == null) {
+                final Tab tab = Tabs.getInstance().getSelectedTab();
+
+                final SiteIdentity siteIdentity = tab.getSiteIdentity();
+                if (siteIdentity.getSecurityMode() == SecurityMode.UNKNOWN) {
                     Log.e(LOGTAG, "Selected tab has no identity data");
                     return;
                 }
 
-                mSiteIdentityPopup.updateIdentity(identityData);
+                mSiteIdentityPopup.updateIdentity(siteIdentity);
                 mSiteIdentityPopup.show();
             }
         };
 
         mFavicon.setOnClickListener(faviconListener);
         mSiteSecurity.setOnClickListener(faviconListener);
 
         mStop.setOnClickListener(new Button.OnClickListener() {
@@ -973,21 +977,20 @@ public class BrowserToolbar extends Geck
 
         if (image != null) {
             image = Bitmap.createScaledBitmap(image, mFaviconSize, mFaviconSize, false);
             mFavicon.setImageBitmap(image);
         } else {
             mFavicon.setImageDrawable(null);
         }
     }
-
-    private void setSecurityMode(String mode) {
-        int imageLevel = SiteIdentityPopup.getSecurityImageLevel(mode);
-        mSiteSecurity.setImageLevel(imageLevel);
-        mShowSiteSecurity = (imageLevel != SiteIdentityPopup.LEVEL_UKNOWN);
+    
+    private void setSecurityMode(SecurityMode mode) {
+        mSiteSecurity.setImageLevel(mode.ordinal());
+        mShowSiteSecurity = (mode != SecurityMode.UNKNOWN);
 
         setPageActionVisibility(mStop.getVisibility() == View.VISIBLE);
     }
 
     public void prepareTabsAnimation(PropertyAnimator animator, boolean tabsAreShown) {
         if (!tabsAreShown) {
             PropertyAnimator buttonsAnimator =
                     new PropertyAnimator(animator.getDuration(), sButtonsInterpolator);
--- a/mobile/android/base/toolbar/SiteIdentityPopup.java
+++ b/mobile/android/base/toolbar/SiteIdentityPopup.java
@@ -3,16 +3,18 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.toolbar;
 
 import org.mozilla.gecko.BrowserApp;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.GeckoAppShell;
 import org.mozilla.gecko.GeckoEvent;
+import org.mozilla.gecko.SiteIdentity;
+import org.mozilla.gecko.SiteIdentity.SecurityMode;
 import org.mozilla.gecko.widget.ArrowPopup;
 import org.mozilla.gecko.widget.DoorHanger;
 import org.mozilla.gecko.widget.DoorHanger.OnButtonClickListener;
 
 import org.json.JSONException;
 import org.json.JSONObject;
 
 import android.content.res.Resources;
@@ -26,29 +28,16 @@ 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 {
     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
-    static final int LEVEL_UKNOWN = 0;
-    static final int LEVEL_IDENTIFIED = 1;
-    static final int LEVEL_VERIFIED = 2;
-    static final int LEVEL_MIXED_CONTENT_BLOCKED = 3;
-    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 LinearLayout mIdentity;
     private TextView mHost;
@@ -61,32 +50,16 @@ public class SiteIdentityPopup extends A
 
     SiteIdentityPopup(BrowserApp activity) {
         super(activity);
 
         mResources = activity.getResources();
         mButtonClickListener = new PopupButtonListener();
     }
 
-    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);
 
@@ -94,43 +67,41 @@ public class SiteIdentityPopup extends A
         mIdentity = (LinearLayout) inflater.inflate(R.layout.site_identity, null);
         mContent.addView(mIdentity);
 
         mHost = (TextView) mIdentity.findViewById(R.id.host);
         mOwner = (TextView) mIdentity.findViewById(R.id.owner);
         mVerifier = (TextView) mIdentity.findViewById(R.id.verifier);
     }
 
-    private void setIdentity(JSONObject identityData) {
-        try {
-            String host = identityData.getString("host");
-            mHost.setText(host);
+    private void setIdentity(SiteIdentity siteIdentity) {
+        if (siteIdentity.getSecurityMode() == SecurityMode.MIXED_CONTENT_LOADED) {
+            // Hide the identity data if there isn't valid site identity data.
+            // Set some top padding on the popup content to create a of light blue
+            // between the popup arrow and the mixed content notification.
+            mContent.setPadding(0, (int) mResources.getDimension(R.dimen.identity_padding_top), 0, 0);
+            mIdentity.setVisibility(View.GONE);
+        } else {
+            mHost.setText(siteIdentity.getHost());
 
-            String owner = identityData.getString("owner");
+            String owner = siteIdentity.getOwner();
 
             // Supplemental data is optional.
-            String supplemental = identityData.optString("supplemental");
+            final String supplemental = siteIdentity.getSupplemental();
             if (!TextUtils.isEmpty(supplemental)) {
                 owner += "\n" + supplemental;
             }
             mOwner.setText(owner);
 
-            String verifier = identityData.getString("verifier");
-            String encrypted = identityData.getString("encrypted");
+            final String verifier = siteIdentity.getVerifier();
+            final String encrypted = siteIdentity.getEncrypted();
             mVerifier.setText(verifier + "\n" + encrypted);
 
             mContent.setPadding(0, 0, 0, 0);
             mIdentity.setVisibility(View.VISIBLE);
-
-        } catch (JSONException e) {
-            // Hide the identity data if there isn't valid site identity data.
-            // Set some top padding on the popup content to create a of light blue
-            // between the popup arrow and the mixed content notification.
-            mContent.setPadding(0, (int) mResources.getDimension(R.dimen.identity_padding_top), 0, 0);
-            mIdentity.setVisibility(View.GONE);
         }
     }
 
     private void addMixedContentNotification(boolean blocked) {
         // Remove any exixting mixed content notification.
         removeMixedContentNotification();
         mMixedContentNotification = new DoorHanger(mActivity, DoorHanger.Theme.DARK);
 
@@ -164,37 +135,31 @@ public class SiteIdentityPopup extends A
             mContent.removeView(mMixedContentNotification);
             mMixedContentNotification = null;
         }
     }
 
     /*
      * @param identityData A JSONObject that holds the current tab's identity data.
      */
-    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 (UNKNOWN.equals(mode)) {
+    void updateIdentity(SiteIdentity siteIdentity) {
+        final SecurityMode mode = siteIdentity.getSecurityMode();
+        if (mode == SecurityMode.UNKNOWN) {
             Log.e(LOGTAG, "Can't show site identity popup in non-identified state");
             return;
         }
 
         if (!mInflated)
             init();
 
-        setIdentity(identityData);
+        setIdentity(siteIdentity);
 
-        if (MIXED_CONTENT_BLOCKED.equals(mode) || MIXED_CONTENT_LOADED.equals(mode)) {
-            addMixedContentNotification(MIXED_CONTENT_BLOCKED.equals(mode));
+        if (mode == SecurityMode.MIXED_CONTENT_LOADED ||
+            mode == SecurityMode.MIXED_CONTENT_BLOCKED) {
+            addMixedContentNotification(mode == SecurityMode.MIXED_CONTENT_BLOCKED);
         }
     }
 
     @Override
     public void dismiss() {
         super.dismiss();
         removeMixedContentNotification();
     }