Commit 5a515f97 authored by David Maunder's avatar David Maunder Committed by Commit Bot

Separate out encryption in PersistedTabDataStorage

Previously non-encryption and encryption were
both handled in the same file with a flag passed in.

With the new approach they are in separate files and
selecting the right file is driven by a configuration
offering better separation of concerns.

Bug: 1059638
Change-Id: Icdb8ff751295a913123e9b16f36198efcb9785fa
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2106491
Commit-Queue: David Maunder <davidjm@chromium.org>
Reviewed-by: default avatarDavid Trainor <dtrainor@chromium.org>
Reviewed-by: default avatarTommy Nyquist <nyquist@chromium.org>
Cr-Commit-Position: refs/heads/master@{#753264}
parent 1e9d6c0e
...@@ -1601,6 +1601,7 @@ chrome_java_sources = [ ...@@ -1601,6 +1601,7 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/tab/TabWebContentsUserData.java", "java/src/org/chromium/chrome/browser/tab/TabWebContentsUserData.java",
"java/src/org/chromium/chrome/browser/tab/TrustedCdn.java", "java/src/org/chromium/chrome/browser/tab/TrustedCdn.java",
"java/src/org/chromium/chrome/browser/tab/state/CriticalPersistedTabData.java", "java/src/org/chromium/chrome/browser/tab/state/CriticalPersistedTabData.java",
"java/src/org/chromium/chrome/browser/tab/state/EncryptedFilePersistedTabDataStorage.java",
"java/src/org/chromium/chrome/browser/tab/state/FilePersistedTabDataStorage.java", "java/src/org/chromium/chrome/browser/tab/state/FilePersistedTabDataStorage.java",
"java/src/org/chromium/chrome/browser/tab/state/PersistedTabData.java", "java/src/org/chromium/chrome/browser/tab/state/PersistedTabData.java",
"java/src/org/chromium/chrome/browser/tab/state/PersistedTabDataConfiguration.java", "java/src/org/chromium/chrome/browser/tab/state/PersistedTabDataConfiguration.java",
......
...@@ -19,6 +19,7 @@ include_rules = [ ...@@ -19,6 +19,7 @@ include_rules = [
"+components/browser_ui/styles/android", "+components/browser_ui/styles/android",
"+components/browser_ui/widget/android", "+components/browser_ui/widget/android",
"+content/public/android/java/src/org/chromium/content_public", "+content/public/android/java/src/org/chromium/content_public",
"+chrome/browser/android/crypto/java/src/org/chromium/chrome/browser/crypto/CipherFactory.java",
"+chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabbedModeTabPersistencePolicy.java", "+chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabbedModeTabPersistencePolicy.java",
"+chrome/android/public/crypto/java/src/org/chromium/chrome/browser/crypto/CipherFactory.java", "+chrome/android/public/crypto/java/src/org/chromium/chrome/browser/crypto/CipherFactory.java",
"+chrome/browser/profiles/android/java/src/org/chromium/chrome/browser/profiles/Profile.java", "+chrome/browser/profiles/android/java/src/org/chromium/chrome/browser/profiles/Profile.java",
......
...@@ -104,8 +104,8 @@ public class CriticalPersistedTabData extends PersistedTabData { ...@@ -104,8 +104,8 @@ public class CriticalPersistedTabData extends PersistedTabData {
// from the {@link Tab}. // from the {@link Tab}.
if (tabImpl.isInitialized()) { if (tabImpl.isInitialized()) {
TabState.WebContentsState webContentsState = TabState.getWebContentsState(tabImpl); TabState.WebContentsState webContentsState = TabState.getWebContentsState(tabImpl);
PersistedTabDataConfiguration config = PersistedTabDataConfiguration config = PersistedTabDataConfiguration.get(
PersistedTabDataConfiguration.get(CriticalPersistedTabData.class); CriticalPersistedTabData.class, tab.isIncognito());
CriticalPersistedTabData criticalPersistedTabData = new CriticalPersistedTabData(tab, CriticalPersistedTabData criticalPersistedTabData = new CriticalPersistedTabData(tab,
tab.getParentId(), tabImpl.getRootId(), tab.getTimestampMillis(), tab.getParentId(), tabImpl.getRootId(), tab.getTimestampMillis(),
webContentsState != null webContentsState != null
......
// Copyright 2020 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.tab.state;
import org.chromium.base.Callback;
import org.chromium.base.Log;
import org.chromium.chrome.browser.crypto.CipherFactory;
import java.util.Locale;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
/**
* Implements {@link PersistedTabDataStorage} but encrypts and decrypts
* as data is stored and retrieved respectively.
*/
public class EncryptedFilePersistedTabDataStorage extends FilePersistedTabDataStorage {
private static final String TAG = "EFPTDS";
@Override
public void save(int tabId, String dataId, byte[] data) {
super.save(tabId, dataId, encrypt(data));
}
@Override
public void restore(int tabId, String dataId, Callback<byte[]> callback) {
super.restore(tabId, dataId, (data) -> { callback.onResult(decrypt(data)); });
}
private static byte[] decrypt(byte[] data) {
try {
if (data == null) {
return null;
}
return CipherFactory.getInstance().getCipher(Cipher.DECRYPT_MODE).doFinal(data);
} catch (BadPaddingException | IllegalBlockSizeException e) {
Log.e(TAG,
String.format(
Locale.ENGLISH, "Error encrypting data. Details: %s", e.getMessage()));
return null;
}
}
private static byte[] encrypt(byte[] data) {
Cipher cipher = CipherFactory.getInstance().getCipher(Cipher.ENCRYPT_MODE);
try {
return cipher.doFinal(data);
} catch (BadPaddingException | IllegalBlockSizeException e) {
Log.e(TAG,
String.format(Locale.ENGLISH, "Problem encrypting data. Details: %s",
e.getMessage()));
return null;
}
}
}
...@@ -11,8 +11,9 @@ import androidx.annotation.VisibleForTesting; ...@@ -11,8 +11,9 @@ import androidx.annotation.VisibleForTesting;
import org.chromium.base.Callback; import org.chromium.base.Callback;
import org.chromium.base.Log; import org.chromium.base.Log;
import org.chromium.base.StreamUtil; import org.chromium.base.StreamUtil;
import org.chromium.chrome.browser.crypto.CipherFactory; import org.chromium.base.task.PostTask;
import org.chromium.chrome.browser.tabmodel.TabbedModeTabPersistencePolicy; import org.chromium.chrome.browser.tabmodel.TabbedModeTabPersistencePolicy;
import org.chromium.content_public.browser.UiThreadTaskTraits;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
...@@ -20,10 +21,6 @@ import java.io.FileOutputStream; ...@@ -20,10 +21,6 @@ import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.Locale; import java.util.Locale;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
/** /**
* {@link PersistedTabDataStorage} which uses a file for the storage * {@link PersistedTabDataStorage} which uses a file for the storage
*/ */
...@@ -31,24 +28,12 @@ public class FilePersistedTabDataStorage implements PersistedTabDataStorage { ...@@ -31,24 +28,12 @@ public class FilePersistedTabDataStorage implements PersistedTabDataStorage {
private static final String TAG = "FilePTDS"; private static final String TAG = "FilePTDS";
@Override @Override
public void save(int tabId, boolean isEncrypted, String dataId, byte[] data) { public void save(int tabId, String dataId, byte[] data) {
// TODO(crbug.com/1059636) these should be queued and executed on a background thread // TODO(crbug.com/1059636) these should be queued and executed on a background thread
// TODO(crbug.com/1059637) we should introduce a retry mechanisms // TODO(crbug.com/1059637) we should introduce a retry mechanisms
// TODO(crbug.com/1059638) abstract out encrypt/decrypt
if (isEncrypted) {
Cipher cipher = CipherFactory.getInstance().getCipher(Cipher.ENCRYPT_MODE);
try {
data = cipher.doFinal(data);
} catch (BadPaddingException | IllegalBlockSizeException e) {
Log.e(TAG,
String.format(Locale.ENGLISH, "Problem encrypting data. Details: %s",
e.getMessage()));
return;
}
}
FileOutputStream outputStream = null; FileOutputStream outputStream = null;
try { try {
outputStream = new FileOutputStream(getFile(tabId, isEncrypted, dataId)); outputStream = new FileOutputStream(getFile(tabId, dataId));
outputStream.write(data); outputStream.write(data);
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
Log.e(TAG, Log.e(TAG,
...@@ -68,41 +53,32 @@ public class FilePersistedTabDataStorage implements PersistedTabDataStorage { ...@@ -68,41 +53,32 @@ public class FilePersistedTabDataStorage implements PersistedTabDataStorage {
} }
@Override @Override
public void restore(int tabId, boolean isEncrypted, String dataId, Callback<byte[]> callback) { public void restore(int tabId, String dataId, Callback<byte[]> callback) {
File file = getFile(tabId, isEncrypted, dataId); File file = getFile(tabId, dataId);
try { try {
AtomicFile atomicFile = new AtomicFile(file); AtomicFile atomicFile = new AtomicFile(file);
byte[] res = atomicFile.readFully(); byte[] res = atomicFile.readFully();
if (isEncrypted) { PostTask.postTask(UiThreadTaskTraits.DEFAULT, () -> { callback.onResult(res); });
Cipher cipher = CipherFactory.getInstance().getCipher(Cipher.DECRYPT_MODE);
res = cipher.doFinal(res);
}
callback.onResult(res);
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
Log.e(TAG, Log.e(TAG,
String.format(Locale.ENGLISH, String.format(Locale.ENGLISH,
"FileNotFoundException while attempting to restore " "FileNotFoundException while attempting to restore "
+ " for Tab %d. Details: %s", + " for Tab %d. Details: %s",
tabId, e.getMessage())); tabId, e.getMessage()));
callback.onResult(null); PostTask.postTask(UiThreadTaskTraits.DEFAULT, () -> { callback.onResult(null); });
} catch (IOException e) { } catch (IOException e) {
Log.e(TAG, Log.e(TAG,
String.format(Locale.ENGLISH, String.format(Locale.ENGLISH,
"IOException while attempting to restore for Tab " "IOException while attempting to restore for Tab "
+ "%d. Details: %s", + "%d. Details: %s",
tabId, e.getMessage())); tabId, e.getMessage()));
callback.onResult(null); PostTask.postTask(UiThreadTaskTraits.DEFAULT, () -> { callback.onResult(null); });
} catch (BadPaddingException | IllegalBlockSizeException e) {
Log.e(TAG,
String.format(
Locale.ENGLISH, "Error encrypting data. Details: %s", e.getMessage()));
callback.onResult(null);
} }
} }
@Override @Override
public void delete(int tabId, boolean isEncrypted, String dataId) { public void delete(int tabId, String dataId) {
File file = getFile(tabId, isEncrypted, dataId); File file = getFile(tabId, dataId);
if (!file.exists()) { if (!file.exists()) {
return; return;
} }
...@@ -113,9 +89,8 @@ public class FilePersistedTabDataStorage implements PersistedTabDataStorage { ...@@ -113,9 +89,8 @@ public class FilePersistedTabDataStorage implements PersistedTabDataStorage {
} }
@VisibleForTesting @VisibleForTesting
protected File getFile(int tabId, boolean isEncrypted, String dataId) { protected File getFile(int tabId, String dataId) {
String encryptedId = isEncrypted ? "Encrypted" : "NotEncrypted";
return new File(TabbedModeTabPersistencePolicy.getOrCreateTabbedModeStateDirectory(), return new File(TabbedModeTabPersistencePolicy.getOrCreateTabbedModeStateDirectory(),
String.format(Locale.ENGLISH, "%d%s%s", tabId, encryptedId, dataId)); String.format(Locale.ENGLISH, "%d%s", tabId, dataId));
} }
} }
...@@ -74,8 +74,9 @@ public abstract class PersistedTabData implements UserData { ...@@ -74,8 +74,9 @@ public abstract class PersistedTabData implements UserData {
callback.onResult(persistedTabDataFromTab); callback.onResult(persistedTabDataFromTab);
return; return;
} }
PersistedTabDataConfiguration config = PersistedTabDataConfiguration.get(clazz); PersistedTabDataConfiguration config =
config.storage.restore(tab.getId(), tab.isIncognito(), config.id, (data) -> { PersistedTabDataConfiguration.get(clazz, tab.isIncognito());
config.storage.restore(tab.getId(), config.id, (data) -> {
T persistedTabData; T persistedTabData;
if (data == null) { if (data == null) {
persistedTabData = supplier.get(); persistedTabData = supplier.get();
...@@ -116,8 +117,7 @@ public abstract class PersistedTabData implements UserData { ...@@ -116,8 +117,7 @@ public abstract class PersistedTabData implements UserData {
*/ */
@VisibleForTesting @VisibleForTesting
protected void save() { protected void save() {
mPersistedTabDataStorage.save( mPersistedTabDataStorage.save(mTab.getId(), mPersistedTabDataId, serialize());
mTab.getId(), mTab.isIncognito(), mPersistedTabDataId, serialize());
} }
/** /**
...@@ -138,7 +138,7 @@ public abstract class PersistedTabData implements UserData { ...@@ -138,7 +138,7 @@ public abstract class PersistedTabData implements UserData {
* @param profile profile * @param profile profile
*/ */
protected void delete() { protected void delete() {
mPersistedTabDataStorage.delete(mTab.getId(), mTab.isIncognito(), mPersistedTabDataId); mPersistedTabDataStorage.delete(mTab.getId(), mPersistedTabDataId);
} }
/** /**
......
...@@ -14,14 +14,18 @@ import java.util.Map; ...@@ -14,14 +14,18 @@ import java.util.Map;
public enum PersistedTabDataConfiguration { public enum PersistedTabDataConfiguration {
// TODO(crbug.com/1059650) investigate should this go in the app code? // TODO(crbug.com/1059650) investigate should this go in the app code?
// Also investigate if the storage instance should be shared. // Also investigate if the storage instance should be shared.
CRITICAL_PERSISTED_TAB_DATA("CPTD", new FilePersistedTabDataStorage()); CRITICAL_PERSISTED_TAB_DATA("CPTD", new FilePersistedTabDataStorage()),
ENCRYPTED_CRITICAL_PERSISTED_TAB_DATA("ECPTD", new EncryptedFilePersistedTabDataStorage());
private static final Map<Class<? extends PersistedTabData>, PersistedTabDataConfiguration> private static final Map<Class<? extends PersistedTabData>, PersistedTabDataConfiguration>
sLookup = new HashMap<>(); sLookup = new HashMap<>();
private static final Map<Class<? extends PersistedTabData>, PersistedTabDataConfiguration>
sEncryptedLookup = new HashMap<>();
static { static {
// TODO(crbug.com/1060187) remove static initializer and initialization lazy // TODO(crbug.com/1060187) remove static initializer and initialization lazy
sLookup.put(CriticalPersistedTabData.class, CRITICAL_PERSISTED_TAB_DATA); sLookup.put(CriticalPersistedTabData.class, CRITICAL_PERSISTED_TAB_DATA);
sEncryptedLookup.put(CriticalPersistedTabData.class, ENCRYPTED_CRITICAL_PERSISTED_TAB_DATA);
} }
public final String id; public final String id;
...@@ -39,7 +43,11 @@ public enum PersistedTabDataConfiguration { ...@@ -39,7 +43,11 @@ public enum PersistedTabDataConfiguration {
/** /**
* Acquire {@link PersistedTabDataConfiguration} for a given {@link PersistedTabData} class * Acquire {@link PersistedTabDataConfiguration} for a given {@link PersistedTabData} class
*/ */
public static PersistedTabDataConfiguration get(Class<? extends PersistedTabData> clazz) { public static PersistedTabDataConfiguration get(
Class<? extends PersistedTabData> clazz, boolean isEncrypted) {
if (isEncrypted) {
return sEncryptedLookup.get(clazz);
}
return sLookup.get(clazz); return sLookup.get(clazz);
} }
} }
...@@ -12,24 +12,21 @@ import org.chromium.base.Callback; ...@@ -12,24 +12,21 @@ import org.chromium.base.Callback;
public interface PersistedTabDataStorage { public interface PersistedTabDataStorage {
/** /**
* @param tabId identifier for the {@link Tab} * @param tabId identifier for the {@link Tab}
* @param isEncrypted true if the data should be encrypted (i.e. for incognito Tabs)
* @param tabDataId unique identifier representing the type {@link PersistedTabData} * @param tabDataId unique identifier representing the type {@link PersistedTabData}
* @param data serialized {@link PersistedTabData} * @param data serialized {@link PersistedTabData}
*/ */
void save(int tabId, boolean isEncrypted, String tabDataId, byte[] data); void save(int tabId, String tabDataId, byte[] data);
/** /**
* @param tabId identifier for the {@link Tab} * @param tabId identifier for the {@link Tab}
* @param isEncrypted true if the stored data is encrypted (i.e. for incognito Tabs)
* @param tabDataId unique identifier representing the type of {@link PersistedTabData} * @param tabDataId unique identifier representing the type of {@link PersistedTabData}
* @param callback to pass back the seraizliaed {@link PersistedTabData} in * @param callback to pass back the seraizliaed {@link PersistedTabData} in
*/ */
void restore(int tabId, boolean isEncrypted, String tabDataId, Callback<byte[]> callback); void restore(int tabId, String tabDataId, Callback<byte[]> callback);
/** /**
* @param tabId identifier for the {@link Tab} * @param tabId identifier for the {@link Tab}
* @param isEncrypted true if the stored data is encrypted (i.e. for incognito Tabs)
* @param tabDataId unique identifier representing the type of {@link PersistedTabData} * @param tabDataId unique identifier representing the type of {@link PersistedTabData}
*/ */
void delete(int tabId, boolean isEncrypted, String tabDataId); void delete(int tabId, String tabDataId);
} }
...@@ -4,9 +4,6 @@ ...@@ -4,9 +4,6 @@
package org.chromium.chrome.browser.tab.state; package org.chromium.chrome.browser.tab.state;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.support.test.filters.SmallTest; import android.support.test.filters.SmallTest;
import org.junit.Assert; import org.junit.Assert;
...@@ -14,16 +11,17 @@ import org.junit.Before; ...@@ -14,16 +11,17 @@ import org.junit.Before;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations;
import org.chromium.base.Callback; import org.chromium.base.Callback;
import org.chromium.base.ThreadUtils;
import org.chromium.base.test.BaseJUnit4ClassRunner; import org.chromium.base.test.BaseJUnit4ClassRunner;
import org.chromium.chrome.browser.tab.MockTab; import org.chromium.chrome.browser.tab.MockTab;
import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.test.ChromeBrowserTestRule; import org.chromium.chrome.test.ChromeBrowserTestRule;
import java.util.concurrent.Semaphore;
/** /**
* Test relating to {@link CriticalPersistedTabData} * Test relating to {@link CriticalPersistedTabData}
*/ */
...@@ -43,14 +41,7 @@ public class CriticalPersistedTabDataTest { ...@@ -43,14 +41,7 @@ public class CriticalPersistedTabDataTest {
private static final int THEME_COLOR = 5; private static final int THEME_COLOR = 5;
private static final int LAUNCH_TYPE_AT_CREATION = 3; private static final int LAUNCH_TYPE_AT_CREATION = 3;
@Mock private CriticalPersistedTabData mCriticalPersistedTabData;
private Callback<Boolean> mBooleanCallback;
@Mock
private Callback<CriticalPersistedTabData> mRestoreCallback;
@Mock
private Callback<CriticalPersistedTabData> mDeleteCallback;
private static Tab mockTab(int id, boolean isEncrypted) { private static Tab mockTab(int id, boolean isEncrypted) {
return new MockTab(id, isEncrypted); return new MockTab(id, isEncrypted);
...@@ -63,44 +54,54 @@ public class CriticalPersistedTabDataTest { ...@@ -63,44 +54,54 @@ public class CriticalPersistedTabDataTest {
@SmallTest @SmallTest
@Test @Test
public void testNonEncryptedSaveRestore() { public void testNonEncryptedSaveRestore() throws InterruptedException {
testSaveRestoreDelete(false); testSaveRestoreDelete(false);
} }
@SmallTest @SmallTest
@Test @Test
public void testEncryptedSaveRestoreDelete() { public void testEncryptedSaveRestoreDelete() throws InterruptedException {
testSaveRestoreDelete(true); testSaveRestoreDelete(true);
} }
private void testSaveRestoreDelete(boolean isEncrypted) { private void testSaveRestoreDelete(boolean isEncrypted) throws InterruptedException {
PersistedTabDataConfiguration config = final Semaphore semaphore = new Semaphore(0);
PersistedTabDataConfiguration.get(CriticalPersistedTabData.class); Callback<CriticalPersistedTabData> callback = new Callback<CriticalPersistedTabData>() {
CriticalPersistedTabData criticalPersistedTabData = @Override
new CriticalPersistedTabData(mockTab(TAB_ID, isEncrypted), PARENT_ID, ROOT_ID, public void onResult(CriticalPersistedTabData res) {
TIMESTAMP, CONTENT_STATE, CONTENT_STATE_VERSION, OPENER_APP_ID, THEME_COLOR, mCriticalPersistedTabData = res;
LAUNCH_TYPE_AT_CREATION, config.storage, config.id); semaphore.release();
criticalPersistedTabData.save(); }
CriticalPersistedTabData.from(mockTab(TAB_ID, isEncrypted), mRestoreCallback); };
ArgumentCaptor<CriticalPersistedTabData> cptdCaptor = ThreadUtils.runOnUiThreadBlocking(() -> {
ArgumentCaptor.forClass(CriticalPersistedTabData.class); PersistedTabDataConfiguration config =
verify(mRestoreCallback, times(1)).onResult(cptdCaptor.capture()); PersistedTabDataConfiguration.get(CriticalPersistedTabData.class, isEncrypted);
CriticalPersistedTabData restored = cptdCaptor.getValue(); CriticalPersistedTabData criticalPersistedTabData =
Assert.assertNotNull(restored); new CriticalPersistedTabData(mockTab(TAB_ID, isEncrypted), PARENT_ID, ROOT_ID,
Assert.assertEquals(restored.getParentId(), PARENT_ID); TIMESTAMP, CONTENT_STATE, CONTENT_STATE_VERSION, OPENER_APP_ID,
Assert.assertEquals(restored.getRootId(), ROOT_ID); THEME_COLOR, LAUNCH_TYPE_AT_CREATION, config.storage, config.id);
Assert.assertEquals(restored.getTimestampMillis(), TIMESTAMP); criticalPersistedTabData.save();
Assert.assertEquals(restored.getContentStateVersion(), CONTENT_STATE_VERSION); CriticalPersistedTabData.from(mockTab(TAB_ID, isEncrypted), callback);
Assert.assertEquals(restored.getOpenerAppId(), OPENER_APP_ID); });
Assert.assertEquals(restored.getThemeColor(), THEME_COLOR); semaphore.acquire();
Assert.assertEquals(restored.getTabLaunchTypeAtCreation(), LAUNCH_TYPE_AT_CREATION); Assert.assertNotNull(mCriticalPersistedTabData);
Assert.assertArrayEquals(restored.getContentStateBytes(), CONTENT_STATE); Assert.assertEquals(mCriticalPersistedTabData.getParentId(), PARENT_ID);
restored.delete(); Assert.assertEquals(mCriticalPersistedTabData.getRootId(), ROOT_ID);
CriticalPersistedTabData.from(mockTab(TAB_ID, isEncrypted), mDeleteCallback); Assert.assertEquals(mCriticalPersistedTabData.getTimestampMillis(), TIMESTAMP);
ArgumentCaptor<CriticalPersistedTabData> cptdDeletedCaptor = Assert.assertEquals(
ArgumentCaptor.forClass(CriticalPersistedTabData.class); mCriticalPersistedTabData.getContentStateVersion(), CONTENT_STATE_VERSION);
verify(mDeleteCallback, times(1)).onResult(cptdDeletedCaptor.capture()); Assert.assertEquals(mCriticalPersistedTabData.getOpenerAppId(), OPENER_APP_ID);
Assert.assertNull(cptdDeletedCaptor.getValue()); Assert.assertEquals(mCriticalPersistedTabData.getThemeColor(), THEME_COLOR);
Assert.assertEquals(
mCriticalPersistedTabData.getTabLaunchTypeAtCreation(), LAUNCH_TYPE_AT_CREATION);
Assert.assertArrayEquals(mCriticalPersistedTabData.getContentStateBytes(), CONTENT_STATE);
ThreadUtils.runOnUiThreadBlocking(() -> {
mCriticalPersistedTabData.delete();
CriticalPersistedTabData.from(mockTab(TAB_ID, isEncrypted), callback);
});
semaphore.acquire();
Assert.assertNull(mCriticalPersistedTabData);
// TODO(crbug.com/1060232) test restored.save() after restored.delete() // TODO(crbug.com/1060232) test restored.save() after restored.delete()
// Also cover // Also cover
// - Multiple (different) TAB_IDs being stored in the same storage. // - Multiple (different) TAB_IDs being stored in the same storage.
......
...@@ -4,9 +4,6 @@ ...@@ -4,9 +4,6 @@
package org.chromium.chrome.browser.tab.state; package org.chromium.chrome.browser.tab.state;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.support.test.filters.SmallTest; import android.support.test.filters.SmallTest;
import org.junit.Assert; import org.junit.Assert;
...@@ -14,15 +11,16 @@ import org.junit.Before; ...@@ -14,15 +11,16 @@ import org.junit.Before;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations;
import org.chromium.base.Callback; import org.chromium.base.Callback;
import org.chromium.base.ThreadUtils;
import org.chromium.base.test.BaseJUnit4ClassRunner; import org.chromium.base.test.BaseJUnit4ClassRunner;
import org.chromium.chrome.test.ChromeBrowserTestRule; import org.chromium.chrome.test.ChromeBrowserTestRule;
import java.io.File; import java.io.File;
import java.util.concurrent.Semaphore;
/** /**
* Tests relating to {@link FilePersistedTabDataStorage} * Tests relating to {@link FilePersistedTabDataStorage}
...@@ -39,6 +37,8 @@ public class FilePersistedTabDataStorageTest { ...@@ -39,6 +37,8 @@ public class FilePersistedTabDataStorageTest {
private static final byte[] DATA = {13, 14}; private static final byte[] DATA = {13, 14};
private static final String DATA_ID = "DataId"; private static final String DATA_ID = "DataId";
private byte[] mResult;
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
...@@ -46,27 +46,41 @@ public class FilePersistedTabDataStorageTest { ...@@ -46,27 +46,41 @@ public class FilePersistedTabDataStorageTest {
@SmallTest @SmallTest
@Test @Test
public void testFilePersistedDataStorageNonEncrypted() { public void testFilePersistedDataStorageNonEncrypted() throws InterruptedException {
testFilePersistedDataStorage(false); testFilePersistedDataStorage(new FilePersistedTabDataStorage());
} }
@SmallTest @SmallTest
@Test @Test
public void testFilePersistedDataStorageEncrypted() { public void testFilePersistedDataStorageEncrypted() throws InterruptedException {
testFilePersistedDataStorage(true); testFilePersistedDataStorage(new EncryptedFilePersistedTabDataStorage());
} }
private void testFilePersistedDataStorage(boolean isEncrypted) { private void testFilePersistedDataStorage(FilePersistedTabDataStorage persistedTabDataStorage)
FilePersistedTabDataStorage filePersistedTabDataStorage = new FilePersistedTabDataStorage(); throws InterruptedException {
filePersistedTabDataStorage.save(TAB_ID, isEncrypted, DATA_ID, DATA); final Semaphore semaphore = new Semaphore(0);
ArgumentCaptor<byte[]> byteArrayCaptor = ArgumentCaptor.forClass(byte[].class); Callback<byte[]> callback = new Callback<byte[]>() {
filePersistedTabDataStorage.restore(TAB_ID, isEncrypted, DATA_ID, mByteArrayCallback); @Override
verify(mByteArrayCallback, times(1)).onResult(byteArrayCaptor.capture()); public void onResult(byte[] res) {
Assert.assertEquals(byteArrayCaptor.getValue().length, 2); mResult = res;
Assert.assertArrayEquals(byteArrayCaptor.getValue(), DATA); semaphore.release();
File file = filePersistedTabDataStorage.getFile(TAB_ID, isEncrypted, DATA_ID); }
};
ThreadUtils.runOnUiThreadBlocking(() -> {
persistedTabDataStorage.save(TAB_ID, DATA_ID, DATA);
persistedTabDataStorage.restore(TAB_ID, DATA_ID, callback);
});
semaphore.acquire();
Assert.assertEquals(mResult.length, 2);
Assert.assertArrayEquals(mResult, DATA);
File file = persistedTabDataStorage.getFile(TAB_ID, DATA_ID);
Assert.assertTrue(file.exists()); Assert.assertTrue(file.exists());
filePersistedTabDataStorage.delete(TAB_ID, isEncrypted, DATA_ID);
ThreadUtils.runOnUiThreadBlocking(() -> {
persistedTabDataStorage.delete(TAB_ID, DATA_ID);
semaphore.release();
});
semaphore.acquire();
Assert.assertFalse(file.exists()); Assert.assertFalse(file.exists());
} }
} }
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