Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(67)

Side by Side Diff: chrome/browser/extensions/api/runtime/chrome_runtime_api_delegate.cc

Issue 1897293005: Rate limit programmatic update checks for extensions (reland) (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: switch to LazyInstance for backoff policy Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/extensions/api/runtime/chrome_runtime_api_delegate.h" 5 #include "chrome/browser/extensions/api/runtime/chrome_runtime_api_delegate.h"
6 6
7 #include <memory>
7 #include <string> 8 #include <string>
8 #include <utility> 9 #include <utility>
10 #include <vector>
9 11
12 #include "base/lazy_instance.h"
10 #include "base/location.h" 13 #include "base/location.h"
11 #include "base/metrics/histogram.h" 14 #include "base/metrics/histogram.h"
12 #include "base/single_thread_task_runner.h" 15 #include "base/single_thread_task_runner.h"
13 #include "base/thread_task_runner_handle.h" 16 #include "base/thread_task_runner_handle.h"
14 #include "base/time/time.h" 17 #include "base/time/time.h"
15 #include "build/build_config.h" 18 #include "build/build_config.h"
16 #include "chrome/browser/extensions/extension_service.h" 19 #include "chrome/browser/extensions/extension_service.h"
17 #include "chrome/browser/extensions/extension_tab_util.h" 20 #include "chrome/browser/extensions/extension_tab_util.h"
18 #include "chrome/browser/extensions/updater/extension_updater.h" 21 #include "chrome/browser/extensions/updater/extension_updater.h"
19 #include "chrome/browser/profiles/profile.h" 22 #include "chrome/browser/profiles/profile.h"
20 #include "chrome/browser/ui/browser_finder.h" 23 #include "chrome/browser/ui/browser_finder.h"
21 #include "chrome/browser/ui/browser_navigator.h" 24 #include "chrome/browser/ui/browser_navigator.h"
22 #include "chrome/browser/ui/browser_navigator_params.h" 25 #include "chrome/browser/ui/browser_navigator_params.h"
23 #include "chrome/browser/ui/browser_window.h" 26 #include "chrome/browser/ui/browser_window.h"
24 #include "components/update_client/update_query_params.h" 27 #include "components/update_client/update_query_params.h"
25 #include "content/public/browser/notification_service.h" 28 #include "content/public/browser/notification_service.h"
29 #include "extensions/browser/extension_registry.h"
26 #include "extensions/browser/extension_system.h" 30 #include "extensions/browser/extension_system.h"
27 #include "extensions/browser/notification_types.h" 31 #include "extensions/browser/notification_types.h"
28 #include "extensions/browser/warning_service.h" 32 #include "extensions/browser/warning_service.h"
29 #include "extensions/browser/warning_set.h" 33 #include "extensions/browser/warning_set.h"
30 #include "extensions/common/api/runtime.h" 34 #include "extensions/common/api/runtime.h"
35 #include "extensions/common/constants.h"
36 #include "net/base/backoff_entry.h"
31 37
32 #if defined(OS_CHROMEOS) 38 #if defined(OS_CHROMEOS)
33 #include "chromeos/dbus/dbus_thread_manager.h" 39 #include "chromeos/dbus/dbus_thread_manager.h"
34 #include "chromeos/dbus/power_manager_client.h" 40 #include "chromeos/dbus/power_manager_client.h"
35 #include "components/user_manager/user_manager.h" 41 #include "components/user_manager/user_manager.h"
36 #endif 42 #endif
37 43
38 using extensions::Extension; 44 using extensions::Extension;
39 using extensions::ExtensionSystem; 45 using extensions::ExtensionSystem;
40 using extensions::ExtensionUpdater; 46 using extensions::ExtensionUpdater;
41 47
42 using extensions::api::runtime::PlatformInfo; 48 using extensions::api::runtime::PlatformInfo;
43 49
44 namespace { 50 namespace {
45 51
46 const char kUpdateThrottled[] = "throttled"; 52 const char kUpdateThrottled[] = "throttled";
47 const char kUpdateNotFound[] = "no_update"; 53 const char kUpdateNotFound[] = "no_update";
48 const char kUpdateFound[] = "update_available"; 54 const char kUpdateFound[] = "update_available";
49 55
50 // If an extension reloads itself within this many miliseconds of reloading 56 // If an extension reloads itself within this many miliseconds of reloading
51 // itself, the reload is considered suspiciously fast. 57 // itself, the reload is considered suspiciously fast.
52 const int kFastReloadTime = 10000; 58 const int kFastReloadTime = 10000;
53 59
54 // After this many suspiciously fast consecutive reloads, an extension will get 60 // After this many suspiciously fast consecutive reloads, an extension will get
55 // disabled. 61 // disabled.
56 const int kFastReloadCount = 5; 62 const int kFastReloadCount = 5;
57 63
64 // A holder class for the policy we use for exponential backoff of update check
65 // requests.
66 class BackoffPolicy {
67 public:
68 BackoffPolicy();
69 ~BackoffPolicy();
70
71 // Gets the single lazy instance.
72 static BackoffPolicy* Get();
73
74 // Returns the actual policy to use.
75 const net::BackoffEntry::Policy* policy() { return &policy_; }
Devlin 2016/04/20 22:09:31 nit: maybe just have Get() return this? static ne
asargent_no_longer_on_chrome 2016/04/20 22:44:59 Good idea, done.
76
77 private:
78 net::BackoffEntry::Policy policy_;
79 };
80
81 // We use a LazyInstance since one of the the policy values references an
82 // extern symbol, which would cause a static initializer to be generated if we
83 // just declared the policy struct as a static variable.
84 base::LazyInstance<BackoffPolicy> g_backoff_policy = LAZY_INSTANCE_INITIALIZER;
85
86 BackoffPolicy::BackoffPolicy() {
87 policy_ = {
88 // num_errors_to_ignore
89 0,
90
91 // initial_delay_ms (note that we set 'always_use_initial_delay' to false
92 // below)
93 1000 * extensions::kDefaultUpdateFrequencySeconds,
94
95 // multiply_factor
96 1,
97
98 // jitter_factor
99 0.1,
100
101 // maximum_backoff_ms (-1 means no maximum)
102 -1,
103
104 // entry_lifetime_ms (-1 means never discard)
105 -1,
106
107 // always_use_initial_delay
108 false,
109 };
110 }
111
112 BackoffPolicy::~BackoffPolicy() {}
113
114 BackoffPolicy* BackoffPolicy::Get() {
115 return g_backoff_policy.Pointer();
116 }
117
118 base::TickClock* g_test_clock = nullptr;
119
58 } // namespace 120 } // namespace
59 121
122 struct ChromeRuntimeAPIDelegate::UpdateCheckInfo {
123 public:
124 UpdateCheckInfo() {
125 if (g_test_clock)
126 backoff.reset(
127 new net::BackoffEntry(BackoffPolicy::Get()->policy(), g_test_clock));
128 else
129 backoff.reset(new net::BackoffEntry(BackoffPolicy::Get()->policy()));
130 }
131
132 std::unique_ptr<net::BackoffEntry> backoff;
133 std::vector<UpdateCheckCallback> callbacks;
134 };
135
60 ChromeRuntimeAPIDelegate::ChromeRuntimeAPIDelegate( 136 ChromeRuntimeAPIDelegate::ChromeRuntimeAPIDelegate(
61 content::BrowserContext* context) 137 content::BrowserContext* context)
62 : browser_context_(context), registered_for_updates_(false) { 138 : browser_context_(context),
139 registered_for_updates_(false),
140 extension_registry_observer_(this) {
63 registrar_.Add(this, 141 registrar_.Add(this,
64 extensions::NOTIFICATION_EXTENSION_UPDATE_FOUND, 142 extensions::NOTIFICATION_EXTENSION_UPDATE_FOUND,
65 content::NotificationService::AllSources()); 143 content::NotificationService::AllSources());
144 extension_registry_observer_.Add(
145 extensions::ExtensionRegistry::Get(browser_context_));
66 } 146 }
67 147
68 ChromeRuntimeAPIDelegate::~ChromeRuntimeAPIDelegate() { 148 ChromeRuntimeAPIDelegate::~ChromeRuntimeAPIDelegate() {
69 } 149 }
70 150
151 // static
152 void ChromeRuntimeAPIDelegate::set_tick_clock_for_tests(
153 base::TickClock* clock) {
154 g_test_clock = clock;
155 }
156
71 void ChromeRuntimeAPIDelegate::AddUpdateObserver( 157 void ChromeRuntimeAPIDelegate::AddUpdateObserver(
72 extensions::UpdateObserver* observer) { 158 extensions::UpdateObserver* observer) {
73 registered_for_updates_ = true; 159 registered_for_updates_ = true;
74 ExtensionSystem::Get(browser_context_) 160 ExtensionSystem::Get(browser_context_)
75 ->extension_service() 161 ->extension_service()
76 ->AddUpdateObserver(observer); 162 ->AddUpdateObserver(observer);
77 } 163 }
78 164
79 void ChromeRuntimeAPIDelegate::RemoveUpdateObserver( 165 void ChromeRuntimeAPIDelegate::RemoveUpdateObserver(
80 extensions::UpdateObserver* observer) { 166 extensions::UpdateObserver* observer) {
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after
145 231
146 bool ChromeRuntimeAPIDelegate::CheckForUpdates( 232 bool ChromeRuntimeAPIDelegate::CheckForUpdates(
147 const std::string& extension_id, 233 const std::string& extension_id,
148 const UpdateCheckCallback& callback) { 234 const UpdateCheckCallback& callback) {
149 ExtensionSystem* system = ExtensionSystem::Get(browser_context_); 235 ExtensionSystem* system = ExtensionSystem::Get(browser_context_);
150 ExtensionService* service = system->extension_service(); 236 ExtensionService* service = system->extension_service();
151 ExtensionUpdater* updater = service->updater(); 237 ExtensionUpdater* updater = service->updater();
152 if (!updater) { 238 if (!updater) {
153 return false; 239 return false;
154 } 240 }
155 if (!updater->CheckExtensionSoon( 241
156 extension_id, 242 UpdateCheckInfo& info = update_check_info_[extension_id];
157 base::Bind(&ChromeRuntimeAPIDelegate::UpdateCheckComplete, 243
158 base::Unretained(this), 244 // If not enough time has elapsed, or we have 10 or more outstanding calls,
159 extension_id))) { 245 // return a status of throttled.
246 if (info.backoff->ShouldRejectRequest() || info.callbacks.size() >= 10) {
160 base::ThreadTaskRunnerHandle::Get()->PostTask( 247 base::ThreadTaskRunnerHandle::Get()->PostTask(
161 FROM_HERE, 248 FROM_HERE,
162 base::Bind(callback, UpdateCheckResult(true, kUpdateThrottled, ""))); 249 base::Bind(callback, UpdateCheckResult(true, kUpdateThrottled, "")));
163 } else { 250 } else {
164 UpdateCallbackList& callbacks = pending_update_checks_[extension_id]; 251 info.callbacks.push_back(callback);
165 callbacks.push_back(callback); 252 updater->CheckExtensionSoon(
253 extension_id, base::Bind(&ChromeRuntimeAPIDelegate::UpdateCheckComplete,
254 base::Unretained(this), extension_id));
166 } 255 }
167 return true; 256 return true;
168 } 257 }
169 258
170 void ChromeRuntimeAPIDelegate::OpenURL(const GURL& uninstall_url) { 259 void ChromeRuntimeAPIDelegate::OpenURL(const GURL& uninstall_url) {
171 Profile* profile = Profile::FromBrowserContext(browser_context_); 260 Profile* profile = Profile::FromBrowserContext(browser_context_);
172 Browser* browser = chrome::FindLastActiveWithProfile(profile); 261 Browser* browser = chrome::FindLastActiveWithProfile(profile);
173 if (!browser) 262 if (!browser)
174 browser = new Browser(Browser::CreateParams(profile)); 263 browser = new Browser(Browser::CreateParams(profile));
175 264
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after
252 DCHECK(type == extensions::NOTIFICATION_EXTENSION_UPDATE_FOUND); 341 DCHECK(type == extensions::NOTIFICATION_EXTENSION_UPDATE_FOUND);
253 typedef const std::pair<std::string, Version> UpdateDetails; 342 typedef const std::pair<std::string, Version> UpdateDetails;
254 const std::string& id = content::Details<UpdateDetails>(details)->first; 343 const std::string& id = content::Details<UpdateDetails>(details)->first;
255 const Version& version = content::Details<UpdateDetails>(details)->second; 344 const Version& version = content::Details<UpdateDetails>(details)->second;
256 if (version.IsValid()) { 345 if (version.IsValid()) {
257 CallUpdateCallbacks( 346 CallUpdateCallbacks(
258 id, UpdateCheckResult(true, kUpdateFound, version.GetString())); 347 id, UpdateCheckResult(true, kUpdateFound, version.GetString()));
259 } 348 }
260 } 349 }
261 350
351 void ChromeRuntimeAPIDelegate::OnExtensionInstalled(
352 content::BrowserContext* browser_context,
353 const Extension* extension,
354 bool is_update) {
355 if (!is_update)
356 return;
357 auto info = update_check_info_.find(extension->id());
358 if (info != update_check_info_.end()) {
359 info->second.backoff->Reset();
360 }
361 }
362
262 void ChromeRuntimeAPIDelegate::UpdateCheckComplete( 363 void ChromeRuntimeAPIDelegate::UpdateCheckComplete(
263 const std::string& extension_id) { 364 const std::string& extension_id) {
264 ExtensionSystem* system = ExtensionSystem::Get(browser_context_); 365 ExtensionSystem* system = ExtensionSystem::Get(browser_context_);
265 ExtensionService* service = system->extension_service(); 366 ExtensionService* service = system->extension_service();
266 const Extension* update = service->GetPendingExtensionUpdate(extension_id); 367 const Extension* update = service->GetPendingExtensionUpdate(extension_id);
368 UpdateCheckInfo& info = update_check_info_[extension_id];
369
370 // We always inform the BackoffEntry of a "failure" here, because we only
371 // want to consider an update check request a success from a throttling
372 // standpoint once the extension goes on to actually update to a new
373 // version. See OnExtensionInstalled for where we reset the BackoffEntry.
374 info.backoff->InformOfRequest(false);
375
267 if (update) { 376 if (update) {
268 CallUpdateCallbacks( 377 CallUpdateCallbacks(
269 extension_id, 378 extension_id,
270 UpdateCheckResult(true, kUpdateFound, update->VersionString())); 379 UpdateCheckResult(true, kUpdateFound, update->VersionString()));
271 } else { 380 } else {
272 CallUpdateCallbacks(extension_id, 381 CallUpdateCallbacks(extension_id,
273 UpdateCheckResult(true, kUpdateNotFound, "")); 382 UpdateCheckResult(true, kUpdateNotFound, ""));
274 } 383 }
275 } 384 }
276 385
277 void ChromeRuntimeAPIDelegate::CallUpdateCallbacks( 386 void ChromeRuntimeAPIDelegate::CallUpdateCallbacks(
278 const std::string& extension_id, 387 const std::string& extension_id,
279 const UpdateCheckResult& result) { 388 const UpdateCheckResult& result) {
280 UpdateCallbackList callbacks = pending_update_checks_[extension_id]; 389 auto it = update_check_info_.find(extension_id);
281 pending_update_checks_.erase(extension_id); 390 if (it == update_check_info_.end())
282 for (UpdateCallbackList::const_iterator iter = callbacks.begin(); 391 return;
283 iter != callbacks.end(); 392 std::vector<UpdateCheckCallback> callbacks;
284 ++iter) { 393 it->second.callbacks.swap(callbacks);
285 const UpdateCheckCallback& callback = *iter; 394 for (const auto& callback : callbacks) {
286 callback.Run(result); 395 callback.Run(result);
287 } 396 }
288 } 397 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698