Bug 1419245 - A2HS badge is shown on site with broken HTTPS. r?walkingice draft
authorNevin Chen <cnevinchen@gmail.com>
Thu, 23 Nov 2017 17:07:59 +0800
changeset 706760 a86112ab17d4803228e5656f84d92f4ac4ba6dfd
parent 706692 709f355a7a8c4ae426d1824841a71ffdb5ce0137
child 709544 02e0774d1a9b933ab8b1cfa092bab395fab1ecf6
push id91915
push userbmo:cnevinchen@gmail.com
push dateMon, 04 Dec 2017 07:55:04 +0000
reviewerswalkingice
bugs1419245
milestone59.0a1
Bug 1419245 - A2HS badge is shown on site with broken HTTPS. r?walkingice MozReview-Commit-ID: BFWdqTcxOi9
mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java
mobile/android/base/java/org/mozilla/gecko/Tab.java
mobile/android/base/java/org/mozilla/gecko/pwa/PwaUtils.java
mobile/android/base/java/org/mozilla/gecko/toolbar/PageActionLayout.java
mobile/android/base/moz.build
--- 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',