|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
|
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 package org.mozilla.gecko.toolbar; |
|
6 |
|
7 import org.mozilla.gecko.BrowserApp; |
|
8 import org.mozilla.gecko.R; |
|
9 import org.mozilla.gecko.GeckoAppShell; |
|
10 import org.mozilla.gecko.GeckoEvent; |
|
11 import org.mozilla.gecko.SiteIdentity; |
|
12 import org.mozilla.gecko.SiteIdentity.SecurityMode; |
|
13 import org.mozilla.gecko.widget.ArrowPopup; |
|
14 import org.mozilla.gecko.widget.DoorHanger; |
|
15 import org.mozilla.gecko.widget.DoorHanger.OnButtonClickListener; |
|
16 |
|
17 import org.json.JSONException; |
|
18 import org.json.JSONObject; |
|
19 |
|
20 import android.content.res.Resources; |
|
21 import android.text.TextUtils; |
|
22 import android.util.Log; |
|
23 import android.view.LayoutInflater; |
|
24 import android.view.View; |
|
25 import android.widget.ImageView; |
|
26 import android.widget.LinearLayout; |
|
27 import android.widget.TextView; |
|
28 |
|
29 /** |
|
30 * SiteIdentityPopup is a singleton class that displays site identity data in |
|
31 * an arrow panel popup hanging from the lock icon in the browser toolbar. |
|
32 */ |
|
33 public class SiteIdentityPopup extends ArrowPopup { |
|
34 private static final String LOGTAG = "GeckoSiteIdentityPopup"; |
|
35 |
|
36 // FIXME: Update this URL for mobile. See bug 885923. |
|
37 private static final String MIXED_CONTENT_SUPPORT_URL = |
|
38 "https://support.mozilla.org/kb/how-does-content-isnt-secure-affect-my-safety"; |
|
39 |
|
40 private SiteIdentity mSiteIdentity; |
|
41 |
|
42 private Resources mResources; |
|
43 |
|
44 private LinearLayout mIdentity; |
|
45 private TextView mHost; |
|
46 private TextView mOwner; |
|
47 private TextView mVerifier; |
|
48 |
|
49 private DoorHanger mMixedContentNotification; |
|
50 |
|
51 private final OnButtonClickListener mButtonClickListener; |
|
52 |
|
53 SiteIdentityPopup(BrowserApp activity) { |
|
54 super(activity); |
|
55 |
|
56 mResources = activity.getResources(); |
|
57 mButtonClickListener = new PopupButtonListener(); |
|
58 } |
|
59 |
|
60 @Override |
|
61 protected void init() { |
|
62 super.init(); |
|
63 |
|
64 // Make the popup focusable so it doesn't inadvertently trigger click events elsewhere |
|
65 // which may reshow the popup (see bug 785156) |
|
66 setFocusable(true); |
|
67 |
|
68 LayoutInflater inflater = LayoutInflater.from(mActivity); |
|
69 mIdentity = (LinearLayout) inflater.inflate(R.layout.site_identity, null); |
|
70 mContent.addView(mIdentity); |
|
71 |
|
72 mHost = (TextView) mIdentity.findViewById(R.id.host); |
|
73 mOwner = (TextView) mIdentity.findViewById(R.id.owner); |
|
74 mVerifier = (TextView) mIdentity.findViewById(R.id.verifier); |
|
75 } |
|
76 |
|
77 private void updateUi() { |
|
78 if (!mInflated) { |
|
79 init(); |
|
80 } |
|
81 |
|
82 if (mSiteIdentity.getSecurityMode() == SecurityMode.MIXED_CONTENT_LOADED || |
|
83 mSiteIdentity.getSecurityMode() == SecurityMode.MIXED_CONTENT_BLOCKED) { |
|
84 // Hide the identity data if there isn't valid site identity data. |
|
85 // Set some top padding on the popup content to create a of light blue |
|
86 // between the popup arrow and the mixed content notification. |
|
87 mContent.setPadding(0, (int) mResources.getDimension(R.dimen.identity_padding_top), 0, 0); |
|
88 mIdentity.setVisibility(View.GONE); |
|
89 } else { |
|
90 mHost.setText(mSiteIdentity.getHost()); |
|
91 |
|
92 String owner = mSiteIdentity.getOwner(); |
|
93 |
|
94 // Supplemental data is optional. |
|
95 final String supplemental = mSiteIdentity.getSupplemental(); |
|
96 if (!TextUtils.isEmpty(supplemental)) { |
|
97 owner += "\n" + supplemental; |
|
98 } |
|
99 mOwner.setText(owner); |
|
100 |
|
101 final String verifier = mSiteIdentity.getVerifier(); |
|
102 final String encrypted = mSiteIdentity.getEncrypted(); |
|
103 mVerifier.setText(verifier + "\n" + encrypted); |
|
104 |
|
105 mContent.setPadding(0, 0, 0, 0); |
|
106 mIdentity.setVisibility(View.VISIBLE); |
|
107 } |
|
108 } |
|
109 |
|
110 private void addMixedContentNotification(boolean blocked) { |
|
111 // Remove any exixting mixed content notification. |
|
112 removeMixedContentNotification(); |
|
113 mMixedContentNotification = new DoorHanger(mActivity, DoorHanger.Theme.DARK); |
|
114 |
|
115 String message; |
|
116 if (blocked) { |
|
117 message = mActivity.getString(R.string.blocked_mixed_content_message_top) + "\n\n" + |
|
118 mActivity.getString(R.string.blocked_mixed_content_message_bottom); |
|
119 } else { |
|
120 message = mActivity.getString(R.string.loaded_mixed_content_message); |
|
121 } |
|
122 mMixedContentNotification.setMessage(message); |
|
123 mMixedContentNotification.addLink(mActivity.getString(R.string.learn_more), MIXED_CONTENT_SUPPORT_URL, "\n\n"); |
|
124 |
|
125 if (blocked) { |
|
126 mMixedContentNotification.setIcon(R.drawable.shield_doorhanger); |
|
127 mMixedContentNotification.addButton(mActivity.getString(R.string.disable_protection), |
|
128 "disable", mButtonClickListener); |
|
129 mMixedContentNotification.addButton(mActivity.getString(R.string.keep_blocking), |
|
130 "keepBlocking", mButtonClickListener); |
|
131 } else { |
|
132 mMixedContentNotification.setIcon(R.drawable.warning_doorhanger); |
|
133 mMixedContentNotification.addButton(mActivity.getString(R.string.enable_protection), |
|
134 "enable", mButtonClickListener); |
|
135 } |
|
136 |
|
137 mContent.addView(mMixedContentNotification); |
|
138 } |
|
139 |
|
140 private void removeMixedContentNotification() { |
|
141 if (mMixedContentNotification != null) { |
|
142 mContent.removeView(mMixedContentNotification); |
|
143 mMixedContentNotification = null; |
|
144 } |
|
145 } |
|
146 |
|
147 /* |
|
148 * @param identityData A JSONObject that holds the current tab's identity data. |
|
149 */ |
|
150 void setSiteIdentity(SiteIdentity siteIdentity) { |
|
151 mSiteIdentity = siteIdentity; |
|
152 } |
|
153 |
|
154 @Override |
|
155 public void show() { |
|
156 if (mSiteIdentity == null) { |
|
157 Log.e(LOGTAG, "Can't show site identity popup for undefined state"); |
|
158 return; |
|
159 } |
|
160 |
|
161 final SecurityMode mode = mSiteIdentity.getSecurityMode(); |
|
162 if (mode == SecurityMode.UNKNOWN) { |
|
163 Log.e(LOGTAG, "Can't show site identity popup in non-identified state"); |
|
164 return; |
|
165 } |
|
166 |
|
167 updateUi(); |
|
168 |
|
169 if (mode == SecurityMode.MIXED_CONTENT_LOADED || |
|
170 mode == SecurityMode.MIXED_CONTENT_BLOCKED) { |
|
171 addMixedContentNotification(mode == SecurityMode.MIXED_CONTENT_BLOCKED); |
|
172 } |
|
173 |
|
174 super.show(); |
|
175 } |
|
176 |
|
177 @Override |
|
178 public void dismiss() { |
|
179 super.dismiss(); |
|
180 removeMixedContentNotification(); |
|
181 } |
|
182 |
|
183 private class PopupButtonListener implements OnButtonClickListener { |
|
184 @Override |
|
185 public void onButtonClick(DoorHanger dh, String tag) { |
|
186 try { |
|
187 JSONObject data = new JSONObject(); |
|
188 data.put("allowMixedContent", tag.equals("disable")); |
|
189 GeckoEvent e = GeckoEvent.createBroadcastEvent("Session:Reload", data.toString()); |
|
190 GeckoAppShell.sendEventToGecko(e); |
|
191 } catch (JSONException e) { |
|
192 Log.e(LOGTAG, "Exception creating message to enable/disable mixed content blocking", e); |
|
193 } |
|
194 |
|
195 dismiss(); |
|
196 } |
|
197 } |
|
198 } |