Commit ef83ccf5 authored by Mike Dougherty's avatar Mike Dougherty Committed by Commit Bot

Attach BreadcrumbStorage to BrowserState as a KeyedService

BreadcrumbPersistentStorage stores breadcrumbs to disk and returns the
breadcrumbs from the persisted file. This will allow breadcrumbs to be
saved during an application run and retrieved after a crash on the next
launch.

Bug: 1003922
Change-Id: Iffa9dc2797b66eda0793c9eafef6bddc8c73200d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1959777
Commit-Queue: Mike Dougherty <michaeldo@chromium.org>
Reviewed-by: default avatarEugene But <eugenebut@chromium.org>
Cr-Commit-Position: refs/heads/master@{#724543}
parent f26c6227
......@@ -30,6 +30,12 @@ source_set("breadcrumbs") {
"breadcrumb_manager_keyed_service_factory.cc",
"breadcrumb_manager_keyed_service_factory.h",
"breadcrumb_manager_observer.h",
"breadcrumb_persistent_storage_keyed_service.cc",
"breadcrumb_persistent_storage_keyed_service.h",
"breadcrumb_persistent_storage_keyed_service_factory.cc",
"breadcrumb_persistent_storage_keyed_service_factory.h",
"breadcrumb_persistent_storage_util.cc",
"breadcrumb_persistent_storage_util.h",
]
configs += [ "//build/config/compiler:enable_arc" ]
......@@ -40,9 +46,9 @@ source_set("unit_tests") {
testonly = true
deps = [
":breadcrumbs",
"//base",
"//base/test:test_support",
"//ios/chrome/browser/browser_state:test_support",
"//ios/chrome/browser/web:test_support",
"//ios/chrome/test:test_support",
"//ios/web/public/test",
"//testing/gtest",
......@@ -51,5 +57,7 @@ source_set("unit_tests") {
sources = [
"breadcrumb_manager_keyed_service_unittest.mm",
"breadcrumb_manager_observer_unittest.mm",
"breadcrumb_persistent_storage_keyed_service_unittest.mm",
"breadcrumb_persistent_storage_util_unittest.mm",
]
}
......@@ -33,6 +33,8 @@ class BreadcrumbManagerKeyedService : public KeyedService {
std::list<std::string> GetEvents(size_t event_count_limit);
// Logs a breadcrumb event with message data |event|.
// NOTE: |event| must not include newline characters as newlines are used by
// BreadcrumbPersistentStore as a deliminator.
void AddEvent(const std::string& event);
// Adds and removes observers.
......
......@@ -2,12 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_observer.h"
#include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_observer.h"
#import "base/macros.h"
#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
#include "ios/chrome/browser/browser_state/test_chrome_browser_state_manager.h"
#import "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_keyed_service.h"
#include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_keyed_service.h"
#include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_keyed_service_factory.h"
#include "ios/chrome/test/ios_chrome_scoped_testing_chrome_browser_state_manager.h"
#include "ios/web/public/test/web_task_environment.h"
......
// Copyright 2019 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 "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_keyed_service.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_keyed_service.h"
#include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_util.h"
namespace {
// Number of existing events to write to disk when beginning to observe a new
// BreadcrumbManager.
const int kPersistedExistingEventsCount = 10;
} // namespace
using breadcrumb_persistent_storage_util::
GetBreadcrumbPersistentStorageFilePath;
BreadcrumbPersistentStorageKeyedService::
BreadcrumbPersistentStorageKeyedService(web::BrowserState* browser_state)
: breadcrumbs_file_path_(
GetBreadcrumbPersistentStorageFilePath(browser_state)) {}
BreadcrumbPersistentStorageKeyedService::
~BreadcrumbPersistentStorageKeyedService() = default;
std::vector<std::string>
BreadcrumbPersistentStorageKeyedService::GetStoredEvents() {
base::File events_file(breadcrumbs_file_path_,
base::File::FLAG_OPEN | base::File::FLAG_READ);
if (!events_file.IsValid()) {
// File may not yet exist.
return std::vector<std::string>();
}
int64_t file_size = events_file.GetLength();
if (file_size <= 0) {
return std::vector<std::string>();
}
std::vector<uint8_t> data;
// Subtract one to account for '\0' at end of file.
data.resize(file_size - 1);
if (!events_file.ReadAndCheck(/*offset=*/0, data)) {
return std::vector<std::string>();
}
std::string persistent_events_string(data.begin(), data.end());
return base::SplitString(persistent_events_string, "\n",
base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
}
void BreadcrumbPersistentStorageKeyedService::ObserveBreadcrumbManager(
BreadcrumbManagerKeyedService* manager) {
if (observered_manager_) {
observered_manager_->RemoveObserver(this);
}
observered_manager_ = manager;
WriteExistingBreadcrumbEvents();
if (observered_manager_) {
observered_manager_->AddObserver(this);
}
}
void BreadcrumbPersistentStorageKeyedService::WriteExistingBreadcrumbEvents() {
if (persisted_events_file_) {
// File already exists, truncate to remove old events.
persisted_events_file_->SetLength(0);
} else if (observered_manager_) {
persisted_events_file_ = std::make_unique<base::File>(
breadcrumbs_file_path_,
base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
}
if (!observered_manager_) {
return;
}
for (auto& event :
observered_manager_->GetEvents(kPersistedExistingEventsCount)) {
WriteBreadcrumbEvent(event);
}
persisted_events_file_->Flush();
}
void BreadcrumbPersistentStorageKeyedService::WriteBreadcrumbEvent(
const std::string& event) {
DCHECK(persisted_events_file_);
persisted_events_file_->WriteAtCurrentPosAndCheck(
base::as_bytes(base::make_span(event)));
persisted_events_file_->WriteAtCurrentPosAndCheck(
base::as_bytes(base::make_span("\n")));
}
void BreadcrumbPersistentStorageKeyedService::EventAdded(
BreadcrumbManagerKeyedService* manager,
const std::string& event) {
WriteBreadcrumbEvent(event);
persisted_events_file_->Flush();
}
// Copyright 2019 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 IOS_CHROME_BROWSER_CRASH_REPORT_BREADCRUMBS_BREADCRUMB_PERSISTENT_STORAGE_KEYED_SERVICE_H_
#define IOS_CHROME_BROWSER_CRASH_REPORT_BREADCRUMBS_BREADCRUMB_PERSISTENT_STORAGE_KEYED_SERVICE_H_
#include <memory>
#include <string>
#include <vector>
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/macros.h"
#include "components/keyed_service/core/keyed_service.h"
#include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_observer.h"
class BreadcrumbManagerKeyedService;
namespace web {
class BrowserState;
}
// Saves and retrieves breadcrumb events to and from disk.
class BreadcrumbPersistentStorageKeyedService
: public BreadcrumbManagerObserver,
public KeyedService {
public:
// Creates an instance to save and retrieve breadcrumb events from the file at
// |file_path|. The file will be created if necessary.
// explicit BreadcrumbPersistentStorageKeyedService(const base::FilePath&
// file_path);
explicit BreadcrumbPersistentStorageKeyedService(
web::BrowserState* browser_state);
~BreadcrumbPersistentStorageKeyedService() override;
// Returns the stored breadcrumb events from disk.
std::vector<std::string> GetStoredEvents();
// Sets the |manager| to observe. Old stored breadcrumbs will be removed, even
// if null is passed for |manager|. As such, this can be used to ensure stale
// data does not take up disk space. If manager is non-null, events will be
// written whenever |manager| receives an event.
void ObserveBreadcrumbManager(BreadcrumbManagerKeyedService* manager);
private:
// Writes |observered_manager_|'s events to disk, overwriting any existing
// persisted breadcrumbs. Removes all stored events if |observered_manager_|
// is null.
void WriteExistingBreadcrumbEvents();
// Appends |event| to |persisted_events_file_|. |persisted_events_file_| must
// already exist.
void WriteBreadcrumbEvent(const std::string& event);
// BreadcrumbManagerObserver
void EventAdded(BreadcrumbManagerKeyedService* manager,
const std::string& event) override;
// The breadcrumb manager currently being observed.
BreadcrumbManagerKeyedService* observered_manager_ = nullptr;
// The path to the breadcrumbs file.
base::FilePath breadcrumbs_file_path_;
// File handle for writing persisted breadcrumbs to |breadcrumbs_file_path_|.
// Lazily created after |ObserveBreadcrumbManager| is called with a non-null
// manager.
std::unique_ptr<base::File> persisted_events_file_;
DISALLOW_COPY_AND_ASSIGN(BreadcrumbPersistentStorageKeyedService);
};
#endif // IOS_CHROME_BROWSER_CRASH_REPORT_BREADCRUMBS_BREADCRUMB_PERSISTENT_STORAGE_KEYED_SERVICE_H_
// Copyright 2019 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 "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_keyed_service_factory.h"
#include "components/keyed_service/ios/browser_state_dependency_manager.h"
#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
#include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_keyed_service.h"
#include "ios/web/public/browser_state.h"
// static
BreadcrumbPersistentStorageKeyedServiceFactory*
BreadcrumbPersistentStorageKeyedServiceFactory::GetInstance() {
static base::NoDestructor<BreadcrumbPersistentStorageKeyedServiceFactory>
instance;
return instance.get();
}
// static
BreadcrumbPersistentStorageKeyedService*
BreadcrumbPersistentStorageKeyedServiceFactory::GetForBrowserState(
ios::ChromeBrowserState* browser_state) {
return static_cast<BreadcrumbPersistentStorageKeyedService*>(
GetInstance()->GetServiceForBrowserState(browser_state, true));
}
BreadcrumbPersistentStorageKeyedServiceFactory::
BreadcrumbPersistentStorageKeyedServiceFactory()
: BrowserStateKeyedServiceFactory(
"BreadcrumbPersistentStorageService",
BrowserStateDependencyManager::GetInstance()) {}
BreadcrumbPersistentStorageKeyedServiceFactory::
~BreadcrumbPersistentStorageKeyedServiceFactory() {}
std::unique_ptr<KeyedService>
BreadcrumbPersistentStorageKeyedServiceFactory::BuildServiceInstanceFor(
web::BrowserState* context) const {
return std::make_unique<BreadcrumbPersistentStorageKeyedService>(context);
}
web::BrowserState*
BreadcrumbPersistentStorageKeyedServiceFactory::GetBrowserStateToUse(
web::BrowserState* context) const {
// Create the service for both normal and incognito browser states.
return context;
}
// Copyright 2019 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 IOS_CHROME_BROWSER_CRASH_REPORT_BREADCRUMBS_BREADCRUMB_PERSISTENT_STORAGE_KEYED_SERVICE_FACTORY_H_
#define IOS_CHROME_BROWSER_CRASH_REPORT_BREADCRUMBS_BREADCRUMB_PERSISTENT_STORAGE_KEYED_SERVICE_FACTORY_H_
#include "base/no_destructor.h"
#include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
class BreadcrumbPersistentStorageKeyedService;
namespace ios {
class ChromeBrowserState;
}
class BreadcrumbPersistentStorageKeyedServiceFactory
: public BrowserStateKeyedServiceFactory {
public:
static BreadcrumbPersistentStorageKeyedServiceFactory* GetInstance();
static BreadcrumbPersistentStorageKeyedService* GetForBrowserState(
ios::ChromeBrowserState* browser_state);
private:
friend class base::NoDestructor<
BreadcrumbPersistentStorageKeyedServiceFactory>;
BreadcrumbPersistentStorageKeyedServiceFactory();
~BreadcrumbPersistentStorageKeyedServiceFactory() override;
// BrowserStateKeyedServiceFactory implementation.
std::unique_ptr<KeyedService> BuildServiceInstanceFor(
web::BrowserState* context) const override;
web::BrowserState* GetBrowserStateToUse(
web::BrowserState* context) const override;
BreadcrumbPersistentStorageKeyedServiceFactory(
const BreadcrumbPersistentStorageKeyedServiceFactory&) = delete;
};
#endif // IOS_CHROME_BROWSER_CRASH_REPORT_BREADCRUMBS_BREADCRUMB_PERSISTENT_STORAGE_KEYED_SERVICE_FACTORY_H_
// Copyright 2019 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 "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_keyed_service.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
#include "ios/chrome/browser/browser_state/test_chrome_browser_state_manager.h"
#include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_keyed_service.h"
#include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_keyed_service_factory.h"
#include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_keyed_service_factory.h"
#include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_util.h"
#include "ios/chrome/test/ios_chrome_scoped_testing_chrome_browser_state_manager.h"
#include "ios/web/public/test/web_task_environment.h"
#include "testing/platform_test.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
using breadcrumb_persistent_storage_util::
GetBreadcrumbPersistentStorageFilePath;
namespace {
// Creates a new BreadcrumbManagerKeyedService for |browser_state|.
std::unique_ptr<KeyedService> BuildBreadcrumbManagerKeyedService(
web::BrowserState* browser_state) {
return std::make_unique<BreadcrumbManagerKeyedService>(
ios::ChromeBrowserState::FromBrowserState(browser_state));
}
// Creates a new BreadcrumbPersistentStorageKeyedService for |browser_state|.
std::unique_ptr<KeyedService> BuildBreadcrumbPersistentStorageKeyedService(
web::BrowserState* browser_state) {
return std::make_unique<BreadcrumbPersistentStorageKeyedService>(
ios::ChromeBrowserState::FromBrowserState(browser_state));
}
}
class BreadcrumbPersistentStorageKeyedServiceTest : public PlatformTest {
protected:
BreadcrumbPersistentStorageKeyedServiceTest()
: scoped_browser_state_manager_(
std::make_unique<TestChromeBrowserStateManager>(base::FilePath())) {
EXPECT_TRUE(scoped_temp_directory_.CreateUniqueTempDir());
base::FilePath directory_name = scoped_temp_directory_.GetPath();
TestChromeBrowserState::Builder test_cbs_builder;
test_cbs_builder.SetPath(directory_name);
test_cbs_builder.AddTestingFactory(
BreadcrumbManagerKeyedServiceFactory::GetInstance(),
base::BindRepeating(&BuildBreadcrumbManagerKeyedService));
test_cbs_builder.AddTestingFactory(
BreadcrumbPersistentStorageKeyedServiceFactory::GetInstance(),
base::BindRepeating(&BuildBreadcrumbPersistentStorageKeyedService));
chrome_browser_state_ = test_cbs_builder.Build();
breadcrumb_manager_ = static_cast<BreadcrumbManagerKeyedService*>(
BreadcrumbManagerKeyedServiceFactory::GetForBrowserState(
chrome_browser_state_.get()));
persistent_storage_ = static_cast<BreadcrumbPersistentStorageKeyedService*>(
BreadcrumbPersistentStorageKeyedServiceFactory::GetForBrowserState(
chrome_browser_state_.get()));
}
~BreadcrumbPersistentStorageKeyedServiceTest() override = default;
void TearDown() override {
persistent_storage_->ObserveBreadcrumbManager(nullptr);
PlatformTest::TearDown();
}
web::WebTaskEnvironment task_environment_;
IOSChromeScopedTestingChromeBrowserStateManager scoped_browser_state_manager_;
std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
base::ScopedTempDir scoped_temp_directory_;
BreadcrumbManagerKeyedService* breadcrumb_manager_;
BreadcrumbPersistentStorageKeyedService* persistent_storage_;
};
// Ensures that events logged after a BreadcrumbManager is already being
// observed are persisted.
TEST_F(BreadcrumbPersistentStorageKeyedServiceTest, PersistMessages) {
persistent_storage_->ObserveBreadcrumbManager(breadcrumb_manager_);
breadcrumb_manager_->AddEvent("event");
auto events = persistent_storage_->GetStoredEvents();
ASSERT_EQ(1ul, events.size());
EXPECT_NE(std::string::npos, events.front().find("event"));
}
// Ensures that events logged before a BreadcrumbManager is being observed
// are persisted.
TEST_F(BreadcrumbPersistentStorageKeyedServiceTest, PersistExistingMessages) {
breadcrumb_manager_->AddEvent("event");
persistent_storage_->ObserveBreadcrumbManager(breadcrumb_manager_);
auto events = persistent_storage_->GetStoredEvents();
ASSERT_EQ(1ul, events.size());
EXPECT_NE(std::string::npos, events.front().find("event"));
}
// Tests that calling |ObserveBreadcrumbManager| with a null manager removes the
// contents of the persistent storage file.
TEST_F(BreadcrumbPersistentStorageKeyedServiceTest, DeletePersistentStorage) {
breadcrumb_manager_->AddEvent("event");
persistent_storage_->ObserveBreadcrumbManager(breadcrumb_manager_);
persistent_storage_->ObserveBreadcrumbManager(/*manager=*/nullptr);
int64_t file_size = -1;
ASSERT_TRUE(base::GetFileSize(
GetBreadcrumbPersistentStorageFilePath(chrome_browser_state_.get()),
&file_size));
EXPECT_EQ(0, file_size);
}
// Copyright 2019 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 "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_util.h"
#include "base/path_service.h"
#include "ios/web/public/browser_state.h"
namespace breadcrumb_persistent_storage_util {
const base::FilePath::CharType kBreadcrumbsFile[] =
FILE_PATH_LITERAL("iOS Breadcrumbs");
base::FilePath GetBreadcrumbPersistentStorageFilePath(
web::BrowserState* browser_state) {
return browser_state->GetStatePath().Append(kBreadcrumbsFile);
}
} // namespace breadcrumb_persistent_storage_util
// Copyright 2019 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 IOS_CHROME_BROWSER_CRASH_REPORT_BREADCRUMBS_BREADCRUMB_PERSISTENT_STORAGE_UTIL_H_
#define IOS_CHROME_BROWSER_CRASH_REPORT_BREADCRUMBS_BREADCRUMB_PERSISTENT_STORAGE_UTIL_H_
#include "base/files/file_path.h"
namespace web {
class BrowserState;
} // namespace web
namespace breadcrumb_persistent_storage_util {
// Returns the path to a file for storing breadcrumbs within |browser_state|'s
// storage directory.
base::FilePath GetBreadcrumbPersistentStorageFilePath(
web::BrowserState* browser_state);
} // namespace breadcrumb_persistent_storage_util
#endif // IOS_CHROME_BROWSER_CRASH_REPORT_BREADCRUMBS_BREADCRUMB_PERSISTENT_STORAGE_UTIL_H_
// Copyright 2019 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 "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_util.h"
#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
#include "ios/chrome/browser/web/chrome_web_test.h"
#include "ios/web/public/browser_state.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
using breadcrumb_persistent_storage_util::
GetBreadcrumbPersistentStorageFilePath;
// Test fixture to test BreadcrumbPersistentStorageUtil.
typedef ChromeWebTest BreadcrumbPersistentStorageUtilTest;
// Tests that the storage paths are unique for different BrowserStates.
TEST_F(BreadcrumbPersistentStorageUtilTest, UniqueStorage) {
base::FilePath path1 =
GetBreadcrumbPersistentStorageFilePath(GetBrowserState());
std::unique_ptr<TestChromeBrowserState> local_browser_state =
TestChromeBrowserState::Builder().Build();
base::FilePath path2 =
GetBreadcrumbPersistentStorageFilePath(local_browser_state.get());
EXPECT_NE(path1, path2);
}
// Tests that the BrowserState returned by
// |BrowserState::GetOffTheRecordChromeBrowserState| does not share a storage
// path with the original BrowserState.
TEST_F(BreadcrumbPersistentStorageUtilTest, UniqueIncognitoStorage) {
base::FilePath path1 =
GetBreadcrumbPersistentStorageFilePath(GetBrowserState());
ios::ChromeBrowserState* off_the_record_browser_state =
chrome_browser_state_->GetOffTheRecordChromeBrowserState();
base::FilePath path2 =
GetBreadcrumbPersistentStorageFilePath(off_the_record_browser_state);
EXPECT_NE(path1, path2);
}
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