• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "chrome/browser/extensions/api/runtime/chrome_runtime_api_delegate.h"
6 
7 #include "base/message_loop/message_loop.h"
8 #include "base/metrics/histogram.h"
9 #include "base/time/time.h"
10 #include "chrome/browser/extensions/extension_service.h"
11 #include "chrome/browser/extensions/updater/extension_updater.h"
12 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/browser/ui/browser_finder.h"
14 #include "chrome/browser/ui/browser_navigator.h"
15 #include "chrome/browser/ui/browser_window.h"
16 #include "components/omaha_query_params/omaha_query_params.h"
17 #include "content/public/browser/notification_service.h"
18 #include "extensions/browser/extension_system.h"
19 #include "extensions/browser/notification_types.h"
20 #include "extensions/browser/warning_service.h"
21 #include "extensions/browser/warning_set.h"
22 #include "extensions/common/api/runtime.h"
23 
24 #if defined(OS_CHROMEOS)
25 #include "chromeos/dbus/dbus_thread_manager.h"
26 #include "chromeos/dbus/power_manager_client.h"
27 #include "components/user_manager/user_manager.h"
28 #endif
29 
30 using extensions::Extension;
31 using extensions::ExtensionSystem;
32 using extensions::ExtensionUpdater;
33 
34 using extensions::core_api::runtime::PlatformInfo;
35 
36 namespace {
37 
38 const char kUpdateThrottled[] = "throttled";
39 const char kUpdateNotFound[] = "no_update";
40 const char kUpdateFound[] = "update_available";
41 
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;
45 
46 // After this many suspiciously fast consecutive reloads, an extension will get
47 // disabled.
48 const int kFastReloadCount = 5;
49 
50 }  // namespace
51 
ChromeRuntimeAPIDelegate(content::BrowserContext * context)52 ChromeRuntimeAPIDelegate::ChromeRuntimeAPIDelegate(
53     content::BrowserContext* context)
54     : browser_context_(context), registered_for_updates_(false) {
55   registrar_.Add(this,
56                  extensions::NOTIFICATION_EXTENSION_UPDATE_FOUND,
57                  content::NotificationService::AllSources());
58 }
59 
~ChromeRuntimeAPIDelegate()60 ChromeRuntimeAPIDelegate::~ChromeRuntimeAPIDelegate() {
61 }
62 
AddUpdateObserver(extensions::UpdateObserver * observer)63 void ChromeRuntimeAPIDelegate::AddUpdateObserver(
64     extensions::UpdateObserver* observer) {
65   registered_for_updates_ = true;
66   ExtensionSystem::Get(browser_context_)
67       ->extension_service()
68       ->AddUpdateObserver(observer);
69 }
70 
RemoveUpdateObserver(extensions::UpdateObserver * 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);
77   }
78 }
79 
GetPreviousExtensionVersion(const Extension * extension)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();
89 }
90 
ReloadExtension(const std::string & extension_id)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++;
101   }
102   if (!reload_info.first.is_null()) {
103     UMA_HISTOGRAM_LONG_TIMES("Extensions.RuntimeReloadTime",
104                              now - reload_info.first);
105   }
106   UMA_HISTOGRAM_COUNTS_100("Extensions.RuntimeReloadFastCount",
107                            reload_info.second);
108   reload_info.first = now;
109 
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::WarningSet warnings;
124     warnings.insert(
125         extensions::Warning::CreateReloadTooFrequentWarning(
126             extension_id));
127     base::MessageLoop::current()->PostTask(
128         FROM_HERE,
129         base::Bind(&extensions::WarningService::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));
141   }
142 }
143 
CheckForUpdates(const std::string & extension_id,const UpdateCheckCallback & callback)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;
152   }
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);
164   }
165   return true;
166 }
167 
OpenURL(const GURL & uninstall_url)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()));
175 
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);
181 }
182 
GetPlatformInfo(PlatformInfo * info)183 bool ChromeRuntimeAPIDelegate::GetPlatformInfo(PlatformInfo* info) {
184   const char* os = omaha_query_params::OmahaQueryParams::GetOS();
185   if (strcmp(os, "mac") == 0) {
186     info->os = PlatformInfo::OS_MAC_;
187   } else if (strcmp(os, "win") == 0) {
188     info->os = PlatformInfo::OS_WIN_;
189   } else if (strcmp(os, "cros") == 0) {
190     info->os = PlatformInfo::OS_CROS_;
191   } else if (strcmp(os, "linux") == 0) {
192     info->os = PlatformInfo::OS_LINUX_;
193   } else if (strcmp(os, "openbsd") == 0) {
194     info->os = PlatformInfo::OS_OPENBSD_;
195   } else {
196     NOTREACHED();
197     return false;
198   }
199 
200   const char* arch = omaha_query_params::OmahaQueryParams::GetArch();
201   if (strcmp(arch, "arm") == 0) {
202     info->arch = PlatformInfo::ARCH_ARM;
203   } else if (strcmp(arch, "x86") == 0) {
204     info->arch = PlatformInfo::ARCH_X86_32;
205   } else if (strcmp(arch, "x64") == 0) {
206     info->arch = PlatformInfo::ARCH_X86_64;
207   } else {
208     NOTREACHED();
209     return false;
210   }
211 
212   const char* nacl_arch = omaha_query_params::OmahaQueryParams::GetNaclArch();
213   if (strcmp(nacl_arch, "arm") == 0) {
214     info->nacl_arch = PlatformInfo::NACL_ARCH_ARM;
215   } else if (strcmp(nacl_arch, "x86-32") == 0) {
216     info->nacl_arch = PlatformInfo::NACL_ARCH_X86_32;
217   } else if (strcmp(nacl_arch, "x86-64") == 0) {
218     info->nacl_arch = PlatformInfo::NACL_ARCH_X86_64;
219   } else {
220     NOTREACHED();
221     return false;
222   }
223 
224   return true;
225 }
226 
RestartDevice(std::string * error_message)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;
234   }
235 #endif
236   *error_message = "Function available only for ChromeOS kiosk mode.";
237   return false;
238 }
239 
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)240 void ChromeRuntimeAPIDelegate::Observe(
241     int type,
242     const content::NotificationSource& source,
243     const content::NotificationDetails& details) {
244   DCHECK(type == extensions::NOTIFICATION_EXTENSION_UPDATE_FOUND);
245   typedef const std::pair<std::string, Version> UpdateDetails;
246   const std::string& id = content::Details<UpdateDetails>(details)->first;
247   const Version& version = content::Details<UpdateDetails>(details)->second;
248   if (version.IsValid()) {
249     CallUpdateCallbacks(
250         id, UpdateCheckResult(true, kUpdateFound, version.GetString()));
251   }
252 }
253 
UpdateCheckComplete(const std::string & extension_id)254 void ChromeRuntimeAPIDelegate::UpdateCheckComplete(
255     const std::string& extension_id) {
256   ExtensionSystem* system = ExtensionSystem::Get(browser_context_);
257   ExtensionService* service = system->extension_service();
258   const Extension* update = service->GetPendingExtensionUpdate(extension_id);
259   if (update) {
260     CallUpdateCallbacks(
261         extension_id,
262         UpdateCheckResult(true, kUpdateFound, update->VersionString()));
263   } else {
264     CallUpdateCallbacks(extension_id,
265                         UpdateCheckResult(true, kUpdateNotFound, ""));
266   }
267 }
268 
CallUpdateCallbacks(const std::string & extension_id,const UpdateCheckResult & result)269 void ChromeRuntimeAPIDelegate::CallUpdateCallbacks(
270     const std::string& extension_id,
271     const UpdateCheckResult& result) {
272   UpdateCallbackList callbacks = pending_update_checks_[extension_id];
273   pending_update_checks_.erase(extension_id);
274   for (UpdateCallbackList::const_iterator iter = callbacks.begin();
275        iter != callbacks.end();
276        ++iter) {
277     const UpdateCheckCallback& callback = *iter;
278     callback.Run(result);
279   }
280 }
281