Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / extensions / api / runtime / chrome_runtime_api_delegate.cc
blob8a61cde885300684f9cbe29f7a610e77116d01ca
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 <string>
8 #include <utility>
10 #include "base/location.h"
11 #include "base/metrics/histogram.h"
12 #include "base/single_thread_task_runner.h"
13 #include "base/thread_task_runner_handle.h"
14 #include "base/time/time.h"
15 #include "chrome/browser/extensions/extension_service.h"
16 #include "chrome/browser/extensions/extension_tab_util.h"
17 #include "chrome/browser/extensions/updater/extension_updater.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/browser/ui/browser_finder.h"
20 #include "chrome/browser/ui/browser_navigator.h"
21 #include "chrome/browser/ui/browser_window.h"
22 #include "components/update_client/update_query_params.h"
23 #include "content/public/browser/notification_service.h"
24 #include "extensions/browser/extension_system.h"
25 #include "extensions/browser/notification_types.h"
26 #include "extensions/browser/warning_service.h"
27 #include "extensions/browser/warning_set.h"
28 #include "extensions/common/api/runtime.h"
30 #if defined(OS_CHROMEOS)
31 #include "chromeos/dbus/dbus_thread_manager.h"
32 #include "chromeos/dbus/power_manager_client.h"
33 #include "components/user_manager/user_manager.h"
34 #endif
36 using extensions::Extension;
37 using extensions::ExtensionSystem;
38 using extensions::ExtensionUpdater;
40 using extensions::api::runtime::PlatformInfo;
42 namespace {
44 const char kUpdateThrottled[] = "throttled";
45 const char kUpdateNotFound[] = "no_update";
46 const char kUpdateFound[] = "update_available";
48 // If an extension reloads itself within this many miliseconds of reloading
49 // itself, the reload is considered suspiciously fast.
50 const int kFastReloadTime = 10000;
52 // After this many suspiciously fast consecutive reloads, an extension will get
53 // disabled.
54 const int kFastReloadCount = 5;
56 } // namespace
58 ChromeRuntimeAPIDelegate::ChromeRuntimeAPIDelegate(
59 content::BrowserContext* context)
60 : browser_context_(context), registered_for_updates_(false) {
61 registrar_.Add(this,
62 extensions::NOTIFICATION_EXTENSION_UPDATE_FOUND,
63 content::NotificationService::AllSources());
66 ChromeRuntimeAPIDelegate::~ChromeRuntimeAPIDelegate() {
69 void ChromeRuntimeAPIDelegate::AddUpdateObserver(
70 extensions::UpdateObserver* observer) {
71 registered_for_updates_ = true;
72 ExtensionSystem::Get(browser_context_)
73 ->extension_service()
74 ->AddUpdateObserver(observer);
77 void ChromeRuntimeAPIDelegate::RemoveUpdateObserver(
78 extensions::UpdateObserver* observer) {
79 if (registered_for_updates_) {
80 ExtensionSystem::Get(browser_context_)
81 ->extension_service()
82 ->RemoveUpdateObserver(observer);
86 base::Version ChromeRuntimeAPIDelegate::GetPreviousExtensionVersion(
87 const Extension* extension) {
88 // Get the previous version to check if this is an upgrade.
89 ExtensionService* service =
90 ExtensionSystem::Get(browser_context_)->extension_service();
91 const Extension* old = service->GetExtensionById(extension->id(), true);
92 if (old)
93 return *old->version();
94 return base::Version();
97 void ChromeRuntimeAPIDelegate::ReloadExtension(
98 const std::string& extension_id) {
99 std::pair<base::TimeTicks, int>& reload_info =
100 last_reload_time_[extension_id];
101 base::TimeTicks now = base::TimeTicks::Now();
102 if (reload_info.first.is_null() ||
103 (now - reload_info.first).InMilliseconds() > kFastReloadTime) {
104 reload_info.second = 0;
105 } else {
106 reload_info.second++;
108 if (!reload_info.first.is_null()) {
109 UMA_HISTOGRAM_LONG_TIMES("Extensions.RuntimeReloadTime",
110 now - reload_info.first);
112 UMA_HISTOGRAM_COUNTS_100("Extensions.RuntimeReloadFastCount",
113 reload_info.second);
114 reload_info.first = now;
116 ExtensionService* service =
117 ExtensionSystem::Get(browser_context_)->extension_service();
118 if (reload_info.second >= kFastReloadCount) {
119 // Unloading an extension clears all warnings, so first terminate the
120 // extension, and then add the warning. Since this is called from an
121 // extension function unloading the extension has to be done
122 // asynchronously. Fortunately PostTask guarentees FIFO order so just
123 // post both tasks.
124 base::ThreadTaskRunnerHandle::Get()->PostTask(
125 FROM_HERE, base::Bind(&ExtensionService::TerminateExtension,
126 service->AsWeakPtr(), extension_id));
127 extensions::WarningSet warnings;
128 warnings.insert(
129 extensions::Warning::CreateReloadTooFrequentWarning(
130 extension_id));
131 base::ThreadTaskRunnerHandle::Get()->PostTask(
132 FROM_HERE, base::Bind(&extensions::WarningService::NotifyWarningsOnUI,
133 browser_context_, warnings));
134 } else {
135 // We can't call ReloadExtension directly, since when this method finishes
136 // it tries to decrease the reference count for the extension, which fails
137 // if the extension has already been reloaded; so instead we post a task.
138 base::ThreadTaskRunnerHandle::Get()->PostTask(
139 FROM_HERE, base::Bind(&ExtensionService::ReloadExtension,
140 service->AsWeakPtr(), 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::ThreadTaskRunnerHandle::Get()->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 Profile* profile = Profile::FromBrowserContext(browser_context_);
170 Browser* browser =
171 chrome::FindLastActiveWithProfile(profile, chrome::GetActiveDesktop());
172 if (!browser)
173 browser =
174 new Browser(Browser::CreateParams(profile, chrome::GetActiveDesktop()));
176 chrome::NavigateParams params(
177 browser, uninstall_url, ui::PAGE_TRANSITION_CLIENT_REDIRECT);
178 params.disposition = NEW_FOREGROUND_TAB;
179 params.user_gesture = false;
180 chrome::Navigate(&params);
183 bool ChromeRuntimeAPIDelegate::GetPlatformInfo(PlatformInfo* info) {
184 const char* os = update_client::UpdateQueryParams::GetOS();
185 if (strcmp(os, "mac") == 0) {
186 info->os = extensions::api::runtime::PLATFORM_OS_MAC;
187 } else if (strcmp(os, "win") == 0) {
188 info->os = extensions::api::runtime::PLATFORM_OS_WIN;
189 } else if (strcmp(os, "cros") == 0) {
190 info->os = extensions::api::runtime::PLATFORM_OS_CROS;
191 } else if (strcmp(os, "linux") == 0) {
192 info->os = extensions::api::runtime::PLATFORM_OS_LINUX;
193 } else if (strcmp(os, "openbsd") == 0) {
194 info->os = extensions::api::runtime::PLATFORM_OS_OPENBSD;
195 } else {
196 NOTREACHED();
197 return false;
200 const char* arch = update_client::UpdateQueryParams::GetArch();
201 if (strcmp(arch, "arm") == 0) {
202 info->arch = extensions::api::runtime::PLATFORM_ARCH_ARM;
203 } else if (strcmp(arch, "x86") == 0) {
204 info->arch = extensions::api::runtime::PLATFORM_ARCH_X86_32;
205 } else if (strcmp(arch, "x64") == 0) {
206 info->arch = extensions::api::runtime::PLATFORM_ARCH_X86_64;
207 } else {
208 NOTREACHED();
209 return false;
212 const char* nacl_arch = update_client::UpdateQueryParams::GetNaclArch();
213 if (strcmp(nacl_arch, "arm") == 0) {
214 info->nacl_arch = extensions::api::runtime::PLATFORM_NACL_ARCH_ARM;
215 } else if (strcmp(nacl_arch, "x86-32") == 0) {
216 info->nacl_arch = extensions::api::runtime::PLATFORM_NACL_ARCH_X86_32;
217 } else if (strcmp(nacl_arch, "x86-64") == 0) {
218 info->nacl_arch = extensions::api::runtime::PLATFORM_NACL_ARCH_X86_64;
219 } else {
220 NOTREACHED();
221 return false;
224 return true;
227 bool ChromeRuntimeAPIDelegate::RestartDevice(std::string* error_message) {
228 #if defined(OS_CHROMEOS)
229 if (user_manager::UserManager::Get()->IsLoggedInAsKioskApp()) {
230 chromeos::DBusThreadManager::Get()
231 ->GetPowerManagerClient()
232 ->RequestRestart();
233 return true;
235 #endif
236 *error_message = "Function available only for ChromeOS kiosk mode.";
237 return false;
240 bool ChromeRuntimeAPIDelegate::OpenOptionsPage(const Extension* extension) {
241 Profile* profile = Profile::FromBrowserContext(browser_context_);
242 Browser* browser =
243 chrome::FindLastActiveWithProfile(profile, chrome::GetActiveDesktop());
244 if (!browser)
245 return false;
246 return extensions::ExtensionTabUtil::OpenOptionsPage(extension, browser);
249 void ChromeRuntimeAPIDelegate::Observe(
250 int type,
251 const content::NotificationSource& source,
252 const content::NotificationDetails& details) {
253 DCHECK(type == extensions::NOTIFICATION_EXTENSION_UPDATE_FOUND);
254 typedef const std::pair<std::string, Version> UpdateDetails;
255 const std::string& id = content::Details<UpdateDetails>(details)->first;
256 const Version& version = content::Details<UpdateDetails>(details)->second;
257 if (version.IsValid()) {
258 CallUpdateCallbacks(
259 id, UpdateCheckResult(true, kUpdateFound, version.GetString()));
263 void ChromeRuntimeAPIDelegate::UpdateCheckComplete(
264 const std::string& extension_id) {
265 ExtensionSystem* system = ExtensionSystem::Get(browser_context_);
266 ExtensionService* service = system->extension_service();
267 const Extension* update = service->GetPendingExtensionUpdate(extension_id);
268 if (update) {
269 CallUpdateCallbacks(
270 extension_id,
271 UpdateCheckResult(true, kUpdateFound, update->VersionString()));
272 } else {
273 CallUpdateCallbacks(extension_id,
274 UpdateCheckResult(true, kUpdateNotFound, ""));
278 void ChromeRuntimeAPIDelegate::CallUpdateCallbacks(
279 const std::string& extension_id,
280 const UpdateCheckResult& result) {
281 UpdateCallbackList callbacks = pending_update_checks_[extension_id];
282 pending_update_checks_.erase(extension_id);
283 for (UpdateCallbackList::const_iterator iter = callbacks.begin();
284 iter != callbacks.end();
285 ++iter) {
286 const UpdateCheckCallback& callback = *iter;
287 callback.Run(result);