0

[WebLayer] Add site isolation on password sites for WebLayer

This change adds logic to WebLayer to detect when a user is typing a
password into a site so we can start isolating that site. This also
handles saving those sites to a pref and the restore/delete logic
related to that pref.

In chrome, the dynamic detection is done by
ContentPasswordManagerDriver, which also does many other things to
support chrome's password autofill. WebLayer uses Android Autofill
on Android, so really only needs the one callback notifying the
user has typed into a password form. To do this, WebLayer has a minimal
implementation of PasswordManagerDriver, which only handles that event.

The browser tests added for this change are heavily based on the site
isolation related tests in chrome_navigation_browsertest.cc.

Note that there is another bug to design a long term solution for site
isolation that will ideally include sharing this logic in a better way
with chrome: http://crbug.com/1088446.

Bug: 1058364
Change-Id: I62615af273b37843450366d3e05676ce228ed8fb
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2239030
Commit-Queue: Clark DuVall <cduvall@chromium.org>
Reviewed-by: John Abd-El-Malek <jam@chromium.org>
Reviewed-by: Alex Moshchuk <alexmos@chromium.org>
Reviewed-by: Maxim Kolosovskiy <kolos@chromium.org>
Cr-Commit-Position: refs/heads/master@{#777420}
This commit is contained in:
Clark DuVall
2020-06-11 18:50:40 +00:00
committed by Commit Bot
parent e68b450b70
commit b12feb9447
19 changed files with 709 additions and 5 deletions

@ -256,10 +256,11 @@ void ContentPasswordManagerDriver::ShowManualFallbackForSaving(
GetPasswordManager()->ShowManualFallbackForSaving(this, form_data);
if (client_->IsIsolationForPasswordSitesEnabled()) {
// This function signals that the user is typing a password into
// password form. Use this as a heuristic to start site-isolating the
// form's site. This is intended to be used primarily when full site
// isolation is not used, such as on Android.
// This function signals that a password field has been filled (whether by
// the user, JS, autofill, or some other means) or a password form has been
// submitted. Use this as a heuristic to start site-isolating the form's
// site. This is intended to be used primarily when full site isolation is
// not used, such as on Android.
content::SiteInstance::StartIsolatingSite(
render_frame_host_->GetSiteInstance()->GetBrowserContext(),
form_data.url);

@ -143,6 +143,10 @@ source_set("weblayer_lib_base") {
"browser/browser_main_parts_impl.h",
"browser/browser_process.cc",
"browser/browser_process.h",
"browser/browsing_data_remover_delegate.cc",
"browser/browsing_data_remover_delegate.h",
"browser/browsing_data_remover_delegate_factory.cc",
"browser/browsing_data_remover_delegate_factory.h",
"browser/client_hints_factory.cc",
"browser/client_hints_factory.h",
"browser/content_browser_client_impl.cc",
@ -181,6 +185,8 @@ source_set("weblayer_lib_base") {
"browser/navigation_impl.h",
"browser/page_load_metrics_initialize.cc",
"browser/page_load_metrics_initialize.h",
"browser/password_manager_driver_factory.cc",
"browser/password_manager_driver_factory.h",
"browser/permissions/geolocation_permission_context_delegate.cc",
"browser/permissions/geolocation_permission_context_delegate.h",
"browser/permissions/permission_decision_auto_blocker_factory.cc",
@ -326,6 +332,7 @@ source_set("weblayer_lib_base") {
"//components/page_info",
"//components/page_load_metrics/browser",
"//components/page_load_metrics/renderer",
"//components/password_manager/content/browser",
"//components/permissions",
"//components/policy/core/browser",
"//components/pref_registry:pref_registry",

@ -2,6 +2,7 @@ include_rules = [
"+cc",
"+components/autofill/android",
"+components/autofill/content/browser",
"+components/autofill/content/common",
"+components/autofill/core/browser",
"+components/autofill/core/common",
"+components/base32",
@ -22,11 +23,13 @@ include_rules = [
"+components/infobars/core",
"+components/javascript_dialogs",
"+components/keyed_service/content",
"+components/keyed_service/core",
"+components/language/core/browser",
"+components/metrics",
"+components/navigation_interception",
"+components/network_time",
"+components/page_load_metrics/browser",
"+components/password_manager/content/browser",
"+components/permissions",
"+components/pref_registry",
"+components/prefs",

@ -21,6 +21,8 @@
#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
#include "components/security_interstitials/content/stateful_ssl_host_state_delegate.h"
#include "components/security_state/core/security_state.h"
#include "components/site_isolation/pref_names.h"
#include "components/site_isolation/site_isolation_policy.h"
#include "components/translate/core/browser/translate_pref_names.h"
#include "components/translate/core/browser/translate_prefs.h"
#include "components/user_prefs/user_prefs.h"
@ -30,6 +32,8 @@
#include "content/public/browser/download_request_utils.h"
#include "content/public/browser/resource_context.h"
#include "content/public/browser/storage_partition.h"
#include "weblayer/browser/browsing_data_remover_delegate.h"
#include "weblayer/browser/browsing_data_remover_delegate_factory.h"
#include "weblayer/browser/client_hints_factory.h"
#include "weblayer/browser/permissions/permission_manager_factory.h"
#include "weblayer/browser/stateful_ssl_host_state_delegate_factory.h"
@ -83,6 +87,8 @@ BrowserContextImpl::BrowserContextImpl(ProfileImpl* profile_impl,
BrowserContextDependencyManager::GetInstance()->CreateBrowserContextServices(
this);
site_isolation::SiteIsolationPolicy::ApplyPersistedIsolatedOrigins(this);
}
BrowserContextImpl::~BrowserContextImpl() {
@ -179,7 +185,7 @@ BrowserContextImpl::GetBackgroundSyncController() {
content::BrowsingDataRemoverDelegate*
BrowserContextImpl::GetBrowsingDataRemoverDelegate() {
return nullptr;
return BrowsingDataRemoverDelegateFactory::GetForBrowserContext(this);
}
download::InProgressDownloadManager*
@ -234,6 +240,8 @@ void BrowserContextImpl::RegisterPrefs(
// through //chrome).
pref_registry->RegisterBooleanPref(
embedder_support::kAlternateErrorPagesEnabled, true);
pref_registry->RegisterListPref(
site_isolation::prefs::kUserTriggeredIsolatedOrigins);
StatefulSSLHostStateDelegate::RegisterProfilePrefs(pref_registry);
HostContentSettingsMap::RegisterProfilePrefs(pref_registry);

@ -0,0 +1,48 @@
// 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/browsing_data_remover_delegate.h"
#include "base/callback.h"
#include "components/prefs/pref_service.h"
#include "components/site_isolation//pref_names.h"
#include "components/user_prefs/user_prefs.h"
#include "content/public/browser/browser_context.h"
namespace weblayer {
BrowsingDataRemoverDelegate::BrowsingDataRemoverDelegate(
content::BrowserContext* browser_context)
: browser_context_(browser_context) {}
BrowsingDataRemoverDelegate::EmbedderOriginTypeMatcher
BrowsingDataRemoverDelegate::GetOriginTypeMatcher() {
return EmbedderOriginTypeMatcher();
}
bool BrowsingDataRemoverDelegate::MayRemoveDownloadHistory() {
return true;
}
void BrowsingDataRemoverDelegate::RemoveEmbedderData(
const base::Time& delete_begin,
const base::Time& delete_end,
uint64_t remove_mask,
content::BrowsingDataFilterBuilder* filter_builder,
uint64_t origin_type_mask,
base::OnceClosure callback) {
// Note: if history is ever added to WebLayer, also remove isolated origins
// when history is cleared.
if (remove_mask & DATA_TYPE_ISOLATED_ORIGINS) {
user_prefs::UserPrefs::Get(browser_context_)
->ClearPref(site_isolation::prefs::kUserTriggeredIsolatedOrigins);
// Note that this does not clear these sites from the in-memory map in
// ChildProcessSecurityPolicy, since that is not supported at runtime. That
// list of isolated sites is not directly exposed to users, though, and
// will be cleared on next restart.
}
std::move(callback).Run();
}
} // namespace weblayer

@ -0,0 +1,56 @@
// 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_BROWSING_DATA_REMOVER_DELEGATE_H_
#define WEBLAYER_BROWSER_BROWSING_DATA_REMOVER_DELEGATE_H_
#include "components/keyed_service/core/keyed_service.h"
#include "content/public/browser/browsing_data_remover.h"
#include "content/public/browser/browsing_data_remover_delegate.h"
namespace content {
class BrowserContext;
}
namespace weblayer {
class BrowsingDataRemoverDelegate : public content::BrowsingDataRemoverDelegate,
public KeyedService {
public:
// This is an extension of content::BrowsingDataRemover::RemoveDataMask which
// includes all datatypes therefrom and adds additional WebLayer-specific
// ones.
enum DataType : uint64_t {
// Embedder can start adding datatypes after the last platform datatype.
DATA_TYPE_EMBEDDER_BEGIN =
content::BrowsingDataRemover::DATA_TYPE_CONTENT_END << 1,
// WebLayer-specific datatypes.
DATA_TYPE_ISOLATED_ORIGINS = DATA_TYPE_EMBEDDER_BEGIN,
};
explicit BrowsingDataRemoverDelegate(
content::BrowserContext* browser_context);
BrowsingDataRemoverDelegate(const BrowsingDataRemoverDelegate&) = delete;
BrowsingDataRemoverDelegate& operator=(const BrowsingDataRemoverDelegate&) =
delete;
// content::BrowsingDataRemoverDelegate:
EmbedderOriginTypeMatcher GetOriginTypeMatcher() override;
bool MayRemoveDownloadHistory() override;
void RemoveEmbedderData(const base::Time& delete_begin,
const base::Time& delete_end,
uint64_t remove_mask,
content::BrowsingDataFilterBuilder* filter_builder,
uint64_t origin_type_mask,
base::OnceClosure callback) override;
private:
content::BrowserContext* browser_context_ = nullptr;
};
} // namespace weblayer
#endif // WEBLAYER_BROWSER_BROWSING_DATA_REMOVER_DELEGATE_H_

@ -0,0 +1,46 @@
// 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/browsing_data_remover_delegate_factory.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "weblayer/browser/browsing_data_remover_delegate.h"
namespace weblayer {
// static
BrowsingDataRemoverDelegate*
BrowsingDataRemoverDelegateFactory::GetForBrowserContext(
content::BrowserContext* browser_context) {
return static_cast<BrowsingDataRemoverDelegate*>(
GetInstance()->GetServiceForBrowserContext(browser_context, true));
}
// static
BrowsingDataRemoverDelegateFactory*
BrowsingDataRemoverDelegateFactory::GetInstance() {
static base::NoDestructor<BrowsingDataRemoverDelegateFactory> factory;
return factory.get();
}
BrowsingDataRemoverDelegateFactory::BrowsingDataRemoverDelegateFactory()
: BrowserContextKeyedServiceFactory(
"BrowsingDataRemoverDelegate",
BrowserContextDependencyManager::GetInstance()) {}
BrowsingDataRemoverDelegateFactory::~BrowsingDataRemoverDelegateFactory() =
default;
KeyedService* BrowsingDataRemoverDelegateFactory::BuildServiceInstanceFor(
content::BrowserContext* context) const {
return new BrowsingDataRemoverDelegate(context);
}
content::BrowserContext*
BrowsingDataRemoverDelegateFactory::GetBrowserContextToUse(
content::BrowserContext* context) const {
return context;
}
} // namespace weblayer

@ -0,0 +1,41 @@
// 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_BROWSING_DATA_REMOVER_DELEGATE_FACTORY_H_
#define WEBLAYER_BROWSER_BROWSING_DATA_REMOVER_DELEGATE_FACTORY_H_
#include "base/no_destructor.h"
#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
namespace weblayer {
class BrowsingDataRemoverDelegate;
class BrowsingDataRemoverDelegateFactory
: public BrowserContextKeyedServiceFactory {
public:
BrowsingDataRemoverDelegateFactory(
const BrowsingDataRemoverDelegateFactory&) = delete;
BrowsingDataRemoverDelegateFactory& operator=(
const BrowsingDataRemoverDelegateFactory&) = delete;
static BrowsingDataRemoverDelegate* GetForBrowserContext(
content::BrowserContext* browser_context);
static BrowsingDataRemoverDelegateFactory* GetInstance();
private:
friend class base::NoDestructor<BrowsingDataRemoverDelegateFactory>;
BrowsingDataRemoverDelegateFactory();
~BrowsingDataRemoverDelegateFactory() override;
// BrowserContextKeyedServiceFactory methods:
KeyedService* BuildServiceInstanceFor(
content::BrowserContext* profile) const override;
content::BrowserContext* GetBrowserContextToUse(
content::BrowserContext* context) const override;
};
} // namespace weblayer
#endif // WEBLAYER_BROWSER_BROWSING_DATA_REMOVER_DELEGATE_FACTORY_H_

@ -27,12 +27,15 @@
#include "components/prefs/json_pref_store.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service_factory.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "components/security_interstitials/content/ssl_cert_reporter.h"
#include "components/security_interstitials/content/ssl_error_handler.h"
#include "components/security_interstitials/content/ssl_error_navigation_throttle.h"
#include "components/site_isolation/pref_names.h"
#include "components/site_isolation/preloaded_isolated_origins.h"
#include "components/site_isolation/site_isolation_policy.h"
#include "components/strings/grit/components_locale_settings.h"
#include "components/user_prefs/user_prefs.h"
#include "components/variations/service/variations_service.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/devtools_manager_delegate.h"
@ -69,6 +72,7 @@
#include "weblayer/browser/http_auth_handler_impl.h"
#include "weblayer/browser/i18n_util.h"
#include "weblayer/browser/navigation_controller_impl.h"
#include "weblayer/browser/password_manager_driver_factory.h"
#include "weblayer/browser/popup_navigation_delegate_impl.h"
#include "weblayer/browser/profile_impl.h"
#include "weblayer/browser/system_network_context_manager.h"
@ -430,6 +434,18 @@ ContentBrowserClientImpl::GetAdditionalSiteIsolationModes() {
return {};
}
void ContentBrowserClientImpl::PersistIsolatedOrigin(
content::BrowserContext* context,
const url::Origin& origin) {
DCHECK(!context->IsOffTheRecord());
ListPrefUpdate update(user_prefs::UserPrefs::Get(context),
site_isolation::prefs::kUserTriggeredIsolatedOrigins);
base::ListValue* list = update.Get();
base::Value value(origin.Serialize());
if (!base::Contains(list->GetList(), value))
list->Append(std::move(value));
}
bool ContentBrowserClientImpl::CanCreateWindow(
content::RenderFrameHost* opener,
const GURL& opener_url,
@ -570,6 +586,13 @@ bool ContentBrowserClientImpl::BindAssociatedReceiverFromFrame(
render_frame_host);
return true;
}
if (interface_name == autofill::mojom::PasswordManagerDriver::Name_) {
PasswordManagerDriverFactory::BindPasswordManagerDriver(
mojo::PendingAssociatedReceiver<autofill::mojom::PasswordManagerDriver>(
std::move(*handle)),
render_frame_host);
return true;
}
return false;
}

@ -68,6 +68,8 @@ class ContentBrowserClientImpl : public content::ContentBrowserClient {
std::vector<url::Origin> GetOriginsRequiringDedicatedProcess() override;
bool ShouldDisableSiteIsolation() override;
std::vector<std::string> GetAdditionalSiteIsolationModes() override;
void PersistIsolatedOrigin(content::BrowserContext* context,
const url::Origin& origin) override;
bool CanCreateWindow(content::RenderFrameHost* opener,
const GURL& opener_url,
const GURL& opener_top_level_frame_url,

@ -0,0 +1,132 @@
// 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/password_manager_driver_factory.h"
#include "components/password_manager/content/browser/bad_message.h"
#include "components/site_isolation/site_isolation_policy.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
#include "mojo/public/cpp/bindings/associated_receiver.h"
namespace weblayer {
// A minimal implementation of autofill::mojom::PasswordManagerDriver which just
// listens for the user to type into a password field.
class PasswordManagerDriverFactory::PasswordManagerDriver
: public autofill::mojom::PasswordManagerDriver {
public:
explicit PasswordManagerDriver(content::RenderFrameHost* render_frame_host)
: render_frame_host_(render_frame_host) {}
void BindPendingReceiver(
mojo::PendingAssociatedReceiver<autofill::mojom::PasswordManagerDriver>
pending_receiver) {
password_manager_receiver_.Bind(std::move(pending_receiver));
}
private:
// autofill::mojom::PasswordManagerDriver:
// Note that these messages received from a potentially compromised renderer.
// For that reason, any access to form data should be validated via
// bad_message::CheckChildProcessSecurityPolicy.
void PasswordFormsParsed(
const std::vector<autofill::FormData>& forms_data) override {}
void PasswordFormsRendered(
const std::vector<autofill::FormData>& visible_forms_data,
bool did_stop_loading) override {}
void PasswordFormSubmitted(const autofill::FormData& form_data) override {}
void ShowManualFallbackForSaving(
const autofill::FormData& form_data) override {
if (!password_manager::bad_message::CheckChildProcessSecurityPolicyForURL(
render_frame_host_, form_data.url,
password_manager::BadMessageReason::
CPMD_BAD_ORIGIN_SHOW_FALLBACK_FOR_SAVING)) {
return;
}
if (site_isolation::SiteIsolationPolicy::
IsIsolationForPasswordSitesEnabled()) {
// This function signals that a password field has been filled (whether by
// the user, JS, autofill, or some other means) or a password form has
// been submitted. Use this as a heuristic to start site-isolating the
// form's site. This is intended to be used primarily when full site
// isolation is not used, such as on Android.
content::SiteInstance::StartIsolatingSite(
render_frame_host_->GetSiteInstance()->GetBrowserContext(),
form_data.url);
}
}
void HideManualFallbackForSaving() override {}
void SameDocumentNavigation(autofill::mojom::SubmissionIndicatorEvent
submission_indication_event) override {}
void RecordSavePasswordProgress(const std::string& log) override {}
void UserModifiedPasswordField() override {}
void UserModifiedNonPasswordField(autofill::FieldRendererId renderer_id,
const base::string16& value) override {}
void ShowPasswordSuggestions(base::i18n::TextDirection text_direction,
const base::string16& typed_username,
int options,
const gfx::RectF& bounds) override {}
void ShowTouchToFill() override {}
void CheckSafeBrowsingReputation(const GURL& form_action,
const GURL& frame_url) override {}
void FocusedInputChanged(
autofill::mojom::FocusedFieldType focused_field_type) override {}
void LogFirstFillingResult(autofill::FormRendererId form_renderer_id,
int32_t result) override {}
mojo::AssociatedReceiver<autofill::mojom::PasswordManagerDriver>
password_manager_receiver_{this};
content::RenderFrameHost* render_frame_host_;
};
PasswordManagerDriverFactory::PasswordManagerDriverFactory(
content::WebContents* web_contents)
: content::WebContentsObserver(web_contents) {}
PasswordManagerDriverFactory::~PasswordManagerDriverFactory() = default;
// static
void PasswordManagerDriverFactory::BindPasswordManagerDriver(
mojo::PendingAssociatedReceiver<autofill::mojom::PasswordManagerDriver>
pending_receiver,
content::RenderFrameHost* render_frame_host) {
content::WebContents* web_contents =
content::WebContents::FromRenderFrameHost(render_frame_host);
if (!web_contents)
return;
PasswordManagerDriverFactory* factory =
PasswordManagerDriverFactory::FromWebContents(web_contents);
if (!factory)
return;
factory->GetDriverForFrame(render_frame_host)
->BindPendingReceiver(std::move(pending_receiver));
}
PasswordManagerDriverFactory::PasswordManagerDriver*
PasswordManagerDriverFactory::GetDriverForFrame(
content::RenderFrameHost* render_frame_host) {
DCHECK_EQ(web_contents(),
content::WebContents::FromRenderFrameHost(render_frame_host));
DCHECK(render_frame_host->IsRenderFrameCreated());
// TryEmplace() will return an iterator to the driver corresponding to
// `render_frame_host`. It creates a new one if required.
return &base::TryEmplace(frame_driver_map_, render_frame_host,
render_frame_host)
.first->second;
}
void PasswordManagerDriverFactory::RenderFrameDeleted(
content::RenderFrameHost* render_frame_host) {
frame_driver_map_.erase(render_frame_host);
}
WEB_CONTENTS_USER_DATA_KEY_IMPL(PasswordManagerDriverFactory)
} // namespace weblayer

@ -0,0 +1,61 @@
// 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_PASSWORD_MANAGER_DRIVER_FACTORY_H_
#define WEBLAYER_BROWSER_PASSWORD_MANAGER_DRIVER_FACTORY_H_
#include <map>
#include "base/supports_user_data.h"
#include "components/autofill/content/common/mojom/autofill_driver.mojom.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/browser/web_contents_user_data.h"
#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
namespace content {
class WebContents;
}
namespace weblayer {
// WebLayer uses the system autofill and not the autofill used by Chrome. This
// factory and the corresponding driver are only used to listen for the
// notification that a password was typed into a form, since this is used as a
// signal to start isolating that site.
// TODO(crbug.com/1088446): Find a way to easily share this with Chrome.
class PasswordManagerDriverFactory
: public content::WebContentsObserver,
public content::WebContentsUserData<PasswordManagerDriverFactory> {
public:
~PasswordManagerDriverFactory() override;
PasswordManagerDriverFactory(const PasswordManagerDriverFactory&) = delete;
PasswordManagerDriverFactory& operator=(const PasswordManagerDriverFactory&) =
delete;
static void BindPasswordManagerDriver(
mojo::PendingAssociatedReceiver<autofill::mojom::PasswordManagerDriver>
pending_receiver,
content::RenderFrameHost* render_frame_host);
private:
class PasswordManagerDriver;
friend class content::WebContentsUserData<PasswordManagerDriverFactory>;
explicit PasswordManagerDriverFactory(content::WebContents* web_contents);
// content::WebContentsObserver:
void RenderFrameDeleted(content::RenderFrameHost* render_frame_host) override;
PasswordManagerDriver* GetDriverForFrame(
content::RenderFrameHost* render_frame_host);
std::map<content::RenderFrameHost*, PasswordManagerDriver> frame_driver_map_;
WEB_CONTENTS_USER_DATA_KEY_DECL();
};
} // namespace weblayer
#endif // WEBLAYER_BROWSER_PASSWORD_MANAGER_DRIVER_FACTORY_H_

@ -25,6 +25,7 @@
#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/browsing_data_remover_delegate.h"
#include "weblayer/browser/cookie_manager_impl.h"
#include "weblayer/browser/tab_impl.h"
@ -177,6 +178,7 @@ void ProfileImpl::ClearBrowsingData(
remove_mask |= content::BrowsingDataRemover::DATA_TYPE_COOKIES;
remove_mask |= content::BrowsingDataRemover::DATA_TYPE_DOM_STORAGE;
remove_mask |= content::BrowsingDataRemover::DATA_TYPE_MEDIA_LICENSES;
remove_mask |= BrowsingDataRemoverDelegate::DATA_TYPE_ISOLATED_ORIGINS;
break;
case BrowsingDataType::CACHE:
remove_mask |= content::BrowsingDataRemover::DATA_TYPE_CACHE;

@ -0,0 +1,256 @@
// 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 "base/base_switches.h"
#include "base/files/file_path.h"
#include "base/system/sys_info.h"
#include "build/build_config.h"
#include "components/prefs/pref_service.h"
#include "components/site_isolation/features.h"
#include "components/site_isolation/pref_names.h"
#include "components/user_prefs/user_prefs.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/site_isolation_policy.h"
#include "content/public/common/content_client.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_navigation_observer.h"
#include "net/dns/mock_host_resolver.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "weblayer/browser/browser_impl.h"
#include "weblayer/browser/content_browser_client_impl.h"
#include "weblayer/browser/profile_impl.h"
#include "weblayer/browser/tab_impl.h"
#include "weblayer/shell/browser/shell.h"
#include "weblayer/test/weblayer_browser_test.h"
#include "weblayer/test/weblayer_browser_test_utils.h"
namespace weblayer {
using testing::IsEmpty;
using testing::UnorderedElementsAre;
class SiteIsolationBrowserTest : public WebLayerBrowserTest {
public:
SiteIsolationBrowserTest() {
feature_list_.InitWithFeaturesAndParameters(
{{site_isolation::features::kSitePerProcessOnlyForHighMemoryClients,
{{site_isolation::features::
kSitePerProcessOnlyForHighMemoryClientsParamName,
"128"}}},
{site_isolation::features::kSiteIsolationForPasswordSites, {}}},
{});
}
std::vector<std::string> GetSavedIsolatedSites() {
PrefService* prefs =
user_prefs::UserPrefs::Get(GetProfile()->GetBrowserContext());
auto* list =
prefs->GetList(site_isolation::prefs::kUserTriggeredIsolatedOrigins);
std::vector<std::string> sites;
for (const base::Value& value : list->GetList())
sites.push_back(value.GetString());
return sites;
}
// WebLayerBrowserTest:
void SetUpOnMainThread() override {
original_client_ = content::SetBrowserClientForTesting(&browser_client_);
host_resolver()->AddRule("*", "127.0.0.1");
embedded_test_server()->ServeFilesFromSourceDirectory("weblayer/test/data");
ASSERT_TRUE(embedded_test_server()->Start());
ASSERT_FALSE(
content::SiteIsolationPolicy::UseDedicatedProcessesForAllSites());
ASSERT_TRUE(
content::SiteIsolationPolicy::AreDynamicIsolatedOriginsEnabled());
}
void TearDownOnMainThread() override {
content::SetBrowserClientForTesting(original_client_);
}
void SetUpCommandLine(base::CommandLine* command_line) override {
command_line->AppendSwitch("ignore-certificate-errors");
// This way the test always sees the same amount of physical memory
// (kLowMemoryDeviceThresholdMB = 512MB), regardless of how much memory is
// available in the testing environment.
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kEnableLowEndDeviceMode);
EXPECT_EQ(512, base::SysInfo::AmountOfPhysicalMemoryMB());
}
content::WebContents* GetWebContents() {
return static_cast<TabImpl*>(shell()->tab())->web_contents();
}
private:
// A browser client which forces off strict site isolation, so the test can
// assume password isolation is enabled.
class SiteIsolationContentBrowserClient : public ContentBrowserClientImpl {
public:
SiteIsolationContentBrowserClient() : ContentBrowserClientImpl(nullptr) {}
bool ShouldEnableStrictSiteIsolation() override { return false; }
};
SiteIsolationContentBrowserClient browser_client_;
content::ContentBrowserClient* original_client_ = nullptr;
base::test::ScopedFeatureList feature_list_;
};
IN_PROC_BROWSER_TEST_F(SiteIsolationBrowserTest,
SiteIsIsolatedAfterEnteringPassword) {
GURL url = embedded_test_server()->GetURL("sub.foo.com",
"/simple_password_form.html");
NavigateAndWaitForCompletion(url, shell());
content::WebContents* contents = GetWebContents();
// foo.com should not be isolated to start with. Verify that a cross-site
// iframe does not become an OOPIF.
EXPECT_FALSE(
contents->GetMainFrame()->GetSiteInstance()->RequiresDedicatedProcess());
std::string kAppendIframe = R"(
var i = document.createElement('iframe');
i.id = 'child';
document.body.appendChild(i);)";
EXPECT_TRUE(content::ExecJs(contents, kAppendIframe));
GURL bar_url(embedded_test_server()->GetURL("bar.com", "/simple_page.html"));
EXPECT_TRUE(NavigateIframeToURL(contents, "child", bar_url));
content::RenderFrameHost* child = ChildFrameAt(contents->GetMainFrame(), 0);
EXPECT_FALSE(child->IsCrossProcessSubframe());
// Fill a form and submit through a <input type="submit"> button.
content::TestNavigationObserver observer(contents);
std::string kFillAndSubmit =
"document.getElementById('username_field').value = 'temp';"
"document.getElementById('password_field').value = 'random';"
"document.getElementById('input_submit_button').click()";
EXPECT_TRUE(content::ExecJs(contents, kFillAndSubmit));
observer.Wait();
// Since there were no script references from other windows, we should've
// swapped BrowsingInstances and put the result of the form submission into a
// dedicated process, locked to foo.com. Check that a cross-site iframe now
// becomes an OOPIF.
EXPECT_TRUE(
contents->GetMainFrame()->GetSiteInstance()->RequiresDedicatedProcess());
EXPECT_TRUE(ExecJs(contents, kAppendIframe));
EXPECT_TRUE(NavigateIframeToURL(contents, "child", bar_url));
child = ChildFrameAt(contents->GetMainFrame(), 0);
EXPECT_TRUE(child->IsCrossProcessSubframe());
}
// TODO(crbug.com/654704): Android does not support PRE_ tests.
#if !defined(OS_ANDROID)
IN_PROC_BROWSER_TEST_F(SiteIsolationBrowserTest,
PRE_IsolatedSitesPersistAcrossRestarts) {
// There shouldn't be any saved isolated origins to start with.
EXPECT_THAT(GetSavedIsolatedSites(), IsEmpty());
// Isolate saved.com and saved2.com persistently.
GURL saved_url =
embedded_test_server()->GetURL("saved.com", "/simple_page.html");
content::SiteInstance::StartIsolatingSite(GetProfile()->GetBrowserContext(),
saved_url);
GURL saved2_url =
embedded_test_server()->GetURL("saved2.com", "/simple_page.html");
content::SiteInstance::StartIsolatingSite(GetProfile()->GetBrowserContext(),
saved2_url);
NavigateAndWaitForCompletion(saved_url, shell());
EXPECT_TRUE(GetWebContents()
->GetMainFrame()
->GetSiteInstance()
->RequiresDedicatedProcess());
// Check that saved.com and saved2.com were saved to disk.
EXPECT_THAT(GetSavedIsolatedSites(),
UnorderedElementsAre("http://saved.com", "http://saved2.com"));
}
IN_PROC_BROWSER_TEST_F(SiteIsolationBrowserTest,
IsolatedSitesPersistAcrossRestarts) {
// Check that saved.com and saved2.com are still saved to disk.
EXPECT_THAT(GetSavedIsolatedSites(),
UnorderedElementsAre("http://saved.com", "http://saved2.com"));
// Check that these sites utilize a dedicated process after restarting, but a
// non-isolated foo.com URL does not.
GURL saved_url =
embedded_test_server()->GetURL("saved.com", "/simple_page.html");
GURL saved2_url =
embedded_test_server()->GetURL("saved2.com", "/simple_page2.html");
GURL foo_url =
embedded_test_server()->GetURL("foo.com", "/simple_page3.html");
NavigateAndWaitForCompletion(saved_url, shell());
content::WebContents* contents = GetWebContents();
EXPECT_TRUE(
contents->GetMainFrame()->GetSiteInstance()->RequiresDedicatedProcess());
NavigateAndWaitForCompletion(saved2_url, shell());
EXPECT_TRUE(
contents->GetMainFrame()->GetSiteInstance()->RequiresDedicatedProcess());
NavigateAndWaitForCompletion(foo_url, shell());
EXPECT_FALSE(
contents->GetMainFrame()->GetSiteInstance()->RequiresDedicatedProcess());
}
#endif
IN_PROC_BROWSER_TEST_F(SiteIsolationBrowserTest, IsolatedSiteIsSavedOnlyOnce) {
GURL saved_url =
embedded_test_server()->GetURL("saved.com", "/simple_page.html");
content::SiteInstance::StartIsolatingSite(GetProfile()->GetBrowserContext(),
saved_url);
content::SiteInstance::StartIsolatingSite(GetProfile()->GetBrowserContext(),
saved_url);
content::SiteInstance::StartIsolatingSite(GetProfile()->GetBrowserContext(),
saved_url);
EXPECT_THAT(GetSavedIsolatedSites(),
UnorderedElementsAre("http://saved.com"));
}
// Verify that serving a Clear-Site-Data header does not clear saved isolated
// sites. Saved isolated sites should only be cleared by user-initiated actions.
IN_PROC_BROWSER_TEST_F(SiteIsolationBrowserTest,
ClearSiteDataHeaderDoesNotClearSavedIsolatedSites) {
// Start an HTTPS server, as Clear-Site-Data is only available on HTTPS URLs.
net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
https_server.AddDefaultHandlers(
base::FilePath(FILE_PATH_LITERAL("weblayer/test/data")));
ASSERT_TRUE(https_server.Start());
// Isolate saved.com and verify it's been saved to disk.
GURL saved_url = https_server.GetURL("saved.com", "/clear_site_data.html");
content::SiteInstance::StartIsolatingSite(GetProfile()->GetBrowserContext(),
saved_url);
EXPECT_THAT(GetSavedIsolatedSites(),
UnorderedElementsAre("https://saved.com"));
// Navigate to a URL that serves a Clear-Site-Data header for cache, cookies,
// and DOM storage. This is the most that a Clear-Site-Data header could
// clear, and this should not clear saved isolated sites.
NavigateAndWaitForCompletion(saved_url, shell());
EXPECT_THAT(GetSavedIsolatedSites(),
UnorderedElementsAre("https://saved.com"));
}
IN_PROC_BROWSER_TEST_F(SiteIsolationBrowserTest,
ExplicitClearBrowsingDataClearsSavedIsolatedSites) {
GURL saved_url =
embedded_test_server()->GetURL("saved.com", "/simple_page.html");
content::SiteInstance::StartIsolatingSite(GetProfile()->GetBrowserContext(),
saved_url);
EXPECT_THAT(GetSavedIsolatedSites(),
UnorderedElementsAre("http://saved.com"));
base::RunLoop run_loop;
base::Time now = base::Time::Now();
GetProfile()->ClearBrowsingData({BrowsingDataType::COOKIES_AND_SITE_DATA},
now - base::TimeDelta::FromDays(1), now,
run_loop.QuitClosure());
run_loop.Run();
EXPECT_THAT(GetSavedIsolatedSites(), IsEmpty());
}
} // namespace weblayer

@ -53,6 +53,7 @@
#include "weblayer/browser/infobar_service.h"
#include "weblayer/browser/navigation_controller_impl.h"
#include "weblayer/browser/page_load_metrics_initialize.h"
#include "weblayer/browser/password_manager_driver_factory.h"
#include "weblayer/browser/permissions/permission_manager_factory.h"
#include "weblayer/browser/persistence/browser_persister.h"
#include "weblayer/browser/popup_navigation_delegate_impl.h"
@ -304,6 +305,7 @@ TabImpl::TabImpl(ProfileImpl* profile,
web_contents_.get(), base::DefaultTickClock::GetInstance(),
HostContentSettingsMapFactory::GetForBrowserContext(
web_contents_->GetBrowserContext()));
PasswordManagerDriverFactory::CreateForWebContents(web_contents_.get());
InitializePageLoadMetricsForWebContents(web_contents_.get());

@ -94,9 +94,11 @@ test("weblayer_browsertests") {
"//components/page_load_metrics/browser:browser",
"//components/security_interstitials/content:security_interstitial_page",
"//components/sessions:test_support",
"//components/site_isolation",
"//components/strings",
"//components/translate/content/browser",
"//components/translate/content/browser:test_support",
"//components/user_prefs",
"//components/variations",
"//components/variations/net",
"//content/public/browser",
@ -117,6 +119,7 @@ test("weblayer_browsertests") {
"../browser/navigation_browsertest.cc",
"../browser/page_load_metrics_browsertest.cc",
"../browser/popup_blocker_browsertest.cc",
"../browser/site_isolation_browsertest.cc",
"../browser/ssl_browsertest.cc",
"../browser/translate_browsertest.cc",
"../browser/weblayer_variations_http_browsertest.cc",

@ -0,0 +1,5 @@
<html>
<head><title>Clear-Site-Data Test Page</title></head>
<body>Clear-Site-Data test body</body>
</html>

@ -0,0 +1,2 @@
HTTP/1.1 200 OK
Clear-Site-Data: "cache", "cookies", "storage"

@ -0,0 +1,6 @@
<html>
<body>
Navigation complete.
This file should not contain any forms.
</body>
</html>