OLD | NEW |
---|---|
(Empty) | |
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/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/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/omaha_query_params/omaha_query_params.h" | |
16 #include "chrome/browser/profiles/profile.h" | |
17 #include "chrome/browser/ui/browser_finder.h" | |
18 #include "chrome/browser/ui/browser_navigator.h" | |
19 #include "chrome/browser/ui/browser_window.h" | |
20 #include "content/public/browser/notification_service.h" | |
21 #include "extensions/browser/extension_system.h" | |
22 | |
23 #if defined(OS_CHROMEOS) | |
24 #include "chrome/browser/chromeos/login/user_manager.h" | |
25 #include "chromeos/dbus/dbus_thread_manager.h" | |
26 #include "chromeos/dbus/power_manager_client.h" | |
27 #endif | |
28 | |
29 using extensions::Extension; | |
30 using extensions::ExtensionSystem; | |
31 using extensions::ExtensionUpdater; | |
32 using extensions::RuntimeAPI; | |
33 | |
34 using extensions::core_api::runtime::GetPlatformInfo::Results::PlatformInfo; | |
35 | |
36 namespace { | |
37 | |
38 const char kUpdatesDisabledError[] = "Autoupdate is not enabled."; | |
39 | |
40 const char kUpdateThrottled[] = "throttled"; | |
41 const char kUpdateNotFound[] = "no_update"; | |
42 const char kUpdateFound[] = "update_available"; | |
43 | |
44 // If an extension reloads itself within this many miliseconds of reloading | |
45 // itself, the reload is considered suspiciously fast. | |
46 const int kFastReloadTime = 10000; | |
47 | |
48 // After this many suspiciously fast consecutive reloads, an extension will get | |
49 // disabled. | |
50 const int kFastReloadCount = 5; | |
51 | |
52 } // namespace | |
53 | |
54 ChromeRuntimeAPIDelegate::ChromeRuntimeAPIDelegate( | |
55 content::BrowserContext* context) | |
56 : browser_context_(context), registered_for_updates_(false) { | |
57 registrar_.Add(this, | |
58 chrome::NOTIFICATION_EXTENSION_UPDATE_FOUND, | |
59 content::NotificationService::AllSources()); | |
60 } | |
61 | |
62 ChromeRuntimeAPIDelegate::~ChromeRuntimeAPIDelegate() { | |
63 } | |
64 | |
65 void ChromeRuntimeAPIDelegate::RegisterUpdateObserver( | |
not at google - send to devlin
2014/05/05 19:26:15
why is this called "RegisterUpdateObserver" while
Ken Rockot(use gerrit already)
2014/05/05 21:08:07
I don't have a good answer. Changed.
| |
66 extensions::UpdateObserver* observer) { | |
67 registered_for_updates_ = true; | |
68 ExtensionSystem::Get(browser_context_) | |
69 ->extension_service() | |
70 ->AddUpdateObserver(observer); | |
71 } | |
72 | |
73 void ChromeRuntimeAPIDelegate::UnregisterUpdateObserver( | |
74 extensions::UpdateObserver* observer) { | |
75 if (registered_for_updates_) { | |
76 ExtensionSystem::Get(browser_context_) | |
77 ->extension_service() | |
78 ->RemoveUpdateObserver(observer); | |
79 } | |
80 } | |
81 | |
82 base::Version ChromeRuntimeAPIDelegate::GetOldExtensionVersion( | |
83 const Extension* extension) { | |
84 // Get the previous version to check if this is an upgrade. | |
85 ExtensionService* service = | |
86 ExtensionSystem::Get(browser_context_)->extension_service(); | |
87 const Extension* old = service->GetExtensionById(extension->id(), true); | |
88 if (old) | |
89 return *old->version(); | |
90 return base::Version(); | |
91 } | |
92 | |
93 void ChromeRuntimeAPIDelegate::MaybeReloadExtension( | |
94 const std::string& extension_id) { | |
95 std::pair<base::TimeTicks, int>& reload_info = | |
96 last_reload_time_[extension_id]; | |
97 base::TimeTicks now = base::TimeTicks::Now(); | |
98 if (reload_info.first.is_null() || | |
99 (now - reload_info.first).InMilliseconds() > kFastReloadTime) { | |
100 reload_info.second = 0; | |
101 } else { | |
102 reload_info.second++; | |
103 } | |
104 if (!reload_info.first.is_null()) { | |
105 UMA_HISTOGRAM_LONG_TIMES("Extensions.RuntimeReloadTime", | |
106 now - reload_info.first); | |
107 } | |
108 UMA_HISTOGRAM_COUNTS_100("Extensions.RuntimeReloadFastCount", | |
109 reload_info.second); | |
110 reload_info.first = now; | |
111 | |
112 ExtensionService* service = | |
113 ExtensionSystem::Get(browser_context_)->extension_service(); | |
114 if (reload_info.second >= kFastReloadCount) { | |
115 // Unloading an extension clears all warnings, so first terminate the | |
116 // extension, and then add the warning. Since this is called from an | |
117 // extension function unloading the extension has to be done | |
118 // asynchronously. Fortunately PostTask guarentees FIFO order so just | |
119 // post both tasks. | |
120 base::MessageLoop::current()->PostTask( | |
121 FROM_HERE, | |
122 base::Bind(&ExtensionService::TerminateExtension, | |
123 service->AsWeakPtr(), | |
124 extension_id)); | |
125 extensions::ExtensionWarningSet warnings; | |
126 warnings.insert( | |
127 extensions::ExtensionWarning::CreateReloadTooFrequentWarning( | |
128 extension_id)); | |
129 base::MessageLoop::current()->PostTask( | |
130 FROM_HERE, | |
131 base::Bind(&extensions::ExtensionWarningService::NotifyWarningsOnUI, | |
132 browser_context_, | |
133 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::MessageLoop::current()->PostTask( | |
139 FROM_HERE, | |
140 base::Bind(&ExtensionService::ReloadExtension, | |
141 service->AsWeakPtr(), | |
142 extension_id)); | |
143 } | |
144 } | |
145 | |
146 bool ChromeRuntimeAPIDelegate::RequestUpdateCheck( | |
147 const std::string& extension_id, | |
148 const RuntimeAPI::UpdateCheckCallback& callback) { | |
149 ExtensionSystem* system = ExtensionSystem::Get(browser_context_); | |
150 ExtensionService* service = system->extension_service(); | |
151 ExtensionUpdater* updater = service->updater(); | |
152 if (!updater) { | |
153 return false; | |
154 } | |
155 if (!updater->CheckExtensionSoon( | |
156 extension_id, | |
157 base::Bind(&ChromeRuntimeAPIDelegate::UpdateCheckComplete, | |
158 base::Unretained(this), | |
159 extension_id))) { | |
160 base::MessageLoop::current()->PostTask( | |
161 FROM_HERE, | |
162 base::Bind(callback, | |
163 RuntimeAPI::UpdateCheckResult(true, kUpdateThrottled, ""))); | |
164 } else { | |
165 UpdateCallbackList& callbacks = pending_update_checks_[extension_id]; | |
166 callbacks.push_back(callback); | |
167 } | |
168 return true; | |
169 } | |
170 | |
171 void ChromeRuntimeAPIDelegate::HandleUninstall(const std::string& extension_id, | |
172 const GURL& uninstall_url) { | |
173 Profile* profile = Profile::FromBrowserContext(browser_context_); | |
174 Browser* browser = | |
175 chrome::FindLastActiveWithProfile(profile, chrome::GetActiveDesktop()); | |
176 if (!browser) | |
177 browser = | |
178 new Browser(Browser::CreateParams(profile, chrome::GetActiveDesktop())); | |
179 | |
180 chrome::NavigateParams params( | |
181 browser, uninstall_url, content::PAGE_TRANSITION_CLIENT_REDIRECT); | |
182 params.disposition = NEW_FOREGROUND_TAB; | |
183 params.user_gesture = false; | |
184 chrome::Navigate(¶ms); | |
185 } | |
186 | |
187 bool ChromeRuntimeAPIDelegate::GetPlatformInfo(PlatformInfo* info) { | |
188 const char* os = chrome::OmahaQueryParams::GetOS(); | |
189 if (strcmp(os, "mac") == 0) { | |
190 info->os = PlatformInfo::OS_MAC_; | |
191 } else if (strcmp(os, "win") == 0) { | |
192 info->os = PlatformInfo::OS_WIN_; | |
193 } else if (strcmp(os, "android") == 0) { | |
194 info->os = PlatformInfo::OS_ANDROID_; | |
195 } else if (strcmp(os, "cros") == 0) { | |
196 info->os = PlatformInfo::OS_CROS_; | |
197 } else if (strcmp(os, "linux") == 0) { | |
198 info->os = PlatformInfo::OS_LINUX_; | |
199 } else if (strcmp(os, "openbsd") == 0) { | |
200 info->os = PlatformInfo::OS_OPENBSD_; | |
201 } else { | |
202 NOTREACHED(); | |
203 return false; | |
204 } | |
205 | |
206 const char* arch = chrome::OmahaQueryParams::GetArch(); | |
207 if (strcmp(arch, "arm") == 0) { | |
208 info->arch = PlatformInfo::ARCH_ARM; | |
209 } else if (strcmp(arch, "x86") == 0) { | |
210 info->arch = PlatformInfo::ARCH_X86_32; | |
211 } else if (strcmp(arch, "x64") == 0) { | |
212 info->arch = PlatformInfo::ARCH_X86_64; | |
213 } else { | |
214 NOTREACHED(); | |
215 return false; | |
216 } | |
217 | |
218 const char* nacl_arch = chrome::OmahaQueryParams::GetNaclArch(); | |
219 if (strcmp(nacl_arch, "arm") == 0) { | |
220 info->nacl_arch = PlatformInfo::NACL_ARCH_ARM; | |
221 } else if (strcmp(nacl_arch, "x86-32") == 0) { | |
222 info->nacl_arch = PlatformInfo::NACL_ARCH_X86_32; | |
223 } else if (strcmp(nacl_arch, "x86-64") == 0) { | |
224 info->nacl_arch = PlatformInfo::NACL_ARCH_X86_64; | |
225 } else { | |
226 NOTREACHED(); | |
227 return false; | |
228 } | |
229 | |
230 return true; | |
231 } | |
232 | |
233 bool ChromeRuntimeAPIDelegate::RequestRestart(std::string* error_message) { | |
234 #if defined(OS_CHROMEOS) | |
235 if (chromeos::UserManager::Get()->IsLoggedInAsKioskApp()) { | |
236 chromeos::DBusThreadManager::Get() | |
237 ->GetPowerManagerClient() | |
238 ->RequestRestart(); | |
239 return true; | |
240 } | |
241 #endif | |
242 *error_message = "Function available only for ChromeOS kiosk mode."; | |
243 return false; | |
244 } | |
245 | |
246 void ChromeRuntimeAPIDelegate::Observe( | |
247 int type, | |
248 const content::NotificationSource& source, | |
249 const content::NotificationDetails& details) { | |
250 DCHECK(type == chrome::NOTIFICATION_EXTENSION_UPDATE_FOUND); | |
251 typedef const std::pair<std::string, Version> UpdateDetails; | |
252 const std::string& id = content::Details<UpdateDetails>(details)->first; | |
253 const Version& version = content::Details<UpdateDetails>(details)->second; | |
254 CallUpdateCallbacks( | |
255 id, | |
256 RuntimeAPI::UpdateCheckResult(true, kUpdateFound, version.GetString())); | |
257 } | |
258 | |
259 void ChromeRuntimeAPIDelegate::UpdateCheckComplete( | |
260 const std::string& extension_id) { | |
261 ExtensionSystem* system = ExtensionSystem::Get(browser_context_); | |
262 ExtensionService* service = system->extension_service(); | |
263 const Extension* update = service->GetPendingExtensionUpdate(extension_id); | |
264 if (update) { | |
265 CallUpdateCallbacks(extension_id, | |
266 RuntimeAPI::UpdateCheckResult( | |
267 true, kUpdateFound, update->VersionString())); | |
268 } else { | |
269 CallUpdateCallbacks( | |
270 extension_id, RuntimeAPI::UpdateCheckResult(true, kUpdateNotFound, "")); | |
271 } | |
272 } | |
273 | |
274 void ChromeRuntimeAPIDelegate::CallUpdateCallbacks( | |
275 const std::string& extension_id, | |
276 const RuntimeAPI::UpdateCheckResult& result) { | |
277 UpdateCallbackList callbacks = pending_update_checks_[extension_id]; | |
278 pending_update_checks_.erase(extension_id); | |
279 for (UpdateCallbackList::const_iterator iter = callbacks.begin(); | |
280 iter != callbacks.end(); | |
281 ++iter) { | |
282 const RuntimeAPI::UpdateCheckCallback& callback = *iter; | |
283 callback.Run(result); | |
284 } | |
285 } | |
OLD | NEW |