Revert 285173 "Removed InProcessBrowserTest::CleanUpOnMainThread()"
[chromium-blink-merge.git] / chrome / browser / extensions / api / runtime / chrome_runtime_api_delegate.cc
blobbc743e98da9645fc6797a137a6c64454c54dfcbc
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
3 // found in the LICENSE file.
5 #include "chrome/browser/extensions/api/runtime/chrome_runtime_api_delegate.h"
7 #include "base/message_loop/message_loop.h"
8 #include "base/metrics/histogram.h"
9 #include "base/time/time.h"
10 #include "chrome/browser/chrome_notification_types.h"
11 #include "chrome/browser/extensions/extension_service.h"
12 #include "chrome/browser/extensions/extension_warning_service.h"
13 #include "chrome/browser/extensions/extension_warning_set.h"
14 #include "chrome/browser/extensions/updater/extension_updater.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "chrome/browser/ui/browser_finder.h"
17 #include "chrome/browser/ui/browser_navigator.h"
18 #include "chrome/browser/ui/browser_window.h"
19 #include "components/omaha_query_params/omaha_query_params.h"
20 #include "content/public/browser/notification_service.h"
21 #include "extensions/browser/extension_system.h"
22 #include "extensions/common/api/runtime.h"
24 #if defined(OS_CHROMEOS)
25 #include "chrome/browser/chromeos/login/users/user_manager.h"
26 #include "chromeos/dbus/dbus_thread_manager.h"
27 #include "chromeos/dbus/power_manager_client.h"
28 #endif
30 using extensions::Extension;
31 using extensions::ExtensionSystem;
32 using extensions::ExtensionUpdater;
34 using extensions::core_api::runtime::PlatformInfo;
36 namespace {
38 const char kUpdateThrottled[] = "throttled";
39 const char kUpdateNotFound[] = "no_update";
40 const char kUpdateFound[] = "update_available";
42 // If an extension reloads itself within this many miliseconds of reloading
43 // itself, the reload is considered suspiciously fast.
44 const int kFastReloadTime = 10000;
46 // After this many suspiciously fast consecutive reloads, an extension will get
47 // disabled.
48 const int kFastReloadCount = 5;
50 } // namespace
52 ChromeRuntimeAPIDelegate::ChromeRuntimeAPIDelegate(
53 content::BrowserContext* context)
54 : browser_context_(context), registered_for_updates_(false) {
55 registrar_.Add(this,
56 chrome::NOTIFICATION_EXTENSION_UPDATE_FOUND,
57 content::NotificationService::AllSources());
60 ChromeRuntimeAPIDelegate::~ChromeRuntimeAPIDelegate() {
63 void ChromeRuntimeAPIDelegate::AddUpdateObserver(
64 extensions::UpdateObserver* observer) {
65 registered_for_updates_ = true;
66 ExtensionSystem::Get(browser_context_)
67 ->extension_service()
68 ->AddUpdateObserver(observer);
71 void ChromeRuntimeAPIDelegate::RemoveUpdateObserver(
72 extensions::UpdateObserver* observer) {
73 if (registered_for_updates_) {
74 ExtensionSystem::Get(browser_context_)
75 ->extension_service()
76 ->RemoveUpdateObserver(observer);
80 base::Version ChromeRuntimeAPIDelegate::GetPreviousExtensionVersion(
81 const Extension* extension) {
82 // Get the previous version to check if this is an upgrade.
83 ExtensionService* service =
84 ExtensionSystem::Get(browser_context_)->extension_service();
85 const Extension* old = service->GetExtensionById(extension->id(), true);
86 if (old)
87 return *old->version();
88 return base::Version();
91 void ChromeRuntimeAPIDelegate::ReloadExtension(
92 const std::string& extension_id) {
93 std::pair<base::TimeTicks, int>& reload_info =
94 last_reload_time_[extension_id];
95 base::TimeTicks now = base::TimeTicks::Now();
96 if (reload_info.first.is_null() ||
97 (now - reload_info.first).InMilliseconds() > kFastReloadTime) {
98 reload_info.second = 0;
99 } else {
100 reload_info.second++;
102 if (!reload_info.first.is_null()) {
103 UMA_HISTOGRAM_LONG_TIMES("Extensions.RuntimeReloadTime",
104 now - reload_info.first);
106 UMA_HISTOGRAM_COUNTS_100("Extensions.RuntimeReloadFastCount",
107 reload_info.second);
108 reload_info.first = now;
110 ExtensionService* service =
111 ExtensionSystem::Get(browser_context_)->extension_service();
112 if (reload_info.second >= kFastReloadCount) {
113 // Unloading an extension clears all warnings, so first terminate the
114 // extension, and then add the warning. Since this is called from an
115 // extension function unloading the extension has to be done
116 // asynchronously. Fortunately PostTask guarentees FIFO order so just
117 // post both tasks.
118 base::MessageLoop::current()->PostTask(
119 FROM_HERE,
120 base::Bind(&ExtensionService::TerminateExtension,
121 service->AsWeakPtr(),
122 extension_id));
123 extensions::ExtensionWarningSet warnings;
124 warnings.insert(
125 extensions::ExtensionWarning::CreateReloadTooFrequentWarning(
126 extension_id));
127 base::MessageLoop::current()->PostTask(
128 FROM_HERE,
129 base::Bind(&extensions::ExtensionWarningService::NotifyWarningsOnUI,
130 browser_context_,
131 warnings));
132 } else {
133 // We can't call ReloadExtension directly, since when this method finishes
134 // it tries to decrease the reference count for the extension, which fails
135 // if the extension has already been reloaded; so instead we post a task.
136 base::MessageLoop::current()->PostTask(
137 FROM_HERE,
138 base::Bind(&ExtensionService::ReloadExtension,
139 service->AsWeakPtr(),
140 extension_id));
144 bool ChromeRuntimeAPIDelegate::CheckForUpdates(
145 const std::string& extension_id,
146 const UpdateCheckCallback& callback) {
147 ExtensionSystem* system = ExtensionSystem::Get(browser_context_);
148 ExtensionService* service = system->extension_service();
149 ExtensionUpdater* updater = service->updater();
150 if (!updater) {
151 return false;
153 if (!updater->CheckExtensionSoon(
154 extension_id,
155 base::Bind(&ChromeRuntimeAPIDelegate::UpdateCheckComplete,
156 base::Unretained(this),
157 extension_id))) {
158 base::MessageLoop::current()->PostTask(
159 FROM_HERE,
160 base::Bind(callback, UpdateCheckResult(true, kUpdateThrottled, "")));
161 } else {
162 UpdateCallbackList& callbacks = pending_update_checks_[extension_id];
163 callbacks.push_back(callback);
165 return true;
168 void ChromeRuntimeAPIDelegate::OpenURL(const GURL& uninstall_url) {
169 #if defined(ENABLE_EXTENSIONS)
170 Profile* profile = Profile::FromBrowserContext(browser_context_);
171 Browser* browser =
172 chrome::FindLastActiveWithProfile(profile, chrome::GetActiveDesktop());
173 if (!browser)
174 browser =
175 new Browser(Browser::CreateParams(profile, chrome::GetActiveDesktop()));
177 chrome::NavigateParams params(
178 browser, uninstall_url, content::PAGE_TRANSITION_CLIENT_REDIRECT);
179 params.disposition = NEW_FOREGROUND_TAB;
180 params.user_gesture = false;
181 chrome::Navigate(&params);
182 #endif
185 bool ChromeRuntimeAPIDelegate::GetPlatformInfo(PlatformInfo* info) {
186 const char* os = omaha_query_params::OmahaQueryParams::GetOS();
187 if (strcmp(os, "mac") == 0) {
188 info->os = PlatformInfo::OS_MAC_;
189 } else if (strcmp(os, "win") == 0) {
190 info->os = PlatformInfo::OS_WIN_;
191 } else if (strcmp(os, "android") == 0) {
192 info->os = PlatformInfo::OS_ANDROID_;
193 } else if (strcmp(os, "cros") == 0) {
194 info->os = PlatformInfo::OS_CROS_;
195 } else if (strcmp(os, "linux") == 0) {
196 info->os = PlatformInfo::OS_LINUX_;
197 } else if (strcmp(os, "openbsd") == 0) {
198 info->os = PlatformInfo::OS_OPENBSD_;
199 } else {
200 NOTREACHED();
201 return false;
204 const char* arch = omaha_query_params::OmahaQueryParams::GetArch();
205 if (strcmp(arch, "arm") == 0) {
206 info->arch = PlatformInfo::ARCH_ARM;
207 } else if (strcmp(arch, "x86") == 0) {
208 info->arch = PlatformInfo::ARCH_X86_32;
209 } else if (strcmp(arch, "x64") == 0) {
210 info->arch = PlatformInfo::ARCH_X86_64;
211 } else {
212 NOTREACHED();
213 return false;
216 const char* nacl_arch = omaha_query_params::OmahaQueryParams::GetNaclArch();
217 if (strcmp(nacl_arch, "arm") == 0) {
218 info->nacl_arch = PlatformInfo::NACL_ARCH_ARM;
219 } else if (strcmp(nacl_arch, "x86-32") == 0) {
220 info->nacl_arch = PlatformInfo::NACL_ARCH_X86_32;
221 } else if (strcmp(nacl_arch, "x86-64") == 0) {
222 info->nacl_arch = PlatformInfo::NACL_ARCH_X86_64;
223 } else {
224 NOTREACHED();
225 return false;
228 return true;
231 bool ChromeRuntimeAPIDelegate::RestartDevice(std::string* error_message) {
232 #if defined(OS_CHROMEOS)
233 if (chromeos::UserManager::Get()->IsLoggedInAsKioskApp()) {
234 chromeos::DBusThreadManager::Get()
235 ->GetPowerManagerClient()
236 ->RequestRestart();
237 return true;
239 #endif
240 *error_message = "Function available only for ChromeOS kiosk mode.";
241 return false;
244 void ChromeRuntimeAPIDelegate::Observe(
245 int type,
246 const content::NotificationSource& source,
247 const content::NotificationDetails& details) {
248 DCHECK(type == chrome::NOTIFICATION_EXTENSION_UPDATE_FOUND);
249 typedef const std::pair<std::string, Version> UpdateDetails;
250 const std::string& id = content::Details<UpdateDetails>(details)->first;
251 const Version& version = content::Details<UpdateDetails>(details)->second;
252 if (version.IsValid()) {
253 CallUpdateCallbacks(
254 id, UpdateCheckResult(true, kUpdateFound, version.GetString()));
258 void ChromeRuntimeAPIDelegate::UpdateCheckComplete(
259 const std::string& extension_id) {
260 ExtensionSystem* system = ExtensionSystem::Get(browser_context_);
261 ExtensionService* service = system->extension_service();
262 const Extension* update = service->GetPendingExtensionUpdate(extension_id);
263 if (update) {
264 CallUpdateCallbacks(
265 extension_id,
266 UpdateCheckResult(true, kUpdateFound, update->VersionString()));
267 } else {
268 CallUpdateCallbacks(extension_id,
269 UpdateCheckResult(true, kUpdateNotFound, ""));
273 void ChromeRuntimeAPIDelegate::CallUpdateCallbacks(
274 const std::string& extension_id,
275 const UpdateCheckResult& result) {
276 UpdateCallbackList callbacks = pending_update_checks_[extension_id];
277 pending_update_checks_.erase(extension_id);
278 for (UpdateCallbackList::const_iterator iter = callbacks.begin();
279 iter != callbacks.end();
280 ++iter) {
281 const UpdateCheckCallback& callback = *iter;
282 callback.Run(result);