blob: 6ba7838f95fedf0ef25e62943468775b4eac4c91 [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/permissions/permission_manager_factory.h"
#include "chrome/browser/search_engines/ui_thread_search_terms_data.h"
#include "chrome/common/url_constants.h"
#include "chrome/common/webui_url_constants.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile.h"
#include "components/permissions/features.h"
#include "components/permissions/permission_manager.h"
#include "components/permissions/permission_util.h"
#include "components/permissions/permissions_client.h"
#include "components/permissions/test/permission_test_util.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/permission_controller.h"
#include "content/public/browser/permission_descriptor_util.h"
#include "content/public/test/navigation_simulator.h"
#include "content/public/test/permissions_test_utils.h"
#include "extensions/buildflags/buildflags.h"
#include "services/network/public/cpp/permissions_policy/permissions_policy_declaration.h"
#include "services/network/public/mojom/permissions_policy/permissions_policy_feature.mojom-shared.h"
#include "testing/gtest/include/gtest/gtest.h"
class ChromePermissionManagerTest : public ChromeRenderViewHostTestHarness {
public:
void SetUp() override {
TestingBrowserProcess::GetGlobal()->CreateGlobalFeaturesForTesting();
ChromeRenderViewHostTestHarness::SetUp();
profile()->SetPermissionControllerDelegate(
permissions::GetPermissionControllerDelegate(GetBrowserContext()));
}
void OnPermissionChange(PermissionStatus permission) {
if (quit_closure_) {
std::move(quit_closure_).Run();
}
callback_called_ = true;
callback_result_ = permission;
}
bool callback_called() const { return callback_called_; }
PermissionStatus callback_result() const { return callback_result_; }
content::RenderFrameHost* AddChildRFH(
content::RenderFrameHost* parent,
const GURL& origin,
network::mojom::PermissionsPolicyFeature feature =
network::mojom::PermissionsPolicyFeature::kNotFound) {
network::ParsedPermissionsPolicy frame_policy = {};
if (feature != network::mojom::PermissionsPolicyFeature::kNotFound) {
frame_policy.emplace_back(
feature,
std::vector{*network::OriginWithPossibleWildcards::FromOrigin(
url::Origin::Create(origin))},
/*self_if_matches=*/std::nullopt,
/*matches_all_origins=*/false,
/*matches_opaque_src=*/false);
}
content::RenderFrameHost* result =
content::RenderFrameHostTester::For(parent)->AppendChildWithPolicy(
"", frame_policy);
content::RenderFrameHostTester::For(result)
->InitializeRenderFrameIfNeeded();
SimulateNavigation(&result, origin);
return result;
}
void SimulateNavigation(content::RenderFrameHost** rfh, const GURL& url) {
auto navigation_simulator =
content::NavigationSimulator::CreateRendererInitiated(url, *rfh);
navigation_simulator->Commit();
*rfh = navigation_simulator->GetFinalRenderFrameHost();
}
void SetPermission(const GURL& url,
blink::PermissionType permission,
PermissionStatus status) {
permissions::PermissionsClient::Get()
->GetSettingsMap(GetBrowserContext())
->SetContentSettingDefaultScope(
url, url,
permissions::PermissionUtil::PermissionTypeToContentSettingsType(
permission),
permissions::PermissionUtil::PermissionStatusToContentSetting(
status));
}
private:
bool callback_called_ = false;
PermissionStatus callback_result_ = PermissionStatus::ASK;
base::OnceClosure quit_closure_;
};
TEST_F(ChromePermissionManagerTest, GetCanonicalOriginSearch) {
const GURL google_com("https://www.google.com");
const GURL google_de("https://www.google.de");
const GURL other_url("https://other.url");
const GURL google_base = GURL(UIThreadSearchTermsData().GoogleBaseURLValue())
.DeprecatedGetOriginAsURL();
const GURL remote_ntp = GURL(std::string("chrome-search://") +
chrome::kChromeSearchRemoteNtpHost);
const GURL other_chrome_search = GURL("chrome-search://not-local-ntp");
const GURL top_level_ntp(chrome::kChromeUINewTabURL);
const GURL webui_ntp = GURL(chrome::kChromeUINewTabPageURL);
// "Normal" URLs are not affected by GetCanonicalOrigin.
EXPECT_EQ(google_com,
permissions::PermissionUtil::GetCanonicalOrigin(
ContentSettingsType::GEOLOCATION, google_com, google_com));
EXPECT_EQ(google_de,
permissions::PermissionUtil::GetCanonicalOrigin(
ContentSettingsType::GEOLOCATION, google_de, google_de));
EXPECT_EQ(other_url,
permissions::PermissionUtil::GetCanonicalOrigin(
ContentSettingsType::GEOLOCATION, other_url, other_url));
EXPECT_EQ(google_base,
permissions::PermissionUtil::GetCanonicalOrigin(
ContentSettingsType::GEOLOCATION, google_base, google_base));
// The WebUI NTP URL gets mapped to the Google base URL.
EXPECT_EQ(google_base,
permissions::PermissionUtil::GetCanonicalOrigin(
ContentSettingsType::GEOLOCATION, webui_ntp, top_level_ntp));
// chrome-search://remote-ntp and other URLs are not affected.
EXPECT_EQ(remote_ntp,
permissions::PermissionUtil::GetCanonicalOrigin(
ContentSettingsType::GEOLOCATION, remote_ntp, top_level_ntp));
EXPECT_EQ(google_com,
permissions::PermissionUtil::GetCanonicalOrigin(
ContentSettingsType::GEOLOCATION, google_com, top_level_ntp));
EXPECT_EQ(other_chrome_search,
permissions::PermissionUtil::GetCanonicalOrigin(
ContentSettingsType::GEOLOCATION, other_chrome_search,
top_level_ntp));
}
TEST_F(ChromePermissionManagerTest, GetCanonicalOriginPermissionDelegation) {
const GURL requesting_origin("https://www.requesting.com");
const GURL embedding_origin("https://www.google.de");
// The embedding origin should be returned except in the case of notifications
// and, if they're enabled, extensions.
EXPECT_EQ(embedding_origin, permissions::PermissionUtil::GetCanonicalOrigin(
ContentSettingsType::GEOLOCATION,
requesting_origin, embedding_origin));
EXPECT_EQ(requesting_origin, permissions::PermissionUtil::GetCanonicalOrigin(
ContentSettingsType::NOTIFICATIONS,
requesting_origin, embedding_origin));
#if BUILDFLAG(ENABLE_EXTENSIONS)
const GURL extensions_requesting_origin(
"chrome-extension://abcdefghijklmnopqrstuvxyz");
EXPECT_EQ(extensions_requesting_origin,
permissions::PermissionUtil::GetCanonicalOrigin(
ContentSettingsType::GEOLOCATION, extensions_requesting_origin,
embedding_origin));
#endif
}
TEST_F(ChromePermissionManagerTest, SubscribeWithPermissionDelegation) {
const char* kOrigin1 = "https://example.com";
const char* kOrigin2 = "https://google.com";
const GURL url1 = GURL(kOrigin1);
const GURL url2 = GURL(kOrigin2);
const auto geolocation_permission_descriptor = content::
PermissionDescriptorUtil::CreatePermissionDescriptorForPermissionType(
blink::PermissionType::GEOLOCATION);
NavigateAndCommit(url1);
content::RenderFrameHost* parent = main_rfh();
content::RenderFrameHost* child = AddChildRFH(parent, url2);
content::PermissionController* permission_controller =
GetBrowserContext()->GetPermissionController();
content::PermissionController::SubscriptionId subscription_id =
content::SubscribeToPermissionStatusChange(
permission_controller, blink::PermissionType::GEOLOCATION,
/*render_process_host=*/nullptr, child, url2,
/*should_include_device_status=*/false,
base::BindRepeating(&ChromePermissionManagerTest::OnPermissionChange,
base::Unretained(this)));
EXPECT_FALSE(callback_called());
// Location should be blocked for the child because it's not delegated.
EXPECT_EQ(PermissionStatus::DENIED,
permission_controller->GetPermissionStatusForCurrentDocument(
geolocation_permission_descriptor, child));
// Allow access for the top level origin.
SetPermission(url1, blink::PermissionType::GEOLOCATION,
PermissionStatus::GRANTED);
EXPECT_EQ(PermissionStatus::GRANTED,
permission_controller->GetPermissionStatusForCurrentDocument(
geolocation_permission_descriptor, parent));
// The child's permission should still be block and no callback should be run.
EXPECT_EQ(PermissionStatus::DENIED,
permission_controller->GetPermissionStatusForCurrentDocument(
geolocation_permission_descriptor, child));
EXPECT_FALSE(callback_called());
// Enabling geolocation by FP should allow the child to request access also.
child = AddChildRFH(parent, url2,
network::mojom::PermissionsPolicyFeature::kGeolocation);
EXPECT_EQ(PermissionStatus::GRANTED,
permission_controller->GetPermissionStatusForCurrentDocument(
geolocation_permission_descriptor, child));
permission_controller->UnsubscribeFromPermissionStatusChange(subscription_id);
}