| Index: chrome/browser/permissions/permission_context_base.cc
|
| diff --git a/chrome/browser/permissions/permission_context_base.cc b/chrome/browser/permissions/permission_context_base.cc
|
| index ffb0e8fb38fdb17272cc9541756ab27ca4150263..8911b967bc0f3e388c2b945333ccdd47fd7280b6 100644
|
| --- a/chrome/browser/permissions/permission_context_base.cc
|
| +++ b/chrome/browser/permissions/permission_context_base.cc
|
| @@ -5,13 +5,18 @@
|
| #include "chrome/browser/permissions/permission_context_base.h"
|
|
|
| #include <stddef.h>
|
| +
|
| +#include <set>
|
| +#include <string>
|
| #include <utility>
|
|
|
| #include "base/callback.h"
|
| #include "base/logging.h"
|
| #include "base/memory/ptr_util.h"
|
| #include "base/strings/stringprintf.h"
|
| +#include "base/timer/timer.h"
|
| #include "build/build_config.h"
|
| +#include "chrome/browser/browser_process.h"
|
| #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
|
| #include "chrome/browser/permissions/permission_decision_auto_blocker.h"
|
| #include "chrome/browser/permissions/permission_request.h"
|
| @@ -21,14 +26,18 @@
|
| #include "chrome/browser/permissions/permission_uma_util.h"
|
| #include "chrome/browser/permissions/permission_util.h"
|
| #include "chrome/browser/profiles/profile.h"
|
| +#include "chrome/browser/safe_browsing/safe_browsing_service.h"
|
| +#include "chrome/common/chrome_features.h"
|
| #include "chrome/common/pref_names.h"
|
| #include "components/content_settings/core/browser/host_content_settings_map.h"
|
| #include "components/content_settings/core/browser/website_settings_registry.h"
|
| #include "components/prefs/pref_service.h"
|
| +#include "components/safe_browsing_db/database_manager.h"
|
| #include "components/variations/variations_associated_data.h"
|
| #include "content/public/browser/browser_thread.h"
|
| #include "content/public/browser/render_frame_host.h"
|
| #include "content/public/browser/web_contents.h"
|
| +#include "content/public/browser/web_contents_observer.h"
|
| #include "content/public/common/origin_util.h"
|
| #include "url/gurl.h"
|
|
|
| @@ -42,6 +51,133 @@ const char PermissionContextBase::kPermissionsKillSwitchFieldStudy[] =
|
| // static
|
| const char PermissionContextBase::kPermissionsKillSwitchBlockedValue[] =
|
| "blocked";
|
| +// Maximum time in milliseconds to wait for safe browsing service to check a
|
| +// url for blacklisting. After this amount of time, the check will be aborted
|
| +// and the url will be treated as not blacklisted.
|
| +// TODO(meredithl): Revisit this once UMA metrics have data about request time.
|
| +const int kCheckUrlTimeoutMs = 2000;
|
| +
|
| +// The client used when checking whether a permission has been blacklisted by
|
| +// Safe Browsing. The check is done asynchronously as no state can be stored in
|
| +// PermissionContextBase (since additional permission requests may be made).
|
| +// This class must be created and destroyed on the UI thread.
|
| +class PermissionsBlacklistingClient
|
| + : public safe_browsing::SafeBrowsingDatabaseManager::Client,
|
| + public base::RefCountedThreadSafe<PermissionsBlacklistingClient>,
|
| + public content::WebContentsObserver {
|
| + public:
|
| + static void CheckSafeBrowsingBlacklist(
|
| + scoped_refptr<safe_browsing::SafeBrowsingDatabaseManager> db_manager,
|
| + content::PermissionType permission_type,
|
| + const GURL& request_origin,
|
| + content::WebContents* web_contents,
|
| + int timeout,
|
| + base::Callback<void(bool)> callback) {
|
| + DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
| +
|
| + new PermissionsBlacklistingClient(db_manager, permission_type,
|
| + request_origin, web_contents, timeout,
|
| + callback);
|
| + }
|
| +
|
| + private:
|
| + friend class base::RefCountedThreadSafe<PermissionsBlacklistingClient>;
|
| +
|
| + PermissionsBlacklistingClient(
|
| + scoped_refptr<safe_browsing::SafeBrowsingDatabaseManager> db_manager,
|
| + content::PermissionType permission_type,
|
| + const GURL& request_origin,
|
| + content::WebContents* web_contents,
|
| + int timeout,
|
| + base::Callback<void(bool)> callback)
|
| + : content::WebContentsObserver(web_contents),
|
| + db_manager_(db_manager),
|
| + permission_type_(permission_type),
|
| + callback_(callback),
|
| + timeout_(timeout),
|
| + is_active_(true) {
|
| + // Balanced by a call to Release() in OnCheckApiBlacklistUrlResult().
|
| + AddRef();
|
| + content::BrowserThread::PostTask(
|
| + content::BrowserThread::IO, FROM_HERE,
|
| + base::Bind(&PermissionsBlacklistingClient::StartCheck, this,
|
| + request_origin));
|
| + }
|
| +
|
| + ~PermissionsBlacklistingClient() override {}
|
| +
|
| + void StartCheck(const GURL& request_origin) {
|
| + DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
| +
|
| + // Start the timer to interrupt into the client callback method with an
|
| + // empty response if Safe Browsing times out.
|
| + safe_browsing::ThreatMetadata empty_metadata;
|
| + timer_ = base::MakeUnique<base::OneShotTimer>();
|
| + timer_->Start(
|
| + FROM_HERE, base::TimeDelta::FromMilliseconds(timeout_),
|
| + base::Bind(&PermissionsBlacklistingClient::OnCheckApiBlacklistUrlResult,
|
| + this, request_origin, empty_metadata));
|
| + db_manager_->CheckApiBlacklistUrl(request_origin, this);
|
| + }
|
| +
|
| + // SafeBrowsingDatabaseManager::Client implementation.
|
| + void OnCheckApiBlacklistUrlResult(
|
| + const GURL& url,
|
| + const safe_browsing::ThreatMetadata& metadata) override {
|
| + DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
| +
|
| + if (timer_->IsRunning())
|
| + timer_->Stop();
|
| + else
|
| + db_manager_->CancelApiCheck(this);
|
| + timer_.reset(nullptr);
|
| +
|
| + // TODO(meredithl): Convert the strings returned from Safe Browsing to the
|
| + // ones used by PermissionUtil for comparison.
|
| + bool permission_blocked =
|
| + metadata.api_permissions.find(PermissionUtil::GetPermissionString(
|
| + permission_type_)) != metadata.api_permissions.end();
|
| +
|
| + content::BrowserThread::PostTask(
|
| + content::BrowserThread::UI, FROM_HERE,
|
| + base::Bind(
|
| + &PermissionsBlacklistingClient::EvaluateBlacklistResultOnUiThread,
|
| + this, permission_blocked));
|
| + }
|
| +
|
| + void EvaluateBlacklistResultOnUiThread(bool permission_blocked) {
|
| + DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
| +
|
| + if (is_active_)
|
| + callback_.Run(permission_blocked);
|
| + Release();
|
| + }
|
| +
|
| + // WebContentsObserver implementation. Sets the flag so that when the database
|
| + // manager returns with a result, it won't attempt to run the callback with a
|
| + // deleted WebContents.
|
| + void WebContentsDestroyed() override {
|
| + is_active_ = false;
|
| + Observe(nullptr);
|
| + }
|
| +
|
| + scoped_refptr<safe_browsing::SafeBrowsingDatabaseManager> db_manager_;
|
| + content::PermissionType permission_type_;
|
| +
|
| + // PermissionContextBase callback to run on the UI thread.
|
| + base::Callback<void(bool)> callback_;
|
| +
|
| + // Timer to abort the Safe Browsing check if it takes too long. Created and
|
| + // used on the IO Thread.
|
| + std::unique_ptr<base::OneShotTimer> timer_;
|
| + int timeout_;
|
| +
|
| + // True if |callback_| should be invoked, if web_contents() is destroyed, this
|
| + // is set to false.
|
| + bool is_active_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(PermissionsBlacklistingClient);
|
| +};
|
|
|
| PermissionContextBase::PermissionContextBase(
|
| Profile* profile,
|
| @@ -50,6 +186,7 @@ PermissionContextBase::PermissionContextBase(
|
| : profile_(profile),
|
| permission_type_(permission_type),
|
| content_settings_type_(content_settings_type),
|
| + safe_browsing_timeout_(kCheckUrlTimeoutMs),
|
| weak_factory_(this) {
|
| #if defined(OS_ANDROID)
|
| permission_queue_controller_.reset(new PermissionQueueController(
|
| @@ -101,12 +238,60 @@ void PermissionContextBase::RequestPermission(
|
| return;
|
| }
|
|
|
| + if (base::FeatureList::IsEnabled(features::kPermissionsBlacklist)) {
|
| + if (!db_manager_) {
|
| + safe_browsing::SafeBrowsingService* sb_service =
|
| + g_browser_process->safe_browsing_service();
|
| + if (sb_service)
|
| + db_manager_ = sb_service->database_manager();
|
| + }
|
| +
|
| + // The client contacts Safe Browsing, and runs the callback with the result.
|
| + PermissionsBlacklistingClient::CheckSafeBrowsingBlacklist(
|
| + db_manager_, permission_type_, requesting_origin, web_contents,
|
| + safe_browsing_timeout_,
|
| + base::Bind(&PermissionContextBase::ContinueRequestPermission,
|
| + weak_factory_.GetWeakPtr(), web_contents, id,
|
| + requesting_origin, embedding_origin, user_gesture,
|
| + callback));
|
| + } else {
|
| + // TODO(meredithl): Add UMA metrics here.
|
| + ContinueRequestPermission(web_contents, id, requesting_origin,
|
| + embedding_origin, user_gesture, callback,
|
| + false /* permission blocked */);
|
| + }
|
| +}
|
| +
|
| +void PermissionContextBase::ContinueRequestPermission(
|
| + content::WebContents* web_contents,
|
| + const PermissionRequestID& id,
|
| + const GURL& requesting_origin,
|
| + const GURL& embedding_origin,
|
| + bool user_gesture,
|
| + const BrowserPermissionCallback& callback,
|
| + bool permission_blocked) {
|
| + DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
| + if (permission_blocked) {
|
| + // TODO(meredithl): Add UMA metrics here.
|
| + web_contents->GetMainFrame()->AddMessageToConsole(
|
| + content::CONSOLE_MESSAGE_LEVEL_LOG,
|
| + base::StringPrintf(
|
| + "%s permission has been auto-blocked.",
|
| + PermissionUtil::GetPermissionString(permission_type_).c_str()));
|
| + // Permission has been blacklisted, block the request.
|
| + // TODO(meredithl): Consider setting the content setting and persisting
|
| + // the decision to block.
|
| + callback.Run(CONTENT_SETTING_BLOCK);
|
| + return;
|
| + }
|
| + // Site is not blacklisted by Safe Browsing for the requested permission.
|
| ContentSetting content_setting =
|
| GetPermissionStatus(requesting_origin, embedding_origin);
|
| if (content_setting == CONTENT_SETTING_ALLOW) {
|
| HostContentSettingsMapFactory::GetForProfile(profile_)->UpdateLastUsage(
|
| requesting_origin, embedding_origin, content_settings_type_);
|
| }
|
| +
|
| if (content_setting == CONTENT_SETTING_ALLOW ||
|
| content_setting == CONTENT_SETTING_BLOCK) {
|
| NotifyPermissionSet(id, requesting_origin, embedding_origin, callback,
|
| @@ -325,3 +510,10 @@ void PermissionContextBase::UpdateContentSetting(
|
| content_settings_type_, std::string(),
|
| content_setting);
|
| }
|
| +
|
| +void PermissionContextBase::SetSafeBrowsingDatabaseManagerAndTimeoutForTest(
|
| + scoped_refptr<safe_browsing::SafeBrowsingDatabaseManager> db_manager,
|
| + int timeout) {
|
| + db_manager_ = db_manager;
|
| + safe_browsing_timeout_ = timeout;
|
| +}
|
|
|