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"
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"
36 using extensions::Extension
;
37 using extensions::ExtensionSystem
;
38 using extensions::ExtensionUpdater
;
40 using extensions::api::runtime::PlatformInfo
;
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
54 const int kFastReloadCount
= 5;
58 ChromeRuntimeAPIDelegate::ChromeRuntimeAPIDelegate(
59 content::BrowserContext
* context
)
60 : browser_context_(context
), registered_for_updates_(false) {
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_
)
74 ->AddUpdateObserver(observer
);
77 void ChromeRuntimeAPIDelegate::RemoveUpdateObserver(
78 extensions::UpdateObserver
* observer
) {
79 if (registered_for_updates_
) {
80 ExtensionSystem::Get(browser_context_
)
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);
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;
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",
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
124 base::ThreadTaskRunnerHandle::Get()->PostTask(
125 FROM_HERE
, base::Bind(&ExtensionService::TerminateExtension
,
126 service
->AsWeakPtr(), extension_id
));
127 extensions::WarningSet warnings
;
129 extensions::Warning::CreateReloadTooFrequentWarning(
131 base::ThreadTaskRunnerHandle::Get()->PostTask(
132 FROM_HERE
, base::Bind(&extensions::WarningService::NotifyWarningsOnUI
,
133 browser_context_
, warnings
));
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();
153 if (!updater
->CheckExtensionSoon(
155 base::Bind(&ChromeRuntimeAPIDelegate::UpdateCheckComplete
,
156 base::Unretained(this),
158 base::ThreadTaskRunnerHandle::Get()->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 Profile
* profile
= Profile::FromBrowserContext(browser_context_
);
171 chrome::FindLastActiveWithProfile(profile
, chrome::GetActiveDesktop());
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(¶ms
);
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
;
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
;
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
;
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()
236 *error_message
= "Function available only for ChromeOS kiosk mode.";
240 bool ChromeRuntimeAPIDelegate::OpenOptionsPage(const Extension
* extension
) {
241 Profile
* profile
= Profile::FromBrowserContext(browser_context_
);
243 chrome::FindLastActiveWithProfile(profile
, chrome::GetActiveDesktop());
246 return extensions::ExtensionTabUtil::OpenOptionsPage(extension
, browser
);
249 void ChromeRuntimeAPIDelegate::Observe(
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()) {
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
);
271 UpdateCheckResult(true, kUpdateFound
, update
->VersionString()));
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();
286 const UpdateCheckCallback
& callback
= *iter
;
287 callback
.Run(result
);