Chromium Code Reviews| Index: chrome/browser/safe_browsing/mojo_safe_browsing_impl.cc |
| diff --git a/chrome/browser/safe_browsing/mojo_safe_browsing_impl.cc b/chrome/browser/safe_browsing/mojo_safe_browsing_impl.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..165bb38a939822d545b956ace5c0cedea2ff5cb4 |
| --- /dev/null |
| +++ b/chrome/browser/safe_browsing/mojo_safe_browsing_impl.cc |
| @@ -0,0 +1,304 @@ |
| +// Copyright 2017 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/safe_browsing/mojo_safe_browsing_impl.h" |
| + |
| +#include <vector> |
| + |
| +#include "base/memory/ptr_util.h" |
| +#include "base/memory/weak_ptr.h" |
| +#include "base/timer/timer.h" |
| +#include "chrome/browser/browser_process.h" |
| +#include "chrome/browser/prerender/prerender_contents.h" |
| +#include "chrome/browser/safe_browsing/ui_manager.h" |
| +#include "components/safe_browsing_db/database_manager.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/common/resource_type.h" |
| +#include "mojo/public/cpp/bindings/strong_binding.h" |
| +#include "net/base/load_flags.h" |
| + |
| +namespace safe_browsing { |
| +namespace { |
| + |
| +// TODO(yzshen): Share such value with safe_browsing::BaseResourceThrottle. |
| +// Maximum time in milliseconds to wait for the SafeBrowsing service reputation |
| +// check. After this amount of time the outstanding check will be aborted, and |
| +// the resource will be treated as if it were safe. |
| +const int kCheckUrlTimeoutMs = 5000; |
| + |
| +content::WebContents* GetWebContentsFromID(int render_process_id, |
| + int render_frame_id) { |
| + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| + content::RenderFrameHost* render_frame_host = |
| + content::RenderFrameHost::FromID(render_process_id, render_frame_id); |
| + if (!render_frame_host) |
| + return nullptr; |
| + |
| + return content::WebContents::FromRenderFrameHost(render_frame_host); |
| +} |
| + |
| +// TODO(yzshen): Handle the case where SafeBrowsing is not enabled, or |
| +// !database_manager()->IsSupported(). |
| +// TODO(yzshen): Make sure it also works on Andorid. |
| +// TODO(yzshen): Do all the logging like what BaseResourceThrottle does. |
| +class SafeBrowsingUrlCheckerImpl : public chrome::mojom::SafeBrowsingUrlChecker, |
| + public SafeBrowsingDatabaseManager::Client { |
| + public: |
| + SafeBrowsingUrlCheckerImpl( |
| + int load_flags, |
| + content::ResourceType resource_type, |
| + scoped_refptr<SafeBrowsingDatabaseManager> database_manager, |
| + scoped_refptr<SafeBrowsingUIManager> ui_manager, |
| + int render_process_id, |
| + int render_frame_id) |
| + : load_flags_(load_flags), |
| + resource_type_(resource_type), |
| + render_process_id_(render_process_id), |
| + render_frame_id_(render_frame_id), |
| + database_manager_(std::move(database_manager)), |
| + ui_manager_(std::move(ui_manager)), |
| + weak_factory_(this) { |
| + DCHECK_NE(resource_type, content::RESOURCE_TYPE_MAIN_FRAME); |
| + DCHECK_NE(resource_type, content::RESOURCE_TYPE_SUB_FRAME); |
| + } |
| + |
| + ~SafeBrowsingUrlCheckerImpl() override { |
| + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| + |
| + if (state_ == STATE_CHECKING_URL) |
| + database_manager_->CancelCheck(this); |
| + |
| + for (size_t i = next_index_; i < callbacks_.size(); ++i) |
| + std::move(callbacks_[i]).Run(false); |
| + } |
| + |
| + // chrome::mojom::SafeBrowsingUrlChecker implementation. |
| + void CheckUrl(const GURL& url, CheckUrlCallback callback) override { |
| + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| + |
| + DVLOG(1) << "SafeBrowsingUrlCheckerImpl checks URL: " << url; |
| + urls_.push_back(url); |
| + callbacks_.push_back(std::move(callback)); |
| + |
| + ProcessUrls(); |
| + } |
| + |
| + private: |
| + // SafeBrowsingDatabaseManager::Client implementation: |
| + void OnCheckBrowseUrlResult(const GURL& url, |
| + SBThreatType threat_type, |
| + const ThreatMetadata& metadata) override { |
| + DCHECK_EQ(STATE_CHECKING_URL, state_); |
| + DCHECK_LT(next_index_, urls_.size()); |
| + DCHECK_EQ(urls_[next_index_], url); |
| + |
| + timer_.Stop(); |
| + if (threat_type == SB_THREAT_TYPE_SAFE) { |
| + state_ = STATE_NONE; |
| + std::move(callbacks_[next_index_]).Run(true); |
| + next_index_++; |
| + ProcessUrls(); |
| + return; |
| + } |
| + |
| + if (load_flags_ & net::LOAD_PREFETCH) { |
| + BlockAndProcessUrls(); |
| + return; |
| + } |
| + |
| + security_interstitials::UnsafeResource resource; |
| + resource.url = url; |
| + resource.original_url = urls_[0]; |
| + if (urls_.size() > 1) |
| + resource.redirect_urls = |
| + std::vector<GURL>(urls_.begin() + 1, urls_.end()); |
| + resource.is_subresource = true; |
| + resource.is_subframe = false; |
| + resource.threat_type = threat_type; |
| + resource.threat_metadata = metadata; |
| + resource.callback = |
| + base::Bind(&SafeBrowsingUrlCheckerImpl::OnBlockingPageComplete, |
| + weak_factory_.GetWeakPtr()); |
| + resource.callback_thread = content::BrowserThread::GetTaskRunnerForThread( |
| + content::BrowserThread::IO); |
| + resource.web_contents_getter = |
| + base::Bind(&GetWebContentsFromID, render_process_id_, render_frame_id_); |
| + resource.threat_source = database_manager_->GetThreatSource(); |
| + |
| + state_ = STATE_DISPLAYING_BLOCKING_PAGE; |
| + |
| + content::BrowserThread::PostTask( |
| + content::BrowserThread::UI, FROM_HERE, |
| + base::Bind(&SafeBrowsingUrlCheckerImpl::StartDisplayingBlockingPage, |
| + weak_factory_.GetWeakPtr(), ui_manager_, resource)); |
| + } |
| + |
| + static void StartDisplayingBlockingPage( |
| + const base::WeakPtr<SafeBrowsingUrlCheckerImpl>& checker, |
| + scoped_refptr<BaseUIManager> ui_manager, |
| + const security_interstitials::UnsafeResource& resource) { |
| + content::WebContents* web_contents = resource.web_contents_getter.Run(); |
| + if (web_contents) { |
| + prerender::PrerenderContents* prerender_contents = |
| + prerender::PrerenderContents::FromWebContents(web_contents); |
| + if (prerender_contents) { |
| + prerender_contents->Destroy(prerender::FINAL_STATUS_SAFE_BROWSING); |
| + } else { |
| + ui_manager->DisplayBlockingPage(resource); |
| + return; |
| + } |
| + } |
| + |
| + // Tab is gone or it's being prerendered. |
| + content::BrowserThread::PostTask( |
| + content::BrowserThread::IO, FROM_HERE, |
| + base::BindOnce(&SafeBrowsingUrlCheckerImpl::BlockAndProcessUrls, |
| + checker)); |
| + } |
| + |
| + void OnCheckUrlTimeout() { |
| + database_manager_->CancelCheck(this); |
| + |
| + OnCheckBrowseUrlResult(urls_[next_index_], |
| + safe_browsing::SB_THREAT_TYPE_SAFE, |
| + ThreatMetadata()); |
| + } |
| + |
| + void ProcessUrls() { |
| + DCHECK_NE(STATE_BLOCKED, state_); |
| + |
| + if (state_ == STATE_CHECKING_URL || |
| + state_ == STATE_DISPLAYING_BLOCKING_PAGE) { |
| + return; |
| + } |
| + |
| + while (next_index_ < urls_.size()) { |
| + DCHECK_EQ(STATE_NONE, state_); |
| + // TODO(yzshen): Consider moving CanCheckResourceType() to the renderer |
| + // side. That would save some IPCs. It requires a method on the |
| + // SafeBrowsing mojo interface to query all supported resource types. |
| + if (!database_manager_->CanCheckResourceType(resource_type_) || |
| + database_manager_->CheckBrowseUrl(urls_[next_index_], this)) { |
| + std::move(callbacks_[next_index_]).Run(true); |
| + next_index_++; |
| + continue; |
| + } |
| + |
| + state_ = STATE_CHECKING_URL; |
| + // Start a timer to abort the check if it takes too long. |
| + timer_.Start(FROM_HERE, |
| + base::TimeDelta::FromMilliseconds(kCheckUrlTimeoutMs), this, |
| + &SafeBrowsingUrlCheckerImpl::OnCheckUrlTimeout); |
| + |
| + break; |
| + } |
| + } |
| + |
| + void BlockAndProcessUrls() { |
| + DVLOG(1) << "SafeBrowsingUrlCheckerImpl blocks URL: " << urls_[next_index_]; |
| + state_ = STATE_BLOCKED; |
| + |
| + // If user decided to not proceed through a warning, mark all the remaining |
| + // redirects as "bad". |
| + for (; next_index_ < callbacks_.size(); ++next_index_) |
| + std::move(callbacks_[next_index_]).Run(false); |
| + } |
| + |
| + void OnBlockingPageComplete(bool proceed) { |
| + DCHECK_EQ(STATE_DISPLAYING_BLOCKING_PAGE, state_); |
| + |
| + if (proceed) { |
| + state_ = STATE_NONE; |
| + std::move(callbacks_[next_index_]).Run(true); |
| + next_index_++; |
| + ProcessUrls(); |
| + } else { |
| + BlockAndProcessUrls(); |
| + } |
| + } |
| + |
| + enum State { |
| + // Haven't started checking or checking is complete. |
| + STATE_NONE, |
| + // We have one outstanding URL-check. |
| + STATE_CHECKING_URL, |
| + // We're displaying a blocking page. |
| + STATE_DISPLAYING_BLOCKING_PAGE, |
| + // The blocking page has returned *not* to proceed. |
| + STATE_BLOCKED |
| + }; |
| + |
| + const int load_flags_; |
| + const content::ResourceType resource_type_; |
| + const int render_process_id_; |
| + const int render_frame_id_; |
| + scoped_refptr<SafeBrowsingDatabaseManager> database_manager_; |
| + scoped_refptr<BaseUIManager> ui_manager_; |
| + |
| + // The redirect chain for this resource, including the original URL and |
| + // subsequent redirect URLs. |
| + std::vector<GURL> urls_; |
| + // Callbacks corresponding to |urls_| to report check results. |urls_| and |
| + // |callbacks_| are always of the same size. |
| + std::vector<CheckUrlCallback> callbacks_; |
| + // |urls_| before |next_index_| have been checked. If |next_index_| is smaller |
| + // than the size of |urls_|, the URL at |next_index_| is being processed. |
| + size_t next_index_ = 0; |
| + |
| + State state_ = STATE_NONE; |
| + |
| + // Timer to abort the SafeBrowsing check if it takes too long. |
| + base::OneShotTimer timer_; |
| + |
| + base::WeakPtrFactory<SafeBrowsingUrlCheckerImpl> weak_factory_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(SafeBrowsingUrlCheckerImpl); |
| +}; |
| + |
| +} // namespace |
| + |
| +MojoSafeBrowsingImpl::MojoSafeBrowsingImpl( |
| + scoped_refptr<SafeBrowsingDatabaseManager> database_manager, |
| + scoped_refptr<SafeBrowsingUIManager> ui_manager, |
| + int render_process_id) |
| + : database_manager_(std::move(database_manager)), |
| + ui_manager_(std::move(ui_manager)), |
| + render_process_id_(render_process_id) {} |
| + |
| +MojoSafeBrowsingImpl::~MojoSafeBrowsingImpl() { |
| + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| +} |
| + |
| +// static |
| +void MojoSafeBrowsingImpl::Create( |
| + scoped_refptr<SafeBrowsingDatabaseManager> database_manager, |
| + scoped_refptr<SafeBrowsingUIManager> ui_manager, |
| + int render_process_id, |
| + const service_manager::BindSourceInfo& source_info, |
| + chrome::mojom::SafeBrowsingRequest request) { |
| + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| + mojo::MakeStrongBinding(base::MakeUnique<MojoSafeBrowsingImpl>( |
| + std::move(database_manager), |
| + std::move(ui_manager), render_process_id), |
| + std::move(request)); |
| +} |
| + |
| +void MojoSafeBrowsingImpl::CreateCheckerAndCheck( |
| + int32_t render_frame_id, |
| + chrome::mojom::SafeBrowsingUrlCheckerRequest request, |
| + const GURL& url, |
| + int32_t load_flags, |
|
Nathan Parker
2017/06/01 18:22:04
load_flags is int32_t here, and int above. Want t
yzshen1
2017/06/01 18:36:52
Yeah. That is a good question.
I used it as int32_
Nathan Parker
2017/06/01 18:38:49
I assume all our platforms have 32-bit "ints" so i
yzshen1
2017/06/01 18:44:44
Done. I added static_cast to be explicit. Thanks!
|
| + content::ResourceType resource_type, |
| + CreateCheckerAndCheckCallback callback) { |
| + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| + auto checker_impl = base::MakeUnique<SafeBrowsingUrlCheckerImpl>( |
| + load_flags, resource_type, database_manager_, ui_manager_, |
| + render_process_id_, render_frame_id); |
| + checker_impl->CheckUrl(url, std::move(callback)); |
| + mojo::MakeStrongBinding(std::move(checker_impl), std::move(request)); |
| +} |
| + |
| +} // namespace safe_browsing |