Commit d0fe7670 authored by Cathy Li's avatar Cathy Li Committed by Commit Bot

[Offline pages]: Add information to broadcasted intent to help mitigate need...

[Offline pages]: Add information to broadcasted intent to help mitigate need for re-fetching the data

Bug: 812022
Change-Id: I58b99dd7b841c1312016217117b2ecd62c65188e
Reviewed-on: https://chromium-review.googlesource.com/991456
Commit-Queue: Cathy Li <chili@chromium.org>
Reviewed-by: default avatarJustin DeWitt <dewittj@chromium.org>
Reviewed-by: default avatarvitaliii <vitaliii@chromium.org>
Cr-Commit-Position: refs/heads/master@{#560969}
parent f222ad13
...@@ -7,9 +7,11 @@ package org.chromium.chrome.browser.offlinepages; ...@@ -7,9 +7,11 @@ package org.chromium.chrome.browser.offlinepages;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle;
import org.chromium.base.ContextUtils; import org.chromium.base.ContextUtils;
import org.chromium.base.Log; import org.chromium.base.Log;
import org.chromium.base.VisibleForTesting;
import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.CalledByNative;
import org.chromium.chrome.browser.AppHooks; import org.chromium.chrome.browser.AppHooks;
...@@ -21,20 +23,38 @@ import org.chromium.chrome.browser.AppHooks; ...@@ -21,20 +23,38 @@ import org.chromium.chrome.browser.AppHooks;
*/ */
public class CctOfflinePageModelObserver { public class CctOfflinePageModelObserver {
private static final String TAG = "CctModelObserver"; private static final String TAG = "CctModelObserver";
// Key for pending intent used by receivers to verify origin.
private static final String ORIGIN_VERIFICATION_KEY = private static final String ORIGIN_VERIFICATION_KEY =
"org.chromium.chrome.extra.CHROME_NAME_PENDING_INTENT"; "org.chromium.chrome.extra.CHROME_NAME_PENDING_INTENT";
private static final String ACTION_OFFLINE_PAGES_UPDATED =
// Key for bundle which stores information about the page changed.
@VisibleForTesting
static final String PAGE_INFO_KEY = "org.chromium.chrome.extra.OFFLINE_PAGE_INFO";
// Key within page info bundle for whether the page was added (true) or removed (false).
@VisibleForTesting
static final String IS_NEW_KEY = "is_new";
// Key within page info bundle for online url.
@VisibleForTesting
static final String URL_KEY = "url";
// Broadcast action.
@VisibleForTesting
static final String ACTION_OFFLINE_PAGES_UPDATED =
"org.chromium.chrome.browser.offlinepages.OFFLINE_PAGES_CHANGED"; "org.chromium.chrome.browser.offlinepages.OFFLINE_PAGES_CHANGED";
@CalledByNative @CalledByNative
private static void onPageChanged(String originString) { @VisibleForTesting
static void onPageChanged(String originString, boolean isNew, String url) {
OfflinePageOrigin origin = new OfflinePageOrigin(originString); OfflinePageOrigin origin = new OfflinePageOrigin(originString);
if (!origin.isChrome()) compareSignaturesAndFireIntent(origin); if (origin.isChrome()) return;
Bundle bundle = new Bundle();
bundle.putBoolean(IS_NEW_KEY, isNew);
bundle.putString(URL_KEY, url);
compareSignaturesAndFireIntent(origin, bundle);
} }
private static void compareSignaturesAndFireIntent(OfflinePageOrigin origin) { private static void compareSignaturesAndFireIntent(OfflinePageOrigin origin, Bundle pageInfo) {
if (!isInWhitelist(origin.getAppName())) { if (!isInWhitelist(origin.getAppName())) {
Log.w(TAG, "Non-whitelisted app"); Log.w(TAG, "Non-whitelisted app: " + origin.getAppName());
return; return;
} }
Context context = ContextUtils.getApplicationContext(); Context context = ContextUtils.getApplicationContext();
...@@ -54,6 +74,7 @@ public class CctOfflinePageModelObserver { ...@@ -54,6 +74,7 @@ public class CctOfflinePageModelObserver {
originVerification.cancel(); originVerification.cancel();
intent.putExtra(ORIGIN_VERIFICATION_KEY, originVerification); intent.putExtra(ORIGIN_VERIFICATION_KEY, originVerification);
intent.putExtra(PAGE_INFO_KEY, pageInfo);
context.sendBroadcast(intent); context.sendBroadcast(intent);
} }
......
...@@ -2081,6 +2081,7 @@ chrome_junit_test_java_sources = [ ...@@ -2081,6 +2081,7 @@ chrome_junit_test_java_sources = [
"junit/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSectionTest.java", "junit/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSectionTest.java",
"junit/src/org/chromium/chrome/browser/ntp/cards/SectionListTest.java", "junit/src/org/chromium/chrome/browser/ntp/cards/SectionListTest.java",
"junit/src/org/chromium/chrome/browser/offlinepages/BackgroundSchedulerTest.java", "junit/src/org/chromium/chrome/browser/offlinepages/BackgroundSchedulerTest.java",
"junit/src/org/chromium/chrome/browser/offlinepages/CctOfflinePageModelObserverTest.java",
"junit/src/org/chromium/chrome/browser/offlinepages/ClientIdTest.java", "junit/src/org/chromium/chrome/browser/offlinepages/ClientIdTest.java",
"junit/src/org/chromium/chrome/browser/offlinepages/OfflineBackgroundTaskTest.java", "junit/src/org/chromium/chrome/browser/offlinepages/OfflineBackgroundTaskTest.java",
"junit/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridgeUnitTest.java", "junit/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridgeUnitTest.java",
......
// Copyright 2018 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.offlinepages;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.AdditionalMatchers.not;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.os.Bundle;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.annotation.Config;
import org.chromium.base.ContextUtils;
import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.chrome.browser.AppHooks;
import org.chromium.chrome.browser.AppHooksImpl;
import java.util.ArrayList;
import java.util.List;
/**
* Test class for {@link CctOfflinePageModelObserver}.
*/
@RunWith(BaseRobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class CctOfflinePageModelObserverTest {
private static final String APP_NAME = "abc.xyz";
private static final String ORIGIN_STRING = "[abc.xyz, []]";
private static final String URL = "http://www.google.com";
@Mock
private Context mContext;
@Mock
private PackageManager mPackageManager;
@Mock
private AppHooksImpl mAppHooks;
private PackageInfo mInfo;
@Before
public void setUp() throws PackageManager.NameNotFoundException {
MockitoAnnotations.initMocks(this);
mInfo = new PackageInfo();
mInfo.signatures = new Signature[0];
when(mPackageManager.getPackageInfo(eq(APP_NAME), anyInt())).thenReturn(mInfo);
when(mPackageManager.getPackageInfo(not(eq(APP_NAME)), anyInt()))
.thenThrow(new PackageManager.NameNotFoundException());
when(mContext.getPackageManager()).thenReturn(mPackageManager);
List<String> whitelist = new ArrayList<>();
whitelist.add(APP_NAME);
when(mAppHooks.getOfflinePagesCctWhitelist()).thenReturn(whitelist);
ContextUtils.initApplicationContextForTests(mContext);
AppHooks.setInstanceForTesting(mAppHooks);
}
@Test
public void testSendBroadcastForAppNameInWhitelist_addedPage() {
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
CctOfflinePageModelObserver.onPageChanged(ORIGIN_STRING, true, URL);
verify(mContext).sendBroadcast(intentCaptor.capture());
Intent intent = intentCaptor.getValue();
assertEquals(APP_NAME, intent.getPackage());
assertEquals(CctOfflinePageModelObserver.ACTION_OFFLINE_PAGES_UPDATED, intent.getAction());
Bundle pageInfo = intent.getParcelableExtra(CctOfflinePageModelObserver.PAGE_INFO_KEY);
assertNotNull(pageInfo);
assertTrue(pageInfo.getBoolean(CctOfflinePageModelObserver.IS_NEW_KEY));
assertEquals(URL, pageInfo.getString(CctOfflinePageModelObserver.URL_KEY));
}
@Test
public void testSendBroadcastForAppNameInWhitelist_deletedPage() {
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
CctOfflinePageModelObserver.onPageChanged(ORIGIN_STRING, false, URL);
verify(mContext).sendBroadcast(intentCaptor.capture());
Intent intent = intentCaptor.getValue();
assertEquals(APP_NAME, intent.getPackage());
assertEquals(CctOfflinePageModelObserver.ACTION_OFFLINE_PAGES_UPDATED, intent.getAction());
Bundle pageInfo = intent.getParcelableExtra(CctOfflinePageModelObserver.PAGE_INFO_KEY);
assertNotNull(pageInfo);
assertFalse(pageInfo.getBoolean(CctOfflinePageModelObserver.IS_NEW_KEY));
assertEquals(URL, pageInfo.getString(CctOfflinePageModelObserver.URL_KEY));
}
@Test
public void testDoesNotSendBroadcastForAppNameNotInWhitelist() {
String originString = "[xyz.abc,[]]";
CctOfflinePageModelObserver.onPageChanged(originString, true, URL);
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
verify(mContext, never()).sendBroadcast(intentCaptor.capture());
}
@Test
public void testDoesNotSendBroadcastForChrome() {
String originString = "";
CctOfflinePageModelObserver.onPageChanged(originString, true, URL);
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
verify(mContext, never()).sendBroadcast(intentCaptor.capture());
}
}
...@@ -323,7 +323,7 @@ class DownloadSuggestionsProviderTest : public testing::Test { ...@@ -323,7 +323,7 @@ class DownloadSuggestionsProviderTest : public testing::Test {
DCHECK(provider_); DCHECK(provider_);
offline_pages::OfflinePageModel::DeletedPageInfo info( offline_pages::OfflinePageModel::DeletedPageInfo info(
item.offline_id, item.system_download_id, item.client_id, item.offline_id, item.system_download_id, item.client_id,
item.request_origin); item.request_origin, item.url);
provider_->OfflinePageDeleted(info); provider_->OfflinePageDeleted(info);
} }
......
...@@ -36,14 +36,16 @@ void CctOriginObserver::OfflinePageAdded(OfflinePageModel* model, ...@@ -36,14 +36,16 @@ void CctOriginObserver::OfflinePageAdded(OfflinePageModel* model,
const OfflinePageItem& added_page) { const OfflinePageItem& added_page) {
JNIEnv* env = base::android::AttachCurrentThread(); JNIEnv* env = base::android::AttachCurrentThread();
Java_CctOfflinePageModelObserver_onPageChanged( Java_CctOfflinePageModelObserver_onPageChanged(
env, ConvertUTF8ToJavaString(env, added_page.request_origin)); env, ConvertUTF8ToJavaString(env, added_page.request_origin), true,
ConvertUTF8ToJavaString(env, added_page.url.spec()));
} }
void CctOriginObserver::OfflinePageDeleted( void CctOriginObserver::OfflinePageDeleted(
const OfflinePageModel::DeletedPageInfo& page_info) { const OfflinePageModel::DeletedPageInfo& page_info) {
JNIEnv* env = base::android::AttachCurrentThread(); JNIEnv* env = base::android::AttachCurrentThread();
Java_CctOfflinePageModelObserver_onPageChanged( Java_CctOfflinePageModelObserver_onPageChanged(
env, ConvertUTF8ToJavaString(env, page_info.request_origin)); env, ConvertUTF8ToJavaString(env, page_info.request_origin), false,
ConvertUTF8ToJavaString(env, page_info.url.spec()));
} }
} // namespace offline_pages } // namespace offline_pages
...@@ -140,7 +140,7 @@ TEST_F(PrefetchedPagesTrackerImplTest, ShouldDeletePrefetchedURLWhenNotified) { ...@@ -140,7 +140,7 @@ TEST_F(PrefetchedPagesTrackerImplTest, ShouldDeletePrefetchedURLWhenNotified) {
tracker.PrefetchedOfflinePageExists(GURL("http://prefetched.com"))); tracker.PrefetchedOfflinePageExists(GURL("http://prefetched.com")));
tracker.OfflinePageDeleted(offline_pages::OfflinePageModel::DeletedPageInfo( tracker.OfflinePageDeleted(offline_pages::OfflinePageModel::DeletedPageInfo(
item.offline_id, kSystemDownloadId, item.client_id, item.offline_id, kSystemDownloadId, item.client_id,
/*request_origin=*/"")); /*request_origin=*/"", item.original_url));
EXPECT_FALSE( EXPECT_FALSE(
tracker.PrefetchedOfflinePageExists(GURL("http://prefetched.com"))); tracker.PrefetchedOfflinePageExists(GURL("http://prefetched.com")));
} }
...@@ -163,7 +163,7 @@ TEST_F(PrefetchedPagesTrackerImplTest, ...@@ -163,7 +163,7 @@ TEST_F(PrefetchedPagesTrackerImplTest,
tracker.OfflinePageDeleted(offline_pages::OfflinePageModel::DeletedPageInfo( tracker.OfflinePageDeleted(offline_pages::OfflinePageModel::DeletedPageInfo(
manually_downloaded_item.offline_id, kSystemDownloadId, manually_downloaded_item.offline_id, kSystemDownloadId,
manually_downloaded_item.client_id, manually_downloaded_item.client_id,
/*request_origin=*/"")); /*request_origin=*/"", manually_downloaded_item.original_url));
EXPECT_TRUE( EXPECT_TRUE(
tracker.PrefetchedOfflinePageExists(GURL("http://prefetched.com"))); tracker.PrefetchedOfflinePageExists(GURL("http://prefetched.com")));
} }
...@@ -274,7 +274,7 @@ TEST_F(PrefetchedPagesTrackerImplTest, ...@@ -274,7 +274,7 @@ TEST_F(PrefetchedPagesTrackerImplTest,
tracker.OfflinePageDeleted(offline_pages::OfflinePageModel::DeletedPageInfo( tracker.OfflinePageDeleted(offline_pages::OfflinePageModel::DeletedPageInfo(
first_item.offline_id, kSystemDownloadId, first_item.client_id, first_item.offline_id, kSystemDownloadId, first_item.client_id,
/*request_origin=*/"")); /*request_origin=*/"", first_item.original_url));
// Only one offline page (out of two) has been removed, the remaining one // Only one offline page (out of two) has been removed, the remaining one
// should be reported here. // should be reported here.
...@@ -299,14 +299,14 @@ TEST_F(PrefetchedPagesTrackerImplTest, ...@@ -299,14 +299,14 @@ TEST_F(PrefetchedPagesTrackerImplTest,
tracker.OfflinePageDeleted(offline_pages::OfflinePageModel::DeletedPageInfo( tracker.OfflinePageDeleted(offline_pages::OfflinePageModel::DeletedPageInfo(
first_item.offline_id, kSystemDownloadId, first_item.client_id, first_item.offline_id, kSystemDownloadId, first_item.client_id,
/*request_origin=*/"")); /*request_origin=*/"", first_item.original_url));
ASSERT_TRUE( ASSERT_TRUE(
tracker.PrefetchedOfflinePageExists(GURL("http://prefetched.com"))); tracker.PrefetchedOfflinePageExists(GURL("http://prefetched.com")));
tracker.OfflinePageDeleted(offline_pages::OfflinePageModel::DeletedPageInfo( tracker.OfflinePageDeleted(offline_pages::OfflinePageModel::DeletedPageInfo(
second_item.offline_id, kSystemDownloadId, second_item.client_id, second_item.offline_id, kSystemDownloadId, second_item.client_id,
/*request_origin=*/"")); /*request_origin=*/"", second_item.original_url));
// All offline pages have been removed, their absence should be reported here. // All offline pages have been removed, their absence should be reported here.
EXPECT_FALSE( EXPECT_FALSE(
......
...@@ -155,7 +155,8 @@ class MockOfflinePageModel : public StubOfflinePageModel { ...@@ -155,7 +155,8 @@ class MockOfflinePageModel : public StubOfflinePageModel {
for (const auto& page : pages) { for (const auto& page : pages) {
if (page.second.client_id.id == guid) { if (page.second.client_id.id == guid) {
DeletedPageInfo info(page.second.offline_id, kSystemDownloadId, DeletedPageInfo info(page.second.offline_id, kSystemDownloadId,
page.second.client_id, page.second.request_origin); page.second.client_id, page.second.request_origin,
page.second.original_url);
observer_->OfflinePageDeleted(info); observer_->OfflinePageDeleted(info);
pages.erase(page.first); pages.erase(page.first);
return; return;
......
...@@ -42,7 +42,7 @@ namespace { ...@@ -42,7 +42,7 @@ namespace {
// please take a look at GetCachedDeletedPageInfoWrappersByUrlPredicateSync. // please take a look at GetCachedDeletedPageInfoWrappersByUrlPredicateSync.
#define INFO_WRAPPER_FIELDS \ #define INFO_WRAPPER_FIELDS \
"offline_id, system_download_id, client_namespace, client_id, file_path, " \ "offline_id, system_download_id, client_namespace, client_id, file_path, " \
"request_origin, access_count, creation_time" "request_origin, access_count, creation_time, online_url"
#define INFO_WRAPPER_FIELD_COUNT 8 #define INFO_WRAPPER_FIELD_COUNT 8
struct DeletedPageInfoWrapper { struct DeletedPageInfoWrapper {
...@@ -56,6 +56,7 @@ struct DeletedPageInfoWrapper { ...@@ -56,6 +56,7 @@ struct DeletedPageInfoWrapper {
// Used by metric collection only: // Used by metric collection only:
int access_count; int access_count;
base::Time creation_time; base::Time creation_time;
GURL url;
}; };
DeletedPageInfoWrapper CreateInfoWrapper(const sql::Statement& statement) { DeletedPageInfoWrapper CreateInfoWrapper(const sql::Statement& statement) {
...@@ -70,6 +71,7 @@ DeletedPageInfoWrapper CreateInfoWrapper(const sql::Statement& statement) { ...@@ -70,6 +71,7 @@ DeletedPageInfoWrapper CreateInfoWrapper(const sql::Statement& statement) {
info_wrapper.access_count = statement.ColumnInt(6); info_wrapper.access_count = statement.ColumnInt(6);
info_wrapper.creation_time = info_wrapper.creation_time =
store_utils::FromDatabaseTime(statement.ColumnInt64(7)); store_utils::FromDatabaseTime(statement.ColumnInt64(7));
info_wrapper.url = GURL(statement.ColumnString(8));
return info_wrapper; return info_wrapper;
} }
...@@ -134,7 +136,8 @@ DeletePageTaskResult DeletePagesByDeletedPageInfoWrappersSync( ...@@ -134,7 +136,8 @@ DeletePageTaskResult DeletePagesByDeletedPageInfoWrappersSync(
if (DeletePageEntryByOfflineIdSync(db, info_wrapper.offline_id)) { if (DeletePageEntryByOfflineIdSync(db, info_wrapper.offline_id)) {
deleted_page_infos.emplace_back( deleted_page_infos.emplace_back(
info_wrapper.offline_id, info_wrapper.system_download_id, info_wrapper.offline_id, info_wrapper.system_download_id,
info_wrapper.client_id, info_wrapper.request_origin); info_wrapper.client_id, info_wrapper.request_origin,
info_wrapper.url);
} }
} }
} }
......
...@@ -27,11 +27,13 @@ OfflinePageModel::DeletedPageInfo::DeletedPageInfo( ...@@ -27,11 +27,13 @@ OfflinePageModel::DeletedPageInfo::DeletedPageInfo(
int64_t offline_id, int64_t offline_id,
int64_t system_download_id, int64_t system_download_id,
const ClientId& client_id, const ClientId& client_id,
const std::string& request_origin) const std::string& request_origin,
const GURL& url)
: offline_id(offline_id), : offline_id(offline_id),
system_download_id(system_download_id), system_download_id(system_download_id),
client_id(client_id), client_id(client_id),
request_origin(request_origin) {} request_origin(request_origin),
url(url) {}
// static // static
bool OfflinePageModel::CanSaveURL(const GURL& url) { bool OfflinePageModel::CanSaveURL(const GURL& url) {
......
...@@ -82,7 +82,8 @@ class OfflinePageModel : public base::SupportsUserData, public KeyedService { ...@@ -82,7 +82,8 @@ class OfflinePageModel : public base::SupportsUserData, public KeyedService {
DeletedPageInfo(int64_t offline_id, DeletedPageInfo(int64_t offline_id,
int64_t system_download_id, int64_t system_download_id,
const ClientId& client_id, const ClientId& client_id,
const std::string& request_origin); const std::string& request_origin,
const GURL& url);
// The ID of the deleted page. // The ID of the deleted page.
int64_t offline_id; int64_t offline_id;
// The system download manager id of the deleted page. This will be 0 if // The system download manager id of the deleted page. This will be 0 if
...@@ -92,6 +93,8 @@ class OfflinePageModel : public base::SupportsUserData, public KeyedService { ...@@ -92,6 +93,8 @@ class OfflinePageModel : public base::SupportsUserData, public KeyedService {
ClientId client_id; ClientId client_id;
// The origin that the page was saved on behalf of. // The origin that the page was saved on behalf of.
std::string request_origin; std::string request_origin;
// URL of the page that was deleted.
GURL url;
}; };
// Observer of the OfflinePageModel. // Observer of the OfflinePageModel.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment