summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorromax <romax@chromium.org>2016-03-15 17:59:03 -0700
committerCommit bot <commit-bot@chromium.org>2016-03-16 01:00:19 +0000
commitb5841b612f8c01518fc64b81ab31210d9bdc7abb (patch)
treeeb1666e7a1fed987a7b2949670d4d3d1ca0d0fbf
parentd420eecfc4f7881986bfbc623c3598a0b72c06c9 (diff)
downloadchromium_src-b5841b612f8c01518fc64b81ab31210d9bdc7abb.zip
chromium_src-b5841b612f8c01518fc64b81ab31210d9bdc7abb.tar.gz
chromium_src-b5841b612f8c01518fc64b81ab31210d9bdc7abb.tar.bz2
[Offline Pages] Fix crash when deleting offline pages.
R=nyquist@chromium.org, fgorski@chromium.org, bburns@chromium.org BUG=593182 Adding null check to getClientIdForOfflineId(), and pass client id along with OfflinePageDeleted() callback. Also got some test for the use cases. nyquist@ for owner review (of the BUILD.gn file). fgorski@ for overall review. Review URL: https://codereview.chromium.org/1776343005 Cr-Commit-Position: refs/heads/master@{#381372}
-rw-r--r--chrome/android/BUILD.gn1
-rw-r--r--chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridge.java18
-rw-r--r--chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridgeTest.java79
-rw-r--r--chrome/browser/android/offline_pages/offline_page_bridge.cc15
-rw-r--r--chrome/browser/android/offline_pages/offline_page_bridge.h8
-rw-r--r--components/offline_pages/offline_page_model.cc29
-rw-r--r--components/offline_pages/offline_page_model.h7
-rw-r--r--components/offline_pages/offline_page_model_unittest.cc19
8 files changed, 153 insertions, 23 deletions
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 10e4134..f7297a6 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -279,6 +279,7 @@ junit_binary("chrome_junit_tests") {
"junit/src/org/chromium/chrome/browser/media/router/cast/TestUtils.java",
"junit/src/org/chromium/chrome/browser/notifications/NotificationUIManagerUnitTest.java",
"junit/src/org/chromium/chrome/browser/ntp/NativePageFactoryTest.java",
+ "junit/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridgeTest.java",
"junit/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtilsTest.java",
"junit/src/org/chromium/chrome/browser/omaha/ResponseParserTest.java",
"junit/src/org/chromium/chrome/browser/omaha/VersionNumberTest.java",
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridge.java
index 52c5f6a..b18caa1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridge.java
@@ -473,8 +473,12 @@ public class OfflinePageBridge {
};
}
- private ClientId getClientIdForOfflineId(long offlineId) {
- return nativeGetPageByOfflineId(mNativeOfflinePageBridge, offlineId).getClientId();
+ protected ClientId getClientIdForOfflineId(long offlineId) {
+ OfflinePageItem item = nativeGetPageByOfflineId(mNativeOfflinePageBridge, offlineId);
+ if (item != null) {
+ return item.getClientId();
+ }
+ return null;
}
@CalledByNative
@@ -505,8 +509,7 @@ public class OfflinePageBridge {
}
@CalledByNative
- private void offlinePageDeleted(long offlineId) {
- ClientId clientId = getClientIdForOfflineId(offlineId);
+ protected void offlinePageDeleted(long offlineId, ClientId clientId) {
for (OfflinePageModelObserver observer : mObservers) {
observer.offlinePageDeleted(offlineId, clientId);
}
@@ -528,6 +531,11 @@ public class OfflinePageBridge {
creationTime, accessCount, lastAccessTimeMs);
}
+ @CalledByNative
+ private static ClientId createClientId(String clientNamespace, String id) {
+ return new ClientId(clientNamespace, id);
+ }
+
private static native int nativeGetFeatureMode();
private static native boolean nativeCanSavePage(String url);
private static native OfflinePageBridge nativeGetOfflinePageBridgeForProfile(Profile profile);
@@ -536,7 +544,7 @@ public class OfflinePageBridge {
long nativeOfflinePageBridge, List<OfflinePageItem> offlinePages);
protected native long[] nativeGetOfflineIdsForClientId(
long nativeOfflinePageBridge, String clientNamespace, String clientId);
- private native OfflinePageItem nativeGetPageByOfflineId(
+ protected native OfflinePageItem nativeGetPageByOfflineId(
long nativeOfflinePageBridge, long offlineId);
private native OfflinePageItem nativeGetPageByOnlineURL(
long nativeOfflinePageBridge, String onlineURL);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridgeTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridgeTest.java
index 5f2d216..0aa7aa8d 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridgeTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridgeTest.java
@@ -16,6 +16,7 @@ import static org.mockito.Mockito.verify;
import org.chromium.base.BaseChromiumApplication;
import org.chromium.base.test.util.Feature;
+import org.chromium.chrome.browser.offlinepages.OfflinePageBridge.OfflinePageModelObserver;
import org.chromium.testing.local.LocalRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
@@ -34,6 +35,31 @@ public class OfflinePageBridgeTest {
private static final String TEST_NAMESPACE = "TEST_NAMESPACE";
private static final String TEST_ID = "TEST_ID";
+ private static final String TEST_URL = "TEST_URL";
+ private static final long TEST_OFFLINE_ID = 42;
+ private static final ClientId TEST_CLIENT_ID = new ClientId(TEST_NAMESPACE, TEST_ID);
+ private static final String TEST_OFFLINE_URL = "TEST_OFFLINE_URL";
+ private static final long TEST_FILESIZE = 12345;
+ private static final long TEST_CREATIONTIMEMS = 150;
+ private static final int TEST_ACCESSCOUNT = 1;
+ private static final long TEST_LASTACCESSTIMEMS = 20160314;
+
+ private static final OfflinePageItem TEST_OFFLINE_PAGE_ITEM = new OfflinePageItem(TEST_URL,
+ TEST_OFFLINE_ID, TEST_NAMESPACE, TEST_ID, TEST_OFFLINE_URL, TEST_FILESIZE,
+ TEST_CREATIONTIMEMS, TEST_ACCESSCOUNT, TEST_LASTACCESSTIMEMS);
+
+ /**
+ * Mocks the observer.
+ */
+ public class MockOfflinePageModelObserver extends OfflinePageModelObserver {
+ public long lastDeletedOfflineId;
+ public ClientId lastDeletedClientId;
+
+ public void offlinePageDeleted(long offlineId, ClientId clientId) {
+ lastDeletedOfflineId = offlineId;
+ lastDeletedClientId = clientId;
+ }
+ }
@Before
public void setUp() throws Exception {
@@ -64,6 +90,59 @@ public class OfflinePageBridgeTest {
.nativeGetOfflineIdsForClientId(anyLong(), anyString(), anyString());
}
+ /**
+ * Tests getClientIdForOfflineId.
+ */
+ @Test
+ @Feature({"OfflinePages"})
+ public void testGetClientIdForOfflineId() {
+ doReturn(TEST_OFFLINE_PAGE_ITEM)
+ .when(mBridge)
+ .nativeGetPageByOfflineId(anyLong(), eq(TEST_OFFLINE_ID));
+
+ mBridge.offlinePageModelLoaded();
+ long testOfflineId = TEST_OFFLINE_ID;
+ ClientId resultClientId = mBridge.getClientIdForOfflineId(testOfflineId);
+ assertEquals(resultClientId, TEST_CLIENT_ID);
+ verify(mBridge, times(1)).getClientIdForOfflineId(eq(TEST_OFFLINE_ID));
+ verify(mBridge, times(1)).nativeGetPageByOfflineId(anyLong(), eq(TEST_OFFLINE_ID));
+ }
+
+ /**
+ * Tests getClientIdForOfflineId for null.
+ */
+ @Test
+ @Feature({"OfflinePages"})
+ public void testGetClientIdForOfflineIdNull() {
+ doReturn(null).when(mBridge).nativeGetPageByOfflineId(anyLong(), eq(TEST_OFFLINE_ID));
+ mBridge.offlinePageModelLoaded();
+ long testOfflineId = TEST_OFFLINE_ID;
+ ClientId resultClientId = mBridge.getClientIdForOfflineId(testOfflineId);
+ assertEquals(resultClientId, null);
+ verify(mBridge, times(1)).getClientIdForOfflineId(eq(TEST_OFFLINE_ID));
+ verify(mBridge, times(1)).nativeGetPageByOfflineId(anyLong(), eq(TEST_OFFLINE_ID));
+ }
+
+ /**
+ * Tests OfflinePageBridge#OfflinePageDeleted() callback with two observers attached.
+ */
+ @Test
+ @Feature({"OfflinePages"})
+ public void testRemovePageByClientId() {
+ MockOfflinePageModelObserver observer1 = new MockOfflinePageModelObserver();
+ MockOfflinePageModelObserver observer2 = new MockOfflinePageModelObserver();
+ mBridge.addObserver(observer1);
+ mBridge.addObserver(observer2);
+
+ ClientId testClientId = new ClientId(TEST_NAMESPACE, TEST_ID);
+ long testOfflineId = 123;
+ mBridge.offlinePageDeleted(testOfflineId, testClientId);
+ assertEquals(testOfflineId, observer1.lastDeletedOfflineId);
+ assertEquals(testClientId, observer1.lastDeletedClientId);
+ assertEquals(testOfflineId, observer2.lastDeletedOfflineId);
+ assertEquals(testClientId, observer2.lastDeletedClientId);
+ }
+
@Test(expected = AssertionError.class)
@Feature({"OfflinePages"})
public void testGetPageByClientId_ModelNotLoaded() {
diff --git a/chrome/browser/android/offline_pages/offline_page_bridge.cc b/chrome/browser/android/offline_pages/offline_page_bridge.cc
index 7f98028..c5021b8 100644
--- a/chrome/browser/android/offline_pages/offline_page_bridge.cc
+++ b/chrome/browser/android/offline_pages/offline_page_bridge.cc
@@ -131,9 +131,11 @@ void OfflinePageBridge::OfflinePageModelChanged(OfflinePageModel* model) {
Java_OfflinePageBridge_offlinePageModelChanged(env, java_ref_.obj());
}
-void OfflinePageBridge::OfflinePageDeleted(int64_t offline_id) {
+void OfflinePageBridge::OfflinePageDeleted(int64_t offline_id,
+ const ClientId& client_id) {
JNIEnv* env = base::android::AttachCurrentThread();
- Java_OfflinePageBridge_offlinePageDeleted(env, java_ref_.obj(), offline_id);
+ Java_OfflinePageBridge_offlinePageDeleted(
+ env, java_ref_.obj(), offline_id, CreateClientId(env, client_id).obj());
}
void OfflinePageBridge::GetAllPages(JNIEnv* env,
@@ -321,6 +323,15 @@ ScopedJavaLocalRef<jobject> OfflinePageBridge::CreateOfflinePageItem(
offline_page.access_count, offline_page.last_access_time.ToJavaTime());
}
+ScopedJavaLocalRef<jobject> OfflinePageBridge::CreateClientId(
+ JNIEnv* env,
+ const ClientId& client_id) const {
+ return Java_OfflinePageBridge_createClientId(
+ env,
+ ConvertUTF8ToJavaString(env, client_id.name_space).obj(),
+ ConvertUTF8ToJavaString(env, client_id.id).obj());
+}
+
bool RegisterOfflinePageBridge(JNIEnv* env) {
return RegisterNativesImpl(env);
}
diff --git a/chrome/browser/android/offline_pages/offline_page_bridge.h b/chrome/browser/android/offline_pages/offline_page_bridge.h
index ceee306..7293639 100644
--- a/chrome/browser/android/offline_pages/offline_page_bridge.h
+++ b/chrome/browser/android/offline_pages/offline_page_bridge.h
@@ -11,6 +11,7 @@
#include "base/android/jni_weak_ref.h"
#include "base/macros.h"
#include "base/supports_user_data.h"
+#include "components/offline_pages/offline_page_item.h"
#include "components/offline_pages/offline_page_model.h"
namespace content {
@@ -35,7 +36,8 @@ class OfflinePageBridge : public OfflinePageModel::Observer,
// OfflinePageModel::Observer implementation.
void OfflinePageModelLoaded(OfflinePageModel* model) override;
void OfflinePageModelChanged(OfflinePageModel* model) override;
- void OfflinePageDeleted(int64_t offline_id) override;
+ void OfflinePageDeleted(int64_t offline_id,
+ const ClientId& client_id) override;
void GetAllPages(JNIEnv* env,
const base::android::JavaParamRef<jobject>& obj,
@@ -113,6 +115,10 @@ class OfflinePageBridge : public OfflinePageModel::Observer,
JNIEnv* env,
const OfflinePageItem& offline_page) const;
+ base::android::ScopedJavaLocalRef<jobject> CreateClientId(
+ JNIEnv* env,
+ const ClientId& clientId) const;
+
base::android::ScopedJavaGlobalRef<jobject> java_ref_;
// Not owned.
content::BrowserContext* browser_context_;
diff --git a/components/offline_pages/offline_page_model.cc b/components/offline_pages/offline_page_model.cc
index 9316a97..ac33771 100644
--- a/components/offline_pages/offline_page_model.cc
+++ b/components/offline_pages/offline_page_model.cc
@@ -520,7 +520,8 @@ void OfflinePageModel::OnMarkPageForDeletionDone(
kFinalDeletionDelay);
FOR_EACH_OBSERVER(Observer, observers_,
- OfflinePageDeleted(offline_page_item.offline_id));
+ OfflinePageDeleted(offline_page_item.offline_id,
+ offline_page_item.client_id));
}
void OfflinePageModel::OnUndoOfflinePageDone(
@@ -662,8 +663,9 @@ void OfflinePageModel::OnRemoveOfflinePagesDone(
// If the page is not marked for deletion at this point, the model has not
// yet informed the observer that the offline page is deleted.
if (!iter->second.IsMarkedForDeletion()) {
- FOR_EACH_OBSERVER(Observer, observers_,
- OfflinePageDeleted(iter->second.offline_id));
+ FOR_EACH_OBSERVER(
+ Observer, observers_,
+ OfflinePageDeleted(iter->second.offline_id, iter->second.client_id));
}
offline_pages_.erase(iter);
}
@@ -694,10 +696,20 @@ void OfflinePageModel::OnFindPagesMissingArchiveFile(
if (ids_of_pages_missing_archive_file->empty())
return;
+ std::vector<std::pair<int64_t, ClientId>> offline_client_id_pairs;
+ for (auto offline_id : *ids_of_pages_missing_archive_file) {
+ // Since we might have deleted pages in between so we have to purge
+ // the list to make sure we still care about them.
+ auto iter = offline_pages_.find(offline_id);
+ if (iter != offline_pages_.end()) {
+ offline_client_id_pairs.push_back(
+ std::make_pair(offline_id, iter->second.client_id));
+ }
+ }
+
DeletePageCallback done_callback(
base::Bind(&OfflinePageModel::OnRemoveOfflinePagesMissingArchiveFileDone,
- weak_ptr_factory_.GetWeakPtr(),
- *ids_of_pages_missing_archive_file));
+ weak_ptr_factory_.GetWeakPtr(), offline_client_id_pairs));
store_->RemoveOfflinePages(
*ids_of_pages_missing_archive_file,
@@ -708,10 +720,11 @@ void OfflinePageModel::OnFindPagesMissingArchiveFile(
}
void OfflinePageModel::OnRemoveOfflinePagesMissingArchiveFileDone(
- const std::vector<int64_t>& offline_ids,
+ const std::vector<std::pair<int64_t, ClientId>>& offline_client_id_pairs,
OfflinePageModel::DeletePageResult /* result */) {
- for (int64_t offline_id : offline_ids) {
- FOR_EACH_OBSERVER(Observer, observers_, OfflinePageDeleted(offline_id));
+ for (const auto& id_pair : offline_client_id_pairs) {
+ FOR_EACH_OBSERVER(Observer, observers_,
+ OfflinePageDeleted(id_pair.first, id_pair.second));
}
}
diff --git a/components/offline_pages/offline_page_model.h b/components/offline_pages/offline_page_model.h
index e26b538..e76be0d 100644
--- a/components/offline_pages/offline_page_model.h
+++ b/components/offline_pages/offline_page_model.h
@@ -115,7 +115,8 @@ class OfflinePageModel : public KeyedService, public base::SupportsUserData {
// Invoked when an offline copy related to |offline_id| was deleted.
// In can be invoked as a result of |CheckForExternalFileDeletion|, if a
// deleted page is detected.
- virtual void OfflinePageDeleted(int64_t offline_id) = 0;
+ virtual void OfflinePageDeleted(int64_t offline_id,
+ const ClientId& client_id) = 0;
protected:
virtual ~Observer() {}
@@ -286,9 +287,9 @@ class OfflinePageModel : public KeyedService, public base::SupportsUserData {
// Callbacks for checking if offline pages are missing archive files.
void OnFindPagesMissingArchiveFile(
- const std::vector<int64_t>* pages_missing_archive_file);
+ const std::vector<int64_t>* ids_of_pages_missing_archive_file);
void OnRemoveOfflinePagesMissingArchiveFileDone(
- const std::vector<int64_t>& offline_ids,
+ const std::vector<std::pair<int64_t, ClientId>>& offline_client_id_pairs,
OfflinePageModel::DeletePageResult result);
// Steps for clearing all.
diff --git a/components/offline_pages/offline_page_model_unittest.cc b/components/offline_pages/offline_page_model_unittest.cc
index 3c4e216..5c9e934 100644
--- a/components/offline_pages/offline_page_model_unittest.cc
+++ b/components/offline_pages/offline_page_model_unittest.cc
@@ -68,7 +68,8 @@ class OfflinePageModelTest
// OfflinePageModel::Observer implementation.
void OfflinePageModelLoaded(OfflinePageModel* model) override;
void OfflinePageModelChanged(OfflinePageModel* model) override;
- void OfflinePageDeleted(int64_t offline_id) override;
+ void OfflinePageDeleted(int64_t offline_id,
+ const ClientId& client_id) override;
// OfflinePageTestArchiver::Observer implementation.
void SetLastPathCreatedByArchiver(const base::FilePath& file_path) override;
@@ -119,6 +120,8 @@ class OfflinePageModelTest
int64_t last_deleted_offline_id() const { return last_deleted_offline_id_; }
+ ClientId last_deleted_client_id() const { return last_deleted_client_id_; }
+
const base::FilePath& last_archiver_path() { return last_archiver_path_; }
private:
@@ -132,6 +135,7 @@ class OfflinePageModelTest
DeletePageResult last_delete_result_;
base::FilePath last_archiver_path_;
int64_t last_deleted_offline_id_;
+ ClientId last_deleted_client_id_;
};
OfflinePageModelTest::OfflinePageModelTest()
@@ -166,8 +170,10 @@ void OfflinePageModelTest::OfflinePageModelChanged(OfflinePageModel* model) {
ASSERT_EQ(model_.get(), model);
}
-void OfflinePageModelTest::OfflinePageDeleted(int64_t offline_id) {
+void OfflinePageModelTest::OfflinePageDeleted(int64_t offline_id,
+ const ClientId& client_id) {
last_deleted_offline_id_ = offline_id;
+ last_deleted_client_id_ = client_id;
}
void OfflinePageModelTest::SetLastPathCreatedByArchiver(
@@ -523,6 +529,7 @@ TEST_F(OfflinePageModelTest, DeletePageSuccessful) {
PumpLoop();
EXPECT_EQ(last_deleted_offline_id(), offline1);
+ EXPECT_EQ(last_deleted_client_id(), kTestPageBookmarkId1);
EXPECT_EQ(DeletePageResult::SUCCESS, last_delete_result());
ASSERT_EQ(1u, store->GetAllPages().size());
EXPECT_EQ(kTestUrl2, store->GetAllPages()[0].url);
@@ -537,6 +544,7 @@ TEST_F(OfflinePageModelTest, DeletePageSuccessful) {
PumpLoop();
EXPECT_EQ(last_deleted_offline_id(), offline2);
+ EXPECT_EQ(last_deleted_client_id(), kTestPageBookmarkId2);
EXPECT_EQ(DeletePageResult::SUCCESS, last_delete_result());
EXPECT_EQ(0u, store->GetAllPages().size());
}
@@ -597,6 +605,7 @@ TEST_F(OfflinePageModelTest, DetectThatOfflineCopyIsMissingAfterLoad) {
PumpLoop();
EXPECT_EQ(last_deleted_offline_id(), offline_id);
+ EXPECT_EQ(last_deleted_client_id(), kTestPageBookmarkId1);
EXPECT_EQ(0UL, model()->GetAllPages().size());
}
@@ -928,14 +937,16 @@ TEST_F(OfflinePageModelBookmarkChangeTest, RemoveBookmark) {
// Creates a bookmark with offline copy.
bookmark_node = CreateBookmarkNode(kTestUrl);
- SavePage(kTestUrl, ClientId(BOOKMARK_NAMESPACE,
- base::Int64ToString(bookmark_node->id())));
+ ClientId client_id =
+ ClientId(BOOKMARK_NAMESPACE, base::Int64ToString(bookmark_node->id()));
+ SavePage(kTestUrl, client_id);
EXPECT_EQ(1UL, model()->GetAllPages().size());
// The offline copy should also be removed upon the bookmark removal.
bookmark_model()->Remove(bookmark_node);
PumpLoop();
EXPECT_EQ(0UL, model()->GetAllPages().size());
+ EXPECT_EQ(client_id, last_deleted_client_id());
}
TEST_F(OfflinePageModelBookmarkChangeTest, UndoBookmarkRemoval) {