1 // Copyright 2014 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "components/permissions/permission_context_base.h"
14 #include "base/feature_list.h"
15 #include "base/functional/bind.h"
16 #include "base/metrics/field_trial.h"
17 #include "base/metrics/field_trial_params.h"
18 #include "base/run_loop.h"
19 #include "base/test/metrics/histogram_tester.h"
20 #include "base/test/scoped_feature_list.h"
21 #include "build/build_config.h"
22 #include "build/chromeos_buildflags.h"
23 #include "components/content_settings/core/browser/content_settings_uma_util.h"
24 #include "components/content_settings/core/browser/host_content_settings_map.h"
25 #include "components/content_settings/core/common/content_settings.h"
26 #include "components/content_settings/core/common/content_settings_types.h"
27 #include "components/permissions/features.h"
28 #include "components/permissions/permission_decision_auto_blocker.h"
29 #include "components/permissions/permission_request_id.h"
30 #include "components/permissions/permission_request_manager.h"
31 #include "components/permissions/permission_uma_util.h"
32 #include "components/permissions/permission_util.h"
33 #include "components/permissions/test/mock_permission_prompt_factory.h"
34 #include "components/permissions/test/test_permissions_client.h"
35 #include "components/ukm/content/source_url_recorder.h"
36 #include "components/ukm/test_ukm_recorder.h"
37 #include "content/public/browser/browser_thread.h"
38 #include "content/public/browser/navigation_entry.h"
39 #include "content/public/browser/permission_result.h"
40 #include "content/public/browser/render_frame_host.h"
41 #include "content/public/browser/web_contents.h"
42 #include "content/public/test/mock_render_process_host.h"
43 #include "content/public/test/test_renderer_host.h"
44 #include "testing/gtest/include/gtest/gtest.h"
45 #include "third_party/blink/public/mojom/permissions_policy/permissions_policy.mojom.h"
46 #include "url/url_util.h"
48 namespace permissions {
50 using PermissionStatus = blink::mojom::PermissionStatus;
52 const char* const kPermissionsKillSwitchFieldStudy =
53 PermissionContextBase::kPermissionsKillSwitchFieldStudy;
54 const char* const kPermissionsKillSwitchBlockedValue =
55 PermissionContextBase::kPermissionsKillSwitchBlockedValue;
56 const char kPermissionsKillSwitchTestGroup[] = "TestGroup";
58 class TestPermissionContext : public PermissionContextBase {
60 TestPermissionContext(content::BrowserContext* browser_context,
61 const ContentSettingsType content_settings_type)
62 : PermissionContextBase(
64 content_settings_type,
65 blink::mojom::PermissionsPolicyFeature::kNotFound),
66 tab_context_updated_(false) {}
68 TestPermissionContext(const TestPermissionContext&) = delete;
69 TestPermissionContext& operator=(const TestPermissionContext&) = delete;
71 ~TestPermissionContext() override {}
73 const std::vector<ContentSetting>& decisions() const { return decisions_; }
75 bool tab_context_updated() const { return tab_context_updated_; }
77 // Once a decision for the requested permission has been made, run the
79 void TrackPermissionDecision(ContentSetting content_setting) {
80 decisions_.push_back(content_setting);
81 // Null check required here as the quit_closure_ can also be run and reset
82 // first from within DecidePermission.
84 std::move(quit_closure_).Run();
88 ContentSetting GetContentSettingFromMap(const GURL& url_a,
90 auto* map = PermissionsClient::Get()->GetSettingsMap(browser_context());
91 return map->GetContentSetting(url_a.DeprecatedGetOriginAsURL(),
92 url_b.DeprecatedGetOriginAsURL(),
93 content_settings_type());
96 void RequestPermission(PermissionRequestData request_data,
97 BrowserPermissionCallback callback) override {
98 base::RunLoop run_loop;
99 quit_closure_ = run_loop.QuitClosure();
100 PermissionContextBase::RequestPermission(std::move(request_data),
101 std::move(callback));
105 void DecidePermission(PermissionRequestData request_data,
106 BrowserPermissionCallback callback) override {
107 PermissionContextBase::DecidePermission(std::move(request_data),
108 std::move(callback));
109 if (respond_permission_) {
110 std::move(respond_permission_).Run();
112 // Stop the run loop from spinning indefinitely if no response callback
113 // has been set, as is the case with TestParallelRequests.
114 std::move(quit_closure_).Run();
118 // Set the callback to run if the permission is being responded to in the
119 // test. This is left empty where no response is needed, such as in parallel
120 // requests, invalid origin, and killswitch.
121 void SetRespondPermissionCallback(base::OnceClosure callback) {
122 respond_permission_ = std::move(callback);
126 void UpdateTabContext(const PermissionRequestID& id,
127 const GURL& requesting_origin,
128 bool allowed) override {
129 tab_context_updated_ = true;
132 bool IsRestrictedToSecureOrigins() const override { return false; }
135 std::vector<ContentSetting> decisions_;
136 bool tab_context_updated_;
137 base::OnceClosure quit_closure_;
138 // Callback for responding to a permission once the request has been completed
139 // (valid URL, kill switch disabled)
140 base::OnceClosure respond_permission_;
143 class TestKillSwitchPermissionContext : public TestPermissionContext {
145 TestKillSwitchPermissionContext(
146 content::BrowserContext* browser_context,
147 const ContentSettingsType content_settings_type)
148 : TestPermissionContext(browser_context, content_settings_type) {
149 ResetFieldTrialList();
152 TestKillSwitchPermissionContext(const TestKillSwitchPermissionContext&) =
154 TestKillSwitchPermissionContext& operator=(
155 const TestKillSwitchPermissionContext&) = delete;
157 void ResetFieldTrialList() {
158 scoped_feature_list_.Reset();
159 scoped_feature_list_.Init();
163 base::test::ScopedFeatureList scoped_feature_list_;
166 class TestSecureOriginRestrictedPermissionContext
167 : public TestPermissionContext {
169 TestSecureOriginRestrictedPermissionContext(
170 content::BrowserContext* browser_context,
171 const ContentSettingsType content_settings_type)
172 : TestPermissionContext(browser_context, content_settings_type) {}
174 TestSecureOriginRestrictedPermissionContext(
175 const TestSecureOriginRestrictedPermissionContext&) = delete;
176 TestSecureOriginRestrictedPermissionContext& operator=(
177 const TestSecureOriginRestrictedPermissionContext&) = delete;
180 bool IsRestrictedToSecureOrigins() const override { return true; }
183 class TestPermissionsClientBypassExtensionOriginCheck
184 : public TestPermissionsClient {
186 bool CanBypassEmbeddingOriginCheck(const GURL& requesting_origin,
187 const GURL& embedding_origin) override {
188 return requesting_origin.SchemeIs("chrome-extension");
192 class PermissionContextBaseTests : public content::RenderViewHostTestHarness {
194 PermissionContextBaseTests(const PermissionContextBaseTests&) = delete;
195 PermissionContextBaseTests& operator=(const PermissionContextBaseTests&) =
199 PermissionContextBaseTests() {}
200 ~PermissionContextBaseTests() override {}
202 // Accept or dismiss the permission prompt.
203 void RespondToPermission(TestPermissionContext* context,
204 const PermissionRequestID& id,
206 ContentSetting response) {
207 DCHECK(response == CONTENT_SETTING_ALLOW ||
208 response == CONTENT_SETTING_BLOCK ||
209 response == CONTENT_SETTING_ASK);
210 using AutoResponseType = PermissionRequestManager::AutoResponseType;
211 AutoResponseType decision = AutoResponseType::DISMISS;
212 if (response == CONTENT_SETTING_ALLOW)
213 decision = AutoResponseType::ACCEPT_ALL;
214 else if (response == CONTENT_SETTING_BLOCK)
215 decision = AutoResponseType::DENY_ALL;
216 prompt_factory_->set_response_type(decision);
219 void TestAskAndDecide_TestContent(ContentSettingsType content_settings_type,
220 ContentSetting decision) {
221 ukm::InitializeSourceUrlRecorderForWebContents(web_contents());
222 ukm::TestAutoSetUkmRecorder ukm_recorder;
223 TestPermissionContext permission_context(browser_context(),
224 content_settings_type);
225 GURL url("https://www.google.com");
227 base::HistogramTester histograms;
229 const PermissionRequestID id(
230 web_contents()->GetPrimaryMainFrame()->GetGlobalId(),
231 PermissionRequestID::RequestLocalId());
232 permission_context.SetRespondPermissionCallback(base::BindOnce(
233 &PermissionContextBaseTests::RespondToPermission,
234 base::Unretained(this), &permission_context, id, url, decision));
235 permission_context.RequestPermission(
236 PermissionRequestData(&permission_context, id,
237 /*user_gesture=*/true, url),
238 base::BindOnce(&TestPermissionContext::TrackPermissionDecision,
239 base::Unretained(&permission_context)));
240 ASSERT_EQ(1u, permission_context.decisions().size());
241 EXPECT_EQ(decision, permission_context.decisions()[0]);
242 EXPECT_TRUE(permission_context.tab_context_updated());
244 std::string decision_string;
245 absl::optional<PermissionAction> action;
246 if (decision == CONTENT_SETTING_ALLOW) {
247 decision_string = "Accepted";
248 action = PermissionAction::GRANTED;
249 } else if (decision == CONTENT_SETTING_BLOCK) {
250 decision_string = "Denied";
251 action = PermissionAction::DENIED;
252 } else if (decision == CONTENT_SETTING_ASK) {
253 decision_string = "Dismissed";
254 action = PermissionAction::DISMISSED;
257 if (!decision_string.empty()) {
258 histograms.ExpectUniqueSample(
259 "Permissions.Prompt." + decision_string + ".PriorDismissCount2." +
260 PermissionUtil::GetPermissionString(content_settings_type),
262 histograms.ExpectUniqueSample(
263 "Permissions.Prompt." + decision_string + ".PriorIgnoreCount2." +
264 PermissionUtil::GetPermissionString(content_settings_type),
266 #if BUILDFLAG(IS_ANDROID)
267 histograms.ExpectUniqueSample(
268 "Permissions.Action.WithDisposition.ModalDialog",
269 static_cast<int>(action.value()), 1);
271 histograms.ExpectUniqueSample(
272 "Permissions.Action.WithDisposition.AnchoredBubble",
273 static_cast<int>(action.value()), 1);
277 EXPECT_EQ(decision, permission_context.GetContentSettingFromMap(url, url));
279 histograms.ExpectUniqueSample(
280 "Permissions.AutoBlocker.EmbargoPromptSuppression",
281 static_cast<int>(PermissionEmbargoStatus::NOT_EMBARGOED), 1);
282 histograms.ExpectUniqueSample(
283 "Permissions.AutoBlocker.EmbargoStatus",
284 static_cast<int>(PermissionEmbargoStatus::NOT_EMBARGOED), 1);
286 if (action.has_value()) {
287 auto entries = ukm_recorder.GetEntriesByName("Permission");
288 EXPECT_EQ(1u, entries.size());
289 auto* entry = entries.front();
290 ukm_recorder.ExpectEntrySourceHasUrl(entry, url);
292 EXPECT_NE(content_settings_uma_util::ContentSettingTypeToHistogramValue(
293 content_settings_type),
296 EXPECT_EQ(*ukm_recorder.GetEntryMetric(entry, "Source"),
297 static_cast<int64_t>(PermissionSourceUI::PROMPT));
299 *ukm_recorder.GetEntryMetric(entry, "PermissionType"),
300 static_cast<int64_t>(
301 content_settings_uma_util::ContentSettingTypeToHistogramValue(
302 content_settings_type)));
303 EXPECT_EQ(*ukm_recorder.GetEntryMetric(entry, "Action"),
304 static_cast<int64_t>(action.value()));
306 #if BUILDFLAG(IS_ANDROID)
308 *ukm_recorder.GetEntryMetric(entry, "PromptDisposition"),
309 static_cast<int64_t>(PermissionPromptDisposition::MODAL_DIALOG));
312 *ukm_recorder.GetEntryMetric(entry, "PromptDisposition"),
313 static_cast<int64_t>(PermissionPromptDisposition::ANCHORED_BUBBLE));
318 void DismissMultipleTimesAndExpectBlock(
320 ContentSettingsType content_settings_type,
321 uint32_t iterations) {
322 base::HistogramTester histograms;
324 // Dismiss |iterations| times. The final dismiss should change the decision
325 // from dismiss to block, and hence change the persisted content setting.
326 for (uint32_t i = 0; i < iterations; ++i) {
327 TestPermissionContext permission_context(browser_context(),
328 content_settings_type);
329 const PermissionRequestID id(
330 web_contents()->GetPrimaryMainFrame()->GetGlobalId(),
331 PermissionRequestID::RequestLocalId());
333 permission_context.SetRespondPermissionCallback(
334 base::BindOnce(&PermissionContextBaseTests::RespondToPermission,
335 base::Unretained(this), &permission_context, id, url,
336 CONTENT_SETTING_ASK));
338 permission_context.RequestPermission(
339 PermissionRequestData(&permission_context, id,
340 /*user_gesture=*/true, url),
341 base::BindOnce(&TestPermissionContext::TrackPermissionDecision,
342 base::Unretained(&permission_context)));
343 histograms.ExpectTotalCount(
344 "Permissions.Prompt.Dismissed.PriorDismissCount2." +
345 PermissionUtil::GetPermissionString(content_settings_type),
347 histograms.ExpectBucketCount(
348 "Permissions.Prompt.Dismissed.PriorDismissCount2." +
349 PermissionUtil::GetPermissionString(content_settings_type),
352 histograms.ExpectTotalCount("Permissions.AutoBlocker.EmbargoStatus",
355 content::PermissionResult result = permission_context.GetPermissionStatus(
356 nullptr /* render_frame_host */, url, url);
358 histograms.ExpectUniqueSample(
359 "Permissions.AutoBlocker.EmbargoPromptSuppression",
360 static_cast<int>(PermissionEmbargoStatus::NOT_EMBARGOED), i + 1);
362 EXPECT_EQ(content::PermissionStatusSource::UNSPECIFIED, result.source);
363 EXPECT_EQ(PermissionStatus::ASK, result.status);
364 histograms.ExpectUniqueSample(
365 "Permissions.AutoBlocker.EmbargoStatus",
366 static_cast<int>(PermissionEmbargoStatus::NOT_EMBARGOED), i + 1);
368 EXPECT_EQ(content::PermissionStatusSource::MULTIPLE_DISMISSALS,
370 EXPECT_EQ(PermissionStatus::DENIED, result.status);
371 histograms.ExpectBucketCount(
372 "Permissions.AutoBlocker.EmbargoStatus",
373 static_cast<int>(PermissionEmbargoStatus::REPEATED_DISMISSALS), 1);
376 ASSERT_EQ(1u, permission_context.decisions().size());
377 EXPECT_EQ(CONTENT_SETTING_ASK, permission_context.decisions()[0]);
378 EXPECT_TRUE(permission_context.tab_context_updated());
381 TestPermissionContext permission_context(browser_context(),
382 content_settings_type);
383 const PermissionRequestID id(
384 web_contents()->GetPrimaryMainFrame()->GetGlobalId(),
385 PermissionRequestID::RequestLocalId());
387 permission_context.SetRespondPermissionCallback(
388 base::BindOnce(&PermissionContextBaseTests::RespondToPermission,
389 base::Unretained(this), &permission_context, id, url,
390 CONTENT_SETTING_ASK));
392 permission_context.RequestPermission(
393 PermissionRequestData(&permission_context, id,
394 /*user_gesture=*/true, url),
395 base::BindOnce(&TestPermissionContext::TrackPermissionDecision,
396 base::Unretained(&permission_context)));
398 content::PermissionResult result = permission_context.GetPermissionStatus(
399 nullptr /* render_frame_host */, url, url);
400 EXPECT_EQ(PermissionStatus::DENIED, result.status);
401 EXPECT_EQ(content::PermissionStatusSource::MULTIPLE_DISMISSALS,
403 histograms.ExpectBucketCount(
404 "Permissions.AutoBlocker.EmbargoPromptSuppression",
405 static_cast<int>(PermissionEmbargoStatus::REPEATED_DISMISSALS), 1);
408 void TestBlockOnSeveralDismissals_TestContent() {
409 GURL url("https://www.google.com");
411 base::HistogramTester histograms;
414 // Ensure that > 3 dismissals behaves correctly when the
415 // BlockPromptsIfDismissedOften feature is off.
416 base::test::ScopedFeatureList feature_list;
417 feature_list.InitAndDisableFeature(
418 features::kBlockPromptsIfDismissedOften);
420 for (uint32_t i = 0; i < 4; ++i) {
421 TestPermissionContext permission_context(
422 browser_context(), ContentSettingsType::GEOLOCATION);
424 const PermissionRequestID id(
425 web_contents()->GetPrimaryMainFrame()->GetGlobalId(),
426 PermissionRequestID::RequestLocalId(i + 1));
428 permission_context.SetRespondPermissionCallback(
429 base::BindOnce(&PermissionContextBaseTests::RespondToPermission,
430 base::Unretained(this), &permission_context, id, url,
431 CONTENT_SETTING_ASK));
432 permission_context.RequestPermission(
433 PermissionRequestData(&permission_context, id,
434 /*user_gesture=*/true, url),
435 base::BindOnce(&TestPermissionContext::TrackPermissionDecision,
436 base::Unretained(&permission_context)));
437 histograms.ExpectTotalCount(
438 "Permissions.Prompt.Dismissed.PriorDismissCount2.Geolocation",
440 histograms.ExpectBucketCount(
441 "Permissions.Prompt.Dismissed.PriorDismissCount2.Geolocation", i,
443 histograms.ExpectUniqueSample(
444 "Permissions.AutoBlocker.EmbargoPromptSuppression",
445 static_cast<int>(PermissionEmbargoStatus::NOT_EMBARGOED), i + 1);
446 histograms.ExpectUniqueSample(
447 "Permissions.AutoBlocker.EmbargoStatus",
448 static_cast<int>(PermissionEmbargoStatus::NOT_EMBARGOED), i + 1);
450 ASSERT_EQ(1u, permission_context.decisions().size());
451 EXPECT_EQ(CONTENT_SETTING_ASK, permission_context.decisions()[0]);
452 EXPECT_TRUE(permission_context.tab_context_updated());
453 EXPECT_EQ(CONTENT_SETTING_ASK,
454 permission_context.GetContentSettingFromMap(url, url));
457 // Flush the dismissal counts.
458 auto* map = PermissionsClient::Get()->GetSettingsMap(browser_context());
459 map->ClearSettingsForOneType(
460 ContentSettingsType::PERMISSION_AUTOBLOCKER_DATA);
464 base::FeatureList::IsEnabled(features::kBlockPromptsIfDismissedOften));
466 // Sanity check independence per permission type by checking two of them.
467 DismissMultipleTimesAndExpectBlock(url, ContentSettingsType::GEOLOCATION,
469 DismissMultipleTimesAndExpectBlock(url, ContentSettingsType::NOTIFICATIONS,
473 void TestVariationBlockOnSeveralDismissals_TestContent() {
474 GURL url("https://www.google.com");
476 base::HistogramTester histograms;
478 std::map<std::string, std::string> params;
480 [PermissionDecisionAutoBlocker::GetPromptDismissCountKeyForTesting()] =
482 base::test::ScopedFeatureList scoped_feature_list;
483 scoped_feature_list.InitAndEnableFeatureWithParameters(
484 features::kBlockPromptsIfDismissedOften, params);
486 std::map<std::string, std::string> actual_params;
487 EXPECT_TRUE(base::GetFieldTrialParamsByFeature(
488 features::kBlockPromptsIfDismissedOften, &actual_params));
489 EXPECT_EQ(params, actual_params);
491 EXPECT_TRUE(base::GetFieldTrialParamsByFeature(
492 features::kBlockPromptsIfDismissedOften, &actual_params));
493 EXPECT_EQ(params, actual_params);
495 for (uint32_t i = 0; i < 5; ++i) {
496 TestPermissionContext permission_context(browser_context(),
497 ContentSettingsType::MIDI_SYSEX);
499 const PermissionRequestID id(
500 web_contents()->GetPrimaryMainFrame()->GetGlobalId(),
501 PermissionRequestID::RequestLocalId(i + 1));
502 permission_context.SetRespondPermissionCallback(
503 base::BindOnce(&PermissionContextBaseTests::RespondToPermission,
504 base::Unretained(this), &permission_context, id, url,
505 CONTENT_SETTING_ASK));
506 permission_context.RequestPermission(
507 PermissionRequestData(&permission_context, id,
508 /*user_gesture=*/true, url),
509 base::BindOnce(&TestPermissionContext::TrackPermissionDecision,
510 base::Unretained(&permission_context)));
512 EXPECT_EQ(1u, permission_context.decisions().size());
513 ASSERT_EQ(CONTENT_SETTING_ASK, permission_context.decisions()[0]);
514 EXPECT_TRUE(permission_context.tab_context_updated());
515 content::PermissionResult result = permission_context.GetPermissionStatus(
516 nullptr /* render_frame_host */, url, url);
518 histograms.ExpectTotalCount(
519 "Permissions.Prompt.Dismissed.PriorDismissCount2.MidiSysEx", i + 1);
520 histograms.ExpectBucketCount(
521 "Permissions.Prompt.Dismissed.PriorDismissCount2.MidiSysEx", i, 1);
522 histograms.ExpectUniqueSample(
523 "Permissions.AutoBlocker.EmbargoPromptSuppression",
524 static_cast<int>(PermissionEmbargoStatus::NOT_EMBARGOED), i + 1);
525 histograms.ExpectTotalCount("Permissions.AutoBlocker.EmbargoStatus",
528 EXPECT_EQ(PermissionStatus::ASK, result.status);
529 EXPECT_EQ(content::PermissionStatusSource::UNSPECIFIED, result.source);
530 histograms.ExpectUniqueSample(
531 "Permissions.AutoBlocker.EmbargoStatus",
532 static_cast<int>(PermissionEmbargoStatus::NOT_EMBARGOED), i + 1);
534 EXPECT_EQ(PermissionStatus::DENIED, result.status);
535 EXPECT_EQ(content::PermissionStatusSource::MULTIPLE_DISMISSALS,
537 histograms.ExpectBucketCount(
538 "Permissions.AutoBlocker.EmbargoStatus",
539 static_cast<int>(PermissionEmbargoStatus::REPEATED_DISMISSALS), 1);
543 // Ensure that we finish in the block state.
544 TestPermissionContext permission_context(browser_context(),
545 ContentSettingsType::MIDI_SYSEX);
546 content::PermissionResult result = permission_context.GetPermissionStatus(
547 nullptr /* render_frame_host */, url, url);
548 EXPECT_EQ(PermissionStatus::DENIED, result.status);
549 EXPECT_EQ(content::PermissionStatusSource::MULTIPLE_DISMISSALS,
553 void TestRequestPermissionInvalidUrl(
554 ContentSettingsType content_settings_type) {
555 base::HistogramTester histograms;
556 TestPermissionContext permission_context(browser_context(),
557 content_settings_type);
559 ASSERT_FALSE(url.is_valid());
560 controller().LoadURL(url, content::Referrer(), ui::PAGE_TRANSITION_TYPED,
563 const PermissionRequestID id(
564 web_contents()->GetPrimaryMainFrame()->GetGlobalId(),
565 PermissionRequestID::RequestLocalId());
566 permission_context.RequestPermission(
567 PermissionRequestData(&permission_context, id,
568 /*user_gesture=*/true, url),
569 base::BindOnce(&TestPermissionContext::TrackPermissionDecision,
570 base::Unretained(&permission_context)));
572 ASSERT_EQ(1u, permission_context.decisions().size());
573 EXPECT_EQ(CONTENT_SETTING_BLOCK, permission_context.decisions()[0]);
574 EXPECT_TRUE(permission_context.tab_context_updated());
575 EXPECT_EQ(CONTENT_SETTING_ASK,
576 permission_context.GetContentSettingFromMap(url, url));
577 histograms.ExpectTotalCount(
578 "Permissions.AutoBlocker.EmbargoPromptSuppression", 0);
581 void TestGrantAndRevoke_TestContent(ContentSettingsType content_settings_type,
582 ContentSetting expected_default) {
583 TestPermissionContext permission_context(browser_context(),
584 content_settings_type);
585 GURL url("https://www.google.com");
588 const PermissionRequestID id(
589 web_contents()->GetPrimaryMainFrame()->GetGlobalId(),
590 PermissionRequestID::RequestLocalId());
591 permission_context.SetRespondPermissionCallback(
592 base::BindOnce(&PermissionContextBaseTests::RespondToPermission,
593 base::Unretained(this), &permission_context, id, url,
594 CONTENT_SETTING_ALLOW));
596 permission_context.RequestPermission(
597 PermissionRequestData(&permission_context, id,
598 /*user_gesture=*/true, url),
599 base::BindOnce(&TestPermissionContext::TrackPermissionDecision,
600 base::Unretained(&permission_context)));
602 ASSERT_EQ(1u, permission_context.decisions().size());
603 EXPECT_EQ(CONTENT_SETTING_ALLOW, permission_context.decisions()[0]);
604 EXPECT_TRUE(permission_context.tab_context_updated());
605 EXPECT_EQ(CONTENT_SETTING_ALLOW,
606 permission_context.GetContentSettingFromMap(url, url));
608 // Try to reset permission.
609 permission_context.ResetPermission(url.DeprecatedGetOriginAsURL(),
610 url.DeprecatedGetOriginAsURL());
611 ContentSetting setting_after_reset =
612 permission_context.GetContentSettingFromMap(url, url);
613 ContentSetting default_setting =
614 PermissionsClient::Get()
615 ->GetSettingsMap(browser_context())
616 ->GetDefaultContentSetting(content_settings_type, nullptr);
617 EXPECT_EQ(default_setting, setting_after_reset);
620 void TestGlobalPermissionsKillSwitch(
621 ContentSettingsType content_settings_type) {
622 TestKillSwitchPermissionContext permission_context(browser_context(),
623 content_settings_type);
624 permission_context.ResetFieldTrialList();
626 EXPECT_FALSE(permission_context.IsPermissionKillSwitchOn());
627 std::map<std::string, std::string> params;
628 params[PermissionUtil::GetPermissionString(content_settings_type)] =
629 kPermissionsKillSwitchBlockedValue;
630 base::AssociateFieldTrialParams(kPermissionsKillSwitchFieldStudy,
631 kPermissionsKillSwitchTestGroup, params);
632 base::FieldTrialList::CreateFieldTrial(kPermissionsKillSwitchFieldStudy,
633 kPermissionsKillSwitchTestGroup);
634 EXPECT_TRUE(permission_context.IsPermissionKillSwitchOn());
637 void TestSecureOriginRestrictedPermissionContextCheck(
638 const std::string& requesting_url_spec,
639 const std::string& embedding_url_spec,
640 bool expect_allowed) {
641 GURL requesting_origin(requesting_url_spec);
642 GURL embedding_origin(embedding_url_spec);
643 TestSecureOriginRestrictedPermissionContext permission_context(
644 browser_context(), ContentSettingsType::GEOLOCATION);
645 bool result = permission_context.IsPermissionAvailableToOrigins(
646 requesting_origin, embedding_origin);
647 EXPECT_EQ(expect_allowed, result)
648 << "test case (requesting, embedding): (" << requesting_url_spec << ", "
649 << embedding_url_spec << ") with secure-origin requirement"
652 // With no secure-origin limitation, this check should always return pass.
653 TestPermissionContext new_context(browser_context(),
654 ContentSettingsType::GEOLOCATION);
655 result = new_context.IsPermissionAvailableToOrigins(requesting_origin,
657 EXPECT_EQ(true, result)
658 << "test case (requesting, embedding): (" << requesting_url_spec << ", "
659 << embedding_url_spec << ") with secure-origin requirement"
663 // Don't call this more than once in the same test, as it persists data to
664 // HostContentSettingsMap.
665 void TestParallelRequests(ContentSetting response) {
666 TestPermissionContext permission_context(browser_context(),
667 ContentSettingsType::GEOLOCATION);
668 GURL url("http://www.google.com");
671 const PermissionRequestID id1(
672 web_contents()->GetPrimaryMainFrame()->GetGlobalId(),
673 PermissionRequestID::RequestLocalId(1));
674 const PermissionRequestID id2(
675 web_contents()->GetPrimaryMainFrame()->GetGlobalId(),
676 PermissionRequestID::RequestLocalId(2));
678 // Request a permission without setting the callback to DecidePermission.
679 permission_context.RequestPermission(
680 PermissionRequestData(&permission_context, id1,
681 /*user_gesture=*/true, url),
682 base::BindOnce(&TestPermissionContext::TrackPermissionDecision,
683 base::Unretained(&permission_context)));
685 EXPECT_EQ(0u, permission_context.decisions().size());
687 // Set the callback, and make a second permission request.
688 permission_context.SetRespondPermissionCallback(base::BindOnce(
689 &PermissionContextBaseTests::RespondToPermission,
690 base::Unretained(this), &permission_context, id1, url, response));
691 permission_context.RequestPermission(
692 PermissionRequestData(&permission_context, id2,
693 /*user_gesture=*/true, url),
694 base::BindOnce(&TestPermissionContext::TrackPermissionDecision,
695 base::Unretained(&permission_context)));
697 ASSERT_EQ(2u, permission_context.decisions().size());
698 EXPECT_EQ(response, permission_context.decisions()[0]);
699 EXPECT_EQ(response, permission_context.decisions()[1]);
700 EXPECT_TRUE(permission_context.tab_context_updated());
702 EXPECT_EQ(response, permission_context.GetContentSettingFromMap(url, url));
705 void TestVirtualURL(const GURL& loaded_url,
706 const GURL& virtual_url,
707 const ContentSetting want_response,
708 const content::PermissionStatusSource& want_source) {
709 TestPermissionContext permission_context(browser_context(),
710 ContentSettingsType::GEOLOCATION);
712 NavigateAndCommit(loaded_url);
713 web_contents()->GetController().GetVisibleEntry()->SetVirtualURL(
716 content::PermissionResult result = permission_context.GetPermissionStatus(
717 web_contents()->GetPrimaryMainFrame(), virtual_url, virtual_url);
718 EXPECT_EQ(result.status,
719 PermissionUtil::ContentSettingToPermissionStatus(want_response));
720 EXPECT_EQ(result.source, want_source);
723 void SetUpUrl(const GURL& url) {
724 NavigateAndCommit(url);
725 prompt_factory_->DocumentOnLoadCompletedInPrimaryMainFrame();
729 // content::RenderViewHostTestHarness:
730 void SetUp() override {
731 content::RenderViewHostTestHarness::SetUp();
732 PermissionRequestManager::CreateForWebContents(web_contents());
733 PermissionRequestManager* manager =
734 PermissionRequestManager::FromWebContents(web_contents());
735 manager->set_enabled_app_level_notification_permission_for_testing(true);
736 prompt_factory_ = std::make_unique<MockPermissionPromptFactory>(manager);
739 void TearDown() override {
740 prompt_factory_.reset();
741 content::RenderViewHostTestHarness::TearDown();
744 std::unique_ptr<MockPermissionPromptFactory> prompt_factory_;
745 TestPermissionsClientBypassExtensionOriginCheck client_;
748 // Simulates clicking Accept. The permission should be granted and
749 // saved for future use.
750 TEST_F(PermissionContextBaseTests, TestAskAndGrant) {
751 TestAskAndDecide_TestContent(ContentSettingsType::NOTIFICATIONS,
752 CONTENT_SETTING_ALLOW);
755 // Simulates clicking Block. The permission should be denied and
756 // saved for future use.
757 TEST_F(PermissionContextBaseTests, TestAskAndBlock) {
758 TestAskAndDecide_TestContent(ContentSettingsType::GEOLOCATION,
759 CONTENT_SETTING_BLOCK);
762 // Simulates clicking Dismiss (X) in the prompt.
763 // The permission should be denied but not saved for future use.
764 TEST_F(PermissionContextBaseTests, TestAskAndDismiss) {
765 TestAskAndDecide_TestContent(ContentSettingsType::MIDI_SYSEX,
766 CONTENT_SETTING_ASK);
769 // Simulates clicking Dismiss (X) in the prompt with the block on too
770 // many dismissals feature active. The permission should be blocked after
771 // several dismissals.
772 TEST_F(PermissionContextBaseTests, TestDismissUntilBlocked) {
773 TestBlockOnSeveralDismissals_TestContent();
776 // Test setting a custom number of dismissals before block via variations.
777 TEST_F(PermissionContextBaseTests, TestDismissVariations) {
778 TestVariationBlockOnSeveralDismissals_TestContent();
781 // Simulates non-valid requesting URL.
782 // The permission should be denied but not saved for future use.
783 TEST_F(PermissionContextBaseTests, TestNonValidRequestingUrl) {
784 TestRequestPermissionInvalidUrl(ContentSettingsType::GEOLOCATION);
785 TestRequestPermissionInvalidUrl(ContentSettingsType::NOTIFICATIONS);
786 TestRequestPermissionInvalidUrl(ContentSettingsType::MIDI_SYSEX);
789 // Simulates granting and revoking of permissions.
790 TEST_F(PermissionContextBaseTests, TestGrantAndRevoke) {
791 TestGrantAndRevoke_TestContent(ContentSettingsType::GEOLOCATION,
792 CONTENT_SETTING_ASK);
793 TestGrantAndRevoke_TestContent(ContentSettingsType::MIDI_SYSEX,
794 CONTENT_SETTING_ASK);
795 #if BUILDFLAG(IS_ANDROID)
796 TestGrantAndRevoke_TestContent(
797 ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER, CONTENT_SETTING_ASK);
798 // TODO(timvolodine): currently no test for
799 // ContentSettingsType::NOTIFICATIONS because notification permissions work
800 // differently with infobars as compared to bubbles (crbug.com/453784).
802 TestGrantAndRevoke_TestContent(ContentSettingsType::NOTIFICATIONS,
803 CONTENT_SETTING_ASK);
807 // Tests the global kill switch by enabling/disabling the Field Trials.
808 TEST_F(PermissionContextBaseTests, TestGlobalKillSwitch) {
809 TestGlobalPermissionsKillSwitch(ContentSettingsType::GEOLOCATION);
810 TestGlobalPermissionsKillSwitch(ContentSettingsType::NOTIFICATIONS);
811 TestGlobalPermissionsKillSwitch(ContentSettingsType::MIDI_SYSEX);
812 TestGlobalPermissionsKillSwitch(ContentSettingsType::DURABLE_STORAGE);
813 #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CHROMEOS_ASH)
814 TestGlobalPermissionsKillSwitch(
815 ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER);
817 TestGlobalPermissionsKillSwitch(ContentSettingsType::MEDIASTREAM_MIC);
818 TestGlobalPermissionsKillSwitch(ContentSettingsType::MEDIASTREAM_CAMERA);
821 // Tests that secure origins are examined if switch is on, or ignored if off.
822 TEST_F(PermissionContextBaseTests,
823 TestSecureOriginRestrictedPermissionContextSwitch) {
824 url::ScopedSchemeRegistryForTests scoped_registry;
825 url::AddSecureScheme("chrome-extension");
827 std::string requesting_url_spec;
828 std::string embedding_url_spec;
829 bool expect_permission_allowed;
831 // Secure-origins that should be allowed.
832 {"https://google.com", "https://foo.com",
833 /*expect_allowed=*/true},
834 {"https://www.bar.com", "https://foo.com",
835 /*expect_allowed=*/true},
836 {"https://localhost", "http://localhost",
837 /*expect_allowed=*/true},
839 {"http://localhost", "https://google.com",
840 /*expect_allowed=*/true},
841 {"https://google.com", "http://localhost",
842 /*expect_allowed=*/true},
843 {"https://foo.com", "file://some-file",
844 /*expect_allowed=*/true},
845 {"file://some-file", "https://foo.com",
846 /*expect_allowed=*/true},
847 {"https://foo.com", "about:blank",
848 /*expect_allowed=*/true},
849 {"about:blank", "https://foo.com",
850 /*expect_allowed=*/true},
852 // Extensions are exempt from checking the embedder chain.
853 {"chrome-extension://some-extension", "http://not-secure.com",
854 /*expect_allowed=*/true},
856 // Insecure-origins that should be blocked.
857 {"http://foo.com", "file://some-file",
858 /*expect_allowed=*/false},
859 {"fake://foo.com", "about:blank",
860 /*expect_allowed=*/false},
861 {"http://localhost", "http://foo.com",
862 /*expect_allowed=*/false},
863 {"http://localhost", "foo.com",
864 /*expect_allowed=*/false},
865 {"http://bar.com", "https://foo.com",
866 /*expect_permission_allowed=*/false},
867 {"https://foo.com", "http://bar.com",
868 /*expect_permission_allowed=*/false},
869 {"http://localhost", "http://foo.com",
870 /*expect_permission_allowed=*/false},
871 {"http://foo.com", "http://localhost",
872 /*expect_permission_allowed=*/false},
873 {"bar.com", "https://foo.com", /*expect_permission_allowed=*/false},
874 {"https://foo.com", "bar.com", /*expect_permission_allowed=*/false}};
875 for (const auto& test_case : kTestCases) {
876 TestSecureOriginRestrictedPermissionContextCheck(
877 test_case.requesting_url_spec, test_case.embedding_url_spec,
878 test_case.expect_permission_allowed);
882 TEST_F(PermissionContextBaseTests, TestParallelRequestsAllowed) {
883 TestParallelRequests(CONTENT_SETTING_ALLOW);
886 TEST_F(PermissionContextBaseTests, TestParallelRequestsBlocked) {
887 TestParallelRequests(CONTENT_SETTING_BLOCK);
890 TEST_F(PermissionContextBaseTests, TestParallelRequestsDismissed) {
891 TestParallelRequests(CONTENT_SETTING_ASK);
894 TEST_F(PermissionContextBaseTests, TestVirtualURLDifferentOrigin) {
895 TestVirtualURL(GURL("http://www.google.com"), GURL("http://foo.com"),
896 CONTENT_SETTING_BLOCK,
897 content::PermissionStatusSource::VIRTUAL_URL_DIFFERENT_ORIGIN);
900 TEST_F(PermissionContextBaseTests, TestVirtualURLNotHTTP) {
901 TestVirtualURL(GURL("chrome://foo"), GURL("chrome://newtab"),
903 content::PermissionStatusSource::UNSPECIFIED);
906 TEST_F(PermissionContextBaseTests, TestVirtualURLSameOrigin) {
907 TestVirtualURL(GURL("http://www.google.com"),
908 GURL("http://www.google.com/foo"), CONTENT_SETTING_ASK,
909 content::PermissionStatusSource::UNSPECIFIED);
912 #if !BUILDFLAG(IS_ANDROID)
913 TEST_F(PermissionContextBaseTests, ExpirationAllow) {
914 base::test::ScopedFeatureList scoped_feature_list;
915 scoped_feature_list.InitWithFeatures(
916 {features::kRecordPermissionExpirationTimestamps}, {});
918 base::Time now = base::Time::Now();
919 TestAskAndDecide_TestContent(ContentSettingsType::GEOLOCATION,
920 CONTENT_SETTING_ALLOW);
922 GURL primary_url("https://www.google.com");
924 auto* hcsm = PermissionsClient::Get()->GetSettingsMap(browser_context());
925 content_settings::SettingInfo info;
926 hcsm->GetWebsiteSetting(primary_url, secondary_url,
927 ContentSettingsType::GEOLOCATION, &info);
929 // The last_visited should lie between today and a week ago.
930 EXPECT_GE(info.metadata.last_visited(), now - base::Days(7));
931 EXPECT_LE(info.metadata.last_visited(), now);
934 TEST_F(PermissionContextBaseTests, ExpirationBlock) {
935 base::test::ScopedFeatureList scoped_feature_list;
936 scoped_feature_list.InitWithFeatures(
937 {features::kRecordPermissionExpirationTimestamps}, {});
939 TestAskAndDecide_TestContent(ContentSettingsType::GEOLOCATION,
940 CONTENT_SETTING_BLOCK);
942 GURL primary_url("https://www.google.com");
944 auto* hcsm = PermissionsClient::Get()->GetSettingsMap(browser_context());
945 content_settings::SettingInfo info;
946 hcsm->GetWebsiteSetting(primary_url, secondary_url,
947 ContentSettingsType::GEOLOCATION, &info);
949 // last_visited is not set for BLOCKed permissions.
950 EXPECT_EQ(base::Time(), info.metadata.last_visited());
952 #endif // !BUILDFLAG(IS_ANDROID)
954 } // namespace permissions