Commit adb9784c authored by Christian Dullweber's avatar Christian Dullweber Committed by Commit Bot

Support filtered deletions in browsingData.remove() api

Add RemovalOptions.origins and RemovalOptions.exludeOrigins
parameters to allow extensions to delete only a specific set of
origins or exclude some origins from deletion.

Bug: 78093, 113621, 924113
Change-Id: I42dbdf785ea5ef1cf9a3a95981a7551f2fadd2e2
Reviewed-on: https://chromium-review.googlesource.com/c/1405012
Commit-Queue: Christian Dullweber <dullweber@chromium.org>
Reviewed-by: default avatarJochen Eisinger <jochen@chromium.org>
Reviewed-by: default avatarDevlin <rdevlin.cronin@chromium.org>
Reviewed-by: default avatarMike West <mkwst@chromium.org>
Cr-Commit-Position: refs/heads/master@{#629921}
parent a1281d41
......@@ -29,9 +29,11 @@
#include "components/browsing_data/core/pref_names.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/browsing_data_filter_builder.h"
#include "content/public/browser/browsing_data_remover.h"
#include "extensions/common/error_utils.h"
#include "extensions/common/extension.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "services/identity/public/cpp/identity_manager.h"
using content::BrowserThread;
......@@ -66,6 +68,8 @@ const char kExtensionsKey[] = "extension";
const char kOriginTypesKey[] = "originTypes";
const char kProtectedWebKey[] = "protectedWeb";
const char kSinceKey[] = "since";
const char kOriginsKey[] = "origins";
const char kExcludeOriginsKey[] = "excludeOrigins";
const char kUnprotectedWebKey[] = "unprotectedWeb";
// Errors!
......@@ -75,18 +79,32 @@ const char kBadDataTypeDetails[] = "Invalid value for data type '%s'.";
const char kDeleteProhibitedError[] =
"Browsing history and downloads are not "
"permitted to be removed.";
const char kNonFilterableError[] =
"At least one data type doesn't support filtering by origin.";
const char kIncompatibleFilterError[] =
"Don't set both 'origins' and 'excludeOrigins' at the same time.";
const char kInvalidOriginError[] = "'%s' is not a valid origin.";
} // namespace extension_browsing_data_api_constants
namespace {
const int64_t kFilterableDataTypes =
ChromeBrowsingDataRemoverDelegate::DATA_TYPE_SITE_DATA |
content::BrowsingDataRemover::DATA_TYPE_CACHE;
static_assert((kFilterableDataTypes &
~ChromeBrowsingDataRemoverDelegate::FILTERABLE_DATA_TYPES) == 0,
"kFilterableDataTypes must be a subset of "
"ChromeBrowsingDataRemoverDelegate::FILTERABLE_DATA_TYPES");
int MaskForKey(const char* key) {
if (strcmp(key, extension_browsing_data_api_constants::kAppCacheKey) == 0)
return content::BrowsingDataRemover::DATA_TYPE_APP_CACHE;
if (strcmp(key, extension_browsing_data_api_constants::kCacheKey) == 0)
return content::BrowsingDataRemover::DATA_TYPE_CACHE;
if (strcmp(key, extension_browsing_data_api_constants::kCookiesKey) == 0) {
if (strcmp(key, extension_browsing_data_api_constants::kCookiesKey) == 0)
return content::BrowsingDataRemover::DATA_TYPE_COOKIES;
}
if (strcmp(key, extension_browsing_data_api_constants::kDownloadsKey) == 0)
return content::BrowsingDataRemover::DATA_TYPE_DOWNLOADS;
if (strcmp(key, extension_browsing_data_api_constants::kFileSystemsKey) == 0)
......@@ -268,11 +286,18 @@ void BrowsingDataSettingsFunction::SetDetails(
BrowsingDataRemoverFunction::BrowsingDataRemoverFunction() : observer_(this) {}
void BrowsingDataRemoverFunction::OnBrowsingDataRemoverDone() {
OnTaskFinished();
}
void BrowsingDataRemoverFunction::OnTaskFinished() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK_GT(pending_tasks_, 0);
if (--pending_tasks_ > 0)
return;
synced_data_deletion_.reset();
observer_.RemoveAll();
this->SendResponse(true);
Release(); // Balanced in RunAsync.
Release(); // Balanced in StartRemoving.
}
bool BrowsingDataRemoverFunction::RunAsync() {
......@@ -290,19 +315,45 @@ bool BrowsingDataRemoverFunction::RunAsync() {
// If |ms_since_epoch| isn't set, default it to 0.
double ms_since_epoch;
if (!options->GetDouble(extension_browsing_data_api_constants::kSinceKey,
&ms_since_epoch))
&ms_since_epoch)) {
ms_since_epoch = 0;
}
// base::Time takes a double that represents seconds since epoch. JavaScript
// gives developers milliseconds, so do a quick conversion before populating
// the object. Also, Time::FromDoubleT converts double time 0 to empty Time
// object. So we need to do special handling here.
remove_since_ = (ms_since_epoch == 0) ?
base::Time::UnixEpoch() :
base::Time::FromDoubleT(ms_since_epoch / 1000.0);
// the object.
remove_since_ = base::Time::FromJsTime(ms_since_epoch);
EXTENSION_FUNCTION_VALIDATE(GetRemovalMask(&removal_mask_));
base::Value* origins =
options->FindKeyOfType(extension_browsing_data_api_constants::kOriginsKey,
base::Value::Type::LIST);
base::Value* exclude_origins = options->FindKeyOfType(
extension_browsing_data_api_constants::kExcludeOriginsKey,
base::Value::Type::LIST);
// Check that only |origins| or |excludeOrigins| can be set.
if (origins && exclude_origins) {
error_ = extension_browsing_data_api_constants::kIncompatibleFilterError;
return false;
}
if (origins) {
if (!ParseOrigins(*origins, &origins_))
return false;
mode_ = content::BrowsingDataFilterBuilder::WHITELIST;
} else {
if (exclude_origins && !ParseOrigins(*exclude_origins, &origins_))
return false;
mode_ = content::BrowsingDataFilterBuilder::BLACKLIST;
}
// Check if a filter is set but non-filterable types are selected.
if ((!origins_.empty() && (removal_mask_ & ~kFilterableDataTypes) != 0)) {
error_ = extension_browsing_data_api_constants::kNonFilterableError;
return false;
}
// Check for prohibited data types.
if (!IsRemovalPermitted(removal_mask_, GetProfile()->GetPrefs())) {
error_ = extension_browsing_data_api_constants::kDeleteProhibitedError;
......@@ -349,7 +400,7 @@ void BrowsingDataRemoverFunction::StartRemoving() {
content::BrowsingDataRemover* remover =
content::BrowserContext::GetBrowsingDataRemover(profile);
// Add a ref (Balanced in OnBrowsingDataRemoverDone)
// Add a ref (Balanced in OnTaskFinished)
AddRef();
// Prevent Sync from being paused, if required.
......@@ -364,9 +415,42 @@ void BrowsingDataRemoverFunction::StartRemoving() {
// we've generated above. We can use a raw pointer here, as the browsing data
// remover is responsible for deleting itself once data removal is complete.
observer_.Add(remover);
remover->RemoveAndReply(
remove_since_, base::Time::Max(),
removal_mask_, origin_type_mask_, this);
DCHECK_EQ(pending_tasks_, 0);
pending_tasks_ = 1;
if (removal_mask_ & content::BrowsingDataRemover::DATA_TYPE_COOKIES &&
!origins_.empty()) {
pending_tasks_ += 1;
removal_mask_ &= ~content::BrowsingDataRemover::DATA_TYPE_COOKIES;
// Cookies are scoped more broadly than origins, so we expand the
// origin filter to registrable domains in order to match all cookies
// that could be applied to an origin. This is the same behavior as
// the Clear-Site-Data header.
auto filter_builder = content::BrowsingDataFilterBuilder::Create(mode_);
for (const auto& origin : origins_) {
std::string domain = GetDomainAndRegistry(
origin.host(),
net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
if (domain.empty())
domain = origin.host(); // IP address or internal hostname.
filter_builder->AddRegisterableDomain(domain);
}
remover->RemoveWithFilterAndReply(
remove_since_, base::Time::Max(),
content::BrowsingDataRemover::DATA_TYPE_COOKIES, origin_type_mask_,
std::move(filter_builder), this);
}
if (removal_mask_) {
pending_tasks_ += 1;
auto filter_builder = content::BrowsingDataFilterBuilder::Create(mode_);
for (const auto& origin : origins_) {
filter_builder->AddOrigin(origin);
}
remover->RemoveWithFilterAndReply(remove_since_, base::Time::Max(),
removal_mask_, origin_type_mask_,
std::move(filter_builder), this);
}
OnTaskFinished();
}
bool BrowsingDataRemoverFunction::ParseOriginTypeMask(
......@@ -423,6 +507,25 @@ bool BrowsingDataRemoverFunction::ParseOriginTypeMask(
return true;
}
bool BrowsingDataRemoverFunction::ParseOrigins(
const base::Value& list_value,
std::vector<url::Origin>* result) {
DCHECK(list_value.is_list());
result->reserve(list_value.GetList().size());
for (const auto& value : list_value.GetList()) {
EXTENSION_FUNCTION_VALIDATE(value.is_string());
url::Origin origin = url::Origin::Create(GURL(value.GetString()));
if (origin.opaque()) {
error_ = base::StringPrintf(
extension_browsing_data_api_constants::kInvalidOriginError,
value.GetString().c_str());
return false;
}
result->push_back(std::move(origin));
}
return true;
}
// Parses the |dataToRemove| argument to generate the removal mask.
// Returns false if parse was not successful, i.e. if 'dataToRemove' is not
// present or any data-type keys don't have supported (boolean) values.
......
......@@ -10,11 +10,13 @@
#define CHROME_BROWSER_EXTENSIONS_API_BROWSING_DATA_BROWSING_DATA_API_H_
#include <string>
#include <vector>
#include "base/scoped_observer.h"
#include "chrome/browser/extensions/chrome_extension_function.h"
#include "components/browsing_data/core/browsing_data_utils.h"
#include "components/signin/core/browser/account_reconcilor.h"
#include "content/public/browser/browsing_data_filter_builder.h"
#include "content/public/browser/browsing_data_remover.h"
class PluginPrefs;
......@@ -53,6 +55,9 @@ extern const char kUnprotectedWebKey[];
// Errors!
extern const char kBadDataTypeDetails[];
extern const char kDeleteProhibitedError[];
extern const char kNonFilterableError[];
extern const char kIncompatibleFilterError[];
extern const char kInvalidOriginError[];
} // namespace extension_browsing_data_api_constants
......@@ -129,12 +134,25 @@ class BrowsingDataRemoverFunction
bool ParseOriginTypeMask(const base::DictionaryValue& options,
int* origin_type_mask);
// Parse the developer-provided list of origins into |result|.
// Returns true if parsing was successful.
bool ParseOrigins(const base::Value& list_value,
std::vector<url::Origin>* result);
// Called when we're ready to start removing data.
void StartRemoving();
// Called when a task is finished. Will finish the extension call when
// |pending_tasks_| reaches zero.
void OnTaskFinished();
base::Time remove_since_;
int removal_mask_;
int origin_type_mask_;
int removal_mask_ = 0;
int origin_type_mask_ = 0;
std::vector<url::Origin> origins_;
content::BrowsingDataFilterBuilder::Mode mode_ =
content::BrowsingDataFilterBuilder::Mode::BLACKLIST;
int pending_tasks_ = 0;
ScopedObserver<content::BrowsingDataRemover,
content::BrowsingDataRemover::Observer>
observer_;
......
......@@ -96,8 +96,7 @@ class ExtensionBrowsingDataTest : public InProcessBrowserTest {
void RunBrowsingDataRemoveFunctionAndCompareRemovalMask(
const std::string& data_types,
int expected_mask) {
scoped_refptr<BrowsingDataRemoveFunction> function =
new BrowsingDataRemoveFunction();
auto function = base::MakeRefCounted<BrowsingDataRemoveFunction>();
SCOPED_TRACE(data_types);
EXPECT_EQ(NULL, RunFunctionAndReturnSingleResult(
function.get(),
......@@ -117,8 +116,7 @@ class ExtensionBrowsingDataTest : public InProcessBrowserTest {
void RunBrowsingDataRemoveFunctionAndCompareOriginTypeMask(
const std::string& protectedStr,
int expected_mask) {
scoped_refptr<BrowsingDataRemoveFunction> function =
new BrowsingDataRemoveFunction();
auto function = base::MakeRefCounted<BrowsingDataRemoveFunction>();
SCOPED_TRACE(protectedStr);
EXPECT_EQ(NULL, RunFunctionAndReturnSingleResult(
function.get(),
......@@ -296,8 +294,7 @@ class ExtensionBrowsingDataTest : public InProcessBrowserTest {
// The kAllowDeletingBrowserHistory pref must be set to false before this
// is called.
void CheckRemovalPermitted(const std::string& data_types, bool permitted) {
scoped_refptr<BrowsingDataRemoveFunction> function =
new BrowsingDataRemoveFunction();
auto function = base::MakeRefCounted<BrowsingDataRemoveFunction>();
std::string args = "[{\"since\": 1}," + data_types + "]";
if (permitted) {
......@@ -377,8 +374,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionBrowsingDataTest, RemovalProhibited) {
}
IN_PROC_BROWSER_TEST_F(ExtensionBrowsingDataTest, RemoveBrowsingDataAll) {
scoped_refptr<BrowsingDataRemoveFunction> function =
new BrowsingDataRemoveFunction();
auto function = base::MakeRefCounted<BrowsingDataRemoveFunction>();
EXPECT_EQ(NULL, RunFunctionAndReturnSingleResult(function.get(),
kRemoveEverythingArguments,
browser()));
......@@ -430,8 +426,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionBrowsingDataTest, Syncing) {
sync_ui_util::GetStatus(profile, sync_service, identity_manager);
ASSERT_EQ(sync_ui_util::SYNCED, sync_status);
// Clear browsing data.
scoped_refptr<BrowsingDataRemoveFunction> function =
new BrowsingDataRemoveFunction();
auto function = base::MakeRefCounted<BrowsingDataRemoveFunction>();
EXPECT_EQ(NULL, RunFunctionAndReturnSingleResult(
function.get(), kRemoveEverythingArguments, browser()));
// Check that the Sync token was not revoked.
......@@ -469,8 +464,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionBrowsingDataTest, SyncError) {
identity_manager);
ASSERT_NE(sync_ui_util::SYNCED, sync_status);
// Clear browsing data.
scoped_refptr<BrowsingDataRemoveFunction> function =
new BrowsingDataRemoveFunction();
auto function = base::MakeRefCounted<BrowsingDataRemoveFunction>();
EXPECT_EQ(NULL, RunFunctionAndReturnSingleResult(
function.get(), kRemoveEverythingArguments, browser()));
// Check that the account was not removed and Sync was paused.
......@@ -495,8 +489,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionBrowsingDataTest, NotSyncing) {
AccountInfo account_info =
identity::MakeAccountAvailable(identity_manager, kAccountEmail);
// Clear browsing data.
scoped_refptr<BrowsingDataRemoveFunction> function =
new BrowsingDataRemoveFunction();
auto function = base::MakeRefCounted<BrowsingDataRemoveFunction>();
EXPECT_EQ(NULL, RunFunctionAndReturnSingleResult(
function.get(), kRemoveEverythingArguments, browser()));
// Check that the account was removed.
......@@ -612,8 +605,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionBrowsingDataTest,
EXPECT_TRUE(serializer.Serialize(*data_to_remove));
}
{
scoped_refptr<BrowsingDataRemoveFunction> remove_function =
new BrowsingDataRemoveFunction();
auto remove_function = base::MakeRefCounted<BrowsingDataRemoveFunction>();
SCOPED_TRACE("remove_json");
EXPECT_EQ(NULL, RunFunctionAndReturnSingleResult(
remove_function.get(),
......
// 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 "chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.h"
#include "chrome/browser/extensions/api/browsing_data/browsing_data_api.h"
#include "chrome/browser/extensions/extension_function_test_utils.h"
#include "chrome/browser/extensions/extension_service_test_base.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/test/base/test_browser_window.h"
#include "content/public/browser/browser_context.h"
#include "content/public/test/mock_browsing_data_remover_delegate.h"
#include "testing/gtest/include/gtest/gtest.h"
using extension_function_test_utils::RunFunctionAndReturnError;
using extension_function_test_utils::RunFunctionAndReturnSingleResult;
namespace extensions {
namespace {
enum OriginTypeMask {
UNPROTECTED_WEB = content::BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB,
PROTECTED_WEB = content::BrowsingDataRemover::ORIGIN_TYPE_PROTECTED_WEB,
EXTENSION = ChromeBrowsingDataRemoverDelegate::ORIGIN_TYPE_EXTENSION
};
}
class BrowsingDataApiTest : public ExtensionServiceTestBase {
protected:
void SetUp() override {
ExtensionServiceTestBase::SetUp();
InitializeEmptyExtensionService();
browser_window_ = std::make_unique<TestBrowserWindow>();
Browser::CreateParams params(profile(), true);
params.type = Browser::TYPE_TABBED;
params.window = browser_window_.get();
browser_ = std::make_unique<Browser>(params);
remover_ = content::BrowserContext::GetBrowsingDataRemover(profile());
remover_->SetEmbedderDelegate(&delegate_);
}
void TearDown() override {
browser_.reset();
browser_window_.reset();
ExtensionServiceTestBase::TearDown();
}
void CheckInvalidRemovalArgs(const std::string& args,
const std::string& expected_error) {
auto function = base::MakeRefCounted<BrowsingDataRemoveFunction>();
EXPECT_EQ(RunFunctionAndReturnError(function.get(), args, browser()),
expected_error)
<< " for " << args;
}
void VerifyFilterBuilder(
const std::string& options,
const content::BrowsingDataFilterBuilder& filter_builder) {
delegate()->ExpectCall(
base::Time::UnixEpoch(), base::Time::Max(),
content::BrowsingDataRemover::DATA_TYPE_LOCAL_STORAGE, UNPROTECTED_WEB,
filter_builder);
auto function = base::MakeRefCounted<BrowsingDataRemoveFunction>();
EXPECT_EQ(RunFunctionAndReturnSingleResult(
function.get(), "[" + options + ", {\"localStorage\": true}]",
browser()),
nullptr)
<< options;
delegate()->VerifyAndClearExpectations();
}
Browser* browser() { return browser_.get(); }
content::MockBrowsingDataRemoverDelegate* delegate() { return &delegate_; }
private:
std::unique_ptr<TestBrowserWindow> browser_window_;
std::unique_ptr<Browser> browser_;
content::BrowsingDataRemover* remover_;
content::MockBrowsingDataRemoverDelegate delegate_;
};
TEST_F(BrowsingDataApiTest, RemoveWithoutFilter) {
auto filter_builder = content::BrowsingDataFilterBuilder::Create(
content::BrowsingDataFilterBuilder::BLACKLIST);
ASSERT_TRUE(filter_builder->IsEmptyBlacklist());
VerifyFilterBuilder("{}", *filter_builder);
}
TEST_F(BrowsingDataApiTest, RemoveWithWhitelistFilter) {
auto filter_builder = content::BrowsingDataFilterBuilder::Create(
content::BrowsingDataFilterBuilder::WHITELIST);
filter_builder->AddOrigin(url::Origin::Create(GURL("http://example.com")));
VerifyFilterBuilder(R"({"origins": ["http://example.com"]})",
*filter_builder);
}
TEST_F(BrowsingDataApiTest, RemoveWithBlacklistFilter) {
auto filter_builder = content::BrowsingDataFilterBuilder::Create(
content::BrowsingDataFilterBuilder::BLACKLIST);
filter_builder->AddOrigin(url::Origin::Create(GURL("http://example.com")));
VerifyFilterBuilder(R"({"excludeOrigins": ["http://example.com"]})",
*filter_builder);
}
TEST_F(BrowsingDataApiTest, RemoveWithSpecialUrlFilter) {
auto filter_builder = content::BrowsingDataFilterBuilder::Create(
content::BrowsingDataFilterBuilder::BLACKLIST);
filter_builder->AddOrigin(url::Origin::Create(GURL("file:///")));
filter_builder->AddOrigin(url::Origin::Create(GURL("http://example.com")));
VerifyFilterBuilder(
R"({"excludeOrigins": ["file:///tmp/foo.html/",
"filesystem:http://example.com/foo.txt"]})",
*filter_builder);
}
TEST_F(BrowsingDataApiTest, RemoveCookiesWithFilter) {
auto filter_builder = content::BrowsingDataFilterBuilder::Create(
content::BrowsingDataFilterBuilder::BLACKLIST);
filter_builder->AddRegisterableDomain("example.com");
delegate()->ExpectCall(base::Time::UnixEpoch(), base::Time::Max(),
content::BrowsingDataRemover::DATA_TYPE_COOKIES,
UNPROTECTED_WEB, *filter_builder);
auto function = base::MakeRefCounted<BrowsingDataRemoveFunction>();
EXPECT_EQ(RunFunctionAndReturnSingleResult(
function.get(),
R"([{"excludeOrigins": ["http://example.com"]},
{"cookies": true}])",
browser()),
nullptr);
delegate()->VerifyAndClearExpectations();
}
// Expect two separate BrowsingDataRemover calls if cookies and localstorage
// are passed with a filter.
TEST_F(BrowsingDataApiTest, RemoveCookiesAndStorageWithFilter) {
auto filter_builder1 = content::BrowsingDataFilterBuilder::Create(
content::BrowsingDataFilterBuilder::WHITELIST);
filter_builder1->AddRegisterableDomain("example.com");
delegate()->ExpectCall(base::Time::UnixEpoch(), base::Time::Max(),
content::BrowsingDataRemover::DATA_TYPE_COOKIES,
UNPROTECTED_WEB, *filter_builder1);
auto filter_builder2 = content::BrowsingDataFilterBuilder::Create(
content::BrowsingDataFilterBuilder::WHITELIST);
filter_builder2->AddOrigin(
url::Origin::Create(GURL("http://www.example.com")));
delegate()->ExpectCall(base::Time::UnixEpoch(), base::Time::Max(),
content::BrowsingDataRemover::DATA_TYPE_LOCAL_STORAGE,
UNPROTECTED_WEB, *filter_builder2);
auto function = base::MakeRefCounted<BrowsingDataRemoveFunction>();
EXPECT_EQ(RunFunctionAndReturnSingleResult(
function.get(),
R"([{"origins": ["http://www.example.com"]},
{"cookies": true, "localStorage": true}])",
browser()),
nullptr);
delegate()->VerifyAndClearExpectations();
}
TEST_F(BrowsingDataApiTest, RemoveWithFilterAndInvalidParameters) {
CheckInvalidRemovalArgs(
R"([{"origins": ["http://example.com"]}, {"passwords": true}])",
extension_browsing_data_api_constants::kNonFilterableError);
CheckInvalidRemovalArgs(
R"([{"origins": ["http://example.com"],
"excludeOrigins": ["https://example.com"]},
{"localStorage": true}])",
extension_browsing_data_api_constants::kIncompatibleFilterError);
CheckInvalidRemovalArgs(
R"([{"origins": ["foo"]}, {"localStorage": true}])",
base::StringPrintf(
extension_browsing_data_api_constants::kInvalidOriginError, "foo"));
CheckInvalidRemovalArgs(
R"([{"excludeOrigins": ["foo"]}, {"localStorage": true}])",
base::StringPrintf(
extension_browsing_data_api_constants::kInvalidOriginError, "foo"));
}
} // namespace extensions
......@@ -52,6 +52,10 @@ namespace extension_function_test_utils {
base::ListValue* ParseList(const std::string& data) {
std::unique_ptr<base::Value> result = base::JSONReader::Read(data);
if (!result) {
ADD_FAILURE() << "Failed to parse: " << data;
return nullptr;
}
base::ListValue* list = NULL;
result->GetAsList(&list);
ignore_result(result.release());
......
......@@ -38,6 +38,18 @@
"description": "Extensions and packaged applications a user has installed (be _really_ careful!)."
}
}
},
"origins": {
"type": "array",
"items": {"type": "string"},
"optional": true,
"description": "When present, only data for origins in this list is deleted. Only supported for cookies, storage and cache. Cookies are cleared for the whole registrable domain."
},
"excludeOrigins": {
"type": "array",
"items": {"type": "string"},
"optional": true,
"description": "When present, data for origins in this list is excluded from deletion. Can't be used together with <code>origins</code>. Only supported for cookies, storage and cache. Cookies are excluded for the whole registrable domain."
}
}
},
......
......@@ -3724,6 +3724,7 @@ test("unit_tests") {
"../browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api_unittest.cc",
"../browser/extensions/api/bookmarks/bookmark_api_helpers_unittest.cc",
"../browser/extensions/api/bookmarks/bookmarks_api_unittest.cc",
"../browser/extensions/api/browsing_data/browsing_data_unittest.cc",
"../browser/extensions/api/chrome_extensions_api_client_unittest.cc",
"../browser/extensions/api/content_settings/content_settings_store_unittest.cc",
"../browser/extensions/api/content_settings/content_settings_unittest.cc",
......
......@@ -110,4 +110,19 @@ bool MockBrowsingDataRemoverDelegate::CallParameters::operator==(
return *a.filter_builder_ == *b.filter_builder_;
}
std::ostream& operator<<(
std::ostream& os,
const MockBrowsingDataRemoverDelegate::CallParameters& p) {
os << "BrowsingDataFilterBuilder: " << std::endl;
os << " delete_begin: " << p.delete_begin_ << std::endl;
os << " delete_end: " << p.delete_end_ << std::endl;
os << " remove_mask: " << p.remove_mask_ << std::endl;
os << " origin_type_mask: " << p.origin_type_mask_ << std::endl;
if (p.should_compare_filter_) {
os << " filter_builder: " << std::endl;
os << " mode: " << p.filter_builder_->GetMode() << std::endl;
}
return os;
}
} // content
......@@ -61,6 +61,9 @@ class MockBrowsingDataRemoverDelegate : public BrowsingDataRemoverDelegate {
bool operator==(const CallParameters& other) const;
private:
friend std::ostream& operator<<(std::ostream& os,
const CallParameters& params);
base::Time delete_begin_;
base::Time delete_end_;
int remove_mask_;
......@@ -68,11 +71,17 @@ class MockBrowsingDataRemoverDelegate : public BrowsingDataRemoverDelegate {
std::unique_ptr<BrowsingDataFilterBuilder> filter_builder_;
bool should_compare_filter_;
};
friend std::ostream& operator<<(std::ostream& os,
const CallParameters& params);
std::list<CallParameters> actual_calls_;
std::list<CallParameters> expected_calls_;
};
std::ostream& operator<<(
std::ostream& os,
const MockBrowsingDataRemoverDelegate::CallParameters& params);
} // content
#endif // CHROME_BROWSER_BROWSING_DATA_MOCK_BROWSING_DATA_REMOVER_DELEGATE_H_
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