Roll src/third_party/WebKit d9c6159:8139f33 (svn 201974:201975)
[chromium-blink-merge.git] / extensions / browser / api / runtime / runtime_api.cc
blob443f93682b6d46cb3e054fa930516c7ebe2e2eac
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 "extensions/browser/api/runtime/runtime_api.h"
7 #include <utility>
9 #include "base/lazy_instance.h"
10 #include "base/logging.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/metrics/histogram.h"
13 #include "base/values.h"
14 #include "base/version.h"
15 #include "content/public/browser/browser_context.h"
16 #include "content/public/browser/child_process_security_policy.h"
17 #include "content/public/browser/notification_service.h"
18 #include "content/public/browser/render_frame_host.h"
19 #include "content/public/browser/render_process_host.h"
20 #include "extensions/browser/api/runtime/runtime_api_delegate.h"
21 #include "extensions/browser/event_router.h"
22 #include "extensions/browser/extension_host.h"
23 #include "extensions/browser/extension_prefs.h"
24 #include "extensions/browser/extension_registry.h"
25 #include "extensions/browser/extension_system.h"
26 #include "extensions/browser/extension_util.h"
27 #include "extensions/browser/extensions_browser_client.h"
28 #include "extensions/browser/lazy_background_task_queue.h"
29 #include "extensions/browser/notification_types.h"
30 #include "extensions/browser/process_manager_factory.h"
31 #include "extensions/common/api/runtime.h"
32 #include "extensions/common/error_utils.h"
33 #include "extensions/common/extension.h"
34 #include "extensions/common/manifest_handlers/background_info.h"
35 #include "extensions/common/manifest_handlers/shared_module_info.h"
36 #include "storage/browser/fileapi/isolated_context.h"
37 #include "url/gurl.h"
39 using content::BrowserContext;
41 namespace extensions {
43 namespace runtime = api::runtime;
45 namespace {
47 const char kNoBackgroundPageError[] = "You do not have a background page.";
48 const char kPageLoadError[] = "Background page failed to load.";
49 const char kFailedToCreateOptionsPage[] = "Could not create an options page.";
50 const char kInstallId[] = "id";
51 const char kInstallReason[] = "reason";
52 const char kInstallReasonChromeUpdate[] = "chrome_update";
53 const char kInstallReasonUpdate[] = "update";
54 const char kInstallReasonInstall[] = "install";
55 const char kInstallReasonSharedModuleUpdate[] = "shared_module_update";
56 const char kInstallPreviousVersion[] = "previousVersion";
57 const char kInvalidUrlError[] = "Invalid URL: \"*\".";
58 const char kPlatformInfoUnavailable[] = "Platform information unavailable.";
60 const char kUpdatesDisabledError[] = "Autoupdate is not enabled.";
62 // A preference key storing the url loaded when an extension is uninstalled.
63 const char kUninstallUrl[] = "uninstall_url";
65 // The name of the directory to be returned by getPackageDirectoryEntry. This
66 // particular value does not matter to user code, but is chosen for consistency
67 // with the equivalent Pepper API.
68 const char kPackageDirectoryPath[] = "crxfs";
70 void DispatchOnStartupEventImpl(BrowserContext* browser_context,
71 const std::string& extension_id,
72 bool first_call,
73 ExtensionHost* host) {
74 // A NULL host from the LazyBackgroundTaskQueue means the page failed to
75 // load. Give up.
76 if (!host && !first_call)
77 return;
79 // Don't send onStartup events to incognito browser contexts.
80 if (browser_context->IsOffTheRecord())
81 return;
83 if (ExtensionsBrowserClient::Get()->IsShuttingDown() ||
84 !ExtensionsBrowserClient::Get()->IsValidContext(browser_context))
85 return;
86 ExtensionSystem* system = ExtensionSystem::Get(browser_context);
87 if (!system)
88 return;
90 // If this is a persistent background page, we want to wait for it to load
91 // (it might not be ready, since this is startup). But only enqueue once.
92 // If it fails to load the first time, don't bother trying again.
93 const Extension* extension =
94 ExtensionRegistry::Get(browser_context)->enabled_extensions().GetByID(
95 extension_id);
96 if (extension && BackgroundInfo::HasPersistentBackgroundPage(extension) &&
97 first_call &&
98 LazyBackgroundTaskQueue::Get(browser_context)
99 ->ShouldEnqueueTask(browser_context, extension)) {
100 LazyBackgroundTaskQueue::Get(browser_context)
101 ->AddPendingTask(browser_context, extension_id,
102 base::Bind(&DispatchOnStartupEventImpl,
103 browser_context, extension_id, false));
104 return;
107 scoped_ptr<base::ListValue> event_args(new base::ListValue());
108 scoped_ptr<Event> event(new Event(events::RUNTIME_ON_STARTUP,
109 runtime::OnStartup::kEventName,
110 event_args.Pass()));
111 EventRouter::Get(browser_context)
112 ->DispatchEventToExtension(extension_id, event.Pass());
115 void SetUninstallURL(ExtensionPrefs* prefs,
116 const std::string& extension_id,
117 const std::string& url_string) {
118 prefs->UpdateExtensionPref(
119 extension_id, kUninstallUrl, new base::StringValue(url_string));
122 std::string GetUninstallURL(ExtensionPrefs* prefs,
123 const std::string& extension_id) {
124 std::string url_string;
125 prefs->ReadPrefAsString(extension_id, kUninstallUrl, &url_string);
126 return url_string;
129 } // namespace
131 ///////////////////////////////////////////////////////////////////////////////
133 static base::LazyInstance<BrowserContextKeyedAPIFactory<RuntimeAPI> >
134 g_factory = LAZY_INSTANCE_INITIALIZER;
136 // static
137 BrowserContextKeyedAPIFactory<RuntimeAPI>* RuntimeAPI::GetFactoryInstance() {
138 return g_factory.Pointer();
141 template <>
142 void BrowserContextKeyedAPIFactory<RuntimeAPI>::DeclareFactoryDependencies() {
143 DependsOn(ProcessManagerFactory::GetInstance());
146 RuntimeAPI::RuntimeAPI(content::BrowserContext* context)
147 : browser_context_(context),
148 dispatch_chrome_updated_event_(false),
149 extension_registry_observer_(this),
150 process_manager_observer_(this) {
151 // RuntimeAPI is redirected in incognito, so |browser_context_| is never
152 // incognito.
153 DCHECK(!browser_context_->IsOffTheRecord());
155 registrar_.Add(this,
156 extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED,
157 content::Source<BrowserContext>(context));
158 extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_));
159 process_manager_observer_.Add(ProcessManager::Get(browser_context_));
161 delegate_ = ExtensionsBrowserClient::Get()->CreateRuntimeAPIDelegate(
162 browser_context_);
164 // Check if registered events are up-to-date. We can only do this once
165 // per browser context, since it updates internal state when called.
166 dispatch_chrome_updated_event_ =
167 ExtensionsBrowserClient::Get()->DidVersionUpdate(browser_context_);
170 RuntimeAPI::~RuntimeAPI() {
173 void RuntimeAPI::Observe(int type,
174 const content::NotificationSource& source,
175 const content::NotificationDetails& details) {
176 DCHECK_EQ(extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED, type);
177 // We're done restarting Chrome after an update.
178 dispatch_chrome_updated_event_ = false;
180 delegate_->AddUpdateObserver(this);
183 void RuntimeAPI::OnExtensionLoaded(content::BrowserContext* browser_context,
184 const Extension* extension) {
185 if (!dispatch_chrome_updated_event_)
186 return;
188 // Dispatch the onInstalled event with reason "chrome_update".
189 base::MessageLoop::current()->PostTask(
190 FROM_HERE,
191 base::Bind(&RuntimeEventRouter::DispatchOnInstalledEvent,
192 browser_context_,
193 extension->id(),
194 Version(),
195 true));
198 void RuntimeAPI::OnExtensionWillBeInstalled(
199 content::BrowserContext* browser_context,
200 const Extension* extension,
201 bool is_update,
202 bool from_ephemeral,
203 const std::string& old_name) {
204 // Ephemeral apps are not considered to be installed and do not receive
205 // the onInstalled() event.
206 if (util::IsEphemeralApp(extension->id(), browser_context_))
207 return;
209 Version old_version = delegate_->GetPreviousExtensionVersion(extension);
211 // Dispatch the onInstalled event.
212 base::MessageLoop::current()->PostTask(
213 FROM_HERE,
214 base::Bind(&RuntimeEventRouter::DispatchOnInstalledEvent,
215 browser_context_,
216 extension->id(),
217 old_version,
218 false));
221 void RuntimeAPI::OnExtensionUninstalled(
222 content::BrowserContext* browser_context,
223 const Extension* extension,
224 UninstallReason reason) {
225 // Ephemeral apps are not considered to be installed, so the uninstall URL
226 // is not invoked when they are removed.
227 if (util::IsEphemeralApp(extension->id(), browser_context_))
228 return;
230 RuntimeEventRouter::OnExtensionUninstalled(
231 browser_context_, extension->id(), reason);
234 void RuntimeAPI::Shutdown() {
235 delegate_->RemoveUpdateObserver(this);
238 void RuntimeAPI::OnAppUpdateAvailable(const Extension* extension) {
239 RuntimeEventRouter::DispatchOnUpdateAvailableEvent(
240 browser_context_, extension->id(), extension->manifest()->value());
243 void RuntimeAPI::OnChromeUpdateAvailable() {
244 RuntimeEventRouter::DispatchOnBrowserUpdateAvailableEvent(browser_context_);
247 void RuntimeAPI::OnBackgroundHostStartup(const Extension* extension) {
248 RuntimeEventRouter::DispatchOnStartupEvent(browser_context_, extension->id());
251 void RuntimeAPI::ReloadExtension(const std::string& extension_id) {
252 delegate_->ReloadExtension(extension_id);
255 bool RuntimeAPI::CheckForUpdates(
256 const std::string& extension_id,
257 const RuntimeAPIDelegate::UpdateCheckCallback& callback) {
258 return delegate_->CheckForUpdates(extension_id, callback);
261 void RuntimeAPI::OpenURL(const GURL& update_url) {
262 delegate_->OpenURL(update_url);
265 bool RuntimeAPI::GetPlatformInfo(runtime::PlatformInfo* info) {
266 return delegate_->GetPlatformInfo(info);
269 bool RuntimeAPI::RestartDevice(std::string* error_message) {
270 return delegate_->RestartDevice(error_message);
273 bool RuntimeAPI::OpenOptionsPage(const Extension* extension) {
274 return delegate_->OpenOptionsPage(extension);
277 ///////////////////////////////////////////////////////////////////////////////
279 // static
280 void RuntimeEventRouter::DispatchOnStartupEvent(
281 content::BrowserContext* context,
282 const std::string& extension_id) {
283 DispatchOnStartupEventImpl(context, extension_id, true, NULL);
286 // static
287 void RuntimeEventRouter::DispatchOnInstalledEvent(
288 content::BrowserContext* context,
289 const std::string& extension_id,
290 const Version& old_version,
291 bool chrome_updated) {
292 if (!ExtensionsBrowserClient::Get()->IsValidContext(context))
293 return;
294 ExtensionSystem* system = ExtensionSystem::Get(context);
295 if (!system)
296 return;
298 scoped_ptr<base::ListValue> event_args(new base::ListValue());
299 base::DictionaryValue* info = new base::DictionaryValue();
300 event_args->Append(info);
301 if (old_version.IsValid()) {
302 info->SetString(kInstallReason, kInstallReasonUpdate);
303 info->SetString(kInstallPreviousVersion, old_version.GetString());
304 } else if (chrome_updated) {
305 info->SetString(kInstallReason, kInstallReasonChromeUpdate);
306 } else {
307 info->SetString(kInstallReason, kInstallReasonInstall);
309 EventRouter* event_router = EventRouter::Get(context);
310 DCHECK(event_router);
311 scoped_ptr<Event> event(new Event(events::RUNTIME_ON_INSTALLED,
312 runtime::OnInstalled::kEventName,
313 event_args.Pass()));
314 event_router->DispatchEventWithLazyListener(extension_id, event.Pass());
316 if (old_version.IsValid()) {
317 const Extension* extension =
318 ExtensionRegistry::Get(context)->enabled_extensions().GetByID(
319 extension_id);
320 if (extension && SharedModuleInfo::IsSharedModule(extension)) {
321 scoped_ptr<ExtensionSet> dependents =
322 system->GetDependentExtensions(extension);
323 for (ExtensionSet::const_iterator i = dependents->begin();
324 i != dependents->end();
325 i++) {
326 scoped_ptr<base::ListValue> sm_event_args(new base::ListValue());
327 base::DictionaryValue* sm_info = new base::DictionaryValue();
328 sm_event_args->Append(sm_info);
329 sm_info->SetString(kInstallReason, kInstallReasonSharedModuleUpdate);
330 sm_info->SetString(kInstallPreviousVersion, old_version.GetString());
331 sm_info->SetString(kInstallId, extension_id);
332 scoped_ptr<Event> sm_event(new Event(events::RUNTIME_ON_INSTALLED,
333 runtime::OnInstalled::kEventName,
334 sm_event_args.Pass()));
335 event_router->DispatchEventWithLazyListener((*i)->id(),
336 sm_event.Pass());
342 // static
343 void RuntimeEventRouter::DispatchOnUpdateAvailableEvent(
344 content::BrowserContext* context,
345 const std::string& extension_id,
346 const base::DictionaryValue* manifest) {
347 ExtensionSystem* system = ExtensionSystem::Get(context);
348 if (!system)
349 return;
351 scoped_ptr<base::ListValue> args(new base::ListValue);
352 args->Append(manifest->DeepCopy());
353 EventRouter* event_router = EventRouter::Get(context);
354 DCHECK(event_router);
355 scoped_ptr<Event> event(new Event(events::RUNTIME_ON_UPDATE_AVAILABLE,
356 runtime::OnUpdateAvailable::kEventName,
357 args.Pass()));
358 event_router->DispatchEventToExtension(extension_id, event.Pass());
361 // static
362 void RuntimeEventRouter::DispatchOnBrowserUpdateAvailableEvent(
363 content::BrowserContext* context) {
364 ExtensionSystem* system = ExtensionSystem::Get(context);
365 if (!system)
366 return;
368 scoped_ptr<base::ListValue> args(new base::ListValue);
369 EventRouter* event_router = EventRouter::Get(context);
370 DCHECK(event_router);
371 scoped_ptr<Event> event(
372 new Event(events::RUNTIME_ON_BROWSER_UPDATE_AVAILABLE,
373 runtime::OnBrowserUpdateAvailable::kEventName, args.Pass()));
374 event_router->BroadcastEvent(event.Pass());
377 // static
378 void RuntimeEventRouter::DispatchOnRestartRequiredEvent(
379 content::BrowserContext* context,
380 const std::string& app_id,
381 api::runtime::OnRestartRequiredReason reason) {
382 ExtensionSystem* system = ExtensionSystem::Get(context);
383 if (!system)
384 return;
386 scoped_ptr<Event> event(
387 new Event(events::RUNTIME_ON_RESTART_REQUIRED,
388 runtime::OnRestartRequired::kEventName,
389 api::runtime::OnRestartRequired::Create(reason)));
390 EventRouter* event_router = EventRouter::Get(context);
391 DCHECK(event_router);
392 event_router->DispatchEventToExtension(app_id, event.Pass());
395 // static
396 void RuntimeEventRouter::OnExtensionUninstalled(
397 content::BrowserContext* context,
398 const std::string& extension_id,
399 UninstallReason reason) {
400 if (!(reason == UNINSTALL_REASON_USER_INITIATED ||
401 reason == UNINSTALL_REASON_MANAGEMENT_API)) {
402 return;
405 GURL uninstall_url(
406 GetUninstallURL(ExtensionPrefs::Get(context), extension_id));
408 if (!uninstall_url.SchemeIsHTTPOrHTTPS()) {
409 // Previous versions of Chrome allowed non-http(s) URLs to be stored in the
410 // prefs. Now they're disallowed, but the old data may still exist.
411 return;
414 RuntimeAPI::GetFactoryInstance()->Get(context)->OpenURL(uninstall_url);
417 ExtensionFunction::ResponseAction RuntimeGetBackgroundPageFunction::Run() {
418 ExtensionHost* host = ProcessManager::Get(browser_context())
419 ->GetBackgroundHostForExtension(extension_id());
420 if (LazyBackgroundTaskQueue::Get(browser_context())
421 ->ShouldEnqueueTask(browser_context(), extension())) {
422 LazyBackgroundTaskQueue::Get(browser_context())
423 ->AddPendingTask(
424 browser_context(), extension_id(),
425 base::Bind(&RuntimeGetBackgroundPageFunction::OnPageLoaded, this));
426 } else if (host) {
427 OnPageLoaded(host);
428 } else {
429 return RespondNow(Error(kNoBackgroundPageError));
432 return RespondLater();
435 void RuntimeGetBackgroundPageFunction::OnPageLoaded(ExtensionHost* host) {
436 if (host) {
437 Respond(NoArguments());
438 } else {
439 Respond(Error(kPageLoadError));
443 ExtensionFunction::ResponseAction RuntimeOpenOptionsPageFunction::Run() {
444 RuntimeAPI* api = RuntimeAPI::GetFactoryInstance()->Get(browser_context());
445 return RespondNow(api->OpenOptionsPage(extension())
446 ? NoArguments()
447 : Error(kFailedToCreateOptionsPage));
450 ExtensionFunction::ResponseAction RuntimeSetUninstallURLFunction::Run() {
451 std::string url_string;
452 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &url_string));
454 if (!url_string.empty() && !GURL(url_string).SchemeIsHTTPOrHTTPS()) {
455 return RespondNow(Error(kInvalidUrlError, url_string));
457 SetUninstallURL(
458 ExtensionPrefs::Get(browser_context()), extension_id(), url_string);
459 return RespondNow(NoArguments());
462 ExtensionFunction::ResponseAction RuntimeReloadFunction::Run() {
463 RuntimeAPI::GetFactoryInstance()->Get(browser_context())->ReloadExtension(
464 extension_id());
465 return RespondNow(NoArguments());
468 ExtensionFunction::ResponseAction RuntimeRequestUpdateCheckFunction::Run() {
469 if (!RuntimeAPI::GetFactoryInstance()
470 ->Get(browser_context())
471 ->CheckForUpdates(
472 extension_id(),
473 base::Bind(&RuntimeRequestUpdateCheckFunction::CheckComplete,
474 this))) {
475 return RespondNow(Error(kUpdatesDisabledError));
477 return RespondLater();
480 void RuntimeRequestUpdateCheckFunction::CheckComplete(
481 const RuntimeAPIDelegate::UpdateCheckResult& result) {
482 if (result.success) {
483 base::DictionaryValue* details = new base::DictionaryValue;
484 details->SetString("version", result.version);
485 Respond(TwoArguments(new base::StringValue(result.response), details));
486 } else {
487 // HMM(kalman): Why does !success not imply Error()?
488 Respond(OneArgument(new base::StringValue(result.response)));
492 ExtensionFunction::ResponseAction RuntimeRestartFunction::Run() {
493 std::string message;
494 bool result =
495 RuntimeAPI::GetFactoryInstance()->Get(browser_context())->RestartDevice(
496 &message);
497 if (!result) {
498 return RespondNow(Error(message));
500 return RespondNow(NoArguments());
503 ExtensionFunction::ResponseAction RuntimeGetPlatformInfoFunction::Run() {
504 runtime::PlatformInfo info;
505 if (!RuntimeAPI::GetFactoryInstance()
506 ->Get(browser_context())
507 ->GetPlatformInfo(&info)) {
508 return RespondNow(Error(kPlatformInfoUnavailable));
510 return RespondNow(
511 ArgumentList(runtime::GetPlatformInfo::Results::Create(info)));
514 ExtensionFunction::ResponseAction
515 RuntimeGetPackageDirectoryEntryFunction::Run() {
516 storage::IsolatedContext* isolated_context =
517 storage::IsolatedContext::GetInstance();
518 DCHECK(isolated_context);
520 std::string relative_path = kPackageDirectoryPath;
521 base::FilePath path = extension_->path();
522 std::string filesystem_id = isolated_context->RegisterFileSystemForPath(
523 storage::kFileSystemTypeNativeLocal, std::string(), path, &relative_path);
525 int renderer_id = render_frame_host()->GetProcess()->GetID();
526 content::ChildProcessSecurityPolicy* policy =
527 content::ChildProcessSecurityPolicy::GetInstance();
528 policy->GrantReadFileSystem(renderer_id, filesystem_id);
529 base::DictionaryValue* dict = new base::DictionaryValue();
530 dict->SetString("fileSystemId", filesystem_id);
531 dict->SetString("baseName", relative_path);
532 return RespondNow(OneArgument(dict));
535 } // namespace extensions