Bug 1409191 - Prefetch manifest before install. r?snorp
This is a "hacked" fix. The key idea is to specify the value in manifest on "add to home screen confirm prompt."
We need to reuse prefetch result for manifest.install().
The plan is to land this patch before Chrome Dev Summit(10/30) for demostration and fix the rest of the issue in a follow up bug.
MozReview-Commit-ID: A4B0ZK7UjyK
--- a/dom/manifest/Manifest.jsm
+++ b/dom/manifest/Manifest.jsm
@@ -74,16 +74,27 @@ class Manifest {
this._browser = browser;
}
async initialise() {
this._store = new JSONFile({path: this._path});
await this._store.load();
}
+ async prefetch(browser) {
+ const manifestData = await ManifestObtainer.browserObtainManifest(browser);
+ const icon = await ManifestIcons.browserFetchIcon(browser, manifestData, 192);
+ const data = {
+ installed: false,
+ manifest: manifestData,
+ cached_icon: icon
+ };
+ return data;
+ }
+
async install() {
const manifestData = await ManifestObtainer.browserObtainManifest(this._browser);
this._store.data = {
installed: true,
manifest: manifestData
};
Manifests.manifestInstalled(this);
this._store.saveSoon();
--- a/mobile/android/base/java/org/mozilla/gecko/Tab.java
+++ b/mobile/android/base/java/org/mozilla/gecko/Tab.java
@@ -21,16 +21,17 @@ import org.mozilla.gecko.icons.IconReque
import org.mozilla.gecko.icons.IconResponse;
import org.mozilla.gecko.icons.Icons;
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.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;
@@ -55,16 +56,17 @@ public class Tab {
private String mFaviconUrl;
private String mApplicationId; // Intended to be null after explicit user action.
private IconRequestBuilder mIconRequestBuilder;
private Future<IconResponse> mRunningIconRequest;
private boolean mHasFeeds;
private String mManifestUrl;
+ private WebAppManifest mWebAppManifest;
private boolean mHasOpenSearch;
private final SiteIdentity mSiteIdentity;
private SiteLogins mSiteLogins;
private BitmapDrawable mThumbnail;
private volatile int mParentId;
// Indicates the url was loaded from a source external to the app. This will be cleared
// when the user explicitly loads a new url (e.g. clicking a link is not explicit).
private final boolean mExternal;
@@ -107,16 +109,24 @@ public class Tab {
public static final int STATE_ERROR = 3;
public static final int LOAD_PROGRESS_INIT = 10;
public static final int LOAD_PROGRESS_START = 20;
public static final int LOAD_PROGRESS_LOCATION_CHANGE = 60;
public static final int LOAD_PROGRESS_LOADED = 80;
public static final int LOAD_PROGRESS_STOP = 100;
+ public WebAppManifest getWebAppManifest() {
+ return mWebAppManifest;
+ }
+
+ public void setWebAppManifest(WebAppManifest mWebAppManifest) {
+ this.mWebAppManifest = mWebAppManifest;
+ }
+
public enum ErrorType {
CERT_ERROR, // Pages with certificate problems
BLOCKED, // Pages blocked for phishing or malware warnings
NET_ERROR, // All other types of error
NONE // Non error pages
}
public Tab(Context context, int id, String url, boolean external, int parentId, String title) {
--- a/mobile/android/base/java/org/mozilla/gecko/Tabs.java
+++ b/mobile/android/base/java/org/mozilla/gecko/Tabs.java
@@ -25,16 +25,17 @@ import org.mozilla.gecko.mozglue.SafeInt
import org.mozilla.gecko.notifications.WhatsNewReceiver;
import org.mozilla.gecko.preferences.GeckoPreferences;
import org.mozilla.gecko.reader.ReaderModeUtils;
import org.mozilla.gecko.util.BundleEventListener;
import org.mozilla.gecko.util.EventCallback;
import org.mozilla.gecko.util.GeckoBundle;
import org.mozilla.gecko.util.JavaUtil;
import org.mozilla.gecko.util.ThreadUtils;
+import org.mozilla.gecko.webapps.WebAppManifest;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.OnAccountsUpdateListener;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
import android.database.sqlite.SQLiteException;
@@ -645,17 +646,21 @@ public class Tabs implements BundleEvent
} else if ("Link:Feed".equals(event)) {
tab.setHasFeeds(true);
notifyListeners(tab, TabEvents.LINK_FEED);
} else if ("Link:OpenSearch".equals(event)) {
tab.setHasOpenSearch(message.getBoolean("visible"));
} else if ("Link:Manifest".equals(event)) {
- tab.setManifestUrl(message.getString("href"));
+ final String url = message.getString("href");
+ final String manifest = message.getString("manifest");
+
+ tab.setManifestUrl(url);
+ tab.setWebAppManifest(WebAppManifest.fromString(url, manifest));
} else if ("DesktopMode:Changed".equals(event)) {
tab.setDesktopMode(message.getBoolean("desktopMode"));
notifyListeners(tab, TabEvents.DESKTOP_MODE_CHANGE);
} else if ("Tab:RecordingChange".equals(event)) {
tab.setRecording(message.getBoolean("recording"));
notifyListeners(tab, TabEvents.RECORDING_CHANGE);
--- a/mobile/android/base/java/org/mozilla/gecko/toolbar/PwaConfirm.java
+++ b/mobile/android/base/java/org/mozilla/gecko/toolbar/PwaConfirm.java
@@ -17,16 +17,17 @@ import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import org.mozilla.gecko.GeckoApplication;
import org.mozilla.gecko.R;
import org.mozilla.gecko.Tab;
import org.mozilla.gecko.Tabs;
+import org.mozilla.gecko.webapps.WebAppManifest;
public class PwaConfirm extends RelativeLayout {
boolean isAnimating = false;
public static PwaConfirm show(Context context) {
if (context instanceof Activity) {
@@ -110,21 +111,25 @@ public class PwaConfirm extends Relative
}
};
findViewById(R.id.pwa_confirm_root).setOnClickListener(dismiss);
findViewById(R.id.pwa_confirm_cancel).setOnClickListener(dismiss);
findViewById(R.id.pwa_confirm_action).setOnClickListener(createShortcut);
final Tab selectedTab = Tabs.getInstance().getSelectedTab();
+ if (selectedTab == null) {
+ return;
+ }
+ final WebAppManifest webAppManifest = selectedTab.getWebAppManifest();
- if (selectedTab != null) {
- ((TextView) findViewById(R.id.pwa_confirm_title)).setText(selectedTab.getTitle());
- ((TextView) findViewById(R.id.pwa_confirm_url)).setText(selectedTab.getURL());
- ((ImageView) findViewById(R.id.pwa_confirm_icon)).setImageBitmap(selectedTab.getFavicon());
+ if (webAppManifest != null) {
+ ((TextView) findViewById(R.id.pwa_confirm_title)).setText(webAppManifest.getName());
+ ((TextView) findViewById(R.id.pwa_confirm_url)).setText(webAppManifest.getStartUri().toString());
+ ((ImageView) findViewById(R.id.pwa_confirm_icon)).setImageBitmap(webAppManifest.getIcon());
}
}
private void setupBackpress() {
setFocusableInTouchMode(true);
requestFocus();
setOnKeyListener(new OnKeyListener() {
@Override
--- a/mobile/android/base/java/org/mozilla/gecko/webapps/WebAppManifest.java
+++ b/mobile/android/base/java/org/mozilla/gecko/webapps/WebAppManifest.java
@@ -63,16 +63,29 @@ public class WebAppManifest {
return new WebAppManifest(manifestUri, manifest, manifestField);
} catch (Exception e) {
Log.e(LOGTAG, "Failed to read webapp manifest", e);
return null;
}
}
+ public static WebAppManifest fromString(final String url, final String input) {
+ try {
+ final Uri manifestUri = Uri.parse(url);
+ final JSONObject manifest = new JSONObject(input);
+ final JSONObject manifestField = manifest.getJSONObject("manifest");
+
+ return new WebAppManifest(manifestUri, manifest, manifestField);
+ } catch (Exception e) {
+ Log.e(LOGTAG, "Failed to read webapp manifest", e);
+ return null;
+ }
+ }
+
private WebAppManifest(final Uri uri, final JSONObject manifest, final JSONObject manifestField) {
mManifestUri = uri;
readManifest(manifest, manifestField);
}
public Integer getThemeColor() {
return mThemeColor;
}
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -3945,22 +3945,30 @@ Tab.prototype = {
type: "Link:Feed",
tabID: this.id
};
} catch (e) {
return null;
}
},
- makeManifestMessage: function(target) {
- return {
- type: "Link:Manifest",
- href: target.href,
- tabID: this.id
- };
+ makeManifestMessage: async function(target) {
+ try {
+ const manifest = await Manifests.getManifest(this.browser, target.href);
+ const data = await manifest.prefetch(this.browser);
+ var cache = {
+ type: "Link:Manifest",
+ href: target.href,
+ manifest: JSON.stringify(data),
+ tabID: this.id
+ };
+ GlobalEventDispatcher.sendRequest(cache);
+ } catch (err) {
+ Log.e("Browser", "error when makeManifestMessage:" + err);
+ }
},
sendOpenSearchMessage: function(eventTarget) {
let type = eventTarget.type && eventTarget.type.toLowerCase();
// Replace all starting or trailing spaces or spaces before "*;" globally w/ "".
type = type.replace(/^\s+|\s*(?:;.*)?$/g, "");
// Check that type matches opensearch.
@@ -4186,17 +4194,18 @@ Tab.prototype = {
return;
jsonMessage = this.makeFeedMessage(target, type);
} else if (list.indexOf("[search]") != -1 && aEvent.type == "DOMLinkAdded") {
this.sendOpenSearchMessage(target);
} else if (list.indexOf("[manifest]") != -1 &&
aEvent.type == "DOMLinkAdded" &&
SharedPreferences.forApp().getBoolPref("android.not_a_preference.pwa")){
- jsonMessage = this.makeManifestMessage(target);
+ this.makeManifestMessage(target);
+ return;
}
if (!jsonMessage)
return;
GlobalEventDispatcher.sendRequest(jsonMessage);
break;
}