| Index: components/safe_browsing/password_protection/password_protection_service.cc
|
| diff --git a/components/safe_browsing/password_protection/password_protection_service.cc b/components/safe_browsing/password_protection/password_protection_service.cc
|
| index c7ec2a9da6a9fde1039c7fe0a56bd3bad6657ce8..fca28e9a0cc21f3d2b805cf930b9d710f95c6962 100644
|
| --- a/components/safe_browsing/password_protection/password_protection_service.cc
|
| +++ b/components/safe_browsing/password_protection/password_protection_service.cc
|
| @@ -4,6 +4,9 @@
|
|
|
| #include "components/safe_browsing/password_protection/password_protection_service.h"
|
|
|
| +#include <stddef.h>
|
| +#include <string>
|
| +
|
| #include "base/base64.h"
|
| #include "base/bind.h"
|
| #include "base/callback.h"
|
| @@ -38,6 +41,7 @@ const char kVerdictProto[] = "verdict_proto";
|
| const int kRequestTimeoutMs = 10000;
|
| const char kPasswordProtectionRequestUrl[] =
|
| "https://sb-ssl.google.com/safebrowsing/clientreport/login";
|
| +const char kPasswordOnFocusCacheKey[] = "password_on_focus_cache_key";
|
|
|
| // Helper function to determine if the given origin matches content settings
|
| // map's patterns.
|
| @@ -87,7 +91,8 @@ PasswordProtectionService::PasswordProtectionService(
|
| scoped_refptr<net::URLRequestContextGetter> request_context_getter,
|
| HistoryService* history_service,
|
| HostContentSettingsMap* host_content_settings_map)
|
| - : stored_verdict_count_(-1),
|
| + : stored_verdict_count_password_on_focus_(-1),
|
| + stored_verdict_count_password_entry_(-1),
|
| database_manager_(database_manager),
|
| request_context_getter_(request_context_getter),
|
| history_service_observer_(this),
|
| @@ -122,24 +127,48 @@ bool PasswordProtectionService::CanGetReputationOfURL(const GURL& url) {
|
| hostname.find('.') != std::string::npos;
|
| }
|
|
|
| +// We cache both types of pings under the same content settings type (
|
| +// CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION). Since UNFAMILIAR_LOGING_PAGE
|
| +// verdicts are only enabled on extended reporting users, we cache them one
|
| +// layer lower in the content setting DictionaryValue than PASSWORD_REUSE_EVENT
|
| +// verdicts.
|
| +// In other words, to cache a UNFAMILIAR_LOGIN_PAGE verdict we needs two levels
|
| +// of keys: (1) origin, (2) cache expression returned in verdict.
|
| +// To cache a PASSWORD_REUSE_EVENT, three levels of keys are used:
|
| +// (1) origin, (2) 2nd level key is always |kPasswordOnFocusCacheKey|,
|
| +// (3) cache expression.
|
| LoginReputationClientResponse::VerdictType
|
| PasswordProtectionService::GetCachedVerdict(
|
| const GURL& url,
|
| + TriggerType trigger_type,
|
| LoginReputationClientResponse* out_response) {
|
| + DCHECK(trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE ||
|
| + trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
|
| +
|
| if (!url.is_valid())
|
| return LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED;
|
|
|
| - DCHECK(content_settings_);
|
| -
|
| GURL hostname = GetHostNameWithHTTPScheme(url);
|
| - std::unique_ptr<base::DictionaryValue> verdict_dictionary =
|
| + std::unique_ptr<base::DictionaryValue> cache_dictionary =
|
| base::DictionaryValue::From(content_settings_->GetWebsiteSetting(
|
| hostname, GURL(), CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION,
|
| std::string(), nullptr));
|
| - // Return early if there is no verdict cached for this origin.
|
| - if (!verdict_dictionary.get() || verdict_dictionary->empty())
|
| +
|
| + if (!cache_dictionary.get() || cache_dictionary->empty())
|
| return LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED;
|
|
|
| + base::DictionaryValue* verdict_dictionary = nullptr;
|
| + if (trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE) {
|
| + // All UNFAMILIAR_LOGIN_PAGE verdicts (a.k.a password on focus ping)
|
| + // are cached under |kPasswordOnFocusCacheKey|.
|
| + if (!cache_dictionary->GetDictionaryWithoutPathExpansion(
|
| + base::StringPiece(kPasswordOnFocusCacheKey), &verdict_dictionary)) {
|
| + return LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED;
|
| + }
|
| + } else {
|
| + verdict_dictionary = cache_dictionary.get();
|
| + }
|
| +
|
| std::vector<std::string> paths;
|
| GeneratePathVariantsWithoutQuery(url, &paths);
|
| int max_path_depth = -1;
|
| @@ -148,8 +177,12 @@ PasswordProtectionService::GetCachedVerdict(
|
| // For all the verdicts of the same origin, we key them by |cache_expression|.
|
| // Its corresponding value is a DictionaryValue contains its creation time and
|
| // the serialized verdict proto.
|
| - for (base::DictionaryValue::Iterator it(*verdict_dictionary.get());
|
| - !it.IsAtEnd(); it.Advance()) {
|
| + for (base::DictionaryValue::Iterator it(*verdict_dictionary); !it.IsAtEnd();
|
| + it.Advance()) {
|
| + if (trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT &&
|
| + it.key() == base::StringPiece(kPasswordOnFocusCacheKey)) {
|
| + continue;
|
| + }
|
| base::DictionaryValue* verdict_entry = nullptr;
|
| CHECK(verdict_dictionary->GetDictionaryWithoutPathExpansion(
|
| it.key() /* cache_expression */, &verdict_entry));
|
| @@ -180,39 +213,65 @@ PasswordProtectionService::GetCachedVerdict(
|
|
|
| void PasswordProtectionService::CacheVerdict(
|
| const GURL& url,
|
| + TriggerType trigger_type,
|
| LoginReputationClientResponse* verdict,
|
| const base::Time& receive_time) {
|
| DCHECK(verdict);
|
| DCHECK(content_settings_);
|
| + DCHECK(trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE ||
|
| + trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
|
|
|
| GURL hostname = GetHostNameWithHTTPScheme(url);
|
| - std::unique_ptr<base::DictionaryValue> verdict_dictionary =
|
| + int* stored_verdict_count =
|
| + trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE
|
| + ? &stored_verdict_count_password_on_focus_
|
| + : &stored_verdict_count_password_entry_;
|
| + std::unique_ptr<base::DictionaryValue> cache_dictionary =
|
| base::DictionaryValue::From(content_settings_->GetWebsiteSetting(
|
| hostname, GURL(), CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION,
|
| std::string(), nullptr));
|
|
|
| - if (!verdict_dictionary.get())
|
| - verdict_dictionary = base::MakeUnique<base::DictionaryValue>();
|
| -
|
| - std::unique_ptr<base::DictionaryValue> verdict_entry =
|
| - CreateDictionaryFromVerdict(verdict, receive_time);
|
| + if (!cache_dictionary || !cache_dictionary.get())
|
| + cache_dictionary = base::MakeUnique<base::DictionaryValue>();
|
| +
|
| + std::unique_ptr<base::DictionaryValue> verdict_entry(
|
| + CreateDictionaryFromVerdict(verdict, receive_time));
|
| +
|
| + base::DictionaryValue* verdict_dictionary = nullptr;
|
| + if (trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE) {
|
| + // All UNFAMILIAR_LOGIN_PAGE verdicts (a.k.a password on focus ping)
|
| + // are cached under |kPasswordOnFocusCacheKey|.
|
| + if (!cache_dictionary->GetDictionaryWithoutPathExpansion(
|
| + base::StringPiece(kPasswordOnFocusCacheKey), &verdict_dictionary)) {
|
| + verdict_dictionary = cache_dictionary->SetDictionaryWithoutPathExpansion(
|
| + base::StringPiece(kPasswordOnFocusCacheKey),
|
| + base::MakeUnique<base::DictionaryValue>());
|
| + }
|
| + } else {
|
| + verdict_dictionary = cache_dictionary.get();
|
| + }
|
|
|
| // Increases stored verdict count if we haven't seen this cache expression
|
| // before.
|
| if (!verdict_dictionary->HasKey(verdict->cache_expression()))
|
| - stored_verdict_count_ = GetStoredVerdictCount() + 1;
|
| + *stored_verdict_count = GetStoredVerdictCount(trigger_type) + 1;
|
| +
|
| // If same cache_expression is already in this verdict_dictionary, we simply
|
| // override it.
|
| verdict_dictionary->SetWithoutPathExpansion(verdict->cache_expression(),
|
| std::move(verdict_entry));
|
| content_settings_->SetWebsiteSettingDefaultScope(
|
| hostname, GURL(), CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION,
|
| - std::string(), std::move(verdict_dictionary));
|
| + std::string(), std::move(cache_dictionary));
|
| }
|
|
|
| void PasswordProtectionService::CleanUpExpiredVerdicts() {
|
| DCHECK(content_settings_);
|
| - if (GetStoredVerdictCount() <= 0)
|
| +
|
| + if (GetStoredVerdictCount(
|
| + LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE) <= 0 &&
|
| + GetStoredVerdictCount(
|
| + LoginReputationClientRequest::PASSWORD_REUSE_EVENT) <= 0)
|
| return;
|
|
|
| ContentSettingsForOneType password_protection_settings;
|
| @@ -224,44 +283,29 @@ void PasswordProtectionService::CleanUpExpiredVerdicts() {
|
| password_protection_settings) {
|
| GURL primary_pattern_url = GURL(source.primary_pattern.ToString());
|
| // Find all verdicts associated with this origin.
|
| - std::unique_ptr<base::DictionaryValue> verdict_dictionary =
|
| + std::unique_ptr<base::DictionaryValue> cache_dictionary =
|
| base::DictionaryValue::From(content_settings_->GetWebsiteSetting(
|
| primary_pattern_url, GURL(),
|
| CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, std::string(), nullptr));
|
| - std::vector<std::string> expired_keys;
|
| - for (base::DictionaryValue::Iterator it(*verdict_dictionary.get());
|
| - !it.IsAtEnd(); it.Advance()) {
|
| - base::DictionaryValue* verdict_entry = nullptr;
|
| - CHECK(verdict_dictionary->GetDictionaryWithoutPathExpansion(
|
| - it.key(), &verdict_entry));
|
| - int verdict_received_time;
|
| - LoginReputationClientResponse verdict;
|
| - CHECK(ParseVerdictEntry(verdict_entry, &verdict_received_time, &verdict));
|
| -
|
| - if (IsCacheExpired(verdict_received_time, verdict.cache_duration_sec())) {
|
| - // Since DictionaryValue::Iterator cannot be used to modify the
|
| - // dictionary, we record the keys of expired verdicts in |expired_keys|
|
| - // and remove them in the next for-loop.
|
| - expired_keys.push_back(it.key());
|
| - }
|
| - }
|
| -
|
| - for (const std::string& key : expired_keys) {
|
| - verdict_dictionary->RemoveWithoutPathExpansion(key, nullptr);
|
| - stored_verdict_count_--;
|
| - }
|
| -
|
| - if (verdict_dictionary->size() == 0u) {
|
| + bool has_expired_password_on_focus_entry = RemoveExpiredVerdicts(
|
| + LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
|
| + cache_dictionary.get());
|
| + bool has_expired_password_reuse_entry = RemoveExpiredVerdicts(
|
| + LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
|
| + cache_dictionary.get());
|
| +
|
| + if (cache_dictionary->size() == 0u) {
|
| content_settings_->ClearSettingsForOneTypeWithPredicate(
|
| CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, base::Time(),
|
| base::Bind(&OriginMatchPrimaryPattern, primary_pattern_url));
|
| - } else if (expired_keys.size() > 0u) {
|
| + } else if (has_expired_password_on_focus_entry ||
|
| + has_expired_password_reuse_entry) {
|
| // Set the website setting of this origin with the updated
|
| // |verdict_diectionary|.
|
| content_settings_->SetWebsiteSettingDefaultScope(
|
| primary_pattern_url, GURL(),
|
| CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, std::string(),
|
| - std::move(verdict_dictionary));
|
| + std::move(cache_dictionary));
|
| }
|
| }
|
| }
|
| @@ -272,14 +316,15 @@ void PasswordProtectionService::StartRequest(
|
| const GURL& password_form_action,
|
| const GURL& password_form_frame_url,
|
| const std::string& saved_domain,
|
| - LoginReputationClientRequest::TriggerType type,
|
| + TriggerType trigger_type,
|
| bool password_field_exists) {
|
| DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
| scoped_refptr<PasswordProtectionRequest> request(
|
| new PasswordProtectionRequest(
|
| web_contents, main_frame_url, password_form_action,
|
| - password_form_frame_url, saved_domain, type, password_field_exists,
|
| - this, GetRequestTimeoutInMS()));
|
| + password_form_frame_url, saved_domain, trigger_type,
|
| + password_field_exists, this, GetRequestTimeoutInMS()));
|
| +
|
| DCHECK(request);
|
| request->Start();
|
| requests_.insert(std::move(request));
|
| @@ -332,11 +377,11 @@ void PasswordProtectionService::RequestFinished(
|
|
|
| if (response) {
|
| if (!already_cached) {
|
| - CacheVerdict(request->main_frame_url(), response.get(),
|
| - base::Time::Now());
|
| + CacheVerdict(request->main_frame_url(), request->trigger_type(),
|
| + response.get(), base::Time::Now());
|
| }
|
|
|
| - if (request->request_type() ==
|
| + if (request->trigger_type() ==
|
| LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE &&
|
| response->verdict_type() == LoginReputationClientResponse::PHISHING &&
|
| base::FeatureList::IsEnabled(kPasswordProtectionInterstitial)) {
|
| @@ -379,30 +424,49 @@ GURL PasswordProtectionService::GetPasswordProtectionRequestUrl() {
|
| return url.Resolve("?key=" + net::EscapeQueryParamValue(api_key, true));
|
| }
|
|
|
| -int PasswordProtectionService::GetStoredVerdictCount() {
|
| +int PasswordProtectionService::GetStoredVerdictCount(TriggerType trigger_type) {
|
| DCHECK(content_settings_);
|
| + DCHECK(trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE ||
|
| + trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
|
| + int* stored_verdict_count =
|
| + trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE
|
| + ? &stored_verdict_count_password_on_focus_
|
| + : &stored_verdict_count_password_entry_;
|
| // If we have already computed this, return its value.
|
| - if (stored_verdict_count_ >= 0)
|
| - return stored_verdict_count_;
|
| + if (*stored_verdict_count >= 0)
|
| + return *stored_verdict_count;
|
|
|
| ContentSettingsForOneType password_protection_settings;
|
| content_settings_->GetSettingsForOneType(
|
| CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, std::string(),
|
| &password_protection_settings);
|
| - stored_verdict_count_ = 0;
|
| + stored_verdict_count_password_on_focus_ = 0;
|
| + stored_verdict_count_password_entry_ = 0;
|
| if (password_protection_settings.empty())
|
| return 0;
|
|
|
| for (const ContentSettingPatternSource& source :
|
| password_protection_settings) {
|
| - std::unique_ptr<base::DictionaryValue> verdict_dictionary =
|
| + std::unique_ptr<base::DictionaryValue> cache_dictionary =
|
| base::DictionaryValue::From(content_settings_->GetWebsiteSetting(
|
| GURL(source.primary_pattern.ToString()), GURL(),
|
| CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, std::string(), nullptr));
|
| - if (verdict_dictionary.get() && !verdict_dictionary->empty())
|
| - stored_verdict_count_ += static_cast<int>(verdict_dictionary->size());
|
| + if (cache_dictionary.get() && !cache_dictionary->empty()) {
|
| + stored_verdict_count_password_entry_ +=
|
| + static_cast<int>(cache_dictionary->size());
|
| + base::DictionaryValue* password_on_focus_dict = nullptr;
|
| + if (cache_dictionary->GetDictionaryWithoutPathExpansion(
|
| + base::StringPiece(kPasswordOnFocusCacheKey),
|
| + &password_on_focus_dict)) {
|
| + // Substracts 1 from password_entry count if |kPasswordOnFocusCacheKey|
|
| + // presents.
|
| + stored_verdict_count_password_entry_ -= 1;
|
| + stored_verdict_count_password_on_focus_ +=
|
| + static_cast<int>(password_on_focus_dict->size());
|
| + }
|
| + }
|
| }
|
| - return stored_verdict_count_;
|
| + return *stored_verdict_count;
|
| }
|
|
|
| int PasswordProtectionService::GetRequestTimeoutInMS() {
|
| @@ -410,7 +474,7 @@ int PasswordProtectionService::GetRequestTimeoutInMS() {
|
| }
|
|
|
| void PasswordProtectionService::FillUserPopulation(
|
| - const LoginReputationClientRequest::TriggerType& request_type,
|
| + TriggerType trigger_type,
|
| LoginReputationClientRequest* request_proto) {
|
| ChromeUserPopulation* user_population = request_proto->mutable_population();
|
| user_population->set_user_population(
|
| @@ -440,8 +504,10 @@ void PasswordProtectionService::OnURLsDeleted(
|
| bool expired,
|
| const history::URLRows& deleted_rows,
|
| const std::set<GURL>& favicon_urls) {
|
| - if (stored_verdict_count_ <= 0)
|
| + if (stored_verdict_count_password_on_focus_ <= 0 &&
|
| + stored_verdict_count_password_entry_ <= 0) {
|
| return;
|
| + }
|
|
|
| BrowserThread::PostTask(
|
| BrowserThread::UI, FROM_HERE,
|
| @@ -463,7 +529,8 @@ void PasswordProtectionService::RemoveContentSettingsOnURLsDeleted(
|
| if (all_history) {
|
| content_settings_->ClearSettingsForOneType(
|
| CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION);
|
| - stored_verdict_count_ = 0;
|
| + stored_verdict_count_password_on_focus_ = 0;
|
| + stored_verdict_count_password_entry_ = 0;
|
| return;
|
| }
|
|
|
| @@ -474,24 +541,106 @@ void PasswordProtectionService::RemoveContentSettingsOnURLsDeleted(
|
| for (const history::URLRow& row : deleted_rows) {
|
| if (!row.url().SchemeIsHTTPOrHTTPS())
|
| continue;
|
| - GURL url_key = GetHostNameWithHTTPScheme(row.url());
|
| - std::unique_ptr<base::DictionaryValue> verdict_dictionary =
|
| - base::DictionaryValue::From(content_settings_->GetWebsiteSetting(
|
| - url_key, GURL(), CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION,
|
| - std::string(), nullptr));
|
|
|
| - // Move on if we have no cached verdict for this deleted history row.
|
| - if (!verdict_dictionary.get() || verdict_dictionary->empty())
|
| - continue;
|
| -
|
| - int verdict_count = static_cast<int>(verdict_dictionary->size());
|
| - stored_verdict_count_ = GetStoredVerdictCount() - verdict_count;
|
| + GURL url_key = GetHostNameWithHTTPScheme(row.url());
|
| + stored_verdict_count_password_on_focus_ =
|
| + GetStoredVerdictCount(
|
| + LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE) -
|
| + GetVerdictCountForURL(
|
| + url_key, LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE);
|
| + stored_verdict_count_password_entry_ =
|
| + GetStoredVerdictCount(
|
| + LoginReputationClientRequest::PASSWORD_REUSE_EVENT) -
|
| + GetVerdictCountForURL(
|
| + url_key, LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
|
| content_settings_->ClearSettingsForOneTypeWithPredicate(
|
| CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, base::Time(),
|
| base::Bind(&OriginMatchPrimaryPattern, url_key));
|
| }
|
| }
|
|
|
| +int PasswordProtectionService::GetVerdictCountForURL(const GURL& url,
|
| + TriggerType trigger_type) {
|
| + DCHECK(trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE ||
|
| + trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
|
| + std::unique_ptr<base::DictionaryValue> cache_dictionary =
|
| + base::DictionaryValue::From(content_settings_->GetWebsiteSetting(
|
| + url, GURL(), CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, std::string(),
|
| + nullptr));
|
| + if (!cache_dictionary.get() || cache_dictionary->empty())
|
| + return 0;
|
| +
|
| + if (trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE) {
|
| + base::DictionaryValue* password_on_focus_dict = nullptr;
|
| + return cache_dictionary->GetDictionaryWithoutPathExpansion(
|
| + base::StringPiece(kPasswordOnFocusCacheKey),
|
| + &password_on_focus_dict)
|
| + ? password_on_focus_dict->size()
|
| + : 0;
|
| + } else {
|
| + return cache_dictionary->HasKey(base::StringPiece(kPasswordOnFocusCacheKey))
|
| + ? cache_dictionary->size() - 1
|
| + : cache_dictionary->size();
|
| + }
|
| +}
|
| +
|
| +bool PasswordProtectionService::RemoveExpiredVerdicts(
|
| + TriggerType trigger_type,
|
| + base::DictionaryValue* cache_dictionary) {
|
| + DCHECK(trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE ||
|
| + trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
|
| + base::DictionaryValue* verdict_dictionary = nullptr;
|
| + if (trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT) {
|
| + verdict_dictionary = cache_dictionary;
|
| + } else {
|
| + if (!cache_dictionary->GetDictionaryWithoutPathExpansion(
|
| + base::StringPiece(kPasswordOnFocusCacheKey), &verdict_dictionary)) {
|
| + return false;
|
| + }
|
| + }
|
| +
|
| + if (!verdict_dictionary || verdict_dictionary->empty())
|
| + return false;
|
| +
|
| + std::vector<std::string> expired_keys;
|
| + for (base::DictionaryValue::Iterator it(*verdict_dictionary); !it.IsAtEnd();
|
| + it.Advance()) {
|
| + if (trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT &&
|
| + it.key() == std::string(kPasswordOnFocusCacheKey))
|
| + continue;
|
| +
|
| + base::DictionaryValue* verdict_entry = nullptr;
|
| + CHECK(verdict_dictionary->GetDictionaryWithoutPathExpansion(
|
| + it.key(), &verdict_entry));
|
| + int verdict_received_time;
|
| + LoginReputationClientResponse verdict;
|
| + CHECK(ParseVerdictEntry(verdict_entry, &verdict_received_time, &verdict));
|
| +
|
| + if (IsCacheExpired(verdict_received_time, verdict.cache_duration_sec())) {
|
| + // Since DictionaryValue::Iterator cannot be used to modify the
|
| + // dictionary, we record the keys of expired verdicts in |expired_keys|
|
| + // and remove them in the next for-loop.
|
| + expired_keys.push_back(it.key());
|
| + }
|
| + }
|
| +
|
| + for (const std::string& key : expired_keys) {
|
| + verdict_dictionary->RemoveWithoutPathExpansion(key, nullptr);
|
| + if (trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT)
|
| + stored_verdict_count_password_entry_--;
|
| + else
|
| + stored_verdict_count_password_on_focus_--;
|
| + }
|
| +
|
| + if (trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE &&
|
| + verdict_dictionary->size() == 0U) {
|
| + cache_dictionary->RemoveWithoutPathExpansion(
|
| + base::StringPiece(kPasswordOnFocusCacheKey), nullptr);
|
| + }
|
| +
|
| + return expired_keys.size() > 0U;
|
| +}
|
| +
|
| // static
|
| bool PasswordProtectionService::ParseVerdictEntry(
|
| base::DictionaryValue* verdict_entry,
|
|
|