Roll src/third_party/WebKit f298044:aa8346d (svn 202628:202629)
[chromium-blink-merge.git] / chrome / browser / extensions / blacklist.cc
blobef1cdfe696222c9179d71bf3be866f517b86c9e5
1 // Copyright 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/extensions/blacklist.h"
7 #include <algorithm>
8 #include <iterator>
10 #include "base/bind.h"
11 #include "base/lazy_instance.h"
12 #include "base/memory/ref_counted.h"
13 #include "base/prefs/pref_service.h"
14 #include "base/single_thread_task_runner.h"
15 #include "base/stl_util.h"
16 #include "base/thread_task_runner_handle.h"
17 #include "chrome/browser/browser_process.h"
18 #include "chrome/browser/chrome_notification_types.h"
19 #include "chrome/browser/extensions/blacklist_factory.h"
20 #include "chrome/browser/extensions/blacklist_state_fetcher.h"
21 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
22 #include "chrome/browser/safe_browsing/safe_browsing_util.h"
23 #include "content/public/browser/notification_details.h"
24 #include "content/public/browser/notification_source.h"
25 #include "extensions/browser/extension_prefs.h"
27 using content::BrowserThread;
29 namespace extensions {
31 namespace {
33 // The safe browsing database manager to use. Make this a global/static variable
34 // rather than a member of Blacklist because Blacklist accesses the real
35 // database manager before it has a chance to get a fake one.
36 class LazySafeBrowsingDatabaseManager {
37 public:
38 LazySafeBrowsingDatabaseManager() {
39 #if defined(SAFE_BROWSING_DB_LOCAL)
40 if (g_browser_process && g_browser_process->safe_browsing_service()) {
41 instance_ =
42 g_browser_process->safe_browsing_service()->database_manager();
44 #endif
47 scoped_refptr<SafeBrowsingDatabaseManager> get() {
48 return instance_;
51 void set(scoped_refptr<SafeBrowsingDatabaseManager> instance) {
52 instance_ = instance;
55 private:
56 scoped_refptr<SafeBrowsingDatabaseManager> instance_;
59 static base::LazyInstance<LazySafeBrowsingDatabaseManager> g_database_manager =
60 LAZY_INSTANCE_INITIALIZER;
62 // Implementation of SafeBrowsingDatabaseManager::Client, the class which is
63 // called back from safebrowsing queries.
65 // Constructed on any thread but lives on the IO from then on.
66 class SafeBrowsingClientImpl
67 : public SafeBrowsingDatabaseManager::Client,
68 public base::RefCountedThreadSafe<SafeBrowsingClientImpl> {
69 public:
70 typedef base::Callback<void(const std::set<std::string>&)> OnResultCallback;
72 // Constructs a client to query the database manager for |extension_ids| and
73 // run |callback| with the IDs of those which have been blacklisted.
74 SafeBrowsingClientImpl(
75 const std::set<std::string>& extension_ids,
76 const OnResultCallback& callback)
77 : callback_task_runner_(base::ThreadTaskRunnerHandle::Get()),
78 callback_(callback) {
79 BrowserThread::PostTask(
80 BrowserThread::IO,
81 FROM_HERE,
82 base::Bind(&SafeBrowsingClientImpl::StartCheck, this,
83 g_database_manager.Get().get(),
84 extension_ids));
87 private:
88 friend class base::RefCountedThreadSafe<SafeBrowsingClientImpl>;
89 ~SafeBrowsingClientImpl() override {}
91 // Pass |database_manager| as a parameter to avoid touching
92 // SafeBrowsingService on the IO thread.
93 void StartCheck(scoped_refptr<SafeBrowsingDatabaseManager> database_manager,
94 const std::set<std::string>& extension_ids) {
95 DCHECK_CURRENTLY_ON(BrowserThread::IO);
96 if (database_manager->CheckExtensionIDs(extension_ids, this)) {
97 // Definitely not blacklisted. Callback immediately.
98 callback_task_runner_->PostTask(
99 FROM_HERE,
100 base::Bind(callback_, std::set<std::string>()));
101 return;
103 // Something might be blacklisted, response will come in
104 // OnCheckExtensionsResult.
105 AddRef(); // Balanced in OnCheckExtensionsResult
108 void OnCheckExtensionsResult(const std::set<std::string>& hits) override {
109 DCHECK_CURRENTLY_ON(BrowserThread::IO);
110 callback_task_runner_->PostTask(FROM_HERE, base::Bind(callback_, hits));
111 Release(); // Balanced in StartCheck.
114 scoped_refptr<base::SingleThreadTaskRunner> callback_task_runner_;
115 OnResultCallback callback_;
117 DISALLOW_COPY_AND_ASSIGN(SafeBrowsingClientImpl);
120 void CheckOneExtensionState(
121 const Blacklist::IsBlacklistedCallback& callback,
122 const Blacklist::BlacklistStateMap& state_map) {
123 callback.Run(state_map.empty() ? NOT_BLACKLISTED : state_map.begin()->second);
126 void GetMalwareFromBlacklistStateMap(
127 const Blacklist::GetMalwareIDsCallback& callback,
128 const Blacklist::BlacklistStateMap& state_map) {
129 std::set<std::string> malware;
130 for (Blacklist::BlacklistStateMap::const_iterator it = state_map.begin();
131 it != state_map.end(); ++it) {
132 // TODO(oleg): UNKNOWN is treated as MALWARE for backwards compatibility.
133 // In future GetMalwareIDs will be removed and the caller will have to
134 // deal with BLACKLISTED_UNKNOWN state returned from GetBlacklistedIDs.
135 if (it->second == BLACKLISTED_MALWARE || it->second == BLACKLISTED_UNKNOWN)
136 malware.insert(it->first);
138 callback.Run(malware);
141 } // namespace
143 Blacklist::Observer::Observer(Blacklist* blacklist) : blacklist_(blacklist) {
144 blacklist_->AddObserver(this);
147 Blacklist::Observer::~Observer() {
148 blacklist_->RemoveObserver(this);
151 Blacklist::ScopedDatabaseManagerForTest::ScopedDatabaseManagerForTest(
152 scoped_refptr<SafeBrowsingDatabaseManager> database_manager)
153 : original_(GetDatabaseManager()) {
154 SetDatabaseManager(database_manager);
157 Blacklist::ScopedDatabaseManagerForTest::~ScopedDatabaseManagerForTest() {
158 SetDatabaseManager(original_);
161 Blacklist::Blacklist(ExtensionPrefs* prefs) {
162 scoped_refptr<SafeBrowsingDatabaseManager> database_manager =
163 g_database_manager.Get().get();
164 if (database_manager.get()) {
165 registrar_.Add(
166 this,
167 chrome::NOTIFICATION_SAFE_BROWSING_UPDATE_COMPLETE,
168 content::Source<SafeBrowsingDatabaseManager>(database_manager.get()));
171 // Clear out the old prefs-backed blacklist, stored as empty extension entries
172 // with just a "blacklisted" property.
174 // TODO(kalman): Delete this block of code, see http://crbug.com/295882.
175 std::set<std::string> blacklisted = prefs->GetBlacklistedExtensions();
176 for (std::set<std::string>::iterator it = blacklisted.begin();
177 it != blacklisted.end(); ++it) {
178 if (!prefs->GetInstalledExtensionInfo(*it))
179 prefs->DeleteExtensionPrefs(*it);
183 Blacklist::~Blacklist() {
186 // static
187 Blacklist* Blacklist::Get(content::BrowserContext* context) {
188 return BlacklistFactory::GetForBrowserContext(context);
191 void Blacklist::GetBlacklistedIDs(const std::set<std::string>& ids,
192 const GetBlacklistedIDsCallback& callback) {
193 DCHECK_CURRENTLY_ON(BrowserThread::UI);
195 if (ids.empty() || !g_database_manager.Get().get().get()) {
196 base::ThreadTaskRunnerHandle::Get()->PostTask(
197 FROM_HERE, base::Bind(callback, BlacklistStateMap()));
198 return;
201 // Constructing the SafeBrowsingClientImpl begins the process of asking
202 // safebrowsing for the blacklisted extensions. The set of blacklisted
203 // extensions returned by SafeBrowsing will then be passed to
204 // GetBlacklistStateIDs to get the particular BlacklistState for each id.
205 new SafeBrowsingClientImpl(
206 ids, base::Bind(&Blacklist::GetBlacklistStateForIDs, AsWeakPtr(),
207 callback));
210 void Blacklist::GetMalwareIDs(const std::set<std::string>& ids,
211 const GetMalwareIDsCallback& callback) {
212 GetBlacklistedIDs(ids, base::Bind(&GetMalwareFromBlacklistStateMap,
213 callback));
217 void Blacklist::IsBlacklisted(const std::string& extension_id,
218 const IsBlacklistedCallback& callback) {
219 std::set<std::string> check;
220 check.insert(extension_id);
221 GetBlacklistedIDs(check, base::Bind(&CheckOneExtensionState, callback));
224 void Blacklist::GetBlacklistStateForIDs(
225 const GetBlacklistedIDsCallback& callback,
226 const std::set<std::string>& blacklisted_ids) {
227 DCHECK_CURRENTLY_ON(BrowserThread::UI);
229 std::set<std::string> ids_unknown_state;
230 BlacklistStateMap extensions_state;
231 for (std::set<std::string>::const_iterator it = blacklisted_ids.begin();
232 it != blacklisted_ids.end(); ++it) {
233 BlacklistStateMap::const_iterator cache_it =
234 blacklist_state_cache_.find(*it);
235 if (cache_it == blacklist_state_cache_.end() ||
236 cache_it->second == BLACKLISTED_UNKNOWN) // Do not return UNKNOWN
237 // from cache, retry request.
238 ids_unknown_state.insert(*it);
239 else
240 extensions_state[*it] = cache_it->second;
243 if (ids_unknown_state.empty()) {
244 callback.Run(extensions_state);
245 } else {
246 // After the extension blacklist states have been downloaded, call this
247 // functions again, but prevent infinite cycle in case server is offline
248 // or some other reason prevents us from receiving the blacklist state for
249 // these extensions.
250 RequestExtensionsBlacklistState(
251 ids_unknown_state,
252 base::Bind(&Blacklist::ReturnBlacklistStateMap, AsWeakPtr(),
253 callback, blacklisted_ids));
257 void Blacklist::ReturnBlacklistStateMap(
258 const GetBlacklistedIDsCallback& callback,
259 const std::set<std::string>& blacklisted_ids) {
260 BlacklistStateMap extensions_state;
261 for (std::set<std::string>::const_iterator it = blacklisted_ids.begin();
262 it != blacklisted_ids.end(); ++it) {
263 BlacklistStateMap::const_iterator cache_it =
264 blacklist_state_cache_.find(*it);
265 if (cache_it != blacklist_state_cache_.end())
266 extensions_state[*it] = cache_it->second;
267 // If for some reason we still haven't cached the state of this extension,
268 // we silently skip it.
271 callback.Run(extensions_state);
274 void Blacklist::RequestExtensionsBlacklistState(
275 const std::set<std::string>& ids, const base::Callback<void()>& callback) {
276 DCHECK_CURRENTLY_ON(BrowserThread::UI);
277 if (!state_fetcher_)
278 state_fetcher_.reset(new BlacklistStateFetcher());
280 state_requests_.push_back(
281 make_pair(std::vector<std::string>(ids.begin(), ids.end()), callback));
282 for (std::set<std::string>::const_iterator it = ids.begin();
283 it != ids.end();
284 ++it) {
285 state_fetcher_->Request(
286 *it,
287 base::Bind(&Blacklist::OnBlacklistStateReceived, AsWeakPtr(), *it));
291 void Blacklist::OnBlacklistStateReceived(const std::string& id,
292 BlacklistState state) {
293 DCHECK_CURRENTLY_ON(BrowserThread::UI);
294 blacklist_state_cache_[id] = state;
296 // Go through the opened requests and call the callbacks for those requests
297 // for which we already got all the required blacklist states.
298 StateRequestsList::iterator requests_it = state_requests_.begin();
299 while (requests_it != state_requests_.end()) {
300 const std::vector<std::string>& ids = requests_it->first;
302 bool have_all_in_cache = true;
303 for (std::vector<std::string>::const_iterator ids_it = ids.begin();
304 ids_it != ids.end();
305 ++ids_it) {
306 if (!ContainsKey(blacklist_state_cache_, *ids_it)) {
307 have_all_in_cache = false;
308 break;
312 if (have_all_in_cache) {
313 requests_it->second.Run();
314 requests_it = state_requests_.erase(requests_it); // returns next element
315 } else {
316 ++requests_it;
321 void Blacklist::SetBlacklistStateFetcherForTest(
322 BlacklistStateFetcher* fetcher) {
323 state_fetcher_.reset(fetcher);
326 BlacklistStateFetcher* Blacklist::ResetBlacklistStateFetcherForTest() {
327 return state_fetcher_.release();
330 void Blacklist::AddObserver(Observer* observer) {
331 DCHECK_CURRENTLY_ON(BrowserThread::UI);
332 observers_.AddObserver(observer);
335 void Blacklist::RemoveObserver(Observer* observer) {
336 DCHECK_CURRENTLY_ON(BrowserThread::UI);
337 observers_.RemoveObserver(observer);
340 // static
341 void Blacklist::SetDatabaseManager(
342 scoped_refptr<SafeBrowsingDatabaseManager> database_manager) {
343 g_database_manager.Get().set(database_manager);
346 // static
347 scoped_refptr<SafeBrowsingDatabaseManager> Blacklist::GetDatabaseManager() {
348 return g_database_manager.Get().get();
351 void Blacklist::Observe(int type,
352 const content::NotificationSource& source,
353 const content::NotificationDetails& details) {
354 DCHECK_EQ(chrome::NOTIFICATION_SAFE_BROWSING_UPDATE_COMPLETE, type);
355 FOR_EACH_OBSERVER(Observer, observers_, OnBlacklistUpdated());
358 } // namespace extensions