Commit 95a6d682 authored by Scott Violet's avatar Scott Violet Committed by Commit Bot

weblayer: adds functions to get/remove persistence ids

BUG=1087464
TEST=covered by tests

Change-Id: I0b9d30e5699a69f17bb64259a90c649966aeeece
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2240419
Commit-Queue: Scott Violet <sky@chromium.org>
Reviewed-by: default avatarEvan Stade <estade@chromium.org>
Cr-Commit-Position: refs/heads/master@{#777557}
parent 9066934c
......@@ -58,6 +58,13 @@ class SessionFileReader {
file_ = std::make_unique<base::File>(
path, base::File::FLAG_OPEN | base::File::FLAG_READ);
}
// Returns true if the file has a valid header. A return value of false
// most likely means the file was not written by this code. This function
// is implicitly called by Read(), but may be called separately for checking
// if the file is valid.
bool HasValidHeader();
// Reads the contents of the file specified in the constructor, returning
// true on success, and filling up |commands| with commands.
bool Read(std::vector<std::unique_ptr<sessions::SessionCommand>>* commands);
......@@ -107,24 +114,32 @@ class SessionFileReader {
// Count of the number of commands encountered.
int command_counter_ = 0;
bool did_check_header_ = false;
DISALLOW_COPY_AND_ASSIGN(SessionFileReader);
};
bool SessionFileReader::Read(
std::vector<std::unique_ptr<sessions::SessionCommand>>* commands) {
bool SessionFileReader::HasValidHeader() {
// This function advances |file| and should only be called once.
DCHECK(!did_check_header_);
did_check_header_ = true;
if (!file_->IsValid())
return false;
FileHeader header;
int read_count;
read_count =
const int read_count =
file_->ReadAtCurrentPos(reinterpret_cast<char*>(&header), sizeof(header));
if (read_count != sizeof(header) || header.signature != kFileSignature) {
const bool encrypt = aead_.get() != nullptr;
if ((encrypt && header.version != kEncryptedFileCurrentVersion) ||
(!encrypt && header.version != kFileCurrentVersion)) {
return false;
}
}
if (read_count != sizeof(header) || header.signature != kFileSignature)
return false;
const bool encrypt = aead_.get() != nullptr;
return (encrypt && header.version == kEncryptedFileCurrentVersion) ||
(!encrypt && header.version == kFileCurrentVersion);
}
bool SessionFileReader::Read(
std::vector<std::unique_ptr<sessions::SessionCommand>>* commands) {
if (!HasValidHeader())
return false;
std::vector<std::unique_ptr<sessions::SessionCommand>> read_commands;
for (std::unique_ptr<sessions::SessionCommand> command = ReadCommand();
......@@ -263,6 +278,12 @@ CommandStorageBackend::CommandStorageBackend(
const base::FilePath& path)
: RefCountedDeleteOnSequence(owning_task_runner), path_(path) {}
// static
bool CommandStorageBackend::IsValidFile(const base::FilePath& path) {
SessionFileReader file_reader(path, {});
return file_reader.HasValidHeader();
}
void CommandStorageBackend::AppendCommands(
std::vector<std::unique_ptr<sessions::SessionCommand>> commands,
bool truncate,
......
......@@ -55,6 +55,9 @@ class SESSIONS_EXPORT CommandStorageBackend
scoped_refptr<base::SequencedTaskRunner> owning_task_runner,
const base::FilePath& path);
// Returns true if the file at |path| was generated by this class.
static bool IsValidFile(const base::FilePath& path);
base::SequencedTaskRunner* owning_task_runner() {
return base::RefCountedDeleteOnSequence<
CommandStorageBackend>::owning_task_runner();
......
......@@ -287,4 +287,20 @@ TEST_F(CommandStorageBackendTest, MaxSizeType) {
expected_size) == 0);
}
TEST_F(CommandStorageBackendTest, IsValidFileWithInvalidFiles) {
base::WriteFile(path_, "z");
EXPECT_FALSE(CommandStorageBackend::IsValidFile(path_));
base::WriteFile(path_, "a longer string that does not match header");
EXPECT_FALSE(CommandStorageBackend::IsValidFile(path_));
}
TEST_F(CommandStorageBackendTest, IsValidFileWithValidFile) {
scoped_refptr<CommandStorageBackend> backend = CreateBackend();
backend->AppendCommands({}, true);
backend = nullptr;
EXPECT_TRUE(CommandStorageBackend::IsValidFile(path_));
}
} // namespace sessions
......@@ -199,6 +199,8 @@ source_set("weblayer_lib_base") {
"browser/persistence/browser_persistence_common.h",
"browser/persistence/browser_persister.cc",
"browser/persistence/browser_persister.h",
"browser/persistence/browser_persister_file_utils.cc",
"browser/persistence/browser_persister_file_utils.h",
"browser/persistence/minimal_browser_persister.cc",
"browser/persistence/minimal_browser_persister.h",
"browser/popup_navigation_delegate_impl.cc",
......
......@@ -9,12 +9,15 @@
#include "base/callback_forward.h"
#include "base/containers/unique_ptr_adapters.h"
#include "base/memory/ptr_util.h"
#include "base/no_destructor.h"
#include "base/path_service.h"
#include "base/stl_util.h"
#include "components/base32/base32.h"
#include "content/public/browser/browser_context.h"
#include "content/public/common/web_preferences.h"
#include "weblayer/browser/feature_list_creator.h"
#include "weblayer/browser/persistence/browser_persister.h"
#include "weblayer/browser/persistence/browser_persister_file_utils.h"
#include "weblayer/browser/persistence/minimal_browser_persister.h"
#include "weblayer/browser/profile_impl.h"
#include "weblayer/browser/tab_impl.h"
......@@ -38,8 +41,17 @@ using base::android::ScopedJavaLocalRef;
namespace weblayer {
// TODO(timvolodine): consider using an observer for this, crbug.com/1068713.
int BrowserImpl::browser_count_ = 0;
namespace {
std::vector<BrowserImpl*>& GetBrowsers() {
static base::NoDestructor<std::vector<BrowserImpl*>> browsers;
return *browsers;
}
} // namespace
// static
constexpr char BrowserImpl::kPersistenceFilePrefix[];
std::unique_ptr<Browser> Browser::Create(
Profile* profile,
......@@ -62,17 +74,19 @@ BrowserImpl::~BrowserImpl() {
while (!tabs_.empty())
RemoveTab(tabs_.back().get());
#endif
profile_->DecrementBrowserImplCount();
browser_count_--;
DCHECK(browser_count_ >= 0);
base::Erase(GetBrowsers(), this);
#if defined(OS_ANDROID)
if (browser_count_ == 0) {
if (GetBrowsers().empty())
BrowserProcess::GetInstance()->StopSafeBrowsingService();
}
#endif
}
// static
const std::vector<BrowserImpl*>& BrowserImpl::GetAllBrowsers() {
return GetBrowsers();
}
TabImpl* BrowserImpl::CreateTabForSessionRestore(
std::unique_ptr<content::WebContents> web_contents,
const std::string& guid) {
......@@ -312,8 +326,7 @@ void BrowserImpl::RemoveObserver(BrowserObserver* observer) {
}
BrowserImpl::BrowserImpl(ProfileImpl* profile) : profile_(profile) {
profile_->IncrementBrowserImplCount();
browser_count_++;
GetBrowsers().push_back(this);
}
void BrowserImpl::RestoreStateIfNecessary(
......@@ -338,10 +351,8 @@ void BrowserImpl::VisibleSecurityStateOfActiveTabChanged() {
}
base::FilePath BrowserImpl::GetBrowserPersisterDataPath() {
base::FilePath base_path = profile_->GetBrowserPersisterDataBaseDir();
DCHECK(!GetPersistenceId().empty());
const std::string encoded_name = base32::Base32Encode(GetPersistenceId());
return base_path.AppendASCII("State" + encoded_name);
return BuildPathForBrowserPersister(
profile_->GetBrowserPersisterDataBaseDir(), GetPersistenceId());
}
#if defined(OS_ANDROID)
......
......@@ -34,10 +34,15 @@ class TabImpl;
class BrowserImpl : public Browser {
public:
// Prefix used for storing persistence state.
static constexpr char kPersistenceFilePrefix[] = "State";
BrowserImpl(const BrowserImpl&) = delete;
BrowserImpl& operator=(const BrowserImpl&) = delete;
~BrowserImpl() override;
static const std::vector<BrowserImpl*>& GetAllBrowsers();
BrowserPersister* browser_persister() { return browser_persister_.get(); }
ProfileImpl* profile() { return profile_; }
......@@ -129,7 +134,6 @@ class BrowserImpl : public Browser {
std::string persistence_id_;
std::unique_ptr<BrowserPersister> browser_persister_;
base::OnceClosure visible_security_state_changed_callback_for_tests_;
static int browser_count_;
};
} // namespace weblayer
......
......@@ -6,9 +6,12 @@
#include "base/bind_helpers.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/guid.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/test/bind_test_util.h"
#include "base/threading/thread_restrictions.h"
#include "build/build_config.h"
#include "components/sessions/core/command_storage_manager_test_helper.h"
#include "content/public/test/browser_test_utils.h"
......@@ -17,6 +20,7 @@
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "weblayer/browser/browser_impl.h"
#include "weblayer/browser/persistence/browser_persister_file_utils.h"
#include "weblayer/browser/profile_impl.h"
#include "weblayer/browser/tab_impl.h"
#include "weblayer/common/weblayer_paths.h"
......@@ -392,4 +396,83 @@ IN_PROC_BROWSER_TEST_F(BrowserPersisterTest, MoveBetweenBrowsers) {
content::WaitForLoadStop(restored_tab_3->web_contents());
}
class BrowserPersisterTestWithTwoPersistedIds : public WebLayerBrowserTest {
public:
// WebLayerBrowserTest:
void SetUpOnMainThread() override {
WebLayerBrowserTest::SetUpOnMainThread();
// Configure two browsers with ids 'x' and 'y'.
ASSERT_TRUE(embedded_test_server()->Start());
std::unique_ptr<BrowserImpl> browser1 = CreateBrowser(GetProfile(), "x");
const GURL url1 = embedded_test_server()->GetURL("/simple_page.html");
NavigateAndWaitForCompletion(url1,
browser1->AddTab(Tab::Create(GetProfile())));
std::unique_ptr<BrowserImpl> browser2 = CreateBrowser(GetProfile(), "y");
const GURL url2 = embedded_test_server()->GetURL("/simple_page3.html");
NavigateAndWaitForCompletion(url2,
browser2->AddTab(Tab::Create(GetProfile())));
// Shut down the browsers.
ShutdownBrowserPersisterAndWait(browser1.get());
browser1.reset();
ShutdownBrowserPersisterAndWait(browser2.get());
browser2.reset();
}
};
IN_PROC_BROWSER_TEST_F(BrowserPersisterTestWithTwoPersistedIds,
GetBrowserPersistenceIds) {
{
// Create a file that has the name of a valid persistence file, but has
// invalid contents.
base::ScopedAllowBlockingForTesting allow_blocking;
base::WriteFile(BuildPathForBrowserPersister(
GetProfile()->GetBrowserPersisterDataBaseDir(), "z"),
"a bogus persistence file");
}
base::RunLoop run_loop;
base::flat_set<std::string> persistence_ids;
GetProfile()->GetBrowserPersistenceIds(
base::BindLambdaForTesting([&](base::flat_set<std::string> ids) {
persistence_ids = std::move(ids);
run_loop.Quit();
}));
run_loop.Run();
ASSERT_EQ(2u, persistence_ids.size());
EXPECT_TRUE(persistence_ids.contains("x"));
EXPECT_TRUE(persistence_ids.contains("y"));
}
IN_PROC_BROWSER_TEST_F(BrowserPersisterTestWithTwoPersistedIds,
RemoveBrowserPersistenceStorage) {
base::FilePath file_path1 = BuildPathForBrowserPersister(
GetProfile()->GetBrowserPersisterDataBaseDir(), "x");
base::FilePath file_path2 = BuildPathForBrowserPersister(
GetProfile()->GetBrowserPersisterDataBaseDir(), "y");
{
base::ScopedAllowBlockingForTesting allow_blocking;
ASSERT_TRUE(base::PathExists(file_path1));
ASSERT_TRUE(base::PathExists(file_path2));
}
base::RunLoop run_loop;
base::flat_set<std::string> persistence_ids;
persistence_ids.insert("x");
persistence_ids.insert("y");
GetProfile()->RemoveBrowserPersistenceStorage(
base::BindLambdaForTesting([&](bool result) {
EXPECT_TRUE(result);
run_loop.Quit();
}),
std::move(persistence_ids));
run_loop.Run();
{
base::ScopedAllowBlockingForTesting allow_blocking;
EXPECT_FALSE(base::PathExists(file_path1));
EXPECT_FALSE(base::PathExists(file_path2));
}
}
} // namespace weblayer
// 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.
#include "weblayer/browser/persistence/browser_persister_file_utils.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/stl_util.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "components/base32/base32.h"
#include "components/sessions/core/command_storage_backend.h"
#include "content/public/browser/browser_thread.h"
#include "weblayer/browser/browser_impl.h"
#include "weblayer/browser/profile_impl.h"
namespace weblayer {
namespace {
bool RemoveBrowserPersistenceStorageOnBackgroundThread(
const base::FilePath& path,
base::flat_set<std::string> ids) {
DCHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
bool all_succeeded = true;
for (const std::string& id : ids) {
DCHECK(!id.empty());
base::FilePath persistence_path = BuildPathForBrowserPersister(path, id);
if (!base::DeleteFile(persistence_path, /* recurse */ false))
all_succeeded = false;
}
return all_succeeded;
}
} // namespace
base::flat_set<std::string> GetBrowserPersistenceIdsOnBackgroundThread(
const base::FilePath& path) {
DCHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
base::flat_set<std::string> ids;
base::FilePath matching_path = base::FilePath().AppendASCII(
std::string(BrowserImpl::kPersistenceFilePrefix) + std::string("*"));
base::FileEnumerator iter(path, /* recursive */ false,
base::FileEnumerator::FILES, matching_path.value());
for (base::FilePath name = iter.Next(); !name.empty(); name = iter.Next()) {
// The name is base32 encoded, which is ascii.
const std::string base_name = iter.GetInfo().GetName().MaybeAsASCII();
if (base_name.size() <= base::size(BrowserImpl::kPersistenceFilePrefix))
continue;
const std::string encoded_id =
base_name.substr(base::size(BrowserImpl::kPersistenceFilePrefix) - 1);
const std::string decoded_id = base32::Base32Decode(encoded_id);
if (!decoded_id.empty() &&
sessions::CommandStorageBackend::IsValidFile(name)) {
ids.insert(decoded_id);
}
}
return ids;
}
base::FilePath BuildPathForBrowserPersister(const base::FilePath& base_path,
const std::string& browser_id) {
DCHECK(!browser_id.empty());
const std::string encoded_name = base32::Base32Encode(browser_id);
return base_path.AppendASCII(BrowserImpl::kPersistenceFilePrefix +
encoded_name);
}
void RemoveBrowserPersistenceStorageImpl(
ProfileImpl* profile,
base::OnceCallback<void(bool)> done_callback,
base::flat_set<std::string> ids) {
// Remove any ids that are actively in use.
for (BrowserImpl* browser : BrowserImpl::GetAllBrowsers()) {
if (browser->profile() == profile)
ids.erase(browser->GetPersistenceId());
}
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE,
{base::MayBlock(), base::TaskPriority::BEST_EFFORT,
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
base::BindOnce(&RemoveBrowserPersistenceStorageOnBackgroundThread,
profile->GetBrowserPersisterDataBaseDir(), std::move(ids)),
std::move(done_callback));
}
} // namespace weblayer
// 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.
#ifndef WEBLAYER_BROWSER_PERSISTENCE_BROWSER_PERSISTER_FILE_UTILS_H_
#define WEBLAYER_BROWSER_PERSISTENCE_BROWSER_PERSISTER_FILE_UTILS_H_
#include <string>
#include "base/callback_forward.h"
#include "base/containers/flat_set.h"
namespace base {
class FilePath;
}
namespace weblayer {
class ProfileImpl;
// Returns the set of known persistence ids for the profile at |path|.
base::flat_set<std::string> GetBrowserPersistenceIdsOnBackgroundThread(
const base::FilePath& path);
// Returns the path to save persistence information. |base_path| is the base
// path of the profile, and |browser_id| the persistence id.
base::FilePath BuildPathForBrowserPersister(const base::FilePath& base_path,
const std::string& browser_id);
// Implementation of RemoveBrowserPersistenceStorage(). Tries to remove all
// the persistence files for the set of browser persistence ids.
void RemoveBrowserPersistenceStorageImpl(
ProfileImpl* profile,
base::OnceCallback<void(bool)> done_callback,
base::flat_set<std::string> ids);
} // namespace weblayer
#endif // WEBLAYER_BROWSER_PERSISTENCE_BROWSER_PERSISTER_FILE_UTILS_H_
......@@ -25,8 +25,10 @@
#include "content/public/browser/storage_partition.h"
#include "services/network/public/mojom/network_context.mojom.h"
#include "weblayer/browser/browser_context_impl.h"
#include "weblayer/browser/browser_impl.h"
#include "weblayer/browser/browsing_data_remover_delegate.h"
#include "weblayer/browser/cookie_manager_impl.h"
#include "weblayer/browser/persistence/browser_persister_file_utils.h"
#include "weblayer/browser/tab_impl.h"
#if defined(OS_ANDROID)
......@@ -130,7 +132,6 @@ ProfileImpl::ProfileImpl(const std::string& name)
}
ProfileImpl::~ProfileImpl() {
DCHECK_EQ(num_browser_impl_, 0u);
if (browser_context_)
browser_context_->ShutdownStoragePartitions();
}
......@@ -205,6 +206,26 @@ CookieManager* ProfileImpl::GetCookieManager() {
return cookie_manager_.get();
}
void ProfileImpl::GetBrowserPersistenceIds(
base::OnceCallback<void(base::flat_set<std::string>)> callback) {
DCHECK(!browser_context_->IsOffTheRecord());
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE,
{base::MayBlock(), base::TaskPriority::BEST_EFFORT,
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
base::BindOnce(&GetBrowserPersistenceIdsOnBackgroundThread,
GetBrowserPersisterDataBaseDir()),
std::move(callback));
}
void ProfileImpl::RemoveBrowserPersistenceStorage(
base::OnceCallback<void(bool)> done_callback,
base::flat_set<std::string> ids) {
DCHECK(!browser_context_->IsOffTheRecord());
RemoveBrowserPersistenceStorageImpl(this, std::move(done_callback),
std::move(ids));
}
// static
void ProfileImpl::NukeDataAfterRemovingData(
std::unique_ptr<ProfileImpl> profile,
......@@ -269,7 +290,7 @@ std::unique_ptr<Profile> Profile::DestroyAndDeleteDataFromDisk(
std::unique_ptr<ProfileImpl> ProfileImpl::DestroyAndDeleteDataFromDisk(
std::unique_ptr<ProfileImpl> profile,
base::OnceClosure done_callback) {
if (profile->num_browser_impl_ > 0)
if (profile->GetNumberOfBrowsers() > 0)
return profile;
GetBackgroundDiskOperationTaskRunner()->PostTaskAndReply(
......@@ -330,7 +351,7 @@ static void JNI_ProfileImpl_EnumerateAllProfileNames(
}
jint ProfileImpl::GetNumBrowserImpl(JNIEnv* env) {
return num_browser_impl_;
return GetNumberOfBrowsers();
}
jlong ProfileImpl::GetBrowserContext(JNIEnv* env) {
......@@ -400,15 +421,6 @@ jboolean ProfileImpl::GetBooleanSetting(JNIEnv* env, jint j_type) {
#endif // OS_ANDROID
void ProfileImpl::IncrementBrowserImplCount() {
num_browser_impl_++;
}
void ProfileImpl::DecrementBrowserImplCount() {
DCHECK_GT(num_browser_impl_, 0u);
num_browser_impl_--;
}
base::FilePath ProfileImpl::GetBrowserPersisterDataBaseDir() const {
return ComputeBrowserPersisterDataBaseDir(info_);
}
......@@ -433,4 +445,13 @@ bool ProfileImpl::GetBooleanSetting(SettingType type) {
NOTREACHED();
}
int ProfileImpl::GetNumberOfBrowsers() {
int count = 0;
for (BrowserImpl* browser : BrowserImpl::GetAllBrowsers()) {
if (browser->profile() == this)
++count;
}
return count;
}
} // namespace weblayer
......@@ -65,6 +65,11 @@ class ProfileImpl : public Profile {
void SetDownloadDirectory(const base::FilePath& directory) override;
void SetDownloadDelegate(DownloadDelegate* delegate) override;
CookieManager* GetCookieManager() override;
void GetBrowserPersistenceIds(
base::OnceCallback<void(base::flat_set<std::string>)> callback) override;
void RemoveBrowserPersistenceStorage(
base::OnceCallback<void(bool)> done_callback,
base::flat_set<std::string> ids) override;
void SetBooleanSetting(SettingType type, bool value) override;
bool GetBooleanSetting(SettingType type) override;
......@@ -93,8 +98,6 @@ class ProfileImpl : public Profile {
jboolean GetBooleanSetting(JNIEnv* env, jint j_type);
#endif
void IncrementBrowserImplCount();
void DecrementBrowserImplCount();
const base::FilePath& download_directory() { return download_directory_; }
// Get the directory where BrowserPersister stores tab state data. This will
......@@ -115,6 +118,9 @@ class ProfileImpl : public Profile {
// Callback when the system locale has been updated.
void OnLocaleChanged();
// Returns the number of Browsers with this profile.
int GetNumberOfBrowsers();
ProfileInfo info_;
std::unique_ptr<BrowserContextImpl> browser_context_;
......@@ -127,8 +133,6 @@ class ProfileImpl : public Profile {
std::unique_ptr<CookieManagerImpl> cookie_manager_;
size_t num_browser_impl_ = 0u;
bool basic_safe_browsing_enabled_ = true;
#if defined(OS_ANDROID)
......
......@@ -5,9 +5,12 @@
#ifndef WEBLAYER_PUBLIC_PROFILE_H_
#define WEBLAYER_PUBLIC_PROFILE_H_
#include <algorithm>
#include <memory>
#include <string>
#include "base/callback_forward.h"
#include "base/containers/flat_set.h"
namespace base {
class FilePath;
}
......@@ -61,6 +64,20 @@ class Profile {
// Gets the cookie manager for this profile.
virtual CookieManager* GetCookieManager() = 0;
// Asynchronously fetches the set of known Browser persistence-ids. See
// Browser::PersistenceInfo for more details on persistence-ids.
virtual void GetBrowserPersistenceIds(
base::OnceCallback<void(base::flat_set<std::string>)> callback) = 0;
// Asynchronously removes the storage associated with the set of
// Browser persistence-ids. This ignores ids actively in use. |done_callback|
// is run with the result of the operation (on the main thread). A value of
// true means all files were removed. A value of false indicates at least one
// of the files could not be removed.
virtual void RemoveBrowserPersistenceStorage(
base::OnceCallback<void(bool)> done_callback,
base::flat_set<std::string> ids) = 0;
// Set the boolean value of the given setting type.
virtual void SetBooleanSetting(SettingType type, bool value) = 0;
......
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