Bug 1419245 - A2HS badge is shown on site with broken HTTPS. r?walkingice
MozReview-Commit-ID: BFWdqTcxOi9
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java
@@ -7,17 +7,16 @@ package org.mozilla.gecko;
import android.Manifest;
import android.app.Activity;
import android.app.Application;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Bitmap;
-import android.graphics.Canvas;
import android.net.Uri;
import android.os.Environment;
import android.os.Process;
import android.os.SystemClock;
import android.provider.MediaStore;
import android.support.design.widget.Snackbar;
import android.text.TextUtils;
import android.util.Base64;
@@ -34,32 +33,30 @@ import org.mozilla.gecko.distribution.Di
import org.mozilla.gecko.gfx.BitmapUtils;
import org.mozilla.gecko.home.HomePanelsManager;
import org.mozilla.gecko.icons.IconCallback;
import org.mozilla.gecko.icons.IconResponse;
import org.mozilla.gecko.icons.Icons;
import org.mozilla.gecko.lwt.LightweightTheme;
import org.mozilla.gecko.mdns.MulticastDNSManager;
import org.mozilla.gecko.media.AudioFocusAgent;
-import org.mozilla.gecko.media.RemoteManager;
import org.mozilla.gecko.notifications.NotificationClient;
import org.mozilla.gecko.notifications.NotificationHelper;
import org.mozilla.gecko.permissions.Permissions;
import org.mozilla.gecko.preferences.DistroSharedPrefsImport;
-import org.mozilla.gecko.util.ActivityUtils;
+import org.mozilla.gecko.pwa.PwaUtils;
import org.mozilla.gecko.telemetry.TelemetryBackgroundReceiver;
import org.mozilla.gecko.util.ActivityResultHandler;
import org.mozilla.gecko.util.BundleEventListener;
import org.mozilla.gecko.util.EventCallback;
import org.mozilla.gecko.util.GeckoBundle;
import org.mozilla.gecko.util.HardwareUtils;
import org.mozilla.gecko.util.PRNGFixes;
import org.mozilla.gecko.util.ShortcutUtils;
import org.mozilla.gecko.util.ThreadUtils;
-import org.mozilla.gecko.util.UIAsyncTask;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.UUID;
@@ -564,17 +561,25 @@ public class GeckoApplication extends Ap
// Creates a homescreen shortcut for a web page.
// This is the entry point from nsIShellService.
@WrapForJNI(calledFrom = "gecko")
public static void createShortcut(final String title, final String url) {
final Tab selectedTab = Tabs.getInstance().getSelectedTab();
final String manifestUrl = selectedTab.getManifestUrl();
if (manifestUrl != null) {
- // If a page has associated manifest, lets install it
+ // If a page has associated manifest, lets install it (PWA A2HS)
+ // At this time, this page must be a secure page.
+ // Please hide PWA badge UI in front end side.
+ // Otherwise we'll throw an exception here.
+ final boolean safeForPwa = PwaUtils.shouldAddPwaShortcut(selectedTab);
+ if (!safeForPwa) {
+ throw new IllegalStateException("This page is not safe for PWA");
+ }
+
final GeckoBundle message = new GeckoBundle();
message.putInt("iconSize", GeckoAppShell.getPreferredIconSize());
message.putString("manifestUrl", manifestUrl);
message.putString("originalUrl", url);
message.putString("originalTitle", title);
EventDispatcher.getInstance().dispatch("Browser:LoadManifest", message);
return;
}
--- a/mobile/android/base/java/org/mozilla/gecko/Tab.java
+++ b/mobile/android/base/java/org/mozilla/gecko/Tab.java
@@ -1,49 +1,47 @@
/* -*- 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 java.util.ArrayList;
import java.util.Map;
import java.util.concurrent.Future;
import java.util.regex.Pattern;
import org.json.JSONObject;
import org.mozilla.gecko.annotation.RobocopTarget;
import org.mozilla.gecko.db.BrowserDB;
import org.mozilla.gecko.db.URLMetadata;
import org.mozilla.gecko.gfx.BitmapUtils;
import org.mozilla.gecko.icons.IconCallback;
import org.mozilla.gecko.icons.IconDescriptor;
import org.mozilla.gecko.icons.IconRequestBuilder;
import org.mozilla.gecko.icons.IconResponse;
import org.mozilla.gecko.icons.Icons;
+import org.mozilla.gecko.pwa.PwaUtils;
import org.mozilla.gecko.reader.ReaderModeUtils;
import org.mozilla.gecko.reader.ReadingListHelper;
import org.mozilla.gecko.toolbar.BrowserToolbar.TabEditingState;
-import org.mozilla.gecko.toolbar.PageActionLayout;
import org.mozilla.gecko.util.GeckoBundle;
import org.mozilla.gecko.util.ShortcutUtils;
import org.mozilla.gecko.util.ThreadUtils;
import org.mozilla.gecko.webapps.WebAppManifest;
import org.mozilla.gecko.widget.SiteLogins;
import android.content.ContentResolver;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.util.Log;
-import android.view.View;
import static org.mozilla.gecko.toolbar.PageActionLayout.PageAction.UUID_PAGE_ACTION_PWA;
public class Tab {
private static final String LOGTAG = "GeckoTab";
private static Pattern sColorPattern;
private final int mId;
@@ -481,17 +479,17 @@ public class Tab {
}
public void updatePageAction() {
if (!ShortcutUtils.isPinShortcutSupported()) {
return;
}
if (mManifestUrl != null) {
- showPwaPageAction();
+ showPwaBadge();
} else {
clearPwaPageAction();
}
}
public void setHasOpenSearch(boolean hasOpenSearch) {
mHasOpenSearch = hasOpenSearch;
@@ -863,24 +861,26 @@ public class Tab {
public void setShouldShowToolbarWithoutAnimationOnFirstSelection(final boolean shouldShowWithoutAnimation) {
mShouldShowToolbarWithoutAnimationOnFirstSelection = shouldShowWithoutAnimation;
}
public boolean getShouldShowToolbarWithoutAnimationOnFirstSelection() {
return mShouldShowToolbarWithoutAnimationOnFirstSelection;
}
+
private void clearPwaPageAction() {
GeckoBundle bundle = new GeckoBundle();
bundle.putString("id", UUID_PAGE_ACTION_PWA);
EventDispatcher.getInstance().dispatch("PageActions:Remove", bundle);
}
- private void showPwaPageAction() {
- if (!isPrivate()) {
+
+ private void showPwaBadge() {
+ if (PwaUtils.shouldAddPwaShortcut(this)) {
GeckoBundle bundle = new GeckoBundle();
bundle.putString("id", UUID_PAGE_ACTION_PWA);
bundle.putString("title", mAppContext.getString(R.string.pwa_add_to_launcher_badge));
bundle.putString("icon", "drawable://add_to_homescreen");
bundle.putBoolean("important", true);
EventDispatcher.getInstance().dispatch("PageActions:Add", bundle);
}
}
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/pwa/PwaUtils.java
@@ -0,0 +1,31 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; 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.pwa;
+
+import android.support.annotation.CheckResult;
+
+import org.mozilla.gecko.Tab;
+
+
+public class PwaUtils {
+ /**
+ * Check if this tab should add as PWA shortcut
+ * From the document :https://developers.google.com/web/fundamentals/security/encrypt-in-transit/why-https
+ * PWA must be served from a secure origin. We also restrict it to
+ * 1. Not allow user accept exception
+ * 2. The tab can't be a private tab.
+ *
+ * @param tab Since website info is in the tab, we pass the tab here.
+ * @return true if the tab shouldAddPwaShortcut
+ */
+ @CheckResult
+ public static boolean shouldAddPwaShortcut(Tab tab) {
+ final boolean secure = tab.getSiteIdentity().isSecure();
+ // This tab is safe for pwa only when the site is absolutely secure.
+ // so no exception is allowed
+ final boolean exception = tab.getSiteIdentity().isSecurityException();
+ return !tab.isPrivate() && secure && !exception;
+ }
+}
--- a/mobile/android/base/java/org/mozilla/gecko/toolbar/PageActionLayout.java
+++ b/mobile/android/base/java/org/mozilla/gecko/toolbar/PageActionLayout.java
@@ -6,16 +6,19 @@
package org.mozilla.gecko.toolbar;
import org.mozilla.gecko.EventDispatcher;
import org.mozilla.gecko.GeckoSharedPrefs;
import org.mozilla.gecko.R;
import org.mozilla.gecko.Tab;
import org.mozilla.gecko.Tabs;
import org.mozilla.gecko.preferences.GeckoPreferences;
+import org.mozilla.gecko.pwa.PwaUtils;
+import org.mozilla.gecko.util.DrawableUtil;
+import org.mozilla.gecko.util.ResourceDrawableUtils;
import org.mozilla.gecko.util.BundleEventListener;
import org.mozilla.gecko.util.DrawableUtil;
import org.mozilla.gecko.util.EventCallback;
import org.mozilla.gecko.util.GeckoBundle;
import org.mozilla.gecko.util.ResourceDrawableUtils;
import org.mozilla.gecko.util.ShortcutUtils;
import org.mozilla.gecko.util.ThreadUtils;
import org.mozilla.gecko.widget.GeckoPopupMenu;
@@ -159,17 +162,17 @@ public class PageActionLayout extends Th
} else if ("PageActions:Remove".equals(event)) {
removePageAction(message.getString("id"));
}
}
private void maybeShowPwaOnboarding(String id) {
// only show pwa at normal mode
final Tab selectedTab = Tabs.getInstance().getSelectedTab();
- if (selectedTab.isPrivate()) {
+ if (!PwaUtils.shouldAddPwaShortcut(selectedTab)) {
return;
}
if (UUID_PAGE_ACTION_PWA.equals(id)) {
final SharedPreferences prefs = GeckoSharedPrefs.forApp(getContext());
final boolean show = prefs.getBoolean(PREF_PWA_ONBOARDING, true);
if (show && ShortcutUtils.isPinShortcutSupported()) {
PwaOnboarding.show(getContext());
prefs.edit().putBoolean(PREF_PWA_ONBOARDING, false).apply();
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -828,16 +828,17 @@ gbjar.sources += ['java/org/mozilla/geck
'prompts/IntentChooserPrompt.java',
'prompts/IntentHandler.java',
'prompts/Prompt.java',
'prompts/PromptInput.java',
'prompts/PromptListAdapter.java',
'prompts/PromptListItem.java',
'prompts/PromptService.java',
'prompts/TabInput.java',
+ 'pwa/PwaUtils.java',
'reader/ReaderModeUtils.java',
'reader/ReadingListHelper.java',
'reader/SavedReaderViewHelper.java',
'RemoteClientsDialogFragment.java',
'Restarter.java',
'restrictions/DefaultConfiguration.java',
'restrictions/GuestProfileConfiguration.java',
'restrictions/Restrictable.java',