summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordominickn <dominickn@chromium.org>2016-03-24 19:02:47 -0700
committerCommit bot <commit-bot@chromium.org>2016-03-25 02:03:48 +0000
commit6e400421b116644b4e8b23c29fd2e0c3886af3af (patch)
treea98aed829f6b42ac19ddc4d2b929bd0773ba4ec7
parent1d958cf0159f68005c992ba4b08e17613fec5cac (diff)
downloadchromium_src-6e400421b116644b4e8b23c29fd2e0c3886af3af.zip
chromium_src-6e400421b116644b4e8b23c29fd2e0c3886af3af.tar.gz
chromium_src-6e400421b116644b4e8b23c29fd2e0c3886af3af.tar.bz2
Store URLs in WebappDataStorage, and purge them when history is cleared.
Sites which have been added to homescreen on Android should be allowed to open in standalone mode in response to push notifications. As Chromium cannot detect what sites are on the homescreen after they have been added, the heuristic is sites which have been launched from homescreen within the last ten days will be opened in standalone from a tap on a notification. This CL lays the groundwork for this feature by storing webapp URLs in WebappDataStorage. This is needed to be able to look up the last used time, which is currently persisted in WebappDataStorage. The last used time and webapp URL combined are history, so this CL also resets both pieces of data when the user clears their history. This requires further changes to allow null values in these fields. The last used time is set appropriately when the app is next launched from homescreen; the URL is reset if it is empty when the app is launched. Additional tests for the behaviour introduced in this CL are added. BUG=541711,570579 Review URL: https://codereview.chromium.org/1749603002 Cr-Commit-Position: refs/heads/master@{#383223}
-rw-r--r--chrome/android/java/src/org/chromium/chrome/browser/ShortcutHelper.java6
-rw-r--r--chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java11
-rw-r--r--chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappDataStorage.java167
-rw-r--r--chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappRegistry.java56
-rw-r--r--chrome/android/javatests/src/org/chromium/chrome/browser/preferences/privacy/ClearBrowsingDataPreferencesTest.java102
-rw-r--r--chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappActivityTestBase.java4
-rw-r--r--chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappModeTest.java8
-rw-r--r--chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebappDataStorageTest.java62
-rw-r--r--chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebappRegistryTest.java173
-rw-r--r--chrome/browser/android/banners/app_banner_data_fetcher_android.cc3
-rw-r--r--chrome/browser/android/shortcut_helper.cc13
-rw-r--r--chrome/browser/android/shortcut_helper.h4
-rw-r--r--chrome/browser/android/webapps/add_to_homescreen_data_fetcher.cc3
-rw-r--r--chrome/browser/android/webapps/webapp_registry.cc20
-rw-r--r--chrome/browser/android/webapps/webapp_registry.h4
-rw-r--r--chrome/browser/browsing_data/browsing_data_remover.cc17
-rw-r--r--chrome/browser/browsing_data/browsing_data_remover.h4
-rw-r--r--chrome/browser/browsing_data/browsing_data_remover_unittest.cc6
18 files changed, 549 insertions, 114 deletions
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ShortcutHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/ShortcutHelper.java
index 00fd54a..f0fb5f6b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ShortcutHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ShortcutHelper.java
@@ -173,13 +173,15 @@ public class ShortcutHelper {
* Creates a storage location and stores the data for a web app using {@link WebappDataStorage}.
* @param context Context to open the WebappDataStorage with.
* @param id ID of the webapp which is storing data.
+ * @param scope scope of the webapp which is storing data.
* @param splashImage Image which should be displayed on the splash screen of
* the webapp. This can be null of there is no image to show.
*/
@SuppressWarnings("unused")
@CalledByNative
- private static void storeWebappData(Context context, String id, Bitmap splashImage) {
- WebappRegistry.registerWebapp(context, id);
+ private static void storeWebappData(Context context, String id, String scope,
+ Bitmap splashImage) {
+ WebappRegistry.registerWebapp(context, id, scope);
WebappDataStorage.open(context, id).updateSplashScreenImage(splashImage);
}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java
index 21db450..b5516dc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java
@@ -219,7 +219,7 @@ public class WebappActivity extends FullScreenActivity {
@Override
public void postInflationStartup() {
- initializeSplashScreen();
+ initializeWebappData();
super.postInflationStartup();
WebappControlContainer controlContainer =
@@ -234,7 +234,7 @@ public class WebappActivity extends FullScreenActivity {
return mWebappInfo;
}
- private void initializeSplashScreen() {
+ private void initializeWebappData() {
final int backgroundColor = ColorUtils.getOpaqueColor(mWebappInfo.backgroundColor(
ApiCompatibilityUtils.getColor(getResources(), R.color.webapp_default_bg)));
@@ -252,6 +252,13 @@ public class WebappActivity extends FullScreenActivity {
? WebappUma.SPLASHSCREEN_COLOR_STATUS_CUSTOM
: WebappUma.SPLASHSCREEN_COLOR_STATUS_DEFAULT);
+ // The scope may have been purged by the user clearing their history. It is
+ // needed to be able to launch a webapp from a notification. Make sure that the
+ // scope exists by restoring it on webapp launch; this method will no-op if the
+ // scope does exist as it should not be possible to overwrite any set scope.
+ WebappDataStorage.setScope(this, mWebappInfo.id(), mWebappInfo.uri().toString());
+
+ // Retrieve the splash image if it exists.
WebappDataStorage.open(this, mWebappInfo.id()).getSplashScreenImage(
new WebappDataStorage.FetchCallback<Bitmap>() {
@Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappDataStorage.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappDataStorage.java
index a7fd579..fabf9fb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappDataStorage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappDataStorage.java
@@ -31,7 +31,13 @@ public class WebappDataStorage {
static final String SHARED_PREFS_FILE_PREFIX = "webapp_";
static final String KEY_SPLASH_ICON = "splash_icon";
static final String KEY_LAST_USED = "last_used";
- static final long INVALID_LAST_USED = -1;
+ static final String KEY_SCOPE = "scope";
+
+ // Unset/invalid constants for last used times and scopes. 0 is used as the null last
+ // used time as WebappRegistry assumes that this is always a valid timestamp.
+ static final long LAST_USED_UNSET = 0;
+ static final long LAST_USED_INVALID = -1;
+ static final String SCOPE_INVALID = "";
private static Factory sFactory = new Factory();
@@ -47,7 +53,7 @@ public class WebappDataStorage {
new AsyncTask<Void, Void, Void>() {
@Override
protected final Void doInBackground(Void... nothing) {
- if (storage.getLastUsedTime() == INVALID_LAST_USED) {
+ if (storage.getLastUsedTime() == LAST_USED_INVALID) {
// If the last used time is invalid then assert that there is no data
// in the WebappDataStorage which needs to be cleaned up.
assert storage.getAllData().isEmpty();
@@ -67,6 +73,7 @@ public class WebappDataStorage {
* @param webappId The ID of the web app the used time is being read for.
* @param callback Called when the last used time has been retrieved.
*/
+ @VisibleForTesting
public static void getLastUsedTime(final Context context, final String webappId,
final FetchCallback<Long> callback) {
new AsyncTask<Void, Void, Long>() {
@@ -74,7 +81,7 @@ public class WebappDataStorage {
protected final Long doInBackground(Void... nothing) {
long lastUsed = new WebappDataStorage(context.getApplicationContext(), webappId)
.getLastUsedTime();
- assert lastUsed != INVALID_LAST_USED;
+ assert lastUsed != LAST_USED_INVALID;
return lastUsed;
}
@@ -86,6 +93,49 @@ public class WebappDataStorage {
}
/**
+ * Asynchronously retrieves the scope stored in this WebappDataStorage. The scope is the URL
+ * over which the webapp data is applied to.
+ * @param context The context to read the SharedPreferences file.
+ * @param webappId The ID of the web app the used time is being read for.
+ * @param callback Called when the scope has been retrieved.
+ */
+ @VisibleForTesting
+ public static void getScope(final Context context, final String webappId,
+ final FetchCallback<String> callback) {
+ new AsyncTask<Void, Void, String>() {
+ @Override
+ protected final String doInBackground(Void... nothing) {
+ return new WebappDataStorage(context.getApplicationContext(), webappId)
+ .getScope();
+ }
+
+ @Override
+ protected final void onPostExecute(String scope) {
+ callback.onDataRetrieved(scope);
+ }
+ }.execute();
+ }
+
+ /**
+ * Asynchronously sets the scope stored in this WebappDataStorage. Does nothing if there
+ * is already a scope stored; since webapps added to homescreen cannot change the scope which
+ * they launch, it is not intended that a WebappDataStorage will be able to change the scope
+ * once it is set.
+ * @param context The context to read the SharedPreferences file.
+ * @param webappId The ID of the web app the used time is being read for.
+ * @param scope The scope to set for the web app.
+ */
+ public static void setScope(final Context context, final String webappId, final String scope) {
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected final Void doInBackground(Void... nothing) {
+ new WebappDataStorage(context.getApplicationContext(), webappId).setScope(scope);
+ return null;
+ }
+ }.execute();
+ }
+
+ /**
* Deletes the data for a web app by clearing all the information inside the SharedPreferences
* file. This does NOT delete the file itself but the file is left empty.
* @param context The context to read the SharedPreferences file.
@@ -97,6 +147,21 @@ public class WebappDataStorage {
}
/**
+ * Deletes the scope and sets last used time to 0 this web app in SharedPreferences.
+ * This does not remove the stored splash screen image (if any) for the app.
+ * @param context The context to read the SharedPreferences file.
+ * @param webappId The ID of the web app being deleted.
+ */
+ static void clearHistory(final Context context, final String webappId) {
+ // The last used time is set to 0 to ensure that a valid value is always present.
+ // If the webapp is not launched prior to the next cleanup, then its remaining data will be
+ // removed. Otherwise, the next launch will update the last used time.
+ assert !ThreadUtils.runningOnUiThread();
+ openSharedPreferences(context, webappId)
+ .edit().putLong(KEY_LAST_USED, LAST_USED_UNSET).remove(KEY_SCOPE).apply();
+ }
+
+ /**
* Sets the factory used to generate WebappDataStorage objects.
*/
@VisibleForTesting
@@ -119,26 +184,72 @@ public class WebappDataStorage {
* @param callback Called when the splash screen image has been retrieved.
* May be null if no image was found.
*/
- public void getSplashScreenImage(FetchCallback<Bitmap> callback) {
- new BitmapFetchTask(KEY_SPLASH_ICON, callback).execute();
+ public void getSplashScreenImage(final FetchCallback<Bitmap> callback) {
+ new AsyncTask<Void, Void, Bitmap>() {
+ @Override
+ protected final Bitmap doInBackground(Void... nothing) {
+ return ShortcutHelper.decodeBitmapFromString(
+ mPreferences.getString(KEY_SPLASH_ICON, null));
+ }
+
+ @Override
+ protected final void onPostExecute(Bitmap result) {
+ callback.onDataRetrieved(result);
+ }
+ }.execute();
}
/*
* Update the information associated with the web app with the specified data.
* @param splashScreenImage The image which should be shown on the splash screen of the web app.
*/
- public void updateSplashScreenImage(Bitmap splashScreenImage) {
- new UpdateTask(splashScreenImage).execute();
+ public void updateSplashScreenImage(final Bitmap splashScreenImage) {
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected final Void doInBackground(Void... nothing) {
+ mPreferences.edit()
+ .putString(KEY_SPLASH_ICON,
+ ShortcutHelper.encodeBitmapAsString(splashScreenImage))
+ .apply();
+ return null;
+ }
+ }.execute();
}
+ /**
+ * Updates the scope stored in this object. Does nothing if there is already a scope stored.
+ * @param scope the scope to store.
+ */
+ void setScope(String scope) {
+ assert !ThreadUtils.runningOnUiThread();
+ if (mPreferences.getString(KEY_SCOPE, SCOPE_INVALID).equals(SCOPE_INVALID)) {
+ mPreferences.edit().putString(KEY_SCOPE, scope).apply();
+ }
+ }
+
+ /**
+ * Returns the scope stored in this object, or "" if it is not stored.
+ */
+ String getScope() {
+ assert !ThreadUtils.runningOnUiThread();
+ return mPreferences.getString(KEY_SCOPE, SCOPE_INVALID);
+ }
+
+ /**
+ * Updates the last used time of this object.
+ * @param lastUsedTime the new last used time.
+ */
void updateLastUsedTime() {
assert !ThreadUtils.runningOnUiThread();
mPreferences.edit().putLong(KEY_LAST_USED, System.currentTimeMillis()).apply();
}
+ /**
+ * Returns the last used time of this object, or -1 if it is not stored.
+ */
long getLastUsedTime() {
assert !ThreadUtils.runningOnUiThread();
- return mPreferences.getLong(KEY_LAST_USED, INVALID_LAST_USED);
+ return mPreferences.getLong(KEY_LAST_USED, LAST_USED_INVALID);
}
private Map<String, ?> getAllData() {
@@ -166,42 +277,4 @@ public class WebappDataStorage {
return new WebappDataStorage(context, webappId);
}
}
-
- private final class BitmapFetchTask extends AsyncTask<Void, Void, Bitmap> {
-
- private final String mKey;
- private final FetchCallback<Bitmap> mCallback;
-
- public BitmapFetchTask(String key, FetchCallback<Bitmap> callback) {
- mKey = key;
- mCallback = callback;
- }
-
- @Override
- protected final Bitmap doInBackground(Void... nothing) {
- return ShortcutHelper.decodeBitmapFromString(mPreferences.getString(mKey, null));
- }
-
- @Override
- protected final void onPostExecute(Bitmap result) {
- mCallback.onDataRetrieved(result);
- }
- }
-
- private final class UpdateTask extends AsyncTask<Void, Void, Void> {
-
- private final Bitmap mSplashImage;
-
- public UpdateTask(Bitmap splashImage) {
- mSplashImage = splashImage;
- }
-
- @Override
- protected Void doInBackground(Void... nothing) {
- mPreferences.edit()
- .putString(KEY_SPLASH_ICON, ShortcutHelper.encodeBitmapAsString(mSplashImage))
- .apply();
- return null;
- }
- }
-} \ No newline at end of file
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappRegistry.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappRegistry.java
index 607aea1e3..9ef04698 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappRegistry.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappRegistry.java
@@ -49,7 +49,8 @@ public class WebappRegistry {
* @param context Context to open the registry with.
* @param webappId The id of the web app to register.
*/
- public static void registerWebapp(final Context context, final String webappId) {
+ public static void registerWebapp(final Context context, final String webappId,
+ final String scope) {
new AsyncTask<Void, Void, Void>() {
@Override
protected final Void doInBackground(Void... nothing) {
@@ -58,10 +59,11 @@ public class WebappRegistry {
boolean added = webapps.add(webappId);
assert added;
- // Update the last used time of the {@link WebappDataStorage} so we can guarantee
- // that the used time will be set (ie. != WebappDataStorage.INVALID_LAST_USED) if a
- // web app appears in the registry.
- new WebappDataStorage(context, webappId).updateLastUsedTime();
+ // Update the last used time, so we can guarantee that a web app which appears in
+ // the registry will have a last used time != WebappDataStorage.LAST_USED_INVALID.
+ WebappDataStorage storage = new WebappDataStorage(context, webappId);
+ storage.setScope(scope);
+ storage.updateLastUsedTime();
preferences.edit().putStringSet(KEY_WEBAPP_SET, webapps).apply();
return null;
}
@@ -89,8 +91,10 @@ public class WebappRegistry {
}
/**
- * Deletes the data for all "old" web apps. i.e. web apps which have not been opened by the user
- * in the last 3 months. Cleanup is run, at most, once a month.
+ * Deletes the data for all "old" web apps.
+ * "Old" web apps have not been opened by the user in the last 3 months, or have had their last
+ * used time set to 0 by the user clearing their history. Cleanup is run, at most, once a month.
+ *
* @param context Context to open the registry with.
* @param currentTime The current time which will be checked to decide if the task should be run
* and if a web app should be cleaned up.
@@ -140,7 +144,7 @@ public class WebappRegistry {
@Override
protected final void onPostExecute(Void nothing) {
- if (callback == null) return;
+ assert callback != null;
callback.run();
}
}.execute();
@@ -156,6 +160,39 @@ public class WebappRegistry {
});
}
+ /**
+ * Deletes the scope and sets the last used time to 0 for all web apps.
+ */
+ @VisibleForTesting
+ static void clearWebappHistory(final Context context, final Runnable callback) {
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected final Void doInBackground(Void... nothing) {
+ SharedPreferences preferences = openSharedPreferences(context);
+ for (String id : getRegisteredWebappIds(preferences)) {
+ WebappDataStorage.clearHistory(context, id);
+ }
+ return null;
+ }
+
+ @Override
+ protected final void onPostExecute(Void nothing) {
+ assert callback != null;
+ callback.run();
+ }
+ }.execute();
+ }
+
+ @CalledByNative
+ static void clearWebappHistory(Context context, final long callbackPointer) {
+ clearWebappHistory(context, new Runnable() {
+ @Override
+ public void run() {
+ nativeOnClearedWebappHistory(callbackPointer);
+ }
+ });
+ }
+
private static SharedPreferences openSharedPreferences(Context context) {
return context.getSharedPreferences(REGISTRY_FILE_NAME, Context.MODE_PRIVATE);
}
@@ -168,4 +205,5 @@ public class WebappRegistry {
}
private static native void nativeOnWebappsUnregistered(long callbackPointer);
-} \ No newline at end of file
+ private static native void nativeOnClearedWebappHistory(long callbackPointer);
+}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/privacy/ClearBrowsingDataPreferencesTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/privacy/ClearBrowsingDataPreferencesTest.java
index 35ed8b2..2b83f9a 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/privacy/ClearBrowsingDataPreferencesTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/privacy/ClearBrowsingDataPreferencesTest.java
@@ -15,6 +15,7 @@ import org.chromium.chrome.browser.preferences.ButtonPreference;
import org.chromium.chrome.browser.preferences.PrefServiceBridge;
import org.chromium.chrome.browser.preferences.Preferences;
import org.chromium.chrome.browser.preferences.privacy.ClearBrowsingDataPreferences.DialogOption;
+import org.chromium.chrome.browser.webapps.WebappDataStorage;
import org.chromium.chrome.browser.webapps.WebappRegistry;
import org.chromium.chrome.test.ChromeActivityTestCaseBase;
import org.chromium.content.browser.test.util.Criteria;
@@ -47,7 +48,7 @@ public class ClearBrowsingDataPreferencesTest
*/
@MediumTest
public void testClearingSiteDataClearsWebapps() throws Exception {
- WebappRegistry.registerWebapp(getActivity(), "first");
+ WebappRegistry.registerWebapp(getActivity(), "first", "https://www.google.com");
WebappRegistry.getRegisteredWebappIds(getActivity(), new WebappRegistry.FetchCallback() {
@Override
public void onWebappIdsRetrieved(Set<String> ids) {
@@ -103,6 +104,105 @@ public class ClearBrowsingDataPreferencesTest
}
/**
+ * Tests that web app scopes and last launch times are cleared when the "history" option is
+ * selected. However, the web app is not removed from the registry.
+ */
+ @MediumTest
+ public void testClearingHistoryClearsWebappScopesAndLaunchTimes() throws Exception {
+ WebappRegistry.registerWebapp(getActivity(), "first", "https://www.google.com");
+ WebappRegistry.getRegisteredWebappIds(getActivity(), new WebappRegistry.FetchCallback() {
+ @Override
+ public void onWebappIdsRetrieved(Set<String> ids) {
+ assertEquals(new HashSet<String>(Arrays.asList("first")), ids);
+ mCallbackCalled = true;
+ }
+ });
+ CriteriaHelper.pollUiThread(new Criteria() {
+ @Override
+ public boolean isSatisfied() {
+ return mCallbackCalled;
+ }
+ });
+ mCallbackCalled = false;
+
+ setDataTypesToClear(Arrays.asList(DialogOption.CLEAR_HISTORY));
+ final Preferences preferences =
+ startPreferences(ClearBrowsingDataPreferences.class.getName());
+
+ ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+ @Override
+ public void run() {
+ ClearBrowsingDataPreferences fragment =
+ (ClearBrowsingDataPreferences) preferences.getFragmentForTest();
+ PreferenceScreen screen = fragment.getPreferenceScreen();
+ ButtonPreference clearButton = (ButtonPreference) screen.findPreference(
+ ClearBrowsingDataPreferences.PREF_CLEAR_BUTTON);
+ clearButton.getOnPreferenceClickListener().onPreferenceClick(clearButton);
+ }
+ });
+ CriteriaHelper.pollUiThread(new Criteria() {
+ @Override
+ public boolean isSatisfied() {
+ ClearBrowsingDataPreferences fragment =
+ (ClearBrowsingDataPreferences) preferences.getFragmentForTest();
+ return fragment.getProgressDialog() == null;
+ }
+ });
+
+ // The web app should still exist in the registry.
+ WebappRegistry.getRegisteredWebappIds(getActivity(), new WebappRegistry.FetchCallback() {
+ @Override
+ public void onWebappIdsRetrieved(Set<String> ids) {
+ assertEquals(new HashSet<String>(Arrays.asList("first")), ids);
+ mCallbackCalled = true;
+ }
+ });
+ CriteriaHelper.pollUiThread(new Criteria() {
+ @Override
+ public boolean isSatisfied() {
+ return mCallbackCalled;
+ }
+ });
+ mCallbackCalled = false;
+
+ // Scope should be empty.
+ WebappDataStorage.getScope(getActivity(), "first",
+ new WebappDataStorage.FetchCallback<String>() {
+ @Override
+ public void onDataRetrieved(String readObject) {
+ assertEquals(readObject, "");
+ mCallbackCalled = true;
+ }
+ }
+ );
+ CriteriaHelper.pollUiThread(new Criteria() {
+ @Override
+ public boolean isSatisfied() {
+ return mCallbackCalled;
+ }
+ });
+ mCallbackCalled = false;
+
+ // The last used time should be 0.
+ WebappDataStorage.getLastUsedTime(getActivity(), "first",
+ new WebappDataStorage.FetchCallback<Long>() {
+ @Override
+ public void onDataRetrieved(Long readObject) {
+ long lastUsed = readObject;
+ assertEquals(lastUsed, 0);
+ mCallbackCalled = true;
+ }
+ }
+ );
+ CriteriaHelper.pollUiThread(new Criteria() {
+ @Override
+ public boolean isSatisfied() {
+ return mCallbackCalled;
+ }
+ });
+ }
+
+ /**
* Tests that a fragment with all options preselected indeed has all checkboxes checked
* on startup, and that deletion with all checkboxes checked completes successfully.
*/
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappActivityTestBase.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappActivityTestBase.java
index 9d5a6b9..8bcc1e5 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappActivityTestBase.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappActivityTestBase.java
@@ -28,6 +28,7 @@ public abstract class WebappActivityTestBase extends ChromeActivityTestCaseBase<
static final String WEBAPP_ID = "webapp_id";
static final String WEBAPP_NAME = "webapp name";
static final String WEBAPP_SHORT_NAME = "webapp short name";
+ static final String WEBAPP_URL = "https://www.google.com";
// Empty 192x192 image generated with:
// ShortcutHelper.encodeBitmapAsString(Bitmap.createBitmap(192, 192, Bitmap.Config.ARGB_4444));
@@ -91,7 +92,8 @@ public abstract class WebappActivityTestBase extends ChromeActivityTestCaseBase<
// Register the webapp so when the data storage is opened, the test doesn't crash. There is
// no race condition with the retrival as AsyncTasks are run sequentially on the background
// thread.
- WebappRegistry.registerWebapp(getInstrumentation().getTargetContext(), WEBAPP_ID);
+ WebappRegistry.registerWebapp(getInstrumentation().getTargetContext(),
+ WEBAPP_ID, WEBAPP_URL);
}
/**
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappModeTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappModeTest.java
index 966cb73..15de884 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappModeTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappModeTest.java
@@ -55,11 +55,13 @@ public class WebappModeTest extends MultiActivityTestBase {
+ "content='width=device-width initial-scale=0.5, maximum-scale=0.5'></head>"
+ "<body bgcolor='#011684'>Webapp 1</body></html>");
private static final String WEBAPP_1_TITLE = "Web app #1";
+ private static final String WEBAPP_1_ORIGIN = "https://www.google.com";
private static final String WEBAPP_2_ID = "webapp_id_2";
private static final String WEBAPP_2_URL =
UrlUtils.encodeHtmlDataUri("<html><body bgcolor='#840116'>Webapp 2</body></html>");
private static final String WEBAPP_2_TITLE = "Web app #2";
+ private static final String WEBAPP_2_ORIGIN = "http://google.com";
private static final String WEBAPP_ICON = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAACXB"
+ "IWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3wQIFB4cxOfiSQAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdG"
@@ -96,8 +98,10 @@ public class WebappModeTest extends MultiActivityTestBase {
// Register the webapps so when the data storage is opened, the test doesn't crash. There is
// no race condition with the retrival as AsyncTasks are run sequentially on the background
// thread.
- WebappRegistry.registerWebapp(getInstrumentation().getTargetContext(), WEBAPP_1_ID);
- WebappRegistry.registerWebapp(getInstrumentation().getTargetContext(), WEBAPP_2_ID);
+ WebappRegistry.registerWebapp(getInstrumentation().getTargetContext(), WEBAPP_1_ID,
+ WEBAPP_1_ORIGIN);
+ WebappRegistry.registerWebapp(getInstrumentation().getTargetContext(), WEBAPP_2_ID,
+ WEBAPP_2_ORIGIN);
}
/**
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebappDataStorageTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebappDataStorageTest.java
index 6cfc8f4..5ff97d6 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebappDataStorageTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebappDataStorageTest.java
@@ -32,6 +32,20 @@ public class WebappDataStorageTest {
private SharedPreferences mSharedPreferences;
private boolean mCallbackCalled;
+ private class FetchCallback<T> implements WebappDataStorage.FetchCallback<T> {
+ T mExpected;
+
+ FetchCallback(T expected) {
+ mExpected = expected;
+ }
+
+ @Override
+ public void onDataRetrieved(T readObject) {
+ mCallbackCalled = true;
+ assertEquals(mExpected, readObject);
+ }
+ }
+
@Before
public void setUp() throws Exception {
mSharedPreferences = Robolectric.application
@@ -49,6 +63,7 @@ public class WebappDataStorageTest {
assertEquals("webapp_", WebappDataStorage.SHARED_PREFS_FILE_PREFIX);
assertEquals("splash_icon", WebappDataStorage.KEY_SPLASH_ICON);
assertEquals("last_used", WebappDataStorage.KEY_LAST_USED);
+ assertEquals("scope", WebappDataStorage.KEY_SCOPE);
}
@Test
@@ -59,13 +74,7 @@ public class WebappDataStorageTest {
.commit();
WebappDataStorage.getLastUsedTime(Robolectric.application, "test",
- new WebappDataStorage.FetchCallback<Long>() {
- @Override
- public void onDataRetrieved(Long readObject) {
- mCallbackCalled = true;
- assertEquals(100L, (long) readObject);
- }
- });
+ new FetchCallback<Long>(new Long(100L)));
BackgroundShadowAsyncTask.runBackgroundTasks();
Robolectric.runUiThreadTasks();
@@ -143,4 +152,41 @@ public class WebappDataStorageTest {
private static Bitmap createBitmap() {
return Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_4444);
}
-} \ No newline at end of file
+
+ @Test
+ @Feature({"Webapp"})
+ public void testScopeRetrieval() throws Exception {
+ final String scope = "http://drive.google.com";
+ mSharedPreferences.edit()
+ .putString(WebappDataStorage.KEY_SCOPE, scope)
+ .commit();
+
+ WebappDataStorage.getScope(Robolectric.application, "test",
+ new FetchCallback<String>(scope));
+ BackgroundShadowAsyncTask.runBackgroundTasks();
+ Robolectric.runUiThreadTasks();
+
+ assertTrue(mCallbackCalled);
+ }
+
+ @Test
+ @Feature({"Webapp"})
+ public void testScopeUpdate() throws Exception {
+ final String scope1 = "http://maps.google.com";
+ final String scope2 = "http://drive.google.com";
+
+ WebappDataStorage.setScope(Robolectric.application, "test", scope1);
+ BackgroundShadowAsyncTask.runBackgroundTasks();
+ Robolectric.runUiThreadTasks();
+
+ assertEquals(scope1, mSharedPreferences.getString(WebappDataStorage.KEY_SCOPE, null));
+
+ // Ensure that calling setScope with a different URL after it has been set
+ // doesn't change the scope stored.
+ WebappDataStorage.setScope(Robolectric.application, "test", scope2);
+ BackgroundShadowAsyncTask.runBackgroundTasks();
+ Robolectric.runUiThreadTasks();
+
+ assertEquals(scope1, mSharedPreferences.getString(WebappDataStorage.KEY_SCOPE, null));
+ }
+}
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebappRegistryTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebappRegistryTest.java
index 2d07c7a..fe5367a 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebappRegistryTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebappRegistryTest.java
@@ -43,6 +43,20 @@ public class WebappRegistryTest {
private SharedPreferences mSharedPreferences;
private boolean mCallbackCalled;
+ private class FetchCallback implements WebappRegistry.FetchCallback {
+ Set<String> mExpected;
+
+ FetchCallback(Set<String> expected) {
+ mExpected = expected;
+ }
+
+ @Override
+ public void onWebappIdsRetrieved(Set<String> actual) {
+ mCallbackCalled = true;
+ assertEquals(mExpected, actual);
+ }
+ }
+
@Before
public void setUp() throws Exception {
mSharedPreferences = Robolectric.application
@@ -63,7 +77,7 @@ public class WebappRegistryTest {
@Test
@Feature({"Webapp"})
public void testWebappRegistrationAddsToSharedPrefs() throws Exception {
- WebappRegistry.registerWebapp(Robolectric.application, "test");
+ WebappRegistry.registerWebapp(Robolectric.application, "test", "https://www.google.com");
BackgroundShadowAsyncTask.runBackgroundTasks();
Set<String> actual = mSharedPreferences.getStringSet(
@@ -74,17 +88,22 @@ public class WebappRegistryTest {
@Test
@Feature({"Webapp"})
- public void testWebappRegistrationUpdatesLastUsed() throws Exception {
+ public void testWebappRegistrationUpdatesLastUsedAndSCOPE() throws Exception {
long before = System.currentTimeMillis();
- WebappRegistry.registerWebapp(Robolectric.application, "test");
+ final String scope = "http://drive.google.com";
+
+ WebappRegistry.registerWebapp(Robolectric.application, "test", scope);
BackgroundShadowAsyncTask.runBackgroundTasks();
long after = System.currentTimeMillis();
SharedPreferences webAppPrefs = Robolectric.application.getSharedPreferences(
WebappDataStorage.SHARED_PREFS_FILE_PREFIX + "test", Context.MODE_PRIVATE);
long actual = webAppPrefs.getLong(WebappDataStorage.KEY_LAST_USED,
- WebappDataStorage.INVALID_LAST_USED);
+ WebappDataStorage.LAST_USED_INVALID);
+ String webAppScope = webAppPrefs.getString(WebappDataStorage.KEY_SCOPE,
+ WebappDataStorage.SCOPE_INVALID);
assertTrue("Timestamp is out of range", before <= actual && actual <= after);
+ assertEquals(scope, webAppScope);
}
@Test
@@ -92,14 +111,7 @@ public class WebappRegistryTest {
public void testWebappIdsRetrieval() throws Exception {
final Set<String> expected = addWebappsToRegistry("first", "second");
- WebappRegistry.getRegisteredWebappIds(Robolectric.application,
- new WebappRegistry.FetchCallback() {
- @Override
- public void onWebappIdsRetrieved(Set<String> actual) {
- mCallbackCalled = true;
- assertEquals(expected, actual);
- }
- });
+ WebappRegistry.getRegisteredWebappIds(Robolectric.application, new FetchCallback(expected));
BackgroundShadowAsyncTask.runBackgroundTasks();
Robolectric.runUiThreadTasks();
@@ -111,21 +123,14 @@ public class WebappRegistryTest {
public void testWebappIdsRetrievalRegisterRetrival() throws Exception {
final Set<String> expected = addWebappsToRegistry("first");
- WebappRegistry.getRegisteredWebappIds(Robolectric.application,
- new WebappRegistry.FetchCallback() {
- @Override
- public void onWebappIdsRetrieved(Set<String> actual) {
- mCallbackCalled = true;
- assertEquals(expected, actual);
- }
- });
+ WebappRegistry.getRegisteredWebappIds(Robolectric.application, new FetchCallback(expected));
BackgroundShadowAsyncTask.runBackgroundTasks();
Robolectric.runUiThreadTasks();
assertTrue(mCallbackCalled);
mCallbackCalled = false;
- WebappRegistry.registerWebapp(Robolectric.application, "second");
+ WebappRegistry.registerWebapp(Robolectric.application, "second", "https://www.google.com");
BackgroundShadowAsyncTask.runBackgroundTasks();
// A copy of the expected set needs to be made as the SharedPreferences is using the copy
@@ -134,13 +139,7 @@ public class WebappRegistryTest {
secondExpected.add("second");
WebappRegistry.getRegisteredWebappIds(Robolectric.application,
- new WebappRegistry.FetchCallback() {
- @Override
- public void onWebappIdsRetrieved(Set<String> actual) {
- mCallbackCalled = true;
- assertEquals(secondExpected, actual);
- }
- });
+ new FetchCallback(secondExpected));
BackgroundShadowAsyncTask.runBackgroundTasks();
Robolectric.runUiThreadTasks();
@@ -167,9 +166,16 @@ public class WebappRegistryTest {
public void testUnregisterClearsRegistry() throws Exception {
addWebappsToRegistry("test");
- WebappRegistry.unregisterAllWebapps(Robolectric.application, null);
+ WebappRegistry.unregisterAllWebapps(Robolectric.application, new Runnable() {
+ @Override
+ public void run() {
+ mCallbackCalled = true;
+ }
+ });
BackgroundShadowAsyncTask.runBackgroundTasks();
+ Robolectric.runUiThreadTasks();
+ assertTrue(mCallbackCalled);
assertTrue(getRegisteredWebapps().isEmpty());
}
@@ -211,7 +217,7 @@ public class WebappRegistryTest {
assertEquals(new HashSet<String>(Arrays.asList("oldWebapp")), actual);
long actualLastUsed = webAppPrefs.getLong(WebappDataStorage.KEY_LAST_USED,
- WebappDataStorage.INVALID_LAST_USED);
+ WebappDataStorage.LAST_USED_INVALID);
assertEquals(Long.MIN_VALUE, actualLastUsed);
// The last cleanup time was set to 0 in setUp() so check that this hasn't changed.
@@ -245,7 +251,7 @@ public class WebappRegistryTest {
assertEquals(new HashSet<String>(Arrays.asList("recentWebapp")), actual);
long actualLastUsed = webAppPrefs.getLong(WebappDataStorage.KEY_LAST_USED,
- WebappDataStorage.INVALID_LAST_USED);
+ WebappDataStorage.LAST_USED_INVALID);
assertEquals(lastUsed, actualLastUsed);
long lastCleanup = mSharedPreferences.getLong(WebappRegistry.KEY_LAST_CLEANUP, -1);
@@ -277,13 +283,112 @@ public class WebappRegistryTest {
assertTrue(actual.isEmpty());
long actualLastUsed = webAppPrefs.getLong(WebappDataStorage.KEY_LAST_USED,
- WebappDataStorage.INVALID_LAST_USED);
- assertEquals(WebappDataStorage.INVALID_LAST_USED, actualLastUsed);
+ WebappDataStorage.LAST_USED_INVALID);
+ assertEquals(WebappDataStorage.LAST_USED_INVALID, actualLastUsed);
long lastCleanup = mSharedPreferences.getLong(WebappRegistry.KEY_LAST_CLEANUP, -1);
assertEquals(currentTime, lastCleanup);
}
+ @Test
+ @Feature({"Webapp"})
+ public void testClearWebappHistoryRunsCallback() throws Exception {
+ WebappRegistry.clearWebappHistory(Robolectric.application, new Runnable() {
+ @Override
+ public void run() {
+ mCallbackCalled = true;
+ }
+ });
+ BackgroundShadowAsyncTask.runBackgroundTasks();
+ Robolectric.runUiThreadTasks();
+
+ assertTrue(mCallbackCalled);
+ }
+
+ @Test
+ @Feature({"Webapp"})
+ public void testClearWebappHistory() throws Exception {
+ final String webappScope1 = "https://www.google.com";
+ final String webappScope2 = "https://drive.google.com";
+ WebappRegistry.registerWebapp(Robolectric.application, "webapp1", webappScope1);
+ WebappRegistry.registerWebapp(Robolectric.application, "webapp2", webappScope2);
+ BackgroundShadowAsyncTask.runBackgroundTasks();
+
+ SharedPreferences webapp1Prefs = Robolectric.application.getSharedPreferences(
+ WebappDataStorage.SHARED_PREFS_FILE_PREFIX + "webapp1", Context.MODE_PRIVATE);
+ SharedPreferences webapp2Prefs = Robolectric.application.getSharedPreferences(
+ WebappDataStorage.SHARED_PREFS_FILE_PREFIX + "webapp2", Context.MODE_PRIVATE);
+
+ WebappRegistry.clearWebappHistory(Robolectric.application, new Runnable() {
+ @Override
+ public void run() {
+ mCallbackCalled = true;
+ }
+ });
+ BackgroundShadowAsyncTask.runBackgroundTasks();
+ Robolectric.runUiThreadTasks();
+
+ assertTrue(mCallbackCalled);
+
+ Set<String> actual = mSharedPreferences.getStringSet(
+ WebappRegistry.KEY_WEBAPP_SET, Collections.<String>emptySet());
+ assertEquals(2, actual.size());
+ assertTrue(actual.contains("webapp1"));
+ assertTrue(actual.contains("webapp2"));
+
+ // Verify that the last used time for both web apps is WebappDataStorage.LAST_USED_UNSET.
+ long actualLastUsed = webapp1Prefs.getLong(
+ WebappDataStorage.KEY_LAST_USED, WebappDataStorage.LAST_USED_UNSET);
+ assertEquals(WebappDataStorage.LAST_USED_UNSET, actualLastUsed);
+ actualLastUsed = webapp2Prefs.getLong(
+ WebappDataStorage.KEY_LAST_USED, WebappDataStorage.LAST_USED_UNSET);
+ assertEquals(WebappDataStorage.LAST_USED_UNSET, actualLastUsed);
+
+ // Verify that the scope for both web apps is WebappDataStorage.SCOPE_INVALID.
+ String actualScope = webapp1Prefs.getString(
+ WebappDataStorage.KEY_SCOPE, WebappDataStorage.SCOPE_INVALID);
+ assertEquals(WebappDataStorage.SCOPE_INVALID, actualScope);
+ actualScope = webapp2Prefs.getString(
+ WebappDataStorage.KEY_SCOPE, WebappDataStorage.SCOPE_INVALID);
+ assertEquals(WebappDataStorage.SCOPE_INVALID, actualScope);
+ }
+
+ @Test
+ @Feature({"Webapp"})
+ public void testOpenAfterClearWebappHistory() throws Exception {
+ final String webappScope = "https://www.google.com";
+ WebappRegistry.registerWebapp(Robolectric.application, "webapp", webappScope);
+ BackgroundShadowAsyncTask.runBackgroundTasks();
+
+ SharedPreferences webappPrefs = Robolectric.application.getSharedPreferences(
+ WebappDataStorage.SHARED_PREFS_FILE_PREFIX + "webapp", Context.MODE_PRIVATE);
+
+ WebappRegistry.clearWebappHistory(Robolectric.application, new Runnable() {
+ @Override
+ public void run() {
+ mCallbackCalled = true;
+ }
+ });
+ BackgroundShadowAsyncTask.runBackgroundTasks();
+
+ // Open the webapp up and set the scope.
+ WebappDataStorage.open(Robolectric.application, "webapp");
+ WebappDataStorage.setScope(Robolectric.application, "webapp", webappScope);
+ BackgroundShadowAsyncTask.runBackgroundTasks();
+ Robolectric.runUiThreadTasks();
+ assertTrue(mCallbackCalled);
+
+ // Verify that the last used time is valid and the scope is updated.
+ long actualLastUsed = webappPrefs.getLong(
+ WebappDataStorage.KEY_LAST_USED, WebappDataStorage.LAST_USED_INVALID);
+ assertTrue(WebappDataStorage.LAST_USED_INVALID != actualLastUsed);
+ assertTrue(WebappDataStorage.LAST_USED_UNSET != actualLastUsed);
+ String actualScope = webappPrefs.getString(
+ WebappDataStorage.KEY_SCOPE, WebappDataStorage.SCOPE_INVALID);
+ assertEquals(webappScope, actualScope);
+ }
+
+
private Set<String> addWebappsToRegistry(String... webapps) {
final Set<String> expected = new HashSet<String>(Arrays.asList(webapps));
mSharedPreferences.edit()
@@ -296,4 +401,4 @@ public class WebappRegistryTest {
return mSharedPreferences.getStringSet(
WebappRegistry.KEY_WEBAPP_SET, Collections.<String>emptySet());
}
-} \ No newline at end of file
+}
diff --git a/chrome/browser/android/banners/app_banner_data_fetcher_android.cc b/chrome/browser/android/banners/app_banner_data_fetcher_android.cc
index c3d0874..276919c 100644
--- a/chrome/browser/android/banners/app_banner_data_fetcher_android.cc
+++ b/chrome/browser/android/banners/app_banner_data_fetcher_android.cc
@@ -71,7 +71,8 @@ void AppBannerDataFetcherAndroid::FetchWebappSplashScreenImage(
image_url,
ideal_splash_image_size_in_dp_,
minimum_splash_image_size_in_dp_,
- webapp_id);
+ webapp_id,
+ web_app_data().start_url.spec());
}
void AppBannerDataFetcherAndroid::ShowBanner(const SkBitmap* icon,
diff --git a/chrome/browser/android/shortcut_helper.cc b/chrome/browser/android/shortcut_helper.cc
index 6c85072..e52f96e 100644
--- a/chrome/browser/android/shortcut_helper.cc
+++ b/chrome/browser/android/shortcut_helper.cc
@@ -128,20 +128,20 @@ void ShortcutHelper::FetchSplashScreenImage(
const GURL& image_url,
const int ideal_splash_image_size_in_dp,
const int minimum_splash_image_size_in_dp,
- const std::string& webapp_id) {
+ const std::string& webapp_id,
+ const std::string& webapp_scope) {
// This is a fire and forget task. It is not vital for the splash screen image
// to be downloaded so if the downloader returns false there is no fallback.
ManifestIconDownloader::Download(
- web_contents,
- image_url,
- ideal_splash_image_size_in_dp,
+ web_contents, image_url, ideal_splash_image_size_in_dp,
minimum_splash_image_size_in_dp,
- base::Bind(&ShortcutHelper::StoreWebappData, webapp_id));
+ base::Bind(&ShortcutHelper::StoreWebappData, webapp_id, webapp_scope));
}
// static
void ShortcutHelper::StoreWebappData(
const std::string& webapp_id,
+ const std::string& webapp_url,
const SkBitmap& splash_image) {
if (splash_image.drawsNothing())
return;
@@ -149,6 +149,8 @@ void ShortcutHelper::StoreWebappData(
JNIEnv* env = base::android::AttachCurrentThread();
ScopedJavaLocalRef<jstring> java_webapp_id =
base::android::ConvertUTF8ToJavaString(env, webapp_id);
+ ScopedJavaLocalRef<jstring> java_webapp_url =
+ base::android::ConvertUTF8ToJavaString(env, webapp_url);
ScopedJavaLocalRef<jobject> java_splash_image =
gfx::ConvertToJavaBitmap(&splash_image);
@@ -156,6 +158,7 @@ void ShortcutHelper::StoreWebappData(
env,
base::android::GetApplicationContext(),
java_webapp_id.obj(),
+ java_webapp_url.obj(),
java_splash_image.obj());
}
diff --git a/chrome/browser/android/shortcut_helper.h b/chrome/browser/android/shortcut_helper.h
index 0292962..972752c 100644
--- a/chrome/browser/android/shortcut_helper.h
+++ b/chrome/browser/android/shortcut_helper.h
@@ -48,10 +48,12 @@ class ShortcutHelper {
const GURL& image_url,
const int ideal_splash_image_size_in_dp,
const int minimum_splash_image_size_in_dp,
- const std::string& webapp_id);
+ const std::string& webapp_id,
+ const std::string& webapp_scope);
// Stores the data of the webapp which is not placed inside the shortcut.
static void StoreWebappData(const std::string& webapp_id,
+ const std::string& webapp_url,
const SkBitmap& splash_image);
// Modify the given icon to matche the launcher requirements, then returns the
diff --git a/chrome/browser/android/webapps/add_to_homescreen_data_fetcher.cc b/chrome/browser/android/webapps/add_to_homescreen_data_fetcher.cc
index 3c023f6..2a3f198 100644
--- a/chrome/browser/android/webapps/add_to_homescreen_data_fetcher.cc
+++ b/chrome/browser/android/webapps/add_to_homescreen_data_fetcher.cc
@@ -170,7 +170,8 @@ void AddToHomescreenDataFetcher::FetchSplashScreenImage(
splash_screen_url_,
ideal_splash_image_size_in_dp_,
minimum_splash_image_size_in_dp_,
- webapp_id);
+ webapp_id,
+ shortcut_info_.url.spec());
}
void AddToHomescreenDataFetcher::FetchFavicon() {
diff --git a/chrome/browser/android/webapps/webapp_registry.cc b/chrome/browser/android/webapps/webapp_registry.cc
index f021511..318244d 100644
--- a/chrome/browser/android/webapps/webapp_registry.cc
+++ b/chrome/browser/android/webapps/webapp_registry.cc
@@ -24,6 +24,17 @@ void WebappRegistry::UnregisterWebapps(const base::Closure& callback) {
callback_pointer);
}
+void WebappRegistry::ClearWebappHistory(const base::Closure& callback) {
+ JNIEnv* env = base::android::AttachCurrentThread();
+ uintptr_t callback_pointer = reinterpret_cast<uintptr_t>(
+ new base::Closure(callback));
+
+ Java_WebappRegistry_clearWebappHistory(
+ env,
+ base::android::GetApplicationContext(),
+ callback_pointer);
+}
+
// Callback used by Java when all web apps have been unregistered.
void OnWebappsUnregistered(JNIEnv* env,
const JavaParamRef<jclass>& clazz,
@@ -33,6 +44,15 @@ void OnWebappsUnregistered(JNIEnv* env,
delete callback;
}
+// Callback used by Java when all web app last used times have been cleared.
+void OnClearedWebappHistory(JNIEnv* env,
+ const JavaParamRef<jclass>& clazz,
+ jlong jcallback) {
+ base::Closure* callback = reinterpret_cast<base::Closure*>(jcallback);
+ callback->Run();
+ delete callback;
+}
+
// static
bool WebappRegistry::RegisterWebappRegistry(JNIEnv* env) {
return RegisterNativesImpl(env);
diff --git a/chrome/browser/android/webapps/webapp_registry.h b/chrome/browser/android/webapps/webapp_registry.h
index 2d0a2cb..d364b6e 100644
--- a/chrome/browser/android/webapps/webapp_registry.h
+++ b/chrome/browser/android/webapps/webapp_registry.h
@@ -26,6 +26,10 @@ class WebappRegistry {
// Cleans up data stored by web apps.
virtual void UnregisterWebapps(const base::Closure& callback);
+ // Removes history data (last used time and URLs) stored by web apps, whilst
+ // leaving other data intact.
+ virtual void ClearWebappHistory(const base::Closure& callback);
+
private:
DISALLOW_COPY_AND_ASSIGN(WebappRegistry);
};
diff --git a/chrome/browser/browsing_data/browsing_data_remover.cc b/chrome/browser/browsing_data/browsing_data_remover.cc
index 25aa931..97d22b7 100644
--- a/chrome/browser/browsing_data/browsing_data_remover.cc
+++ b/chrome/browser/browsing_data/browsing_data_remover.cc
@@ -561,6 +561,14 @@ void BrowsingDataRemover::RemoveImpl(const TimeRange& time_range,
base::Bind(&BrowsingDataRemover::OnClearedPrecacheHistory,
weak_ptr_factory_.GetWeakPtr()));
}
+
+ // Clear the history information (last launch time and origin URL) of any
+ // registered webapps. The webapp_registry makes a JNI call into a Java-side
+ // AsyncTask, so don't wait for the reply.
+ waiting_for_clear_webapp_history_ = true;
+ webapp_registry_->ClearWebappHistory(
+ base::Bind(&BrowsingDataRemover::OnClearedWebappHistory,
+ weak_ptr_factory_.GetWeakPtr()));
#endif
data_reduction_proxy::DataReductionProxySettings*
@@ -930,6 +938,8 @@ void BrowsingDataRemover::RemoveImpl(const TimeRange& time_range,
#if BUILDFLAG(ANDROID_JAVA_UI)
if (remove_mask & REMOVE_WEBAPP_DATA) {
+ // Clear all data associated with registered webapps. The webapp_registry
+ // makes a JNI call into a Java-side AsyncTask, so don't wait for the reply.
waiting_for_clear_webapp_data_ = true;
webapp_registry_->UnregisterWebapps(
base::Bind(&BrowsingDataRemover::OnClearedWebappData,
@@ -1020,6 +1030,7 @@ bool BrowsingDataRemover::AllDone() {
#if BUILDFLAG(ANDROID_JAVA_UI)
!waiting_for_clear_precache_history_ &&
!waiting_for_clear_webapp_data_ &&
+ !waiting_for_clear_webapp_history_ &&
!waiting_for_clear_offline_page_data_ &&
#endif
#if defined(ENABLE_WEBRTC)
@@ -1219,6 +1230,12 @@ void BrowsingDataRemover::OnClearedWebappData() {
NotifyIfDone();
}
+void BrowsingDataRemover::OnClearedWebappHistory() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ waiting_for_clear_webapp_history_ = false;
+ NotifyIfDone();
+}
+
void BrowsingDataRemover::OnClearedOfflinePageData() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
waiting_for_clear_offline_page_data_ = false;
diff --git a/chrome/browser/browsing_data/browsing_data_remover.h b/chrome/browser/browsing_data/browsing_data_remover.h
index fc2dfd9..b5f1a6f 100644
--- a/chrome/browser/browsing_data/browsing_data_remover.h
+++ b/chrome/browser/browsing_data/browsing_data_remover.h
@@ -393,6 +393,9 @@ class BrowsingDataRemover : public KeyedService
// Callback on UI thread when the webapp data has been cleared.
void OnClearedWebappData();
+ // Callback on UI thread when the webapp history has been cleared.
+ void OnClearedWebappHistory();
+
// Callback on UI thread when the offline page data has been cleared.
void OnClearedOfflinePageData();
#endif
@@ -457,6 +460,7 @@ class BrowsingDataRemover : public KeyedService
#if BUILDFLAG(ANDROID_JAVA_UI)
bool waiting_for_clear_precache_history_ = false;
bool waiting_for_clear_webapp_data_ = false;
+ bool waiting_for_clear_webapp_history_ = false;
bool waiting_for_clear_offline_page_data_ = false;
#endif
bool waiting_for_clear_storage_partition_data_ = false;
diff --git a/chrome/browser/browsing_data/browsing_data_remover_unittest.cc b/chrome/browser/browsing_data/browsing_data_remover_unittest.cc
index a966547..132db68 100644
--- a/chrome/browser/browsing_data/browsing_data_remover_unittest.cc
+++ b/chrome/browser/browsing_data/browsing_data_remover_unittest.cc
@@ -274,6 +274,12 @@ class TestWebappRegistry : public WebappRegistry {
BrowserThread::PostDelayedTask(BrowserThread::UI, FROM_HERE, callback,
base::TimeDelta::FromMilliseconds(10));
}
+
+ void ClearWebappHistory(const base::Closure& callback) override {
+ // Mocks out a JNI call and runs the callback as a delayed task.
+ BrowserThread::PostDelayedTask(BrowserThread::UI, FROM_HERE, callback,
+ base::TimeDelta::FromMilliseconds(10));
+ }
};
#endif