Chromium Code Reviews| Index: chrome/android/java/src/org/chromium/chrome/browser/webapps/ManifestUpgradeDetector.java |
| diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/ManifestUpgradeDetector.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/ManifestUpgradeDetector.java |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..b80f1a8cc0137121eabf66c1a45b12ffdfcec2f7 |
| --- /dev/null |
| +++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/ManifestUpgradeDetector.java |
| @@ -0,0 +1,216 @@ |
| +// Copyright 2016 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +package org.chromium.chrome.browser.webapps; |
| + |
| +import android.content.pm.ApplicationInfo; |
| +import android.content.pm.PackageManager; |
| +import android.net.Uri; |
| + |
| +import org.chromium.base.CommandLine; |
| +import org.chromium.base.ContextUtils; |
| +import org.chromium.base.Log; |
| +import org.chromium.base.VisibleForTesting; |
| +import org.chromium.base.annotations.CalledByNative; |
| +import org.chromium.chrome.browser.tab.EmptyTabObserver; |
| +import org.chromium.chrome.browser.tab.Tab; |
| +import org.chromium.content_public.browser.WebContents; |
| + |
| +/** |
| + * This class interacts with C++ to detect whether resources in web manifest of a WebAPK has been |
|
pkotwicz
2016/07/20 21:47:53
Nit: has -> have
Xi Han
2016/07/22 17:13:11
Done.
|
| + * updated. |
| + */ |
| +public class ManifestUpgradeDetector extends EmptyTabObserver { |
| + @VisibleForTesting |
| + public static final String SWITCH_USE_START_URL_FOR_TESTING = |
| + "switch-use-start-url-for-testing"; |
| + /** |
| + * The META_* is the metadata stored in WebAPK's AndroidManifest.xml.The values must stay in |
|
pkotwicz
2016/07/20 21:47:53
How about: "The names of <meta-data> in the WebAPK
Xi Han
2016/07/22 17:13:11
Done.
|
| + * sync with {@link org.chromium.webapk.lib.runtime_library.HostBrowserLauncher}. |
| + */ |
| + private static final String META_DATA_START_URL = "startUrl"; |
| + |
| + /** Pointer to the native side ManifestUpgradeDetector. */ |
| + private long mNativePointer; |
| + |
| + /** The tab that is being observed. */ |
| + private final Tab mTab; |
| + |
| + private WebappInfo mWebappInfo; |
| + private String mStartUrl; |
| + |
| + private static final String TAG = "cr_UpgradeDetector"; |
| + |
| + public ManifestUpgradeDetector(Tab tab, WebappInfo info) { |
| + mTab = tab; |
| + mWebappInfo = info; |
| + } |
| + |
| + @VisibleForTesting |
| + public void setStartUrl(String startUrl) { |
| + mStartUrl = startUrl; |
| + } |
| + |
| + /** |
| + * Starts fetching the web manifest resources. |
| + */ |
| + public void start() { |
| + if (mWebappInfo.webManifestUri() == null |
| + || mWebappInfo.webManifestUri().equals(Uri.EMPTY)) { |
| + return; |
| + } |
| + |
| + if (mNativePointer == 0) { |
| + getMetaDataFromAndroidManifest(); |
| + mNativePointer = nativeInitialize(mTab.getWebContents(), |
| + mWebappInfo.scopeUri().toString(), mWebappInfo.webManifestUri().toString()); |
| + } |
| + mTab.addObserver(this); |
| + nativeStart(mNativePointer); |
| + } |
| + |
| + private void getMetaDataFromAndroidManifest() { |
| + if (CommandLine.getInstance().hasSwitch(SWITCH_USE_START_URL_FOR_TESTING)) { |
| + // The start URL is set via {@link setStartUrl()} for testing. |
| + return; |
| + } |
| + try { |
| + ApplicationInfo appinfo = |
| + ContextUtils.getApplicationContext().getPackageManager().getApplicationInfo( |
| + mWebappInfo.webApkPackageName(), PackageManager.GET_META_DATA); |
| + mStartUrl = appinfo.metaData.getString(META_DATA_START_URL); |
| + } catch (PackageManager.NameNotFoundException e) { |
| + e.printStackTrace(); |
| + } |
| + } |
| + |
| + /** |
| + * Puts the object in a state where it is safe to be destroyed. |
| + */ |
| + public void destroy() { |
| + if (mNativePointer != 0) { |
| + nativeDestroy(mNativePointer); |
| + } |
| + mNativePointer = 0; |
| + } |
| + |
| + @Override |
| + public void onWebContentsSwapped(Tab tab, boolean didStartLoad, |
| + boolean didFinishLoad) { |
| + updatePointers(); |
| + } |
| + |
| + @Override |
| + public void onContentChanged(Tab tab) { |
| + updatePointers(); |
| + } |
| + |
| + /** |
| + * Updates which WebContents the native ManifestUpgradeDetector is monitoring. |
| + */ |
| + private void updatePointers() { |
| + nativeReplaceWebContents(mNativePointer, mTab.getWebContents()); |
| + } |
| + |
| + /** |
| + * Checks whether an upgraded WebAPK needs to be downloaded from the WebAPK server. |
| + */ |
| + @CalledByNative |
| + private void onDataAvailable(String startUrl, String scope, String name, String shortName, |
| + int displayMode, int orientation, long themeColor, long backgroundColor) { |
| + mTab.removeObserver(this); |
| + destroy(); |
| + |
| + // TODO(hanxi): crbug.com/627824. Validate whether the new WebappInfo is |
| + // WebAPK-compatible. |
| + final WebappInfo newInfo = WebappInfo.create(mWebappInfo.id(), startUrl, |
| + scope, mWebappInfo.encodedIcon(), name, shortName, displayMode, orientation, |
| + mWebappInfo.source(), themeColor, backgroundColor, false, |
|
pkotwicz
2016/07/20 21:47:53
Can we do mWebappInfo.isIconGenerated() instead of
Xi Han
2016/07/22 17:13:11
Done.
|
| + mWebappInfo.webApkPackageName(), mWebappInfo.webManifestUri().toString()); |
| + if (requireUpgrade(newInfo, startUrl)) { |
| + upgrade(); |
| + } |
| + updateWebappDataStorage(newInfo); |
| + } |
| + |
| + /** |
| + * Checks whether properties, like start URL, short name, background color, are changed. The |
| + * change of any of them requires an upgrade of the WebAPK. {@link mWebappInfo} keeps the |
| + * latest version of these properties, so no metadata query is needed. |
| + */ |
| + protected boolean requireUpgrade(WebappInfo newInfo, String startUrl) { |
| + // Scope is used by WebAPK's intent filter. It determines which URLs are opened by |
| + // the WebAPK instead of Chrome. |
| + boolean scopeMatch = mWebappInfo.scopeUri().equals(newInfo.scopeUri()); |
| + if (!scopeMatch) { |
| + // Sometimes the scope doesn't match due to a missing "/" at the end of the scope URL. |
| + // Print log to find such cases. |
| + Log.d(TAG, "Needs to request update since the scope from WebappInfo (%s) doesn't match" |
| + + "the one fetched from Web Manifest(%s).", mWebappInfo.scopeUri().toString(), |
| + newInfo.scopeUri().toString()); |
| + return true; |
| + } |
| + |
| + // Start URL is the initial URL when the WebAPK is launched. A change in the |
| + // start URL requires the WebAPK to be re-installed because the initial URL starts |
| + // loading prior to the WebAPK's WebappDataStorage being fetched. |
| + if (!mStartUrl.equals(startUrl)) { |
| + return true; |
| + } |
| + |
| + // Short name is used as the WebAPK's name in the app list. |
| + if (!mWebappInfo.shortName().equals(newInfo.shortName())) { |
| + return true; |
| + } |
| + |
| + // Background color is the splash screen's background color. A change in the |
| + // background color requires the WebAPK to be re-installed because a |
| + // background-color-only splash screen is displayed prior to the WebAPK's |
| + // WebappDataStorage being fetched. There can be a several second delay between the |
| + // background-color-only splash screen being displayed and the WebAPK's |
| + // WebappDataStorage being fetched. |
| + if (mWebappInfo.backgroundColor() != newInfo.backgroundColor()) { |
| + return true; |
| + } |
| + |
| + // TODO(hanxi): Add icon comparison. |
| + return false; |
| + } |
| + |
| + /** |
| + * Updates WebappDataStorage. |
| + */ |
| + private void updateWebappDataStorage(WebappInfo newInfo) { |
| + mWebappInfo = newInfo; |
| + |
| + WebappRegistry.getWebappDataStorage(ContextUtils.getApplicationContext(), mWebappInfo.id(), |
| + new WebappRegistry.FetchWebappDataStorageCallback() { |
| + @Override |
| + public void onWebappDataStorageRetrieved(WebappDataStorage storage) { |
| + if (storage == null) { |
| + WebappRegistry.registerWebapp(ContextUtils.getApplicationContext(), |
| + mWebappInfo.id(), |
| + new WebappRegistry.FetchWebappDataStorageCallback() { |
| + @Override |
| + public void onWebappDataStorageRetrieved( |
| + WebappDataStorage storage) { |
| + storage.updateFromWebappInfo(mWebappInfo); |
| + } |
| + }); |
| + return; |
| + } |
| + storage.updateFromWebappInfo(mWebappInfo); |
| + } |
| + }); |
| + } |
| + |
| + private void upgrade() {} |
| + |
| + private native long nativeInitialize(WebContents webContents, String scope, |
| + String webManifestUrl); |
| + private native void nativeReplaceWebContents(long nativeManifestUpgradeDetector, |
| + WebContents webContents); |
| + private native void nativeDestroy(long nativeManifestUpgradeDetector); |
| + private native void nativeStart(long nativeManifestUpgradeDetector); |
| +} |