| // Copyright 2016 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/permissions/permission_decision_auto_blocker.h" |
| |
| #include <map> |
| #include <memory> |
| |
| #include "base/bind.h" |
| #include "base/run_loop.h" |
| #include "base/test/histogram_tester.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "base/test/simple_test_clock.h" |
| #include "chrome/browser/content_settings/host_content_settings_map_factory.h" |
| #include "chrome/browser/permissions/permission_uma_util.h" |
| #include "chrome/browser/permissions/permission_util.h" |
| #include "chrome/common/chrome_features.h" |
| #include "chrome/test/base/chrome_render_view_host_test_harness.h" |
| #include "chrome/test/base/testing_profile.h" |
| #include "components/safe_browsing/db/test_database_manager.h" |
| |
| namespace { |
| |
| bool FilterGoogle(const GURL& url) { |
| return url == "https://www.google.com/"; |
| } |
| |
| bool FilterAll(const GURL& url) { |
| return true; |
| } |
| |
| class MockSafeBrowsingDatabaseManager |
| : public safe_browsing::TestSafeBrowsingDatabaseManager { |
| public: |
| explicit MockSafeBrowsingDatabaseManager(bool perform_callback, bool enabled) |
| : perform_callback_(perform_callback), enabled_(enabled) {} |
| |
| bool CheckApiBlacklistUrl( |
| const GURL& url, |
| safe_browsing::SafeBrowsingDatabaseManager::Client* client) override { |
| // Return true when able to synchronously determine that the url is safe. |
| if (!enabled_) { |
| return true; |
| } |
| |
| if (perform_callback_) { |
| safe_browsing::ThreatMetadata metadata; |
| const auto& blacklisted_permissions = permissions_blacklist_.find(url); |
| if (blacklisted_permissions != permissions_blacklist_.end()) |
| metadata.api_permissions = blacklisted_permissions->second; |
| client->OnCheckApiBlacklistUrlResult(url, metadata); |
| } |
| return false; |
| } |
| |
| bool CancelApiCheck(Client* client) override { |
| DCHECK(!perform_callback_); |
| // Returns true when client check could be stopped. |
| return true; |
| } |
| |
| void BlacklistUrlPermissions(const GURL& url, |
| const std::set<std::string> permissions) { |
| permissions_blacklist_[url] = permissions; |
| } |
| |
| void SetPerformCallback(bool perform_callback) { |
| perform_callback_ = perform_callback; |
| } |
| |
| protected: |
| ~MockSafeBrowsingDatabaseManager() override {} |
| |
| private: |
| bool perform_callback_; |
| bool enabled_; |
| std::map<GURL, std::set<std::string>> permissions_blacklist_; |
| |
| DISALLOW_COPY_AND_ASSIGN(MockSafeBrowsingDatabaseManager); |
| }; |
| |
| } // namespace |
| |
| class PermissionDecisionAutoBlockerUnitTest |
| : public ChromeRenderViewHostTestHarness { |
| protected: |
| void SetUp() override { |
| ChromeRenderViewHostTestHarness::SetUp(); |
| autoblocker_ = PermissionDecisionAutoBlocker::GetForProfile(profile()); |
| feature_list_.InitWithFeatures({features::kBlockPromptsIfDismissedOften, |
| features::kBlockPromptsIfIgnoredOften, |
| features::kPermissionsBlacklist}, |
| {}); |
| last_embargoed_status_ = false; |
| std::unique_ptr<base::SimpleTestClock> clock = |
| std::make_unique<base::SimpleTestClock>(); |
| clock_ = clock.get(); |
| autoblocker_->SetClockForTesting(std::move(clock)); |
| callback_was_run_ = false; |
| } |
| |
| void SetSafeBrowsingDatabaseManagerAndTimeoutForTesting( |
| scoped_refptr<MockSafeBrowsingDatabaseManager> db_manager, |
| int timeout) { |
| autoblocker_->SetSafeBrowsingDatabaseManagerAndTimeoutForTesting(db_manager, |
| timeout); |
| } |
| |
| void CheckSafeBrowsingBlacklist(const GURL& url, |
| ContentSettingsType permission) { |
| base::RunLoop run_loop; |
| autoblocker_->CheckSafeBrowsingBlacklist( |
| nullptr, url, permission, |
| base::Bind(&PermissionDecisionAutoBlockerUnitTest::SetLastEmbargoStatus, |
| base::Unretained(this), run_loop.QuitClosure())); |
| run_loop.Run(); |
| } |
| |
| // Manually placing an (origin, permission) pair under embargo for |
| // blacklisting. To embargo on dismissals, RecordDismissAndEmbargo can be |
| // used. |
| void PlaceUnderBlacklistEmbargo(const GURL& url, |
| ContentSettingsType permission) { |
| autoblocker_->PlaceUnderEmbargo( |
| url, permission, |
| PermissionDecisionAutoBlocker::kPermissionBlacklistEmbargoKey); |
| } |
| |
| PermissionDecisionAutoBlocker* autoblocker() { return autoblocker_; } |
| |
| void SetLastEmbargoStatus(base::Closure quit_closure, bool status) { |
| callback_was_run_ = true; |
| last_embargoed_status_ = status; |
| if (quit_closure) { |
| quit_closure.Run(); |
| quit_closure.Reset(); |
| } |
| } |
| |
| bool last_embargoed_status() { return last_embargoed_status_; } |
| |
| bool callback_was_run() { return callback_was_run_; } |
| |
| base::SimpleTestClock* clock() { return clock_; } |
| |
| private: |
| PermissionDecisionAutoBlocker* autoblocker_; |
| base::test::ScopedFeatureList feature_list_; |
| base::SimpleTestClock* clock_; |
| bool last_embargoed_status_; |
| bool callback_was_run_; |
| }; |
| |
| // Check removing the the embargo for a single permission on a site works, and |
| // that it doesn't interfere with other embargoed permissions or the same |
| // permission embargoed on other sites. |
| TEST_F(PermissionDecisionAutoBlockerUnitTest, RemoveEmbargoByUrl) { |
| GURL url1("https://www.google.com"); |
| GURL url2("https://www.example.com"); |
| |
| // Record dismissals for location and notifications in |url1|. |
| EXPECT_FALSE(autoblocker()->RecordDismissAndEmbargo( |
| url1, CONTENT_SETTINGS_TYPE_GEOLOCATION)); |
| EXPECT_FALSE(autoblocker()->RecordDismissAndEmbargo( |
| url1, CONTENT_SETTINGS_TYPE_GEOLOCATION)); |
| EXPECT_TRUE(autoblocker()->RecordDismissAndEmbargo( |
| url1, CONTENT_SETTINGS_TYPE_GEOLOCATION)); |
| EXPECT_FALSE(autoblocker()->RecordDismissAndEmbargo( |
| url1, CONTENT_SETTINGS_TYPE_NOTIFICATIONS)); |
| EXPECT_FALSE(autoblocker()->RecordDismissAndEmbargo( |
| url1, CONTENT_SETTINGS_TYPE_NOTIFICATIONS)); |
| EXPECT_TRUE(autoblocker()->RecordDismissAndEmbargo( |
| url1, CONTENT_SETTINGS_TYPE_NOTIFICATIONS)); |
| // Record dismissals for location in |url2|. |
| EXPECT_FALSE(autoblocker()->RecordDismissAndEmbargo( |
| url2, CONTENT_SETTINGS_TYPE_GEOLOCATION)); |
| EXPECT_FALSE(autoblocker()->RecordDismissAndEmbargo( |
| url2, CONTENT_SETTINGS_TYPE_GEOLOCATION)); |
| EXPECT_TRUE(autoblocker()->RecordDismissAndEmbargo( |
| url2, CONTENT_SETTINGS_TYPE_GEOLOCATION)); |
| |
| // Verify all dismissals recorded above resulted in embargo. |
| PermissionResult result = |
| autoblocker()->GetEmbargoResult(url1, CONTENT_SETTINGS_TYPE_GEOLOCATION); |
| EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting); |
| EXPECT_EQ(PermissionStatusSource::MULTIPLE_DISMISSALS, result.source); |
| result = autoblocker()->GetEmbargoResult(url1, |
| CONTENT_SETTINGS_TYPE_NOTIFICATIONS); |
| EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting); |
| EXPECT_EQ(PermissionStatusSource::MULTIPLE_DISMISSALS, result.source); |
| result = |
| autoblocker()->GetEmbargoResult(url2, CONTENT_SETTINGS_TYPE_GEOLOCATION); |
| EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting); |
| EXPECT_EQ(PermissionStatusSource::MULTIPLE_DISMISSALS, result.source); |
| |
| // Remove the embargo on notifications. Verify it is no longer under embargo, |
| // but location still is. |
| autoblocker()->RemoveEmbargoByUrl(url1, CONTENT_SETTINGS_TYPE_NOTIFICATIONS); |
| result = |
| autoblocker()->GetEmbargoResult(url1, CONTENT_SETTINGS_TYPE_GEOLOCATION); |
| EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting); |
| EXPECT_EQ(PermissionStatusSource::MULTIPLE_DISMISSALS, result.source); |
| result = autoblocker()->GetEmbargoResult(url1, |
| CONTENT_SETTINGS_TYPE_NOTIFICATIONS); |
| // If not under embargo, GetEmbargoResult() returns a setting of ASK. |
| EXPECT_EQ(CONTENT_SETTING_ASK, result.content_setting); |
| EXPECT_EQ(PermissionStatusSource::UNSPECIFIED, result.source); |
| // Verify |url2|'s embargo is still intact as well. |
| result = |
| autoblocker()->GetEmbargoResult(url2, CONTENT_SETTINGS_TYPE_GEOLOCATION); |
| EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting); |
| EXPECT_EQ(PermissionStatusSource::MULTIPLE_DISMISSALS, result.source); |
| } |
| |
| // Test that removing embargo from blacklisted permissions also works. |
| TEST_F(PermissionDecisionAutoBlockerUnitTest, |
| RemoveEmbargoByUrlForBlacklistedPermission) { |
| GURL url("https://www.example.com"); |
| |
| // Place under embargo and verify. |
| PlaceUnderBlacklistEmbargo(url, CONTENT_SETTINGS_TYPE_GEOLOCATION); |
| PermissionResult result = |
| autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_GEOLOCATION); |
| EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting); |
| EXPECT_EQ(PermissionStatusSource::SAFE_BROWSING_BLACKLIST, result.source); |
| |
| // Remove embargo and verify. |
| autoblocker()->RemoveEmbargoByUrl(url, CONTENT_SETTINGS_TYPE_GEOLOCATION); |
| result = |
| autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_GEOLOCATION); |
| EXPECT_EQ(CONTENT_SETTING_ASK, result.content_setting); |
| EXPECT_EQ(PermissionStatusSource::UNSPECIFIED, result.source); |
| } |
| |
| // Test it still only takes one more dismissal to re-trigger embargo after |
| // removing the embargo status for a site. |
| TEST_F(PermissionDecisionAutoBlockerUnitTest, |
| DismissAfterRemovingEmbargoByURL) { |
| GURL url("https://www.example.com"); |
| |
| // Record dismissals for location. |
| EXPECT_FALSE(autoblocker()->RecordDismissAndEmbargo( |
| url, CONTENT_SETTINGS_TYPE_GEOLOCATION)); |
| EXPECT_FALSE(autoblocker()->RecordDismissAndEmbargo( |
| url, CONTENT_SETTINGS_TYPE_GEOLOCATION)); |
| EXPECT_TRUE(autoblocker()->RecordDismissAndEmbargo( |
| url, CONTENT_SETTINGS_TYPE_GEOLOCATION)); |
| |
| // Verify location is under embargo. |
| PermissionResult result = |
| autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_GEOLOCATION); |
| EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting); |
| EXPECT_EQ(PermissionStatusSource::MULTIPLE_DISMISSALS, result.source); |
| |
| // Remove embargo and verify this is true. |
| autoblocker()->RemoveEmbargoByUrl(url, CONTENT_SETTINGS_TYPE_GEOLOCATION); |
| result = |
| autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_GEOLOCATION); |
| EXPECT_EQ(CONTENT_SETTING_ASK, result.content_setting); |
| EXPECT_EQ(PermissionStatusSource::UNSPECIFIED, result.source); |
| |
| // Record another dismissal and verify location is under embargo again. |
| autoblocker()->RecordDismissAndEmbargo(url, |
| CONTENT_SETTINGS_TYPE_GEOLOCATION); |
| result = |
| autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_GEOLOCATION); |
| EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting); |
| EXPECT_EQ(PermissionStatusSource::MULTIPLE_DISMISSALS, result.source); |
| } |
| |
| TEST_F(PermissionDecisionAutoBlockerUnitTest, RemoveCountsByUrl) { |
| GURL url1("https://www.google.com"); |
| GURL url2("https://www.example.com"); |
| |
| // Record some dismissals. |
| EXPECT_FALSE(autoblocker()->RecordDismissAndEmbargo( |
| url1, CONTENT_SETTINGS_TYPE_GEOLOCATION)); |
| EXPECT_EQ(1, autoblocker()->GetDismissCount( |
| url1, CONTENT_SETTINGS_TYPE_GEOLOCATION)); |
| |
| EXPECT_FALSE(autoblocker()->RecordDismissAndEmbargo( |
| url1, CONTENT_SETTINGS_TYPE_GEOLOCATION)); |
| EXPECT_EQ(2, autoblocker()->GetDismissCount( |
| url1, CONTENT_SETTINGS_TYPE_GEOLOCATION)); |
| |
| EXPECT_TRUE(autoblocker()->RecordDismissAndEmbargo( |
| url1, CONTENT_SETTINGS_TYPE_GEOLOCATION)); |
| EXPECT_EQ(3, autoblocker()->GetDismissCount( |
| url1, CONTENT_SETTINGS_TYPE_GEOLOCATION)); |
| |
| EXPECT_FALSE(autoblocker()->RecordDismissAndEmbargo( |
| url2, CONTENT_SETTINGS_TYPE_GEOLOCATION)); |
| EXPECT_EQ(1, autoblocker()->GetDismissCount( |
| url2, CONTENT_SETTINGS_TYPE_GEOLOCATION)); |
| |
| EXPECT_FALSE(autoblocker()->RecordDismissAndEmbargo( |
| url1, CONTENT_SETTINGS_TYPE_NOTIFICATIONS)); |
| EXPECT_EQ(1, autoblocker()->GetDismissCount( |
| url1, CONTENT_SETTINGS_TYPE_NOTIFICATIONS)); |
| |
| // Record some ignores. |
| EXPECT_FALSE(autoblocker()->RecordIgnoreAndEmbargo( |
| url1, CONTENT_SETTINGS_TYPE_MIDI_SYSEX)); |
| EXPECT_EQ( |
| 1, autoblocker()->GetIgnoreCount(url1, CONTENT_SETTINGS_TYPE_MIDI_SYSEX)); |
| EXPECT_FALSE(autoblocker()->RecordIgnoreAndEmbargo( |
| url1, CONTENT_SETTINGS_TYPE_DURABLE_STORAGE)); |
| EXPECT_EQ(1, autoblocker()->GetIgnoreCount( |
| url1, CONTENT_SETTINGS_TYPE_DURABLE_STORAGE)); |
| EXPECT_FALSE(autoblocker()->RecordIgnoreAndEmbargo( |
| url2, CONTENT_SETTINGS_TYPE_GEOLOCATION)); |
| EXPECT_FALSE(autoblocker()->RecordIgnoreAndEmbargo( |
| url2, CONTENT_SETTINGS_TYPE_GEOLOCATION)); |
| EXPECT_EQ(2, autoblocker()->GetIgnoreCount( |
| url2, CONTENT_SETTINGS_TYPE_GEOLOCATION)); |
| |
| autoblocker()->RemoveCountsByUrl(base::Bind(&FilterGoogle)); |
| |
| // Expect that url1's actions are gone, but url2's remain. |
| EXPECT_EQ(0, autoblocker()->GetDismissCount( |
| url1, CONTENT_SETTINGS_TYPE_GEOLOCATION)); |
| EXPECT_EQ(0, autoblocker()->GetDismissCount( |
| url1, CONTENT_SETTINGS_TYPE_NOTIFICATIONS)); |
| EXPECT_EQ( |
| 0, autoblocker()->GetIgnoreCount(url1, CONTENT_SETTINGS_TYPE_MIDI_SYSEX)); |
| EXPECT_EQ(0, autoblocker()->GetIgnoreCount( |
| url1, CONTENT_SETTINGS_TYPE_DURABLE_STORAGE)); |
| |
| EXPECT_EQ(1, autoblocker()->GetDismissCount( |
| url2, CONTENT_SETTINGS_TYPE_GEOLOCATION)); |
| EXPECT_EQ(2, autoblocker()->GetIgnoreCount( |
| url2, CONTENT_SETTINGS_TYPE_GEOLOCATION)); |
| |
| // Add some more actions. |
| EXPECT_FALSE(autoblocker()->RecordDismissAndEmbargo( |
| url1, CONTENT_SETTINGS_TYPE_GEOLOCATION)); |
| EXPECT_EQ(1, autoblocker()->GetDismissCount( |
| url1, CONTENT_SETTINGS_TYPE_GEOLOCATION)); |
| |
| EXPECT_FALSE(autoblocker()->RecordDismissAndEmbargo( |
| url1, CONTENT_SETTINGS_TYPE_NOTIFICATIONS)); |
| EXPECT_EQ(1, autoblocker()->GetDismissCount( |
| url1, CONTENT_SETTINGS_TYPE_NOTIFICATIONS)); |
| |
| EXPECT_FALSE(autoblocker()->RecordDismissAndEmbargo( |
| url2, CONTENT_SETTINGS_TYPE_GEOLOCATION)); |
| EXPECT_EQ(2, autoblocker()->GetDismissCount( |
| url2, CONTENT_SETTINGS_TYPE_GEOLOCATION)); |
| |
| EXPECT_FALSE(autoblocker()->RecordIgnoreAndEmbargo( |
| url1, CONTENT_SETTINGS_TYPE_GEOLOCATION)); |
| EXPECT_EQ(1, autoblocker()->GetIgnoreCount( |
| url1, CONTENT_SETTINGS_TYPE_GEOLOCATION)); |
| EXPECT_FALSE(autoblocker()->RecordIgnoreAndEmbargo( |
| url1, CONTENT_SETTINGS_TYPE_NOTIFICATIONS)); |
| EXPECT_EQ(1, autoblocker()->GetIgnoreCount( |
| url1, CONTENT_SETTINGS_TYPE_NOTIFICATIONS)); |
| EXPECT_FALSE(autoblocker()->RecordIgnoreAndEmbargo( |
| url1, CONTENT_SETTINGS_TYPE_DURABLE_STORAGE)); |
| EXPECT_EQ(1, autoblocker()->GetIgnoreCount( |
| url1, CONTENT_SETTINGS_TYPE_DURABLE_STORAGE)); |
| EXPECT_FALSE(autoblocker()->RecordIgnoreAndEmbargo( |
| url2, CONTENT_SETTINGS_TYPE_MIDI_SYSEX)); |
| EXPECT_EQ( |
| 1, autoblocker()->GetIgnoreCount(url2, CONTENT_SETTINGS_TYPE_MIDI_SYSEX)); |
| |
| // Remove everything and expect that it's all gone. |
| autoblocker()->RemoveCountsByUrl(base::Bind(&FilterAll)); |
| |
| EXPECT_EQ(0, autoblocker()->GetDismissCount( |
| url1, CONTENT_SETTINGS_TYPE_GEOLOCATION)); |
| EXPECT_EQ(0, autoblocker()->GetDismissCount( |
| url1, CONTENT_SETTINGS_TYPE_NOTIFICATIONS)); |
| EXPECT_EQ(0, autoblocker()->GetDismissCount( |
| url2, CONTENT_SETTINGS_TYPE_GEOLOCATION)); |
| |
| EXPECT_EQ(0, autoblocker()->GetIgnoreCount( |
| url1, CONTENT_SETTINGS_TYPE_GEOLOCATION)); |
| EXPECT_EQ(0, autoblocker()->GetIgnoreCount( |
| url1, CONTENT_SETTINGS_TYPE_NOTIFICATIONS)); |
| EXPECT_EQ(0, autoblocker()->GetIgnoreCount( |
| url2, CONTENT_SETTINGS_TYPE_GEOLOCATION)); |
| EXPECT_EQ(0, autoblocker()->GetIgnoreCount( |
| url2, CONTENT_SETTINGS_TYPE_DURABLE_STORAGE)); |
| EXPECT_EQ( |
| 0, autoblocker()->GetIgnoreCount(url2, CONTENT_SETTINGS_TYPE_MIDI_SYSEX)); |
| } |
| |
| // Test that an origin that has been blacklisted for a permission is embargoed. |
| TEST_F(PermissionDecisionAutoBlockerUnitTest, TestUpdateEmbargoBlacklist) { |
| GURL url("https://www.google.com"); |
| base::HistogramTester histograms; |
| |
| scoped_refptr<MockSafeBrowsingDatabaseManager> db_manager = |
| new MockSafeBrowsingDatabaseManager(true /* perform_callback */, |
| true /* enabled */); |
| std::set<std::string> blacklisted_permissions{"GEOLOCATION"}; |
| db_manager->BlacklistUrlPermissions(url, blacklisted_permissions); |
| SetSafeBrowsingDatabaseManagerAndTimeoutForTesting(db_manager, |
| 2000 /* timeout in ms */); |
| |
| CheckSafeBrowsingBlacklist(url, CONTENT_SETTINGS_TYPE_GEOLOCATION); |
| EXPECT_TRUE(callback_was_run()); |
| EXPECT_TRUE(last_embargoed_status()); |
| histograms.ExpectUniqueSample( |
| "Permissions.AutoBlocker.SafeBrowsingResponse", |
| static_cast<int>(SafeBrowsingResponse::BLACKLISTED), 1); |
| histograms.ExpectTotalCount( |
| "Permissions.AutoBlocker.SafeBrowsingResponseTime", 1); |
| } |
| |
| // Test that an origin that is blacklisted for a permission will not be placed |
| // under embargo for another permission. |
| TEST_F(PermissionDecisionAutoBlockerUnitTest, TestRequestNotBlacklisted) { |
| GURL url("https://www.google.com"); |
| clock()->SetNow(base::Time::Now()); |
| base::HistogramTester histograms; |
| |
| scoped_refptr<MockSafeBrowsingDatabaseManager> db_manager = |
| new MockSafeBrowsingDatabaseManager(true /* perform_callback */, |
| true /* enabled */); |
| std::set<std::string> blacklisted_permissions{"GEOLOCATION"}; |
| db_manager->BlacklistUrlPermissions(url, blacklisted_permissions); |
| SetSafeBrowsingDatabaseManagerAndTimeoutForTesting(db_manager, |
| 0 /* timeout in ms */); |
| |
| CheckSafeBrowsingBlacklist(url, CONTENT_SETTINGS_TYPE_NOTIFICATIONS); |
| EXPECT_FALSE(last_embargoed_status()); |
| histograms.ExpectUniqueSample( |
| "Permissions.AutoBlocker.SafeBrowsingResponse", |
| static_cast<int>(SafeBrowsingResponse::NOT_BLACKLISTED), 1); |
| histograms.ExpectTotalCount( |
| "Permissions.AutoBlocker.SafeBrowsingResponseTime", 1); |
| } |
| |
| // Check that we do not apply embargo to the plugins content type, as prompts |
| // should be triggered only when necessary by Html5ByDefault. |
| TEST_F(PermissionDecisionAutoBlockerUnitTest, |
| PluginsNotEmbargoedByMultipleDismissesOrIgnores) { |
| GURL url("https://www.google.com"); |
| |
| // Check dismisses first. |
| autoblocker()->RecordDismissAndEmbargo(url, CONTENT_SETTINGS_TYPE_PLUGINS); |
| autoblocker()->RecordDismissAndEmbargo(url, CONTENT_SETTINGS_TYPE_PLUGINS); |
| PermissionResult result = |
| autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_PLUGINS); |
| |
| EXPECT_EQ(CONTENT_SETTING_ASK, result.content_setting); |
| EXPECT_EQ(PermissionStatusSource::UNSPECIFIED, result.source); |
| EXPECT_EQ(2, |
| autoblocker()->GetDismissCount(url, CONTENT_SETTINGS_TYPE_PLUGINS)); |
| |
| // The third dismiss would normally embargo, but this shouldn't happen for |
| // plugins. |
| EXPECT_FALSE(autoblocker()->RecordDismissAndEmbargo( |
| url, CONTENT_SETTINGS_TYPE_PLUGINS)); |
| result = autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_PLUGINS); |
| |
| EXPECT_EQ(CONTENT_SETTING_ASK, result.content_setting); |
| EXPECT_EQ(PermissionStatusSource::UNSPECIFIED, result.source); |
| EXPECT_EQ(3, |
| autoblocker()->GetDismissCount(url, CONTENT_SETTINGS_TYPE_PLUGINS)); |
| |
| // Extra one for sanity checking. |
| EXPECT_FALSE(autoblocker()->RecordDismissAndEmbargo( |
| url, CONTENT_SETTINGS_TYPE_PLUGINS)); |
| result = autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_PLUGINS); |
| |
| EXPECT_EQ(CONTENT_SETTING_ASK, result.content_setting); |
| EXPECT_EQ(PermissionStatusSource::UNSPECIFIED, result.source); |
| EXPECT_EQ(4, |
| autoblocker()->GetDismissCount(url, CONTENT_SETTINGS_TYPE_PLUGINS)); |
| |
| // Check ignores. |
| autoblocker()->RecordIgnoreAndEmbargo(url, CONTENT_SETTINGS_TYPE_PLUGINS); |
| autoblocker()->RecordIgnoreAndEmbargo(url, CONTENT_SETTINGS_TYPE_PLUGINS); |
| autoblocker()->RecordIgnoreAndEmbargo(url, CONTENT_SETTINGS_TYPE_PLUGINS); |
| result = autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_PLUGINS); |
| |
| EXPECT_EQ(CONTENT_SETTING_ASK, result.content_setting); |
| EXPECT_EQ(PermissionStatusSource::UNSPECIFIED, result.source); |
| EXPECT_EQ(3, |
| autoblocker()->GetIgnoreCount(url, CONTENT_SETTINGS_TYPE_PLUGINS)); |
| |
| // The fourth ignore would normally embargo, but this shouldn't happen for |
| // plugins. |
| EXPECT_FALSE(autoblocker()->RecordIgnoreAndEmbargo( |
| url, CONTENT_SETTINGS_TYPE_PLUGINS)); |
| result = autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_PLUGINS); |
| |
| EXPECT_EQ(CONTENT_SETTING_ASK, result.content_setting); |
| EXPECT_EQ(PermissionStatusSource::UNSPECIFIED, result.source); |
| EXPECT_EQ(4, |
| autoblocker()->GetIgnoreCount(url, CONTENT_SETTINGS_TYPE_PLUGINS)); |
| |
| // Extra one for sanity checking. |
| EXPECT_FALSE(autoblocker()->RecordIgnoreAndEmbargo( |
| url, CONTENT_SETTINGS_TYPE_PLUGINS)); |
| result = autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_PLUGINS); |
| |
| EXPECT_EQ(CONTENT_SETTING_ASK, result.content_setting); |
| EXPECT_EQ(PermissionStatusSource::UNSPECIFIED, result.source); |
| EXPECT_EQ(5, |
| autoblocker()->GetIgnoreCount(url, CONTENT_SETTINGS_TYPE_PLUGINS)); |
| } |
| |
| // Check that GetEmbargoResult returns the correct value when the embargo is set |
| // and expires. |
| TEST_F(PermissionDecisionAutoBlockerUnitTest, CheckEmbargoStatus) { |
| GURL url("https://www.google.com"); |
| clock()->SetNow(base::Time::Now()); |
| |
| // Check the default state. |
| PermissionResult result = |
| autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_GEOLOCATION); |
| EXPECT_EQ(CONTENT_SETTING_ASK, result.content_setting); |
| EXPECT_EQ(PermissionStatusSource::UNSPECIFIED, result.source); |
| |
| // Place under embargo and verify. |
| PlaceUnderBlacklistEmbargo(url, CONTENT_SETTINGS_TYPE_GEOLOCATION); |
| result = |
| autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_GEOLOCATION); |
| EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting); |
| EXPECT_EQ(PermissionStatusSource::SAFE_BROWSING_BLACKLIST, result.source); |
| |
| // Check that the origin is not under embargo for a different permission. |
| result = |
| autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_NOTIFICATIONS); |
| EXPECT_EQ(CONTENT_SETTING_ASK, result.content_setting); |
| EXPECT_EQ(PermissionStatusSource::UNSPECIFIED, result.source); |
| |
| // Confirm embargo status during the embargo period. |
| clock()->Advance(base::TimeDelta::FromDays(5)); |
| result = |
| autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_GEOLOCATION); |
| EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting); |
| EXPECT_EQ(PermissionStatusSource::SAFE_BROWSING_BLACKLIST, result.source); |
| |
| // Check embargo is lifted on expiry day. A small offset after the exact |
| // embargo expiration date has been added to account for any precision errors |
| // when removing the date stored as a double from the permission dictionary. |
| clock()->Advance(base::TimeDelta::FromHours(3 * 24 + 1)); |
| result = |
| autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_GEOLOCATION); |
| EXPECT_EQ(CONTENT_SETTING_ASK, result.content_setting); |
| EXPECT_EQ(PermissionStatusSource::UNSPECIFIED, result.source); |
| |
| // Check embargo is lifted well after the expiry day. |
| clock()->Advance(base::TimeDelta::FromDays(1)); |
| result = |
| autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_GEOLOCATION); |
| EXPECT_EQ(CONTENT_SETTING_ASK, result.content_setting); |
| EXPECT_EQ(PermissionStatusSource::UNSPECIFIED, result.source); |
| |
| // Place under embargo again and verify the embargo status. |
| PlaceUnderBlacklistEmbargo(url, CONTENT_SETTINGS_TYPE_NOTIFICATIONS); |
| clock()->Advance(base::TimeDelta::FromDays(1)); |
| result = |
| autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_NOTIFICATIONS); |
| EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting); |
| EXPECT_EQ(PermissionStatusSource::SAFE_BROWSING_BLACKLIST, result.source); |
| } |
| |
| // Tests the alternating pattern of the block on multiple dismiss behaviour. On |
| // N dismissals, the origin to be embargoed for the requested permission and |
| // automatically blocked. Each time the embargo is lifted, the site gets another |
| // chance to request the permission, but if it is again dismissed it is placed |
| // under embargo again and its permission requests blocked. |
| TEST_F(PermissionDecisionAutoBlockerUnitTest, TestDismissEmbargoBackoff) { |
| GURL url("https://www.google.com"); |
| clock()->SetNow(base::Time::Now()); |
| base::HistogramTester histograms; |
| |
| // Record some dismisses. |
| EXPECT_FALSE(autoblocker()->RecordDismissAndEmbargo( |
| url, CONTENT_SETTINGS_TYPE_GEOLOCATION)); |
| EXPECT_FALSE(autoblocker()->RecordDismissAndEmbargo( |
| url, CONTENT_SETTINGS_TYPE_GEOLOCATION)); |
| |
| // A request with < 3 prior dismisses should not be automatically blocked. |
| PermissionResult result = |
| autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_GEOLOCATION); |
| EXPECT_EQ(CONTENT_SETTING_ASK, result.content_setting); |
| EXPECT_EQ(PermissionStatusSource::UNSPECIFIED, result.source); |
| |
| // After the 3rd dismiss subsequent permission requests should be autoblocked. |
| EXPECT_TRUE(autoblocker()->RecordDismissAndEmbargo( |
| url, CONTENT_SETTINGS_TYPE_GEOLOCATION)); |
| result = |
| autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_GEOLOCATION); |
| EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting); |
| EXPECT_EQ(PermissionStatusSource::MULTIPLE_DISMISSALS, result.source); |
| |
| histograms.ExpectTotalCount("Permissions.AutoBlocker.SafeBrowsingResponse", |
| 0); |
| histograms.ExpectTotalCount( |
| "Permissions.AutoBlocker.SafeBrowsingResponseTime", 0); |
| // Accelerate time forward, check that the embargo status is lifted and the |
| // request won't be automatically blocked. |
| clock()->Advance(base::TimeDelta::FromDays(8)); |
| result = |
| autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_GEOLOCATION); |
| EXPECT_EQ(CONTENT_SETTING_ASK, result.content_setting); |
| EXPECT_EQ(PermissionStatusSource::UNSPECIFIED, result.source); |
| |
| // Record another dismiss, subsequent requests should be autoblocked again. |
| EXPECT_TRUE(autoblocker()->RecordDismissAndEmbargo( |
| url, CONTENT_SETTINGS_TYPE_GEOLOCATION)); |
| result = |
| autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_GEOLOCATION); |
| EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting); |
| EXPECT_EQ(PermissionStatusSource::MULTIPLE_DISMISSALS, result.source); |
| |
| // Accelerate time again, check embargo is lifted and another permission |
| // request is let through. |
| clock()->Advance(base::TimeDelta::FromDays(8)); |
| result = |
| autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_GEOLOCATION); |
| EXPECT_EQ(CONTENT_SETTING_ASK, result.content_setting); |
| EXPECT_EQ(PermissionStatusSource::UNSPECIFIED, result.source); |
| |
| // Record another dismiss, subsequent requests should be autoblocked again. |
| EXPECT_TRUE(autoblocker()->RecordDismissAndEmbargo( |
| url, CONTENT_SETTINGS_TYPE_GEOLOCATION)); |
| result = |
| autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_GEOLOCATION); |
| EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting); |
| EXPECT_EQ(PermissionStatusSource::MULTIPLE_DISMISSALS, result.source); |
| histograms.ExpectTotalCount("Permissions.AutoBlocker.SafeBrowsingResponse", |
| 0); |
| histograms.ExpectTotalCount( |
| "Permissions.AutoBlocker.SafeBrowsingResponseTime", 0); |
| } |
| |
| // Tests the alternating pattern of the block on multiple ignores behaviour. |
| TEST_F(PermissionDecisionAutoBlockerUnitTest, TestIgnoreEmbargoBackoff) { |
| GURL url("https://www.google.com"); |
| clock()->SetNow(base::Time::Now()); |
| base::HistogramTester histograms; |
| |
| // Record some ignores. |
| EXPECT_FALSE(autoblocker()->RecordIgnoreAndEmbargo( |
| url, CONTENT_SETTINGS_TYPE_MIDI_SYSEX)); |
| EXPECT_FALSE(autoblocker()->RecordIgnoreAndEmbargo( |
| url, CONTENT_SETTINGS_TYPE_MIDI_SYSEX)); |
| |
| // A request with < 4 prior ignores should not be automatically blocked. |
| PermissionResult result = |
| autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_MIDI_SYSEX); |
| EXPECT_EQ(CONTENT_SETTING_ASK, result.content_setting); |
| EXPECT_EQ(PermissionStatusSource::UNSPECIFIED, result.source); |
| |
| // After the 4th ignore subsequent permission requests should be autoblocked. |
| EXPECT_FALSE(autoblocker()->RecordIgnoreAndEmbargo( |
| url, CONTENT_SETTINGS_TYPE_MIDI_SYSEX)); |
| EXPECT_TRUE(autoblocker()->RecordIgnoreAndEmbargo( |
| url, CONTENT_SETTINGS_TYPE_MIDI_SYSEX)); |
| result = |
| autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_MIDI_SYSEX); |
| EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting); |
| EXPECT_EQ(PermissionStatusSource::MULTIPLE_IGNORES, result.source); |
| |
| histograms.ExpectTotalCount("Permissions.AutoBlocker.SafeBrowsingResponse", |
| 0); |
| histograms.ExpectTotalCount( |
| "Permissions.AutoBlocker.SafeBrowsingResponseTime", 0); |
| // Accelerate time forward, check that the embargo status is lifted and the |
| // request won't be automatically blocked. |
| clock()->Advance(base::TimeDelta::FromDays(8)); |
| result = |
| autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_MIDI_SYSEX); |
| EXPECT_EQ(CONTENT_SETTING_ASK, result.content_setting); |
| EXPECT_EQ(PermissionStatusSource::UNSPECIFIED, result.source); |
| |
| // Record another dismiss, subsequent requests should be autoblocked again. |
| EXPECT_TRUE(autoblocker()->RecordIgnoreAndEmbargo( |
| url, CONTENT_SETTINGS_TYPE_MIDI_SYSEX)); |
| result = |
| autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_MIDI_SYSEX); |
| EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting); |
| EXPECT_EQ(PermissionStatusSource::MULTIPLE_IGNORES, result.source); |
| |
| // Accelerate time again, check embargo is lifted and another permission |
| // request is let through. |
| clock()->Advance(base::TimeDelta::FromDays(8)); |
| result = |
| autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_MIDI_SYSEX); |
| EXPECT_EQ(CONTENT_SETTING_ASK, result.content_setting); |
| EXPECT_EQ(PermissionStatusSource::UNSPECIFIED, result.source); |
| |
| // Record another dismiss, subsequent requests should be autoblocked again. |
| EXPECT_TRUE(autoblocker()->RecordIgnoreAndEmbargo( |
| url, CONTENT_SETTINGS_TYPE_MIDI_SYSEX)); |
| result = |
| autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_MIDI_SYSEX); |
| EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting); |
| EXPECT_EQ(PermissionStatusSource::MULTIPLE_IGNORES, result.source); |
| histograms.ExpectTotalCount("Permissions.AutoBlocker.SafeBrowsingResponse", |
| 0); |
| histograms.ExpectTotalCount( |
| "Permissions.AutoBlocker.SafeBrowsingResponseTime", 0); |
| } |
| |
| // Test the logic for a combination of blacklisting and dismissal embargo. |
| TEST_F(PermissionDecisionAutoBlockerUnitTest, TestExpiringOverlappingEmbargo) { |
| GURL url("https://www.google.com"); |
| clock()->SetNow(base::Time::Now()); |
| |
| // Place under blacklist embargo and check the status. |
| PlaceUnderBlacklistEmbargo(url, CONTENT_SETTINGS_TYPE_GEOLOCATION); |
| clock()->Advance(base::TimeDelta::FromDays(5)); |
| PermissionResult result = |
| autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_GEOLOCATION); |
| EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting); |
| EXPECT_EQ(PermissionStatusSource::SAFE_BROWSING_BLACKLIST, result.source); |
| |
| // Record dismisses to place it under dismissal embargo. |
| EXPECT_FALSE(autoblocker()->RecordDismissAndEmbargo( |
| url, CONTENT_SETTINGS_TYPE_GEOLOCATION)); |
| EXPECT_FALSE(autoblocker()->RecordDismissAndEmbargo( |
| url, CONTENT_SETTINGS_TYPE_GEOLOCATION)); |
| EXPECT_TRUE(autoblocker()->RecordDismissAndEmbargo( |
| url, CONTENT_SETTINGS_TYPE_GEOLOCATION)); |
| |
| // Accelerate time to a point where the blacklist embargo should be expired |
| // and check that dismissal embargo is still set. |
| clock()->Advance(base::TimeDelta::FromDays(3)); |
| result = |
| autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_GEOLOCATION); |
| EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting); |
| EXPECT_EQ(PermissionStatusSource::MULTIPLE_DISMISSALS, result.source); |
| |
| // Record an ignore embargo. |
| EXPECT_FALSE(autoblocker()->RecordIgnoreAndEmbargo( |
| url, CONTENT_SETTINGS_TYPE_GEOLOCATION)); |
| EXPECT_FALSE(autoblocker()->RecordIgnoreAndEmbargo( |
| url, CONTENT_SETTINGS_TYPE_GEOLOCATION)); |
| EXPECT_FALSE(autoblocker()->RecordIgnoreAndEmbargo( |
| url, CONTENT_SETTINGS_TYPE_GEOLOCATION)); |
| EXPECT_TRUE(autoblocker()->RecordIgnoreAndEmbargo( |
| url, CONTENT_SETTINGS_TYPE_GEOLOCATION)); |
| |
| // Ensure the ignore embargo is still set. |
| clock()->Advance(base::TimeDelta::FromDays(5)); |
| result = |
| autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_GEOLOCATION); |
| EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting); |
| EXPECT_EQ(PermissionStatusSource::MULTIPLE_IGNORES, result.source); |
| } |
| |
| TEST_F(PermissionDecisionAutoBlockerUnitTest, TestSafeBrowsingTimeout) { |
| GURL url("https://www.google.com"); |
| clock()->SetNow(base::Time::Now()); |
| base::HistogramTester histograms; |
| |
| scoped_refptr<MockSafeBrowsingDatabaseManager> db_manager = |
| new MockSafeBrowsingDatabaseManager(false /* perform_callback */, |
| true /* enabled */); |
| std::set<std::string> blacklisted_permissions{"GEOLOCATION"}; |
| db_manager->BlacklistUrlPermissions(url, blacklisted_permissions); |
| SetSafeBrowsingDatabaseManagerAndTimeoutForTesting(db_manager, |
| 0 /* timeout in ms */); |
| |
| CheckSafeBrowsingBlacklist(url, CONTENT_SETTINGS_TYPE_GEOLOCATION); |
| EXPECT_TRUE(callback_was_run()); |
| EXPECT_FALSE(last_embargoed_status()); |
| |
| PermissionResult result = |
| autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_GEOLOCATION); |
| EXPECT_EQ(CONTENT_SETTING_ASK, result.content_setting); |
| EXPECT_EQ(PermissionStatusSource::UNSPECIFIED, result.source); |
| |
| histograms.ExpectUniqueSample("Permissions.AutoBlocker.SafeBrowsingResponse", |
| static_cast<int>(SafeBrowsingResponse::TIMEOUT), |
| 1); |
| histograms.ExpectTotalCount( |
| "Permissions.AutoBlocker.SafeBrowsingResponseTime", 1); |
| db_manager->SetPerformCallback(true); |
| SetSafeBrowsingDatabaseManagerAndTimeoutForTesting(db_manager, |
| 2000 /* timeout in ms */); |
| |
| clock()->Advance(base::TimeDelta::FromDays(1)); |
| CheckSafeBrowsingBlacklist(url, CONTENT_SETTINGS_TYPE_GEOLOCATION); |
| EXPECT_TRUE(callback_was_run()); |
| EXPECT_TRUE(last_embargoed_status()); |
| histograms.ExpectTotalCount("Permissions.AutoBlocker.SafeBrowsingResponse", |
| 2); |
| histograms.ExpectTotalCount( |
| "Permissions.AutoBlocker.SafeBrowsingResponseTime", 2); |
| histograms.ExpectBucketCount( |
| "Permissions.AutoBlocker.SafeBrowsingResponse", |
| static_cast<int>(SafeBrowsingResponse::BLACKLISTED), 1); |
| clock()->Advance(base::TimeDelta::FromDays(1)); |
| result = |
| autoblocker()->GetEmbargoResult(url, CONTENT_SETTINGS_TYPE_GEOLOCATION); |
| EXPECT_EQ(CONTENT_SETTING_BLOCK, result.content_setting); |
| EXPECT_EQ(PermissionStatusSource::SAFE_BROWSING_BLACKLIST, result.source); |
| } |
| |
| // Test that a blacklisted permission should not be autoblocked if the database |
| // manager is disabled. |
| TEST_F(PermissionDecisionAutoBlockerUnitTest, TestDisabledDatabaseManager) { |
| GURL url("https://www.google.com"); |
| scoped_refptr<MockSafeBrowsingDatabaseManager> db_manager = |
| new MockSafeBrowsingDatabaseManager(true /* perform_callback */, |
| false /* enabled */); |
| std::set<std::string> blacklisted_permissions{"GEOLOCATION"}; |
| db_manager->BlacklistUrlPermissions(url, blacklisted_permissions); |
| SetSafeBrowsingDatabaseManagerAndTimeoutForTesting(db_manager, |
| 2000 /* timeout in ms */); |
| CheckSafeBrowsingBlacklist(url, CONTENT_SETTINGS_TYPE_GEOLOCATION); |
| EXPECT_TRUE(callback_was_run()); |
| EXPECT_FALSE(last_embargoed_status()); |
| } |
| |
| TEST_F(PermissionDecisionAutoBlockerUnitTest, TestSafeBrowsingResponse) { |
| GURL url("https://www.google.com"); |
| clock()->SetNow(base::Time::Now()); |
| base::HistogramTester histograms; |
| |
| scoped_refptr<MockSafeBrowsingDatabaseManager> db_manager = |
| new MockSafeBrowsingDatabaseManager(true /* perform_callback */, |
| true /* enabled */); |
| std::set<std::string> blacklisted_permissions{"GEOLOCATION"}; |
| db_manager->BlacklistUrlPermissions(url, blacklisted_permissions); |
| SetSafeBrowsingDatabaseManagerAndTimeoutForTesting(db_manager, |
| 0 /* timeout in ms */); |
| |
| CheckSafeBrowsingBlacklist(url, CONTENT_SETTINGS_TYPE_NOTIFICATIONS); |
| EXPECT_FALSE(last_embargoed_status()); |
| histograms.ExpectUniqueSample( |
| "Permissions.AutoBlocker.SafeBrowsingResponse", |
| static_cast<int>(SafeBrowsingResponse::NOT_BLACKLISTED), 1); |
| } |