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 @@ ...@@ -29,9 +29,11 @@
#include "components/browsing_data/core/pref_names.h" #include "components/browsing_data/core/pref_names.h"
#include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.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 "content/public/browser/browsing_data_remover.h"
#include "extensions/common/error_utils.h" #include "extensions/common/error_utils.h"
#include "extensions/common/extension.h" #include "extensions/common/extension.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "services/identity/public/cpp/identity_manager.h" #include "services/identity/public/cpp/identity_manager.h"
using content::BrowserThread; using content::BrowserThread;
...@@ -66,6 +68,8 @@ const char kExtensionsKey[] = "extension"; ...@@ -66,6 +68,8 @@ const char kExtensionsKey[] = "extension";
const char kOriginTypesKey[] = "originTypes"; const char kOriginTypesKey[] = "originTypes";
const char kProtectedWebKey[] = "protectedWeb"; const char kProtectedWebKey[] = "protectedWeb";
const char kSinceKey[] = "since"; const char kSinceKey[] = "since";
const char kOriginsKey[] = "origins";
const char kExcludeOriginsKey[] = "excludeOrigins";
const char kUnprotectedWebKey[] = "unprotectedWeb"; const char kUnprotectedWebKey[] = "unprotectedWeb";
// Errors! // Errors!
...@@ -75,18 +79,32 @@ const char kBadDataTypeDetails[] = "Invalid value for data type '%s'."; ...@@ -75,18 +79,32 @@ const char kBadDataTypeDetails[] = "Invalid value for data type '%s'.";
const char kDeleteProhibitedError[] = const char kDeleteProhibitedError[] =
"Browsing history and downloads are not " "Browsing history and downloads are not "
"permitted to be removed."; "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 extension_browsing_data_api_constants
namespace { 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) { int MaskForKey(const char* key) {
if (strcmp(key, extension_browsing_data_api_constants::kAppCacheKey) == 0) if (strcmp(key, extension_browsing_data_api_constants::kAppCacheKey) == 0)
return content::BrowsingDataRemover::DATA_TYPE_APP_CACHE; return content::BrowsingDataRemover::DATA_TYPE_APP_CACHE;
if (strcmp(key, extension_browsing_data_api_constants::kCacheKey) == 0) if (strcmp(key, extension_browsing_data_api_constants::kCacheKey) == 0)
return content::BrowsingDataRemover::DATA_TYPE_CACHE; 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; return content::BrowsingDataRemover::DATA_TYPE_COOKIES;
}
if (strcmp(key, extension_browsing_data_api_constants::kDownloadsKey) == 0) if (strcmp(key, extension_browsing_data_api_constants::kDownloadsKey) == 0)
return content::BrowsingDataRemover::DATA_TYPE_DOWNLOADS; return content::BrowsingDataRemover::DATA_TYPE_DOWNLOADS;
if (strcmp(key, extension_browsing_data_api_constants::kFileSystemsKey) == 0) if (strcmp(key, extension_browsing_data_api_constants::kFileSystemsKey) == 0)
...@@ -268,11 +286,18 @@ void BrowsingDataSettingsFunction::SetDetails( ...@@ -268,11 +286,18 @@ void BrowsingDataSettingsFunction::SetDetails(
BrowsingDataRemoverFunction::BrowsingDataRemoverFunction() : observer_(this) {} BrowsingDataRemoverFunction::BrowsingDataRemoverFunction() : observer_(this) {}
void BrowsingDataRemoverFunction::OnBrowsingDataRemoverDone() { void BrowsingDataRemoverFunction::OnBrowsingDataRemoverDone() {
OnTaskFinished();
}
void BrowsingDataRemoverFunction::OnTaskFinished() {
DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK_GT(pending_tasks_, 0);
if (--pending_tasks_ > 0)
return;
synced_data_deletion_.reset(); synced_data_deletion_.reset();
observer_.RemoveAll(); observer_.RemoveAll();
this->SendResponse(true); this->SendResponse(true);
Release(); // Balanced in RunAsync. Release(); // Balanced in StartRemoving.
} }
bool BrowsingDataRemoverFunction::RunAsync() { bool BrowsingDataRemoverFunction::RunAsync() {
...@@ -290,19 +315,45 @@ bool BrowsingDataRemoverFunction::RunAsync() { ...@@ -290,19 +315,45 @@ bool BrowsingDataRemoverFunction::RunAsync() {
// If |ms_since_epoch| isn't set, default it to 0. // If |ms_since_epoch| isn't set, default it to 0.
double ms_since_epoch; double ms_since_epoch;
if (!options->GetDouble(extension_browsing_data_api_constants::kSinceKey, if (!options->GetDouble(extension_browsing_data_api_constants::kSinceKey,
&ms_since_epoch)) &ms_since_epoch)) {
ms_since_epoch = 0; ms_since_epoch = 0;
}
// base::Time takes a double that represents seconds since epoch. JavaScript // base::Time takes a double that represents seconds since epoch. JavaScript
// gives developers milliseconds, so do a quick conversion before populating // gives developers milliseconds, so do a quick conversion before populating
// the object. Also, Time::FromDoubleT converts double time 0 to empty Time // the object.
// object. So we need to do special handling here. remove_since_ = base::Time::FromJsTime(ms_since_epoch);
remove_since_ = (ms_since_epoch == 0) ?
base::Time::UnixEpoch() :
base::Time::FromDoubleT(ms_since_epoch / 1000.0);
EXTENSION_FUNCTION_VALIDATE(GetRemovalMask(&removal_mask_)); 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. // Check for prohibited data types.
if (!IsRemovalPermitted(removal_mask_, GetProfile()->GetPrefs())) { if (!IsRemovalPermitted(removal_mask_, GetProfile()->GetPrefs())) {
error_ = extension_browsing_data_api_constants::kDeleteProhibitedError; error_ = extension_browsing_data_api_constants::kDeleteProhibitedError;
...@@ -349,7 +400,7 @@ void BrowsingDataRemoverFunction::StartRemoving() { ...@@ -349,7 +400,7 @@ void BrowsingDataRemoverFunction::StartRemoving() {
content::BrowsingDataRemover* remover = content::BrowsingDataRemover* remover =
content::BrowserContext::GetBrowsingDataRemover(profile); content::BrowserContext::GetBrowsingDataRemover(profile);
// Add a ref (Balanced in OnBrowsingDataRemoverDone) // Add a ref (Balanced in OnTaskFinished)
AddRef(); AddRef();
// Prevent Sync from being paused, if required. // Prevent Sync from being paused, if required.
...@@ -364,9 +415,42 @@ void BrowsingDataRemoverFunction::StartRemoving() { ...@@ -364,9 +415,42 @@ void BrowsingDataRemoverFunction::StartRemoving() {
// we've generated above. We can use a raw pointer here, as the browsing data // 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. // remover is responsible for deleting itself once data removal is complete.
observer_.Add(remover); observer_.Add(remover);
remover->RemoveAndReply(
remove_since_, base::Time::Max(), DCHECK_EQ(pending_tasks_, 0);
removal_mask_, origin_type_mask_, this); 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( bool BrowsingDataRemoverFunction::ParseOriginTypeMask(
...@@ -423,6 +507,25 @@ bool BrowsingDataRemoverFunction::ParseOriginTypeMask( ...@@ -423,6 +507,25 @@ bool BrowsingDataRemoverFunction::ParseOriginTypeMask(
return true; 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. // Parses the |dataToRemove| argument to generate the removal mask.
// Returns false if parse was not successful, i.e. if 'dataToRemove' is not // 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. // present or any data-type keys don't have supported (boolean) values.
......
...@@ -10,11 +10,13 @@ ...@@ -10,11 +10,13 @@
#define CHROME_BROWSER_EXTENSIONS_API_BROWSING_DATA_BROWSING_DATA_API_H_ #define CHROME_BROWSER_EXTENSIONS_API_BROWSING_DATA_BROWSING_DATA_API_H_
#include <string> #include <string>
#include <vector>
#include "base/scoped_observer.h" #include "base/scoped_observer.h"
#include "chrome/browser/extensions/chrome_extension_function.h" #include "chrome/browser/extensions/chrome_extension_function.h"
#include "components/browsing_data/core/browsing_data_utils.h" #include "components/browsing_data/core/browsing_data_utils.h"
#include "components/signin/core/browser/account_reconcilor.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" #include "content/public/browser/browsing_data_remover.h"
class PluginPrefs; class PluginPrefs;
...@@ -53,6 +55,9 @@ extern const char kUnprotectedWebKey[]; ...@@ -53,6 +55,9 @@ extern const char kUnprotectedWebKey[];
// Errors! // Errors!
extern const char kBadDataTypeDetails[]; extern const char kBadDataTypeDetails[];
extern const char kDeleteProhibitedError[]; extern const char kDeleteProhibitedError[];
extern const char kNonFilterableError[];
extern const char kIncompatibleFilterError[];
extern const char kInvalidOriginError[];
} // namespace extension_browsing_data_api_constants } // namespace extension_browsing_data_api_constants
...@@ -129,12 +134,25 @@ class BrowsingDataRemoverFunction ...@@ -129,12 +134,25 @@ class BrowsingDataRemoverFunction
bool ParseOriginTypeMask(const base::DictionaryValue& options, bool ParseOriginTypeMask(const base::DictionaryValue& options,
int* origin_type_mask); 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. // Called when we're ready to start removing data.
void StartRemoving(); void StartRemoving();
// Called when a task is finished. Will finish the extension call when
// |pending_tasks_| reaches zero.
void OnTaskFinished();
base::Time remove_since_; base::Time remove_since_;
int removal_mask_; int removal_mask_ = 0;
int origin_type_mask_; 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, ScopedObserver<content::BrowsingDataRemover,
content::BrowsingDataRemover::Observer> content::BrowsingDataRemover::Observer>
observer_; observer_;
......
...@@ -96,8 +96,7 @@ class ExtensionBrowsingDataTest : public InProcessBrowserTest { ...@@ -96,8 +96,7 @@ class ExtensionBrowsingDataTest : public InProcessBrowserTest {
void RunBrowsingDataRemoveFunctionAndCompareRemovalMask( void RunBrowsingDataRemoveFunctionAndCompareRemovalMask(
const std::string& data_types, const std::string& data_types,
int expected_mask) { int expected_mask) {
scoped_refptr<BrowsingDataRemoveFunction> function = auto function = base::MakeRefCounted<BrowsingDataRemoveFunction>();
new BrowsingDataRemoveFunction();
SCOPED_TRACE(data_types); SCOPED_TRACE(data_types);
EXPECT_EQ(NULL, RunFunctionAndReturnSingleResult( EXPECT_EQ(NULL, RunFunctionAndReturnSingleResult(
function.get(), function.get(),
...@@ -117,8 +116,7 @@ class ExtensionBrowsingDataTest : public InProcessBrowserTest { ...@@ -117,8 +116,7 @@ class ExtensionBrowsingDataTest : public InProcessBrowserTest {
void RunBrowsingDataRemoveFunctionAndCompareOriginTypeMask( void RunBrowsingDataRemoveFunctionAndCompareOriginTypeMask(
const std::string& protectedStr, const std::string& protectedStr,
int expected_mask) { int expected_mask) {
scoped_refptr<BrowsingDataRemoveFunction> function = auto function = base::MakeRefCounted<BrowsingDataRemoveFunction>();
new BrowsingDataRemoveFunction();
SCOPED_TRACE(protectedStr); SCOPED_TRACE(protectedStr);
EXPECT_EQ(NULL, RunFunctionAndReturnSingleResult( EXPECT_EQ(NULL, RunFunctionAndReturnSingleResult(
function.get(), function.get(),
...@@ -296,8 +294,7 @@ class ExtensionBrowsingDataTest : public InProcessBrowserTest { ...@@ -296,8 +294,7 @@ class ExtensionBrowsingDataTest : public InProcessBrowserTest {
// The kAllowDeletingBrowserHistory pref must be set to false before this // The kAllowDeletingBrowserHistory pref must be set to false before this
// is called. // is called.
void CheckRemovalPermitted(const std::string& data_types, bool permitted) { void CheckRemovalPermitted(const std::string& data_types, bool permitted) {
scoped_refptr<BrowsingDataRemoveFunction> function = auto function = base::MakeRefCounted<BrowsingDataRemoveFunction>();
new BrowsingDataRemoveFunction();
std::string args = "[{\"since\": 1}," + data_types + "]"; std::string args = "[{\"since\": 1}," + data_types + "]";
if (permitted) { if (permitted) {
...@@ -377,8 +374,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionBrowsingDataTest, RemovalProhibited) { ...@@ -377,8 +374,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionBrowsingDataTest, RemovalProhibited) {
} }
IN_PROC_BROWSER_TEST_F(ExtensionBrowsingDataTest, RemoveBrowsingDataAll) { IN_PROC_BROWSER_TEST_F(ExtensionBrowsingDataTest, RemoveBrowsingDataAll) {
scoped_refptr<BrowsingDataRemoveFunction> function = auto function = base::MakeRefCounted<BrowsingDataRemoveFunction>();
new BrowsingDataRemoveFunction();
EXPECT_EQ(NULL, RunFunctionAndReturnSingleResult(function.get(), EXPECT_EQ(NULL, RunFunctionAndReturnSingleResult(function.get(),
kRemoveEverythingArguments, kRemoveEverythingArguments,
browser())); browser()));
...@@ -430,8 +426,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionBrowsingDataTest, Syncing) { ...@@ -430,8 +426,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionBrowsingDataTest, Syncing) {
sync_ui_util::GetStatus(profile, sync_service, identity_manager); sync_ui_util::GetStatus(profile, sync_service, identity_manager);
ASSERT_EQ(sync_ui_util::SYNCED, sync_status); ASSERT_EQ(sync_ui_util::SYNCED, sync_status);
// Clear browsing data. // Clear browsing data.
scoped_refptr<BrowsingDataRemoveFunction> function = auto function = base::MakeRefCounted<BrowsingDataRemoveFunction>();
new BrowsingDataRemoveFunction();
EXPECT_EQ(NULL, RunFunctionAndReturnSingleResult( EXPECT_EQ(NULL, RunFunctionAndReturnSingleResult(
function.get(), kRemoveEverythingArguments, browser())); function.get(), kRemoveEverythingArguments, browser()));
// Check that the Sync token was not revoked. // Check that the Sync token was not revoked.
...@@ -469,8 +464,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionBrowsingDataTest, SyncError) { ...@@ -469,8 +464,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionBrowsingDataTest, SyncError) {
identity_manager); identity_manager);
ASSERT_NE(sync_ui_util::SYNCED, sync_status); ASSERT_NE(sync_ui_util::SYNCED, sync_status);
// Clear browsing data. // Clear browsing data.
scoped_refptr<BrowsingDataRemoveFunction> function = auto function = base::MakeRefCounted<BrowsingDataRemoveFunction>();
new BrowsingDataRemoveFunction();
EXPECT_EQ(NULL, RunFunctionAndReturnSingleResult( EXPECT_EQ(NULL, RunFunctionAndReturnSingleResult(
function.get(), kRemoveEverythingArguments, browser())); function.get(), kRemoveEverythingArguments, browser()));
// Check that the account was not removed and Sync was paused. // Check that the account was not removed and Sync was paused.
...@@ -495,8 +489,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionBrowsingDataTest, NotSyncing) { ...@@ -495,8 +489,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionBrowsingDataTest, NotSyncing) {
AccountInfo account_info = AccountInfo account_info =
identity::MakeAccountAvailable(identity_manager, kAccountEmail); identity::MakeAccountAvailable(identity_manager, kAccountEmail);
// Clear browsing data. // Clear browsing data.
scoped_refptr<BrowsingDataRemoveFunction> function = auto function = base::MakeRefCounted<BrowsingDataRemoveFunction>();
new BrowsingDataRemoveFunction();
EXPECT_EQ(NULL, RunFunctionAndReturnSingleResult( EXPECT_EQ(NULL, RunFunctionAndReturnSingleResult(
function.get(), kRemoveEverythingArguments, browser())); function.get(), kRemoveEverythingArguments, browser()));
// Check that the account was removed. // Check that the account was removed.
...@@ -612,8 +605,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionBrowsingDataTest, ...@@ -612,8 +605,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionBrowsingDataTest,
EXPECT_TRUE(serializer.Serialize(*data_to_remove)); EXPECT_TRUE(serializer.Serialize(*data_to_remove));
} }
{ {
scoped_refptr<BrowsingDataRemoveFunction> remove_function = auto remove_function = base::MakeRefCounted<BrowsingDataRemoveFunction>();
new BrowsingDataRemoveFunction();
SCOPED_TRACE("remove_json"); SCOPED_TRACE("remove_json");
EXPECT_EQ(NULL, RunFunctionAndReturnSingleResult( EXPECT_EQ(NULL, RunFunctionAndReturnSingleResult(
remove_function.get(), 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 { ...@@ -52,6 +52,10 @@ namespace extension_function_test_utils {
base::ListValue* ParseList(const std::string& data) { base::ListValue* ParseList(const std::string& data) {
std::unique_ptr<base::Value> result = base::JSONReader::Read(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; base::ListValue* list = NULL;
result->GetAsList(&list); result->GetAsList(&list);
ignore_result(result.release()); ignore_result(result.release());
......
...@@ -38,6 +38,18 @@ ...@@ -38,6 +38,18 @@
"description": "Extensions and packaged applications a user has installed (be _really_ careful!)." "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") { ...@@ -3724,6 +3724,7 @@ test("unit_tests") {
"../browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api_unittest.cc", "../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/bookmark_api_helpers_unittest.cc",
"../browser/extensions/api/bookmarks/bookmarks_api_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/chrome_extensions_api_client_unittest.cc",
"../browser/extensions/api/content_settings/content_settings_store_unittest.cc", "../browser/extensions/api/content_settings/content_settings_store_unittest.cc",
"../browser/extensions/api/content_settings/content_settings_unittest.cc", "../browser/extensions/api/content_settings/content_settings_unittest.cc",
......
...@@ -110,4 +110,19 @@ bool MockBrowsingDataRemoverDelegate::CallParameters::operator==( ...@@ -110,4 +110,19 @@ bool MockBrowsingDataRemoverDelegate::CallParameters::operator==(
return *a.filter_builder_ == *b.filter_builder_; 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 } // content
...@@ -61,6 +61,9 @@ class MockBrowsingDataRemoverDelegate : public BrowsingDataRemoverDelegate { ...@@ -61,6 +61,9 @@ class MockBrowsingDataRemoverDelegate : public BrowsingDataRemoverDelegate {
bool operator==(const CallParameters& other) const; bool operator==(const CallParameters& other) const;
private: private:
friend std::ostream& operator<<(std::ostream& os,
const CallParameters& params);
base::Time delete_begin_; base::Time delete_begin_;
base::Time delete_end_; base::Time delete_end_;
int remove_mask_; int remove_mask_;
...@@ -68,11 +71,17 @@ class MockBrowsingDataRemoverDelegate : public BrowsingDataRemoverDelegate { ...@@ -68,11 +71,17 @@ class MockBrowsingDataRemoverDelegate : public BrowsingDataRemoverDelegate {
std::unique_ptr<BrowsingDataFilterBuilder> filter_builder_; std::unique_ptr<BrowsingDataFilterBuilder> filter_builder_;
bool should_compare_filter_; bool should_compare_filter_;
}; };
friend std::ostream& operator<<(std::ostream& os,
const CallParameters& params);
std::list<CallParameters> actual_calls_; std::list<CallParameters> actual_calls_;
std::list<CallParameters> expected_calls_; std::list<CallParameters> expected_calls_;
}; };
std::ostream& operator<<(
std::ostream& os,
const MockBrowsingDataRemoverDelegate::CallParameters& params);
} // content } // content
#endif // CHROME_BROWSER_BROWSING_DATA_MOCK_BROWSING_DATA_REMOVER_DELEGATE_H_ #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