[go: nahoru, domu]

blob: 84fd3abf9aba202d40c9c87d83815af075a1645f [file] [log] [blame]
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/custom_handlers/protocol_handler_registry.h"
#include <stddef.h>
#include <memory>
#include <set>
#include <utility>
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/scoped_observation.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_feature_list.h"
#include "base/values.h"
#include "build/build_config.h"
#include "components/custom_handlers/pref_names.h"
#include "components/custom_handlers/protocol_handler.h"
#include "components/custom_handlers/test_protocol_handler_registry_delegate.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/sync_preferences/pref_service_syncable.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "components/user_prefs/user_prefs.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/test_browser_context.h"
#include "content/public/test/test_renderer_host.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/security/protocol_handler_security_level.h"
using content::BrowserThread;
namespace custom_handlers {
base::Value::Dict GetProtocolHandlerValue(const std::string& protocol,
const std::string& url) {
base::Value::Dict value;
value.Set("protocol", protocol);
value.Set("url", url);
return value;
}
base::Value::Dict GetProtocolHandlerValueWithDefault(
const std::string& protocol,
const std::string& url,
bool is_default) {
base::Value::Dict value = GetProtocolHandlerValue(protocol, url);
value.Set("default", is_default);
return value;
}
class ProtocolHandlerChangeListener : public ProtocolHandlerRegistry::Observer {
public:
explicit ProtocolHandlerChangeListener(ProtocolHandlerRegistry* registry) {
registry_observation_.Observe(registry);
}
ProtocolHandlerChangeListener(const ProtocolHandlerChangeListener&) = delete;
ProtocolHandlerChangeListener& operator=(
const ProtocolHandlerChangeListener&) = delete;
~ProtocolHandlerChangeListener() override = default;
int events() { return events_; }
bool notified() { return events_ > 0; }
void Clear() { events_ = 0; }
// ProtocolHandlerRegistry::Observer:
void OnProtocolHandlerRegistryChanged() override { ++events_; }
private:
int events_ = 0;
base::ScopedObservation<ProtocolHandlerRegistry,
ProtocolHandlerRegistry::Observer>
registry_observation_{this};
};
class QueryProtocolHandlerOnChange : public ProtocolHandlerRegistry::Observer {
public:
explicit QueryProtocolHandlerOnChange(ProtocolHandlerRegistry* registry)
: local_registry_(registry) {
registry_observation_.Observe(registry);
}
QueryProtocolHandlerOnChange(const QueryProtocolHandlerOnChange&) = delete;
QueryProtocolHandlerOnChange& operator=(const QueryProtocolHandlerOnChange&) =
delete;
// ProtocolHandlerRegistry::Observer:
void OnProtocolHandlerRegistryChanged() override {
std::vector<std::string> output;
local_registry_->GetRegisteredProtocols(&output);
called_ = true;
}
bool called() const { return called_; }
private:
raw_ptr<ProtocolHandlerRegistry> local_registry_;
bool called_ = false;
base::ScopedObservation<ProtocolHandlerRegistry,
ProtocolHandlerRegistry::Observer>
registry_observation_{this};
};
class ProtocolHandlerRegistryTest : public testing::Test {
protected:
ProtocolHandlerRegistryTest()
: test_protocol_handler_(CreateProtocolHandler("news", "news")) {}
TestProtocolHandlerRegistryDelegate* delegate() const { return delegate_; }
ProtocolHandlerRegistry* registry() { return registry_.get(); }
const ProtocolHandler& test_protocol_handler() const {
return test_protocol_handler_;
}
PrefService* GetPrefs() {
DCHECK(browser_context_);
return user_prefs::UserPrefs::Get(browser_context_.get());
}
ProtocolHandler CreateProtocolHandler(
const std::string& protocol,
const GURL& url,
blink::ProtocolHandlerSecurityLevel security_level =
blink::ProtocolHandlerSecurityLevel::kStrict) {
return ProtocolHandler::CreateProtocolHandler(protocol, url,
security_level);
}
ProtocolHandler CreateProtocolHandler(const std::string& protocol,
const std::string& name) {
return CreateProtocolHandler(protocol, GURL("https://" + name + "/%s"));
}
ProtocolHandler CreateWebAppProtocolHandler(const std::string& protocol,
const GURL& url,
const std::string& app_id) {
return ProtocolHandler::CreateWebAppProtocolHandler(protocol, url, app_id);
}
bool ProtocolHandlerCanRegisterProtocol(
const std::string& protocol,
const GURL& handler_url,
blink::ProtocolHandlerSecurityLevel security_level) {
registry()->OnAcceptRegisterProtocolHandler(
CreateProtocolHandler(protocol, handler_url, security_level));
return registry()->IsHandledProtocol(protocol);
}
void RecreateRegistry(bool initialize) {
TeadDownRegistry();
SetUpRegistry(initialize);
}
int InPrefHandlerCount() {
const base::Value::List& in_pref_handlers = GetPrefs()->GetList(
custom_handlers::prefs::kRegisteredProtocolHandlers);
return static_cast<int>(in_pref_handlers.size());
}
int InMemoryHandlerCount() {
int in_memory_handler_count = 0;
auto it = registry()->protocol_handlers_.begin();
for (; it != registry()->protocol_handlers_.end(); ++it)
in_memory_handler_count += it->second.size();
return in_memory_handler_count;
}
int InPrefIgnoredHandlerCount() {
const base::Value::List& in_pref_ignored_handlers =
GetPrefs()->GetList(custom_handlers::prefs::kIgnoredProtocolHandlers);
return static_cast<int>(in_pref_ignored_handlers.size());
}
int InMemoryIgnoredHandlerCount() {
int in_memory_ignored_handler_count = 0;
auto it = registry()->ignored_protocol_handlers_.begin();
for (; it != registry()->ignored_protocol_handlers_.end(); ++it)
in_memory_ignored_handler_count++;
return in_memory_ignored_handler_count;
}
// It creates a new instance of the ProtocolHandlerRegistry class,
// initializing it if |initialize| is true, for the registry_ member variable.
void SetUpRegistry(bool initialize) {
DCHECK(browser_context_);
auto delegate = std::make_unique<TestProtocolHandlerRegistryDelegate>();
delegate_ = delegate.get();
registry_ = std::make_unique<ProtocolHandlerRegistry>(GetPrefs(),
std::move(delegate));
if (initialize)
registry_->InitProtocolSettings();
}
void TeadDownRegistry() {
delegate_ = nullptr;
registry_->Shutdown();
registry_.reset();
}
void SetUp() override {
browser_context_ = std::make_unique<content::TestBrowserContext>();
user_prefs::UserPrefs::Set(browser_context_.get(), &pref_service_);
ProtocolHandlerRegistry::RegisterProfilePrefs(pref_service_.registry());
CHECK(GetPrefs());
SetUpRegistry(true);
test_protocol_handler_ =
CreateProtocolHandler("news", GURL("https://test.com/%s"));
}
void TearDown() override { TeadDownRegistry(); }
private:
content::BrowserTaskEnvironment task_environment_;
std::unique_ptr<content::TestBrowserContext> browser_context_;
sync_preferences::TestingPrefServiceSyncable pref_service_;
raw_ptr<TestProtocolHandlerRegistryDelegate>
delegate_; // Registry assumes ownership of delegate_.
std::unique_ptr<ProtocolHandlerRegistry> registry_;
ProtocolHandler test_protocol_handler_;
};
TEST_F(ProtocolHandlerRegistryTest, AcceptProtocolHandlerHandlesProtocol) {
ASSERT_FALSE(registry()->IsHandledProtocol("news"));
registry()->OnAcceptRegisterProtocolHandler(test_protocol_handler());
ASSERT_TRUE(registry()->IsHandledProtocol("news"));
}
TEST_F(ProtocolHandlerRegistryTest, DeniedProtocolIsntHandledUntilAccepted) {
registry()->OnDenyRegisterProtocolHandler(test_protocol_handler());
ASSERT_FALSE(registry()->IsHandledProtocol("news"));
registry()->OnAcceptRegisterProtocolHandler(test_protocol_handler());
ASSERT_TRUE(registry()->IsHandledProtocol("news"));
}
TEST_F(ProtocolHandlerRegistryTest, ClearDefaultMakesProtocolNotHandled) {
registry()->OnAcceptRegisterProtocolHandler(test_protocol_handler());
registry()->ClearDefault("news");
ASSERT_FALSE(registry()->IsHandledProtocol("news"));
ASSERT_TRUE(registry()->GetHandlerFor("news").IsEmpty());
}
TEST_F(ProtocolHandlerRegistryTest, DisableDeregistersProtocolHandlers) {
ASSERT_FALSE(delegate()->IsExternalHandlerRegistered("news"));
registry()->OnAcceptRegisterProtocolHandler(test_protocol_handler());
ASSERT_TRUE(delegate()->IsExternalHandlerRegistered("news"));
registry()->Disable();
ASSERT_FALSE(delegate()->IsExternalHandlerRegistered("news"));
registry()->Enable();
ASSERT_TRUE(delegate()->IsExternalHandlerRegistered("news"));
}
TEST_F(ProtocolHandlerRegistryTest, IgnoreProtocolHandler) {
registry()->OnIgnoreRegisterProtocolHandler(test_protocol_handler());
ASSERT_TRUE(registry()->IsIgnored(test_protocol_handler()));
registry()->RemoveIgnoredHandler(test_protocol_handler());
ASSERT_FALSE(registry()->IsIgnored(test_protocol_handler()));
}
TEST_F(ProtocolHandlerRegistryTest, IgnoreEquivalentProtocolHandler) {
ProtocolHandler ph1 = CreateProtocolHandler("news", GURL("https://test/%s"));
ProtocolHandler ph2 = CreateProtocolHandler("news", GURL("https://test/%s"));
registry()->OnIgnoreRegisterProtocolHandler(ph1);
ASSERT_TRUE(registry()->IsIgnored(ph1));
ASSERT_TRUE(registry()->HasIgnoredEquivalent(ph2));
registry()->RemoveIgnoredHandler(ph1);
ASSERT_FALSE(registry()->IsIgnored(ph1));
ASSERT_FALSE(registry()->HasIgnoredEquivalent(ph2));
}
TEST_F(ProtocolHandlerRegistryTest, SaveAndLoad) {
ProtocolHandler stuff_protocol_handler(
CreateProtocolHandler("stuff", "stuff"));
registry()->OnAcceptRegisterProtocolHandler(test_protocol_handler());
registry()->OnIgnoreRegisterProtocolHandler(stuff_protocol_handler);
ASSERT_TRUE(registry()->IsHandledProtocol("news"));
ASSERT_TRUE(registry()->IsIgnored(stuff_protocol_handler));
delegate()->Reset();
RecreateRegistry(true);
ASSERT_TRUE(registry()->IsHandledProtocol("news"));
ASSERT_TRUE(registry()->IsIgnored(stuff_protocol_handler));
}
TEST_F(ProtocolHandlerRegistryTest, Encode) {
base::Time now = base::Time::Now();
ProtocolHandler handler("news", GURL("https://example.com"), "app_id", now,
blink::ProtocolHandlerSecurityLevel::kStrict);
auto value = handler.Encode();
ProtocolHandler recreated = ProtocolHandler::CreateProtocolHandler(value);
EXPECT_EQ("news", recreated.protocol());
EXPECT_EQ(GURL("https://example.com"), recreated.url());
EXPECT_EQ(now, recreated.last_modified());
}
TEST_F(ProtocolHandlerRegistryTest, GetHandlersBetween) {
base::Time now = base::Time::Now();
base::Time one_hour_ago = now - base::Hours(1);
base::Time two_hours_ago = now - base::Hours(2);
ProtocolHandler handler1("bitcoin", GURL("https://example.com"),
two_hours_ago,
blink::ProtocolHandlerSecurityLevel::kStrict);
ProtocolHandler handler2("geo", GURL("https://example.com"), one_hour_ago,
blink::ProtocolHandlerSecurityLevel::kStrict);
ProtocolHandler handler3("im", GURL("https://example.com"), now,
blink::ProtocolHandlerSecurityLevel::kStrict);
registry()->OnAcceptRegisterProtocolHandler(handler1);
registry()->OnAcceptRegisterProtocolHandler(handler2);
registry()->OnAcceptRegisterProtocolHandler(handler3);
EXPECT_EQ(
std::vector<ProtocolHandler>({handler1, handler2, handler3}),
registry()->GetUserDefinedHandlers(base::Time(), base::Time::Max()));
EXPECT_EQ(
std::vector<ProtocolHandler>({handler2, handler3}),
registry()->GetUserDefinedHandlers(one_hour_ago, base::Time::Max()));
EXPECT_EQ(std::vector<ProtocolHandler>({handler1, handler2}),
registry()->GetUserDefinedHandlers(base::Time(), now));
}
TEST_F(ProtocolHandlerRegistryTest, ClearHandlersBetween) {
base::Time now = base::Time::Now();
base::Time one_hour_ago = now - base::Hours(1);
base::Time two_hours_ago = now - base::Hours(2);
GURL url("https://example.com");
ProtocolHandler handler1("bitcoin", url, two_hours_ago,
blink::ProtocolHandlerSecurityLevel::kStrict);
ProtocolHandler handler2("geo", url, one_hour_ago,
blink::ProtocolHandlerSecurityLevel::kStrict);
ProtocolHandler handler3("im", url, now,
blink::ProtocolHandlerSecurityLevel::kStrict);
ProtocolHandler ignored1("irc", url, two_hours_ago,
blink::ProtocolHandlerSecurityLevel::kStrict);
ProtocolHandler ignored2("ircs", url, one_hour_ago,
blink::ProtocolHandlerSecurityLevel::kStrict);
ProtocolHandler ignored3("magnet", url, now,
blink::ProtocolHandlerSecurityLevel::kStrict);
registry()->OnAcceptRegisterProtocolHandler(handler1);
registry()->OnAcceptRegisterProtocolHandler(handler2);
registry()->OnAcceptRegisterProtocolHandler(handler3);
registry()->OnIgnoreRegisterProtocolHandler(ignored1);
registry()->OnIgnoreRegisterProtocolHandler(ignored2);
registry()->OnIgnoreRegisterProtocolHandler(ignored3);
EXPECT_TRUE(registry()->IsHandledProtocol("bitcoin"));
EXPECT_TRUE(registry()->IsHandledProtocol("geo"));
EXPECT_TRUE(registry()->IsHandledProtocol("im"));
EXPECT_TRUE(registry()->IsIgnored(ignored1));
EXPECT_TRUE(registry()->IsIgnored(ignored2));
EXPECT_TRUE(registry()->IsIgnored(ignored3));
// Delete handler2 and ignored2.
registry()->ClearUserDefinedHandlers(one_hour_ago, now);
EXPECT_TRUE(registry()->IsHandledProtocol("bitcoin"));
EXPECT_FALSE(registry()->IsHandledProtocol("geo"));
EXPECT_TRUE(registry()->IsHandledProtocol("im"));
EXPECT_TRUE(registry()->IsIgnored(ignored1));
EXPECT_FALSE(registry()->IsIgnored(ignored2));
EXPECT_TRUE(registry()->IsIgnored(ignored3));
// Delete all.
registry()->ClearUserDefinedHandlers(base::Time(), base::Time::Max());
EXPECT_FALSE(registry()->IsHandledProtocol("bitcoin"));
EXPECT_FALSE(registry()->IsHandledProtocol("geo"));
EXPECT_FALSE(registry()->IsHandledProtocol("im"));
EXPECT_FALSE(registry()->IsIgnored(ignored1));
EXPECT_FALSE(registry()->IsIgnored(ignored2));
EXPECT_FALSE(registry()->IsIgnored(ignored3));
}
TEST_F(ProtocolHandlerRegistryTest, TestEnabledDisabled) {
registry()->Disable();
ASSERT_FALSE(registry()->enabled());
registry()->Enable();
ASSERT_TRUE(registry()->enabled());
}
TEST_F(ProtocolHandlerRegistryTest,
DisallowRegisteringExternallyHandledProtocols) {
ASSERT_TRUE(!delegate()->IsExternalHandlerRegistered("news"));
delegate()->RegisterExternalHandler("news");
ASSERT_FALSE(registry()->CanSchemeBeOverridden("news"));
}
TEST_F(ProtocolHandlerRegistryTest, RemovingHandlerMeansItCanBeAddedAgain) {
registry()->OnAcceptRegisterProtocolHandler(test_protocol_handler());
ASSERT_TRUE(registry()->CanSchemeBeOverridden("news"));
registry()->RemoveHandler(test_protocol_handler());
ASSERT_TRUE(registry()->CanSchemeBeOverridden("news"));
}
TEST_F(ProtocolHandlerRegistryTest, TestStartsAsDefault) {
registry()->OnAcceptRegisterProtocolHandler(test_protocol_handler());
ASSERT_TRUE(registry()->IsDefault(test_protocol_handler()));
}
TEST_F(ProtocolHandlerRegistryTest, TestClearDefault) {
ProtocolHandler ph1 = CreateProtocolHandler("news", "test1");
ProtocolHandler ph2 = CreateProtocolHandler("news", "test2");
registry()->OnAcceptRegisterProtocolHandler(ph1);
registry()->OnAcceptRegisterProtocolHandler(ph2);
registry()->OnAcceptRegisterProtocolHandler(ph1);
registry()->ClearDefault("news");
ASSERT_FALSE(registry()->IsDefault(ph1));
ASSERT_FALSE(registry()->IsDefault(ph2));
}
TEST_F(ProtocolHandlerRegistryTest, TestGetHandlerFor) {
ProtocolHandler ph1 = CreateProtocolHandler("news", "test1");
ProtocolHandler ph2 = CreateProtocolHandler("news", "test2");
registry()->OnAcceptRegisterProtocolHandler(ph1);
registry()->OnAcceptRegisterProtocolHandler(ph2);
registry()->OnAcceptRegisterProtocolHandler(ph2);
ASSERT_EQ(ph2, registry()->GetHandlerFor("news"));
ASSERT_TRUE(registry()->IsHandledProtocol("news"));
}
TEST_F(ProtocolHandlerRegistryTest, TestMostRecentHandlerIsDefault) {
ProtocolHandler ph1 = CreateProtocolHandler("news", "test1");
ProtocolHandler ph2 = CreateProtocolHandler("news", "test2");
registry()->OnAcceptRegisterProtocolHandler(ph1);
registry()->OnAcceptRegisterProtocolHandler(ph2);
ASSERT_FALSE(registry()->IsDefault(ph1));
ASSERT_TRUE(registry()->IsDefault(ph2));
}
TEST_F(ProtocolHandlerRegistryTest, TestOnAcceptRegisterProtocolHandler) {
ProtocolHandler ph1 = CreateProtocolHandler("news", "test1");
ProtocolHandler ph2 = CreateProtocolHandler("news", "test2");
registry()->OnAcceptRegisterProtocolHandler(ph1);
registry()->OnAcceptRegisterProtocolHandler(ph2);
registry()->OnAcceptRegisterProtocolHandler(ph1);
ASSERT_TRUE(registry()->IsDefault(ph1));
ASSERT_FALSE(registry()->IsDefault(ph2));
registry()->OnAcceptRegisterProtocolHandler(ph2);
ASSERT_FALSE(registry()->IsDefault(ph1));
ASSERT_TRUE(registry()->IsDefault(ph2));
}
TEST_F(ProtocolHandlerRegistryTest, TestDefaultSaveLoad) {
ProtocolHandler ph1 = CreateProtocolHandler("news", "test1");
ProtocolHandler ph2 = CreateProtocolHandler("news", "test2");
registry()->OnDenyRegisterProtocolHandler(ph1);
registry()->OnDenyRegisterProtocolHandler(ph2);
registry()->OnAcceptRegisterProtocolHandler(ph2);
registry()->Disable();
RecreateRegistry(true);
ASSERT_FALSE(registry()->enabled());
registry()->Enable();
ASSERT_FALSE(registry()->IsDefault(ph1));
ASSERT_TRUE(registry()->IsDefault(ph2));
RecreateRegistry(true);
ASSERT_TRUE(registry()->enabled());
}
TEST_F(ProtocolHandlerRegistryTest, TestRemoveHandler) {
ProtocolHandler ph1 = CreateProtocolHandler("news", "test1");
registry()->OnAcceptRegisterProtocolHandler(ph1);
registry()->OnAcceptRegisterProtocolHandler(ph1);
registry()->RemoveHandler(ph1);
ASSERT_FALSE(registry()->IsRegistered(ph1));
ASSERT_FALSE(registry()->IsHandledProtocol("news"));
registry()->OnIgnoreRegisterProtocolHandler(ph1);
ASSERT_FALSE(registry()->IsRegistered(ph1));
ASSERT_TRUE(registry()->IsIgnored(ph1));
registry()->RemoveHandler(ph1);
ASSERT_FALSE(registry()->IsRegistered(ph1));
ASSERT_FALSE(registry()->IsIgnored(ph1));
}
TEST_F(ProtocolHandlerRegistryTest, TestIsRegistered) {
ProtocolHandler ph1 = CreateProtocolHandler("news", "test1");
ProtocolHandler ph2 = CreateProtocolHandler("news", "test2");
registry()->OnAcceptRegisterProtocolHandler(ph1);
registry()->OnAcceptRegisterProtocolHandler(ph2);
ASSERT_TRUE(registry()->IsRegistered(ph1));
}
TEST_F(ProtocolHandlerRegistryTest, TestIsEquivalentRegistered) {
ProtocolHandler ph1 = CreateProtocolHandler("news", GURL("https://test/%s"));
ProtocolHandler ph2 = CreateProtocolHandler("news", GURL("https://test/%s"));
registry()->OnAcceptRegisterProtocolHandler(ph1);
ASSERT_TRUE(registry()->IsRegistered(ph1));
ASSERT_TRUE(registry()->HasRegisteredEquivalent(ph2));
}
TEST_F(ProtocolHandlerRegistryTest, TestSilentlyRegisterHandler) {
ProtocolHandler ph1 =
CreateProtocolHandler("news", GURL("https://test/1/%s"));
ProtocolHandler ph2 =
CreateProtocolHandler("news", GURL("https://test/2/%s"));
ProtocolHandler ph3 =
CreateProtocolHandler("ignore", GURL("https://test/%s"));
ProtocolHandler ph4 =
CreateProtocolHandler("ignore", GURL("https://test/%s"));
ASSERT_FALSE(registry()->SilentlyHandleRegisterHandlerRequest(ph1));
ASSERT_FALSE(registry()->IsRegistered(ph1));
registry()->OnAcceptRegisterProtocolHandler(ph1);
ASSERT_TRUE(registry()->IsRegistered(ph1));
ASSERT_TRUE(registry()->SilentlyHandleRegisterHandlerRequest(ph2));
ASSERT_FALSE(registry()->IsRegistered(ph1));
ASSERT_TRUE(registry()->IsRegistered(ph2));
ASSERT_FALSE(registry()->SilentlyHandleRegisterHandlerRequest(ph3));
ASSERT_FALSE(registry()->IsRegistered(ph3));
registry()->OnIgnoreRegisterProtocolHandler(ph3);
ASSERT_FALSE(registry()->IsRegistered(ph3));
ASSERT_TRUE(registry()->IsIgnored(ph3));
ASSERT_TRUE(registry()->SilentlyHandleRegisterHandlerRequest(ph4));
ASSERT_FALSE(registry()->IsRegistered(ph4));
ASSERT_TRUE(registry()->HasIgnoredEquivalent(ph4));
}
TEST_F(ProtocolHandlerRegistryTest, TestRemoveHandlerRemovesDefault) {
ProtocolHandler ph1 = CreateProtocolHandler("news", "test1");
ProtocolHandler ph2 = CreateProtocolHandler("news", "test2");
ProtocolHandler ph3 = CreateProtocolHandler("news", "test3");
registry()->OnAcceptRegisterProtocolHandler(ph1);
registry()->OnAcceptRegisterProtocolHandler(ph2);
registry()->OnAcceptRegisterProtocolHandler(ph3);
registry()->OnAcceptRegisterProtocolHandler(ph1);
registry()->RemoveHandler(ph1);
ASSERT_FALSE(registry()->IsDefault(ph1));
}
TEST_F(ProtocolHandlerRegistryTest, TestGetHandlersFor) {
ProtocolHandler ph1 = CreateProtocolHandler("news", "test1");
ProtocolHandler ph2 = CreateProtocolHandler("news", "test2");
ProtocolHandler ph3 = CreateProtocolHandler("news", "test3");
registry()->OnAcceptRegisterProtocolHandler(ph1);
registry()->OnAcceptRegisterProtocolHandler(ph2);
registry()->OnAcceptRegisterProtocolHandler(ph3);
ProtocolHandlerRegistry::ProtocolHandlerList handlers =
registry()->GetHandlersFor("news");
ASSERT_EQ(static_cast<size_t>(3), handlers.size());
ASSERT_EQ(ph3, handlers[0]);
ASSERT_EQ(ph2, handlers[1]);
ASSERT_EQ(ph1, handlers[2]);
}
TEST_F(ProtocolHandlerRegistryTest, TestGetRegisteredProtocols) {
std::vector<std::string> protocols;
registry()->GetRegisteredProtocols(&protocols);
ASSERT_EQ(static_cast<size_t>(0), protocols.size());
registry()->GetHandlersFor("news");
protocols.clear();
registry()->GetRegisteredProtocols(&protocols);
ASSERT_EQ(static_cast<size_t>(0), protocols.size());
}
TEST_F(ProtocolHandlerRegistryTest, TestIsHandledProtocol) {
registry()->GetHandlersFor("news");
ASSERT_FALSE(registry()->IsHandledProtocol("news"));
}
TEST_F(ProtocolHandlerRegistryTest, TestObserver) {
ProtocolHandler ph1 = CreateProtocolHandler("news", "test1");
ProtocolHandlerChangeListener counter(registry());
registry()->OnAcceptRegisterProtocolHandler(ph1);
ASSERT_TRUE(counter.notified());
counter.Clear();
registry()->Disable();
ASSERT_TRUE(counter.notified());
counter.Clear();
registry()->Enable();
ASSERT_TRUE(counter.notified());
counter.Clear();
registry()->RemoveHandler(ph1);
ASSERT_TRUE(counter.notified());
counter.Clear();
}
TEST_F(ProtocolHandlerRegistryTest, TestReentrantObserver) {
QueryProtocolHandlerOnChange queryer(registry());
ProtocolHandler ph1 = CreateProtocolHandler("news", "test1");
registry()->OnAcceptRegisterProtocolHandler(ph1);
ASSERT_TRUE(queryer.called());
}
TEST_F(ProtocolHandlerRegistryTest, TestProtocolsWithNoDefaultAreHandled) {
ProtocolHandler ph1 = CreateProtocolHandler("news", "test1");
registry()->OnAcceptRegisterProtocolHandler(ph1);
registry()->ClearDefault("news");
std::vector<std::string> handled_protocols;
registry()->GetRegisteredProtocols(&handled_protocols);
ASSERT_EQ(static_cast<size_t>(1), handled_protocols.size());
ASSERT_EQ("news", handled_protocols[0]);
}
TEST_F(ProtocolHandlerRegistryTest, TestDisablePreventsHandling) {
ProtocolHandler ph1 = CreateProtocolHandler("news", "test1");
registry()->OnAcceptRegisterProtocolHandler(ph1);
ASSERT_TRUE(registry()->IsHandledProtocol("news"));
registry()->Disable();
ASSERT_FALSE(registry()->IsHandledProtocol("news"));
}
TEST_F(ProtocolHandlerRegistryTest, TestOSRegistration) {
ProtocolHandler ph_do1 = CreateProtocolHandler("news", "test1");
ProtocolHandler ph_do2 = CreateProtocolHandler("news", "test2");
ProtocolHandler ph_dont = CreateProtocolHandler("im", "test3");
ASSERT_FALSE(delegate()->IsFakeRegisteredWithOS("news"));
ASSERT_FALSE(delegate()->IsFakeRegisteredWithOS("im"));
registry()->OnAcceptRegisterProtocolHandler(ph_do1);
registry()->OnDenyRegisterProtocolHandler(ph_dont);
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(delegate()->IsFakeRegisteredWithOS("news"));
ASSERT_FALSE(delegate()->IsFakeRegisteredWithOS("im"));
// This should not register with the OS, if it does the delegate
// will assert for us. We don't need to wait for the message loop
// as it should not go through to the shell worker.
registry()->OnAcceptRegisterProtocolHandler(ph_do2);
}
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
// TODO(benwells): When Linux support is more reliable and
// http://crbug.com/88255 is fixed this test will pass.
#define MAYBE_TestOSRegistrationFailure DISABLED_TestOSRegistrationFailure
#else
#define MAYBE_TestOSRegistrationFailure TestOSRegistrationFailure
#endif
TEST_F(ProtocolHandlerRegistryTest, MAYBE_TestOSRegistrationFailure) {
ProtocolHandler ph_do = CreateProtocolHandler("news", "test1");
ProtocolHandler ph_dont = CreateProtocolHandler("im", "test2");
ASSERT_FALSE(registry()->IsHandledProtocol("news"));
ASSERT_FALSE(registry()->IsHandledProtocol("im"));
registry()->OnAcceptRegisterProtocolHandler(ph_do);
base::RunLoop().RunUntilIdle();
delegate()->set_force_os_failure(true);
registry()->OnAcceptRegisterProtocolHandler(ph_dont);
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(registry()->IsHandledProtocol("news"));
ASSERT_EQ(static_cast<size_t>(1), registry()->GetHandlersFor("news").size());
ASSERT_FALSE(registry()->IsHandledProtocol("im"));
ASSERT_EQ(static_cast<size_t>(1), registry()->GetHandlersFor("im").size());
}
TEST_F(ProtocolHandlerRegistryTest, TestRemovingDefaultFallsBackToOldDefault) {
ProtocolHandler ph1 = CreateProtocolHandler("mailto", "test1");
ProtocolHandler ph2 = CreateProtocolHandler("mailto", "test2");
ProtocolHandler ph3 = CreateProtocolHandler("mailto", "test3");
registry()->OnAcceptRegisterProtocolHandler(ph1);
registry()->OnAcceptRegisterProtocolHandler(ph2);
registry()->OnAcceptRegisterProtocolHandler(ph3);
ASSERT_TRUE(registry()->IsDefault(ph3));
registry()->RemoveHandler(ph3);
ASSERT_TRUE(registry()->IsDefault(ph2));
registry()->OnAcceptRegisterProtocolHandler(ph3);
ASSERT_TRUE(registry()->IsDefault(ph3));
registry()->RemoveHandler(ph2);
ASSERT_TRUE(registry()->IsDefault(ph3));
registry()->RemoveHandler(ph3);
ASSERT_TRUE(registry()->IsDefault(ph1));
}
TEST_F(ProtocolHandlerRegistryTest, TestRemovingDefaultDoesntChangeHandlers) {
ProtocolHandler ph1 = CreateProtocolHandler("mailto", "test1");
ProtocolHandler ph2 = CreateProtocolHandler("mailto", "test2");
ProtocolHandler ph3 = CreateProtocolHandler("mailto", "test3");
registry()->OnAcceptRegisterProtocolHandler(ph1);
registry()->OnAcceptRegisterProtocolHandler(ph2);
registry()->OnAcceptRegisterProtocolHandler(ph3);
registry()->RemoveHandler(ph3);
ProtocolHandlerRegistry::ProtocolHandlerList handlers =
registry()->GetHandlersFor("mailto");
ASSERT_EQ(static_cast<size_t>(2), handlers.size());
ASSERT_EQ(ph2, handlers[0]);
ASSERT_EQ(ph1, handlers[1]);
}
TEST_F(ProtocolHandlerRegistryTest, TestReplaceHandler) {
ProtocolHandler ph1 =
CreateProtocolHandler("mailto", GURL("https://test.com/%s"));
ProtocolHandler ph2 =
CreateProtocolHandler("mailto", GURL("https://test.com/updated-url/%s"));
registry()->OnAcceptRegisterProtocolHandler(ph1);
ASSERT_TRUE(registry()->AttemptReplace(ph2));
const ProtocolHandler& handler(registry()->GetHandlerFor("mailto"));
ASSERT_EQ(handler.url(), ph2.url());
}
TEST_F(ProtocolHandlerRegistryTest, TestReplaceNonDefaultHandler) {
ProtocolHandler ph1 =
CreateProtocolHandler("mailto", GURL("https://test.com/%s"));
ProtocolHandler ph2 =
CreateProtocolHandler("mailto", GURL("https://test.com/updated-url/%s"));
ProtocolHandler ph3 =
CreateProtocolHandler("mailto", GURL("https://else.com/%s"));
registry()->OnAcceptRegisterProtocolHandler(ph1);
registry()->OnAcceptRegisterProtocolHandler(ph3);
ASSERT_TRUE(registry()->AttemptReplace(ph2));
const ProtocolHandler& handler(registry()->GetHandlerFor("mailto"));
ASSERT_EQ(handler.url(), ph3.url());
}
TEST_F(ProtocolHandlerRegistryTest, TestReplaceRemovesStaleHandlers) {
ProtocolHandler ph1 =
CreateProtocolHandler("mailto", GURL("https://test.com/%s"));
ProtocolHandler ph2 =
CreateProtocolHandler("mailto", GURL("https://test.com/updated-url/%s"));
ProtocolHandler ph3 =
CreateProtocolHandler("mailto", GURL("https://test.com/third/%s"));
registry()->OnAcceptRegisterProtocolHandler(ph1);
registry()->OnAcceptRegisterProtocolHandler(ph2);
// This should replace the previous two handlers.
ASSERT_TRUE(registry()->AttemptReplace(ph3));
const ProtocolHandler& handler(registry()->GetHandlerFor("mailto"));
ASSERT_EQ(handler.url(), ph3.url());
registry()->RemoveHandler(ph3);
ASSERT_TRUE(registry()->GetHandlerFor("mailto").IsEmpty());
}
TEST_F(ProtocolHandlerRegistryTest, TestIsSameOrigin) {
ProtocolHandler ph1 =
CreateProtocolHandler("mailto", GURL("https://test.com/%s"));
ProtocolHandler ph2 =
CreateProtocolHandler("mailto", GURL("https://test.com/updated-url/%s"));
ProtocolHandler ph3 =
CreateProtocolHandler("mailto", GURL("https://other.com/%s"));
ASSERT_EQ(ph1.url().DeprecatedGetOriginAsURL() ==
ph2.url().DeprecatedGetOriginAsURL(),
ph1.IsSameOrigin(ph2));
ASSERT_EQ(ph1.url().DeprecatedGetOriginAsURL() ==
ph2.url().DeprecatedGetOriginAsURL(),
ph2.IsSameOrigin(ph1));
ASSERT_EQ(ph2.url().DeprecatedGetOriginAsURL() ==
ph3.url().DeprecatedGetOriginAsURL(),
ph2.IsSameOrigin(ph3));
ASSERT_EQ(ph3.url().DeprecatedGetOriginAsURL() ==
ph2.url().DeprecatedGetOriginAsURL(),
ph3.IsSameOrigin(ph2));
}
TEST_F(ProtocolHandlerRegistryTest, TestInstallDefaultHandler) {
RecreateRegistry(false);
registry()->AddPredefinedHandler(
CreateProtocolHandler("news", GURL("https://test.com/%s")));
registry()->InitProtocolSettings();
std::vector<std::string> protocols;
registry()->GetRegisteredProtocols(&protocols);
ASSERT_EQ(static_cast<size_t>(1), protocols.size());
EXPECT_TRUE(registry()->IsHandledProtocol("news"));
auto handlers =
registry()->GetUserDefinedHandlers(base::Time(), base::Time::Max());
EXPECT_TRUE(handlers.empty());
registry()->ClearUserDefinedHandlers(base::Time(), base::Time::Max());
EXPECT_TRUE(registry()->IsHandledProtocol("news"));
}
#define URL_p1u1 "https://p1u1.com/%s"
#define URL_p1u2 "https://p1u2.com/%s"
#define URL_p1u3 "https://p1u3.com/%s"
#define URL_p2u1 "https://p2u1.com/%s"
#define URL_p2u2 "https://p2u2.com/%s"
#define URL_p3u1 "https://p3u1.com/%s"
TEST_F(ProtocolHandlerRegistryTest, TestPrefPolicyOverlapRegister) {
base::Value::List handlers_registered_by_pref;
base::Value::List handlers_registered_by_policy;
handlers_registered_by_pref.Append(
GetProtocolHandlerValueWithDefault("news", URL_p1u2, true));
handlers_registered_by_pref.Append(
GetProtocolHandlerValueWithDefault("news", URL_p1u1, true));
handlers_registered_by_pref.Append(
GetProtocolHandlerValueWithDefault("news", URL_p1u2, false));
handlers_registered_by_policy.Append(
GetProtocolHandlerValueWithDefault("news", URL_p1u1, false));
handlers_registered_by_policy.Append(
GetProtocolHandlerValueWithDefault("mailto", URL_p3u1, true));
GetPrefs()->SetList(custom_handlers::prefs::kRegisteredProtocolHandlers,
std::move(handlers_registered_by_pref));
GetPrefs()->SetList(custom_handlers::prefs::kPolicyRegisteredProtocolHandlers,
std::move(handlers_registered_by_policy));
registry()->InitProtocolSettings();
// Duplicate p1u2 eliminated in memory but not yet saved in pref
ProtocolHandler p1u1 = CreateProtocolHandler("news", GURL(URL_p1u1));
ProtocolHandler p1u2 = CreateProtocolHandler("news", GURL(URL_p1u2));
ASSERT_EQ(InPrefHandlerCount(), 3);
ASSERT_EQ(InMemoryHandlerCount(), 3);
ASSERT_TRUE(registry()->IsDefault(p1u1));
ASSERT_FALSE(registry()->IsDefault(p1u2));
ProtocolHandler p2u1 = CreateProtocolHandler("im", GURL(URL_p2u1));
registry()->OnDenyRegisterProtocolHandler(p2u1);
// Duplicate p1u2 saved in pref and a new handler added to pref and memory
ASSERT_EQ(InPrefHandlerCount(), 3);
ASSERT_EQ(InMemoryHandlerCount(), 4);
ASSERT_FALSE(registry()->IsDefault(p2u1));
registry()->RemoveHandler(p1u1);
// p1u1 removed from user pref but not from memory due to policy.
ASSERT_EQ(InPrefHandlerCount(), 2);
ASSERT_EQ(InMemoryHandlerCount(), 4);
ASSERT_TRUE(registry()->IsDefault(p1u1));
ProtocolHandler p3u1 = CreateProtocolHandler("mailto", GURL(URL_p3u1));
registry()->RemoveHandler(p3u1);
// p3u1 not removed from memory due to policy and it was never in pref.
ASSERT_EQ(InPrefHandlerCount(), 2);
ASSERT_EQ(InMemoryHandlerCount(), 4);
ASSERT_TRUE(registry()->IsDefault(p3u1));
registry()->RemoveHandler(p1u2);
// p1u2 removed from user pref and memory.
ASSERT_EQ(InPrefHandlerCount(), 1);
ASSERT_EQ(InMemoryHandlerCount(), 3);
ASSERT_TRUE(registry()->IsDefault(p1u1));
ProtocolHandler p1u3 = CreateProtocolHandler("news", GURL(URL_p1u3));
registry()->OnAcceptRegisterProtocolHandler(p1u3);
// p1u3 added to pref and memory.
ASSERT_EQ(InPrefHandlerCount(), 2);
ASSERT_EQ(InMemoryHandlerCount(), 4);
ASSERT_FALSE(registry()->IsDefault(p1u1));
ASSERT_TRUE(registry()->IsDefault(p1u3));
registry()->RemoveHandler(p1u3);
// p1u3 the default handler for p1 removed from user pref and memory.
ASSERT_EQ(InPrefHandlerCount(), 1);
ASSERT_EQ(InMemoryHandlerCount(), 3);
ASSERT_FALSE(registry()->IsDefault(p1u3));
ASSERT_TRUE(registry()->IsDefault(p1u1));
ASSERT_TRUE(registry()->IsDefault(p3u1));
ASSERT_FALSE(registry()->IsDefault(p2u1));
}
TEST_F(ProtocolHandlerRegistryTest, TestPrefPolicyOverlapIgnore) {
base::Value::List handlers_ignored_by_pref;
base::Value::List handlers_ignored_by_policy;
handlers_ignored_by_pref.Append(GetProtocolHandlerValue("news", URL_p1u1));
handlers_ignored_by_pref.Append(GetProtocolHandlerValue("news", URL_p1u2));
handlers_ignored_by_pref.Append(GetProtocolHandlerValue("news", URL_p1u2));
handlers_ignored_by_pref.Append(GetProtocolHandlerValue("mailto", URL_p3u1));
handlers_ignored_by_policy.Append(GetProtocolHandlerValue("news", URL_p1u2));
handlers_ignored_by_policy.Append(GetProtocolHandlerValue("news", URL_p1u3));
handlers_ignored_by_policy.Append(GetProtocolHandlerValue("im", URL_p2u1));
GetPrefs()->SetList(custom_handlers::prefs::kIgnoredProtocolHandlers,
std::move(handlers_ignored_by_pref));
GetPrefs()->SetList(custom_handlers::prefs::kPolicyIgnoredProtocolHandlers,
std::move(handlers_ignored_by_policy));
registry()->InitProtocolSettings();
// Duplicate p1u2 eliminated in memory but not yet saved in pref
ASSERT_EQ(InPrefIgnoredHandlerCount(), 4);
ASSERT_EQ(InMemoryIgnoredHandlerCount(), 5);
ProtocolHandler p2u2 = CreateProtocolHandler("im", GURL(URL_p2u2));
registry()->OnIgnoreRegisterProtocolHandler(p2u2);
// Duplicate p1u2 eliminated in pref, p2u2 added to pref and memory.
ASSERT_EQ(InPrefIgnoredHandlerCount(), 4);
ASSERT_EQ(InMemoryIgnoredHandlerCount(), 6);
ProtocolHandler p2u1 = CreateProtocolHandler("im", GURL(URL_p2u1));
registry()->RemoveIgnoredHandler(p2u1);
// p2u1 installed by policy so cant be removed.
ASSERT_EQ(InPrefIgnoredHandlerCount(), 4);
ASSERT_EQ(InMemoryIgnoredHandlerCount(), 6);
ProtocolHandler p1u2 = CreateProtocolHandler("news", GURL(URL_p1u2));
registry()->RemoveIgnoredHandler(p1u2);
// p1u2 installed by policy and pref so it is removed from pref and not from
// memory.
ASSERT_EQ(InPrefIgnoredHandlerCount(), 3);
ASSERT_EQ(InMemoryIgnoredHandlerCount(), 6);
ProtocolHandler p1u1 = CreateProtocolHandler("news", GURL(URL_p1u1));
registry()->RemoveIgnoredHandler(p1u1);
// p1u1 installed by pref so it is removed from pref and memory.
ASSERT_EQ(InPrefIgnoredHandlerCount(), 2);
ASSERT_EQ(InMemoryIgnoredHandlerCount(), 5);
registry()->RemoveIgnoredHandler(p2u2);
// p2u2 installed by user so it is removed from pref and memory.
ASSERT_EQ(InPrefIgnoredHandlerCount(), 1);
ASSERT_EQ(InMemoryIgnoredHandlerCount(), 4);
registry()->OnIgnoreRegisterProtocolHandler(p2u1);
// p2u1 installed by user but it is already installed by policy, so it is
// added to pref.
ASSERT_EQ(InPrefIgnoredHandlerCount(), 2);
ASSERT_EQ(InMemoryIgnoredHandlerCount(), 4);
registry()->RemoveIgnoredHandler(p2u1);
// p2u1 installed by user and policy, so it is removed from pref alone.
ASSERT_EQ(InPrefIgnoredHandlerCount(), 1);
ASSERT_EQ(InMemoryIgnoredHandlerCount(), 4);
}
TEST_F(ProtocolHandlerRegistryTest, TestURIPercentEncoding) {
ProtocolHandler ph =
CreateProtocolHandler("web+custom", GURL("https://test.com/url=%s"));
registry()->OnAcceptRegisterProtocolHandler(ph);
// Normal case.
GURL translated_url = ph.TranslateUrl(GURL("web+custom://custom/handler"));
ASSERT_EQ(translated_url,
GURL("https://test.com/url=web%2Bcustom%3A%2F%2Fcustom%2Fhandler"));
// Percent-encoding.
translated_url = ph.TranslateUrl(GURL("web+custom://custom/%20handler"));
ASSERT_EQ(
translated_url,
GURL("https://test.com/url=web%2Bcustom%3A%2F%2Fcustom%2F%2520handler"));
// Space character.
translated_url = ph.TranslateUrl(GURL("web+custom://custom handler"));
ASSERT_EQ(translated_url,
GURL("https://test.com/url=web%2Bcustom%3A%2F%2Fcustom%20handler"));
// Query parameters.
translated_url = ph.TranslateUrl(GURL("web+custom://custom?foo=bar&bar=baz"));
ASSERT_EQ(translated_url,
GURL("https://test.com/"
"url=web%2Bcustom%3A%2F%2Fcustom%3Ffoo%3Dbar%26bar%3Dbaz"));
// Non-ASCII characters.
translated_url = ph.TranslateUrl(GURL("web+custom://custom/<>`{}#?\"'😂"));
ASSERT_EQ(translated_url, GURL("https://test.com/"
"url=web%2Bcustom%3A%2F%2Fcustom%2F%3C%3E%60%"
"7B%7D%23%3F%2522'%25F0%259F%2598%2582"));
// ASCII characters from the C0 controls percent-encode set.
// GURL constructor encodes U+001F and U+007F as "%1F" and "%7F" first,
// Then the protocol handler translator encodes them to "%25%1F" and "%25%7F"
// again. That's why the expected result has double-encoded URL.
translated_url = ph.TranslateUrl(GURL("web+custom://custom/\x1fhandler"));
ASSERT_EQ(
translated_url,
GURL("https://test.com/url=web%2Bcustom%3A%2F%2Fcustom%2F%251Fhandler"));
translated_url = ph.TranslateUrl(GURL("web+custom://custom/\x7Fhandler"));
ASSERT_EQ(
translated_url,
GURL("https://test.com/url=web%2Bcustom%3A%2F%2Fcustom%2F%257Fhandler"));
// Path percent-encode set.
translated_url =
ph.TranslateUrl(GURL("web+custom://custom/handler=#download"));
ASSERT_EQ(translated_url,
GURL("https://test.com/"
"url=web%2Bcustom%3A%2F%2Fcustom%2Fhandler%3D%23download"));
// Userinfo percent-encode set.
translated_url = ph.TranslateUrl(GURL("web+custom://custom/handler:@id="));
ASSERT_EQ(translated_url,
GURL("https://test.com/"
"url=web%2Bcustom%3A%2F%2Fcustom%2Fhandler%3A%40id%3D"));
}
TEST_F(ProtocolHandlerRegistryTest, TestMultiplePlaceholders) {
ProtocolHandler ph =
CreateProtocolHandler("news", GURL("https://example.com/%s/url=%s"));
registry()->OnAcceptRegisterProtocolHandler(ph);
GURL translated_url = ph.TranslateUrl(GURL("test:duplicated_placeholders"));
// When URL contains multiple placeholders, only the first placeholder should
// be changed to the given URL.
ASSERT_EQ(translated_url,
GURL("https://example.com/test%3Aduplicated_placeholders/url=%s"));
}
TEST_F(ProtocolHandlerRegistryTest, InvalidHandlers) {
// Invalid protocol.
registry()->OnAcceptRegisterProtocolHandler(
CreateProtocolHandler("foo", GURL("https://www.google.com/handler%s")));
ASSERT_FALSE(registry()->IsHandledProtocol("foo"));
registry()->OnAcceptRegisterProtocolHandler(
CreateProtocolHandler("web", GURL("https://www.google.com/handler%s")));
ASSERT_FALSE(registry()->IsHandledProtocol("web"));
registry()->OnAcceptRegisterProtocolHandler(
CreateProtocolHandler("web+", GURL("https://www.google.com/handler%s")));
ASSERT_FALSE(registry()->IsHandledProtocol("web+"));
registry()->OnAcceptRegisterProtocolHandler(
CreateProtocolHandler("https", GURL("https://www.google.com/handler%s")));
ASSERT_FALSE(registry()->IsHandledProtocol("https"));
EXPECT_FALSE(ProtocolHandlerCanRegisterProtocol(
"ext+", GURL("https://www.google.com/handler%s"),
blink::ProtocolHandlerSecurityLevel::kStrict));
EXPECT_FALSE(ProtocolHandlerCanRegisterProtocol(
"ext+foo", GURL("https://www.google.com/handler%s"),
blink::ProtocolHandlerSecurityLevel::kStrict));
// Invalid handler URL.
// data: URL.
registry()->OnAcceptRegisterProtocolHandler(CreateProtocolHandler(
"news",
GURL("data:text/html,<html><body><b>hello world</b></body></html>%s")));
ASSERT_FALSE(registry()->IsHandledProtocol("news"));
// ftp:// URL.
registry()->OnAcceptRegisterProtocolHandler(
CreateProtocolHandler("news", GURL("ftp://www.google.com/handler%s")));
ASSERT_FALSE(registry()->IsHandledProtocol("news"));
// blob:// URL
registry()->OnAcceptRegisterProtocolHandler(CreateProtocolHandler(
"news", GURL("blob:https://www.google.com/"
"f2d8c47d-17d0-4bf5-8f0a-76e42cbed3bf/%s")));
ASSERT_FALSE(registry()->IsHandledProtocol("news"));
// http:// URL
registry()->OnAcceptRegisterProtocolHandler(
CreateProtocolHandler("news", GURL("http://www.google.com/handler%s")));
ASSERT_FALSE(registry()->IsHandledProtocol("news"));
// filesystem:// URL
registry()->OnAcceptRegisterProtocolHandler(CreateProtocolHandler(
"news", GURL("filesystem:https://www.google.com/"
"f2d8c47d-17d0-4bf5-8f0a-76e42cbed3bf/%s")));
ASSERT_FALSE(registry()->IsHandledProtocol("news"));
}
// See
// https://html.spec.whatwg.org/multipage/system-state.html#normalize-protocol-handler-parameters
TEST_F(ProtocolHandlerRegistryTest, WebPlusPrefix) {
// Not ASCII alphas.
registry()->OnAcceptRegisterProtocolHandler(CreateProtocolHandler(
"web+***", GURL("https://www.google.com/handler%s")));
ASSERT_FALSE(registry()->IsHandledProtocol("web+***"));
registry()->OnAcceptRegisterProtocolHandler(CreateProtocolHandler(
"web+123", GURL("https://www.google.com/handler%s")));
ASSERT_FALSE(registry()->IsHandledProtocol("web+123"));
registry()->OnAcceptRegisterProtocolHandler(CreateProtocolHandler(
"web+ ", GURL("https://www.google.com/handler%s")));
ASSERT_FALSE(registry()->IsHandledProtocol("web+ "));
registry()->OnAcceptRegisterProtocolHandler(CreateProtocolHandler(
"web+name123", GURL("https://www.google.com/handler%s")));
ASSERT_FALSE(registry()->IsHandledProtocol("web+name123"));
// ASCII lower alphas.
registry()->OnAcceptRegisterProtocolHandler(
CreateProtocolHandler("web+abcdefghijklmnopqrstuvwxyz",
GURL("https://www.google.com/handler%s")));
ASSERT_TRUE(registry()->IsHandledProtocol("web+abcdefghijklmnopqrstuvwxyz"));
// ASCII upper alphas are lowercased.
registry()->OnAcceptRegisterProtocolHandler(
CreateProtocolHandler("web+ZYXWVUTSRQPONMLKJIHGFEDCBA",
GURL("https://www.google.com/handler%s")));
ASSERT_TRUE(registry()->IsHandledProtocol("web+zyxwvutsrqponmlkjihgfedcba"));
}
TEST_F(ProtocolHandlerRegistryTest, ProtocolHandlerSecurityLevels) {
GURL https_handler_url("https://www.google.com/handler%s");
// Invalid protocol.
EXPECT_FALSE(ProtocolHandlerCanRegisterProtocol(
"foo", https_handler_url,
blink::ProtocolHandlerSecurityLevel::kExtensionFeatures));
EXPECT_FALSE(ProtocolHandlerCanRegisterProtocol(
"web", https_handler_url,
blink::ProtocolHandlerSecurityLevel::kExtensionFeatures));
EXPECT_FALSE(ProtocolHandlerCanRegisterProtocol(
"web+", https_handler_url,
blink::ProtocolHandlerSecurityLevel::kExtensionFeatures));
EXPECT_FALSE(ProtocolHandlerCanRegisterProtocol(
"https", https_handler_url,
blink::ProtocolHandlerSecurityLevel::kExtensionFeatures));
EXPECT_FALSE(ProtocolHandlerCanRegisterProtocol(
"ext+", https_handler_url,
blink::ProtocolHandlerSecurityLevel::kExtensionFeatures));
EXPECT_FALSE(ProtocolHandlerCanRegisterProtocol(
"ext+foo", https_handler_url,
blink::ProtocolHandlerSecurityLevel::kUntrustedOrigins));
// Invalid handler URL.
// data: URL.
EXPECT_FALSE(ProtocolHandlerCanRegisterProtocol(
"news",
GURL("data:text/html,<html><body><b>hello "
"world</b></body></html>%s"),
blink::ProtocolHandlerSecurityLevel::kExtensionFeatures));
// ftp:// URL.
EXPECT_FALSE(ProtocolHandlerCanRegisterProtocol(
"news", GURL("ftp://www.google.com/handler%s"),
blink::ProtocolHandlerSecurityLevel::kExtensionFeatures));
// blob:// URL
EXPECT_FALSE(ProtocolHandlerCanRegisterProtocol(
"news",
GURL("blob:https://www.google.com/"
"f2d8c47d-17d0-4bf5-8f0a-76e42cbed3bf/%s"),
blink::ProtocolHandlerSecurityLevel::kExtensionFeatures));
// http:// URL
EXPECT_FALSE(ProtocolHandlerCanRegisterProtocol(
"news", GURL("http://www.google.com/handler%s"),
blink::ProtocolHandlerSecurityLevel::kExtensionFeatures));
// filesystem:// URL
EXPECT_FALSE(ProtocolHandlerCanRegisterProtocol(
"news",
GURL("filesystem:https://www.google.com/"
"f2d8c47d-17d0-4bf5-8f0a-76e42cbed3bf/%s"),
blink::ProtocolHandlerSecurityLevel::kExtensionFeatures));
// ext+foo scheme.
EXPECT_TRUE(ProtocolHandlerCanRegisterProtocol(
"ext+foo", https_handler_url,
blink::ProtocolHandlerSecurityLevel::kExtensionFeatures));
}
namespace {
enum class ProtocolTestMode {
kFtpOffPaytoOn,
kFtpOnPaytoOff,
kFtpOnPaytoOn,
kFtpOffPaytoOff,
};
} // namespace
class ProtocolHandlerRegistrySchemeTest
: public ProtocolHandlerRegistryTest,
public ::testing::WithParamInterface<ProtocolTestMode> {
public:
~ProtocolHandlerRegistrySchemeTest() override = default;
private:
void SetUp() override {
ProtocolHandlerRegistryTest::SetUp();
switch (GetParam()) {
case ProtocolTestMode::kFtpOffPaytoOn:
scoped_feature_list_.InitWithFeatures(
{blink::features::kSafelistPaytoToRegisterProtocolHandler},
{blink::features::kSafelistFTPToRegisterProtocolHandler});
break;
case ProtocolTestMode::kFtpOnPaytoOff:
scoped_feature_list_.InitWithFeatures(
{blink::features::kSafelistFTPToRegisterProtocolHandler},
{blink::features::kSafelistPaytoToRegisterProtocolHandler});
break;
case ProtocolTestMode::kFtpOnPaytoOn:
scoped_feature_list_.InitWithFeatures(
{blink::features::kSafelistFTPToRegisterProtocolHandler,
blink::features::kSafelistPaytoToRegisterProtocolHandler}, {});
break;
case ProtocolTestMode::kFtpOffPaytoOff:
default:
scoped_feature_list_.InitWithFeatures({},
{blink::features::kSafelistFTPToRegisterProtocolHandler,
blink::features::kSafelistPaytoToRegisterProtocolHandler});
break;
}
}
base::test::ScopedFeatureList scoped_feature_list_;
};
INSTANTIATE_TEST_SUITE_P(All,
ProtocolHandlerRegistrySchemeTest,
testing::Values(ProtocolTestMode::kFtpOffPaytoOn,
ProtocolTestMode::kFtpOnPaytoOff,
ProtocolTestMode::kFtpOnPaytoOn,
ProtocolTestMode::kFtpOffPaytoOff));
// See
// https://html.spec.whatwg.org/multipage/system-state.html#safelisted-scheme
TEST_P(ProtocolHandlerRegistrySchemeTest, SafelistedSchemes) {
const std::string kSchemes[] = {
"bitcoin", "cabal", "dat", "did", "doi", "dweb",
"ethereum", "geo", "hyper", "im", "ipfs", "ipns",
"irc", "ircs", "magnet", "mailto", "mms", "news",
"nntp", "openpgp4fpr", "sip", "sms", "smsto", "ssb",
"ssh", "tel", "urn", "webcal", "wtai", "xmpp"};
const std::string kFtpSchemes[] = {"ftp", "ftps", "sftp"};
const std::string kPaytoScheme = "payto";
for (auto& scheme : kSchemes) {
registry()->OnAcceptRegisterProtocolHandler(
CreateProtocolHandler(scheme, GURL("https://example.com/url=%s")));
ASSERT_TRUE(registry()->IsHandledProtocol(scheme));
}
for (auto& scheme : kFtpSchemes) {
registry()->OnAcceptRegisterProtocolHandler(
CreateProtocolHandler(scheme, GURL("https://example.com/url=%s")));
if (GetParam() == ProtocolTestMode::kFtpOnPaytoOff ||
GetParam() == ProtocolTestMode::kFtpOnPaytoOn) {
ASSERT_TRUE(registry()->IsHandledProtocol(scheme));
} else {
ASSERT_FALSE(registry()->IsHandledProtocol(scheme));
}
}
registry()->OnAcceptRegisterProtocolHandler(
CreateProtocolHandler(kPaytoScheme, GURL("https://example.com/url=%s")));
if (GetParam() == ProtocolTestMode::kFtpOffPaytoOn ||
GetParam() == ProtocolTestMode::kFtpOnPaytoOn) {
ASSERT_TRUE(registry()->IsHandledProtocol(kPaytoScheme));
} else {
ASSERT_FALSE(registry()->IsHandledProtocol(kPaytoScheme));
}
}
namespace {
enum class CredentialsTestMode {
kStripCredentials,
kKeepCredentials,
};
} // namespace
class ProtocolHandlerRegistryCredentialsTest
: public ProtocolHandlerRegistryTest,
public ::testing::WithParamInterface<CredentialsTestMode> {
public:
~ProtocolHandlerRegistryCredentialsTest() override = default;
private:
void SetUp() override {
ProtocolHandlerRegistryTest::SetUp();
if (GetParam() == CredentialsTestMode::kStripCredentials) {
scoped_feature_list_.InitAndEnableFeature(
features::kStripCredentialsForExternalProtocolHandler);
} else {
scoped_feature_list_.InitAndDisableFeature(
features::kStripCredentialsForExternalProtocolHandler);
}
}
base::test::ScopedFeatureList scoped_feature_list_;
};
INSTANTIATE_TEST_SUITE_P(
All,
ProtocolHandlerRegistryCredentialsTest,
testing::Values(CredentialsTestMode::kStripCredentials,
CredentialsTestMode::kKeepCredentials));
// See
// https://html.spec.whatwg.org/multipage/system-state.html#security-and-privacy
// guidance on mitigating credential leaks.
TEST_P(ProtocolHandlerRegistryCredentialsTest,
NoCredentialsForStandardSchemes) {
ProtocolHandler ph =
CreateProtocolHandler("ftp", GURL("https://example.com/url=%s"));
registry()->OnAcceptRegisterProtocolHandler(ph);
EXPECT_EQ(ph.TranslateUrl(GURL("ftp://example/y")),
GURL("https://example.com/url=ftp%3A%2F%2Fexample%2Fy"));
if (GetParam() == CredentialsTestMode::kStripCredentials) {
EXPECT_EQ(ph.TranslateUrl(GURL("ftp://user@example/y")),
GURL("https://example.com/url=ftp%3A%2F%2Fexample%2Fy"));
EXPECT_EQ(ph.TranslateUrl(GURL("ftp://:password@example/y")),
GURL("https://example.com/url=ftp%3A%2F%2Fexample%2Fy"));
EXPECT_EQ(ph.TranslateUrl(GURL("ftp://user:password@example/y")),
GURL("https://example.com/url=ftp%3A%2F%2Fexample%2Fy"));
EXPECT_EQ(ph.TranslateUrl(GURL("ftp://user:password@example/y#ref")),
GURL("https://example.com/url=ftp%3A%2F%2Fexample%2Fy%23ref"));
} else {
EXPECT_EQ(ph.TranslateUrl(GURL("ftp://user@example/y")),
GURL("https://example.com/url=ftp%3A%2F%2Fuser%40example%2Fy"));
EXPECT_EQ(
ph.TranslateUrl(GURL("ftp://:password@example/y")),
GURL("https://example.com/url=ftp%3A%2F%2F%3Apassword%40example%2Fy"));
EXPECT_EQ(ph.TranslateUrl(GURL("ftp://user:password@example/y")),
GURL("https://example.com/"
"url=ftp%3A%2F%2Fuser%3Apassword%40example%2Fy"));
EXPECT_EQ(ph.TranslateUrl(GURL("ftp://user:password@example/y#ref")),
GURL("https://example.com/"
"url=ftp%3A%2F%2Fuser%3Apassword%40example%2Fy%23ref"));
}
}
TEST_F(ProtocolHandlerRegistryTest, CredentialsForNonStandardSchemes) {
ProtocolHandler ph =
CreateProtocolHandler("web+bool", GURL("https://example.com/url=%s"));
registry()->OnAcceptRegisterProtocolHandler(ph);
EXPECT_EQ(ph.TranslateUrl(GURL("web+bool://user:password@example/y")),
GURL("https://example.com/"
"url=web%2Bbool%3A%2F%2Fuser%3Apassword%40example%2Fy"));
}
} // namespace custom_handlers