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"
30 using extensions::Extension
;
31 using extensions::ExtensionSystem
;
32 using extensions::ExtensionUpdater
;
34 using extensions::core_api::runtime::PlatformInfo
;
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
48 const int kFastReloadCount
= 5;
52 ChromeRuntimeAPIDelegate::ChromeRuntimeAPIDelegate(
53 content::BrowserContext
* context
)
54 : browser_context_(context
), registered_for_updates_(false) {
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_
)
68 ->AddUpdateObserver(observer
);
71 void ChromeRuntimeAPIDelegate::RemoveUpdateObserver(
72 extensions::UpdateObserver
* observer
) {
73 if (registered_for_updates_
) {
74 ExtensionSystem::Get(browser_context_
)
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);
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;
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",
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
118 base::MessageLoop::current()->PostTask(
120 base::Bind(&ExtensionService::TerminateExtension
,
121 service
->AsWeakPtr(),
123 extensions::ExtensionWarningSet warnings
;
125 extensions::ExtensionWarning::CreateReloadTooFrequentWarning(
127 base::MessageLoop::current()->PostTask(
129 base::Bind(&extensions::ExtensionWarningService::NotifyWarningsOnUI
,
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(
138 base::Bind(&ExtensionService::ReloadExtension
,
139 service
->AsWeakPtr(),
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();
153 if (!updater
->CheckExtensionSoon(
155 base::Bind(&ChromeRuntimeAPIDelegate::UpdateCheckComplete
,
156 base::Unretained(this),
158 base::MessageLoop::current()->PostTask(
160 base::Bind(callback
, UpdateCheckResult(true, kUpdateThrottled
, "")));
162 UpdateCallbackList
& callbacks
= pending_update_checks_
[extension_id
];
163 callbacks
.push_back(callback
);
168 void ChromeRuntimeAPIDelegate::OpenURL(const GURL
& uninstall_url
) {
169 #if defined(ENABLE_EXTENSIONS)
170 Profile
* profile
= Profile::FromBrowserContext(browser_context_
);
172 chrome::FindLastActiveWithProfile(profile
, chrome::GetActiveDesktop());
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(¶ms
);
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_
;
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
;
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
;
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()
240 *error_message
= "Function available only for ChromeOS kiosk mode.";
244 void ChromeRuntimeAPIDelegate::Observe(
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()) {
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
);
266 UpdateCheckResult(true, kUpdateFound
, update
->VersionString()));
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();
281 const UpdateCheckCallback
& callback
= *iter
;
282 callback
.Run(result
);