/* 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.goanna.toolbar; import org.mozilla.goanna.AboutPages; import org.mozilla.goanna.R; import org.mozilla.goanna.GoannaAppShell; import org.mozilla.goanna.GoannaEvent; import org.mozilla.goanna.SiteIdentity; import org.mozilla.goanna.SiteIdentity.SecurityMode; import org.mozilla.goanna.SiteIdentity.MixedMode; import org.mozilla.goanna.SiteIdentity.TrackingMode; import org.mozilla.goanna.Tab; import org.mozilla.goanna.Tabs; import org.mozilla.goanna.widget.ArrowPopup; import org.mozilla.goanna.widget.DoorHanger; import org.mozilla.goanna.widget.DoorHanger.OnButtonClickListener; import org.json.JSONException; import org.json.JSONObject; import android.content.Context; import android.text.TextUtils; import android.util.Log; import android.view.LayoutInflater; import android.view.View; 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 { private static final String LOGTAG = "GoannaSiteIdentityPopup"; private static final String MIXED_CONTENT_SUPPORT_URL = "https://support.mozilla.org/kb/how-does-insecure-content-affect-safety-android"; private static final String TRACKING_CONTENT_SUPPORT_URL = "https://support.mozilla.org/kb/firefox-android-tracking-protection"; private SiteIdentity mSiteIdentity; private LinearLayout mIdentity; private LinearLayout mIdentityKnownContainer; private LinearLayout mIdentityUnknownContainer; private TextView mHost; private TextView mOwnerLabel; private TextView mOwner; private TextView mVerifier; private DoorHanger mMixedContentNotification; private DoorHanger mTrackingContentNotification; private final OnButtonClickListener mButtonClickListener; public SiteIdentityPopup(Context context) { super(context); mButtonClickListener = new PopupButtonListener(); } @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); LayoutInflater inflater = LayoutInflater.from(mContext); mIdentity = (LinearLayout) inflater.inflate(R.layout.site_identity, null); mContent.addView(mIdentity); mIdentityKnownContainer = (LinearLayout) mIdentity.findViewById(R.id.site_identity_known_container); mIdentityUnknownContainer = (LinearLayout) mIdentity.findViewById(R.id.site_identity_unknown_container); mHost = (TextView) mIdentityKnownContainer.findViewById(R.id.host); mOwnerLabel = (TextView) mIdentityKnownContainer.findViewById(R.id.owner_label); mOwner = (TextView) mIdentityKnownContainer.findViewById(R.id.owner); mVerifier = (TextView) mIdentityKnownContainer.findViewById(R.id.verifier); } private void updateIdentity(final SiteIdentity siteIdentity) { if (!mInflated) { init(); } final boolean isIdentityKnown = (siteIdentity.getSecurityMode() != SecurityMode.UNKNOWN); toggleIdentityKnownContainerVisibility(isIdentityKnown); if (isIdentityKnown) { updateIdentityInformation(siteIdentity); } } private void toggleIdentityKnownContainerVisibility(final boolean isIdentityKnown) { if (isIdentityKnown) { mIdentityKnownContainer.setVisibility(View.VISIBLE); mIdentityUnknownContainer.setVisibility(View.GONE); } else { mIdentityKnownContainer.setVisibility(View.GONE); mIdentityUnknownContainer.setVisibility(View.VISIBLE); } } private void updateIdentityInformation(final SiteIdentity siteIdentity) { mHost.setText(siteIdentity.getHost()); String owner = siteIdentity.getOwner(); if (owner == null) { mOwnerLabel.setVisibility(View.GONE); mOwner.setVisibility(View.GONE); } else { mOwnerLabel.setVisibility(View.VISIBLE); mOwner.setVisibility(View.VISIBLE); // Supplemental data is optional. final String supplemental = siteIdentity.getSupplemental(); if (!TextUtils.isEmpty(supplemental)) { owner += "\n" + supplemental; } mOwner.setText(owner); } final String verifier = siteIdentity.getVerifier(); final String encrypted = siteIdentity.getEncrypted(); mVerifier.setText(verifier + "\n" + encrypted); } private void addMixedContentNotification(boolean blocked) { // Remove any existing mixed content notification. removeMixedContentNotification(); mMixedContentNotification = new DoorHanger(mContext, DoorHanger.Theme.DARK); int icon; String message; if (blocked) { icon = R.drawable.shield_enabled_doorhanger; message = mContext.getString(R.string.blocked_mixed_content_message_top) + "\n\n" + mContext.getString(R.string.blocked_mixed_content_message_bottom); } else { icon = R.drawable.shield_disabled_doorhanger; message = mContext.getString(R.string.loaded_mixed_content_message); } mMixedContentNotification.setIcon(icon); mMixedContentNotification.setMessage(message); mMixedContentNotification.addLink(mContext.getString(R.string.learn_more), MIXED_CONTENT_SUPPORT_URL, "\n\n"); addNotificationButtons(mMixedContentNotification, blocked); mContent.addView(mMixedContentNotification); } private void removeMixedContentNotification() { if (mMixedContentNotification != null) { mContent.removeView(mMixedContentNotification); mMixedContentNotification = null; } } private void addTrackingContentNotification(boolean blocked) { // Remove any existing tracking content notification. removeTrackingContentNotification(); mTrackingContentNotification = new DoorHanger(mContext, DoorHanger.Theme.DARK); int icon; String message; if (blocked) { icon = R.drawable.shield_enabled_doorhanger; message = mContext.getString(R.string.blocked_tracking_content_message_top) + "\n\n" + mContext.getString(R.string.blocked_tracking_content_message_bottom); } else { icon = R.drawable.shield_disabled_doorhanger; message = mContext.getString(R.string.loaded_tracking_content_message_top) + "\n\n" + mContext.getString(R.string.loaded_tracking_content_message_bottom); } mTrackingContentNotification.setIcon(icon); mTrackingContentNotification.setMessage(message); mTrackingContentNotification.addLink(mContext.getString(R.string.learn_more), TRACKING_CONTENT_SUPPORT_URL, "\n\n"); addNotificationButtons(mTrackingContentNotification, blocked); mContent.addView(mTrackingContentNotification); } private void removeTrackingContentNotification() { if (mTrackingContentNotification != null) { mContent.removeView(mTrackingContentNotification); mTrackingContentNotification = null; } } private void addNotificationButtons(DoorHanger dh, boolean blocked) { if (blocked) { dh.addButton(mContext.getString(R.string.disable_protection), "disable", mButtonClickListener); dh.addButton(mContext.getString(R.string.keep_blocking), "keepBlocking", mButtonClickListener); } else { dh.addButton(mContext.getString(R.string.enable_protection), "enable", mButtonClickListener); } } /* * @param identityData A JSONObject that holds the current tab's identity data. */ void setSiteIdentity(SiteIdentity siteIdentity) { mSiteIdentity = siteIdentity; } @Override public void show() { if (mSiteIdentity == null) { Log.e(LOGTAG, "Can't show site identity popup for undefined state"); return; } // about: has an unknown SiteIdentity in code, but showing "This // site's identity is unknown" is misleading! So don't show a popup. final Tab selectedTab = Tabs.getInstance().getSelectedTab(); if (selectedTab != null && AboutPages.isAboutPage(selectedTab.getURL())) { Log.d(LOGTAG, "We don't show site identity popups for about: pages"); return; } updateIdentity(mSiteIdentity); final MixedMode mixedMode = mSiteIdentity.getMixedMode(); if (mixedMode != MixedMode.UNKNOWN) { addMixedContentNotification(mixedMode == MixedMode.MIXED_CONTENT_BLOCKED); } final TrackingMode trackingMode = mSiteIdentity.getTrackingMode(); if (trackingMode != TrackingMode.UNKNOWN) { addTrackingContentNotification(trackingMode == TrackingMode.TRACKING_CONTENT_BLOCKED); } showDividers(); super.show(); } // Show the right dividers private void showDividers() { final int count = mContent.getChildCount(); DoorHanger lastVisibleDoorHanger = null; for (int i = 0; i < count; i++) { final View child = mContent.getChildAt(i); if (!(child instanceof DoorHanger)) { continue; } DoorHanger dh = (DoorHanger) child; dh.showDivider(); if (dh.getVisibility() == View.VISIBLE) { lastVisibleDoorHanger = dh; } } if (lastVisibleDoorHanger != null) { lastVisibleDoorHanger.hideDivider(); } } @Override public void dismiss() { super.dismiss(); removeMixedContentNotification(); removeTrackingContentNotification(); } private class PopupButtonListener implements OnButtonClickListener { @Override public void onButtonClick(DoorHanger dh, String tag) { try { JSONObject data = new JSONObject(); data.put("allowContent", tag.equals("disable")); data.put("contentType", (dh == mMixedContentNotification ? "mixed" : "tracking")); GoannaEvent e = GoannaEvent.createBroadcastEvent("Session:Reload", data.toString()); GoannaAppShell.sendEventToGoanna(e); } catch (JSONException e) { Log.e(LOGTAG, "Exception creating message to enable/disable content blocking", e); } dismiss(); } } }