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"
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 "chrome/browser/chrome_notification_types.h"
16 #include "content/public/browser/browser_context.h"
17 #include "content/public/browser/child_process_security_policy.h"
18 #include "content/public/browser/notification_service.h"
19 #include "content/public/browser/render_process_host.h"
20 #include "content/public/browser/render_view_host.h"
21 #include "extensions/browser/api/runtime/runtime_api_delegate.h"
22 #include "extensions/browser/event_router.h"
23 #include "extensions/browser/extension_host.h"
24 #include "extensions/browser/extension_prefs.h"
25 #include "extensions/browser/extension_registry.h"
26 #include "extensions/browser/extension_system.h"
27 #include "extensions/browser/extension_util.h"
28 #include "extensions/browser/extensions_browser_client.h"
29 #include "extensions/browser/lazy_background_task_queue.h"
30 #include "extensions/browser/process_manager.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"
37 #include "webkit/browser/fileapi/isolated_context.h"
39 using content::BrowserContext;
41 namespace extensions {
43 namespace runtime = core_api::runtime;
47 const char kNoBackgroundPageError[] = "You do not have a background page.";
48 const char kPageLoadError[] = "Background page failed to load.";
49 const char kInstallId[] = "id";
50 const char kInstallReason[] = "reason";
51 const char kInstallReasonChromeUpdate[] = "chrome_update";
52 const char kInstallReasonUpdate[] = "update";
53 const char kInstallReasonInstall[] = "install";
54 const char kInstallReasonSharedModuleUpdate[] = "shared_module_update";
55 const char kInstallPreviousVersion[] = "previousVersion";
56 const char kInvalidUrlError[] = "Invalid URL.";
57 const char kPlatformInfoUnavailable[] = "Platform information unavailable.";
59 const char kUpdatesDisabledError[] = "Autoupdate is not enabled.";
61 // A preference key storing the url loaded when an extension is uninstalled.
62 const char kUninstallUrl[] = "uninstall_url";
64 // The name of the directory to be returned by getPackageDirectoryEntry. This
65 // particular value does not matter to user code, but is chosen for consistency
66 // with the equivalent Pepper API.
67 const char kPackageDirectoryPath[] = "crxfs";
69 void DispatchOnStartupEventImpl(BrowserContext* browser_context,
70 const std::string& extension_id,
72 ExtensionHost* host) {
73 // A NULL host from the LazyBackgroundTaskQueue means the page failed to
75 if (!host && !first_call)
78 // Don't send onStartup events to incognito browser contexts.
79 if (browser_context->IsOffTheRecord())
82 if (ExtensionsBrowserClient::Get()->IsShuttingDown() ||
83 !ExtensionsBrowserClient::Get()->IsValidContext(browser_context))
85 ExtensionSystem* system = ExtensionSystem::Get(browser_context);
89 // If this is a persistent background page, we want to wait for it to load
90 // (it might not be ready, since this is startup). But only enqueue once.
91 // If it fails to load the first time, don't bother trying again.
92 const Extension* extension =
93 ExtensionRegistry::Get(browser_context)->enabled_extensions().GetByID(
95 if (extension && BackgroundInfo::HasPersistentBackgroundPage(extension) &&
97 system->lazy_background_task_queue()->ShouldEnqueueTask(browser_context,
99 system->lazy_background_task_queue()->AddPendingTask(
103 &DispatchOnStartupEventImpl, browser_context, extension_id, false));
107 scoped_ptr<base::ListValue> event_args(new base::ListValue());
108 scoped_ptr<Event> event(
109 new Event(runtime::OnStartup::kEventName, event_args.Pass()));
110 system->event_router()->DispatchEventToExtension(extension_id, event.Pass());
113 void SetUninstallURL(ExtensionPrefs* prefs,
114 const std::string& extension_id,
115 const std::string& url_string) {
116 prefs->UpdateExtensionPref(
117 extension_id, kUninstallUrl, new base::StringValue(url_string));
120 #if defined(ENABLE_EXTENSIONS)
121 std::string GetUninstallURL(ExtensionPrefs* prefs,
122 const std::string& extension_id) {
123 std::string url_string;
124 prefs->ReadPrefAsString(extension_id, kUninstallUrl, &url_string);
127 #endif // defined(ENABLE_EXTENSIONS)
131 ///////////////////////////////////////////////////////////////////////////////
133 static base::LazyInstance<BrowserContextKeyedAPIFactory<RuntimeAPI> >
134 g_factory = LAZY_INSTANCE_INITIALIZER;
137 BrowserContextKeyedAPIFactory<RuntimeAPI>* RuntimeAPI::GetFactoryInstance() {
138 return g_factory.Pointer();
141 RuntimeAPI::RuntimeAPI(content::BrowserContext* context)
142 : browser_context_(context), dispatch_chrome_updated_event_(false) {
144 chrome::NOTIFICATION_EXTENSIONS_READY,
145 content::Source<BrowserContext>(context));
147 chrome::NOTIFICATION_EXTENSION_LOADED_DEPRECATED,
148 content::Source<BrowserContext>(context));
150 chrome::NOTIFICATION_EXTENSION_INSTALLED_DEPRECATED,
151 content::Source<BrowserContext>(context));
153 chrome::NOTIFICATION_EXTENSION_UNINSTALLED_DEPRECATED,
154 content::Source<BrowserContext>(context));
156 delegate_ = ExtensionsBrowserClient::Get()->CreateRuntimeAPIDelegate(
159 // Check if registered events are up-to-date. We can only do this once
160 // per browser context, since it updates internal state when called.
161 dispatch_chrome_updated_event_ =
162 ExtensionsBrowserClient::Get()->DidVersionUpdate(browser_context_);
165 RuntimeAPI::~RuntimeAPI() {
166 delegate_->RemoveUpdateObserver(this);
169 void RuntimeAPI::Observe(int type,
170 const content::NotificationSource& source,
171 const content::NotificationDetails& details) {
173 case chrome::NOTIFICATION_EXTENSIONS_READY: {
177 case chrome::NOTIFICATION_EXTENSION_LOADED_DEPRECATED: {
178 const Extension* extension =
179 content::Details<const Extension>(details).ptr();
180 OnExtensionLoaded(extension);
183 case chrome::NOTIFICATION_EXTENSION_INSTALLED_DEPRECATED: {
184 const Extension* extension =
185 content::Details<const InstalledExtensionInfo>(details)->extension;
186 OnExtensionInstalled(extension);
189 case chrome::NOTIFICATION_EXTENSION_UNINSTALLED_DEPRECATED: {
190 const Extension* extension =
191 content::Details<const Extension>(details).ptr();
192 OnExtensionUninstalled(extension);
201 void RuntimeAPI::OnExtensionsReady() {
202 // We're done restarting Chrome after an update.
203 dispatch_chrome_updated_event_ = false;
205 delegate_->AddUpdateObserver(this);
207 // RuntimeAPI is redirected in incognito, so |browser_context_| is never
208 // incognito. We don't observe incognito ProcessManagers but that is OK
209 // because we don't send onStartup events to incognito browser contexts.
210 DCHECK(!browser_context_->IsOffTheRecord());
211 // Some tests use partially constructed Profiles without a process manager.
212 ExtensionSystem* extension_system = ExtensionSystem::Get(browser_context_);
213 if (extension_system->process_manager())
214 extension_system->process_manager()->AddObserver(this);
217 void RuntimeAPI::OnExtensionLoaded(const Extension* extension) {
218 if (!dispatch_chrome_updated_event_)
221 // Dispatch the onInstalled event with reason "chrome_update".
222 base::MessageLoop::current()->PostTask(
224 base::Bind(&RuntimeEventRouter::DispatchOnInstalledEvent,
231 void RuntimeAPI::OnExtensionInstalled(const Extension* extension) {
232 // Ephemeral apps are not considered to be installed and do not receive
233 // the onInstalled() event.
234 if (util::IsEphemeralApp(extension->id(), browser_context_))
237 Version old_version = delegate_->GetPreviousExtensionVersion(extension);
239 // Dispatch the onInstalled event.
240 base::MessageLoop::current()->PostTask(
242 base::Bind(&RuntimeEventRouter::DispatchOnInstalledEvent,
249 void RuntimeAPI::OnExtensionUninstalled(const Extension* extension) {
250 // Ephemeral apps are not considered to be installed, so the uninstall URL
251 // is not invoked when they are removed.
252 if (util::IsEphemeralApp(extension->id(), browser_context_))
255 RuntimeEventRouter::OnExtensionUninstalled(browser_context_, extension->id());
258 void RuntimeAPI::Shutdown() {
259 // ExtensionSystem deletes its ProcessManager during the Shutdown() phase, so
260 // the observer must be removed here and not in the RuntimeAPI destructor.
261 ProcessManager* process_manager =
262 ExtensionSystem::Get(browser_context_)->process_manager();
263 // Some tests use partially constructed Profiles without a process manager.
265 process_manager->RemoveObserver(this);
268 void RuntimeAPI::OnAppUpdateAvailable(const Extension* extension) {
269 RuntimeEventRouter::DispatchOnUpdateAvailableEvent(
270 browser_context_, extension->id(), extension->manifest()->value());
273 void RuntimeAPI::OnChromeUpdateAvailable() {
274 RuntimeEventRouter::DispatchOnBrowserUpdateAvailableEvent(browser_context_);
277 void RuntimeAPI::OnBackgroundHostStartup(const Extension* extension) {
278 RuntimeEventRouter::DispatchOnStartupEvent(browser_context_, extension->id());
281 void RuntimeAPI::ReloadExtension(const std::string& extension_id) {
282 delegate_->ReloadExtension(extension_id);
285 bool RuntimeAPI::CheckForUpdates(
286 const std::string& extension_id,
287 const RuntimeAPIDelegate::UpdateCheckCallback& callback) {
288 return delegate_->CheckForUpdates(extension_id, callback);
291 void RuntimeAPI::OpenURL(const GURL& update_url) {
292 delegate_->OpenURL(update_url);
295 bool RuntimeAPI::GetPlatformInfo(runtime::PlatformInfo* info) {
296 return delegate_->GetPlatformInfo(info);
299 bool RuntimeAPI::RestartDevice(std::string* error_message) {
300 return delegate_->RestartDevice(error_message);
303 ///////////////////////////////////////////////////////////////////////////////
306 void RuntimeEventRouter::DispatchOnStartupEvent(
307 content::BrowserContext* context,
308 const std::string& extension_id) {
309 DispatchOnStartupEventImpl(context, extension_id, true, NULL);
313 void RuntimeEventRouter::DispatchOnInstalledEvent(
314 content::BrowserContext* context,
315 const std::string& extension_id,
316 const Version& old_version,
317 bool chrome_updated) {
318 if (!ExtensionsBrowserClient::Get()->IsValidContext(context))
320 ExtensionSystem* system = ExtensionSystem::Get(context);
324 scoped_ptr<base::ListValue> event_args(new base::ListValue());
325 base::DictionaryValue* info = new base::DictionaryValue();
326 event_args->Append(info);
327 if (old_version.IsValid()) {
328 info->SetString(kInstallReason, kInstallReasonUpdate);
329 info->SetString(kInstallPreviousVersion, old_version.GetString());
330 } else if (chrome_updated) {
331 info->SetString(kInstallReason, kInstallReasonChromeUpdate);
333 info->SetString(kInstallReason, kInstallReasonInstall);
335 DCHECK(system->event_router());
336 scoped_ptr<Event> event(
337 new Event(runtime::OnInstalled::kEventName, event_args.Pass()));
338 system->event_router()->DispatchEventWithLazyListener(extension_id,
341 if (old_version.IsValid()) {
342 const Extension* extension =
343 ExtensionRegistry::Get(context)->enabled_extensions().GetByID(
345 if (extension && SharedModuleInfo::IsSharedModule(extension)) {
346 scoped_ptr<ExtensionSet> dependents =
347 system->GetDependentExtensions(extension);
348 for (ExtensionSet::const_iterator i = dependents->begin();
349 i != dependents->end();
351 scoped_ptr<base::ListValue> sm_event_args(new base::ListValue());
352 base::DictionaryValue* sm_info = new base::DictionaryValue();
353 sm_event_args->Append(sm_info);
354 sm_info->SetString(kInstallReason, kInstallReasonSharedModuleUpdate);
355 sm_info->SetString(kInstallPreviousVersion, old_version.GetString());
356 sm_info->SetString(kInstallId, extension_id);
357 scoped_ptr<Event> sm_event(
358 new Event(runtime::OnInstalled::kEventName, sm_event_args.Pass()));
359 system->event_router()->DispatchEventWithLazyListener((*i)->id(),
367 void RuntimeEventRouter::DispatchOnUpdateAvailableEvent(
368 content::BrowserContext* context,
369 const std::string& extension_id,
370 const base::DictionaryValue* manifest) {
371 ExtensionSystem* system = ExtensionSystem::Get(context);
375 scoped_ptr<base::ListValue> args(new base::ListValue);
376 args->Append(manifest->DeepCopy());
377 DCHECK(system->event_router());
378 scoped_ptr<Event> event(
379 new Event(runtime::OnUpdateAvailable::kEventName, args.Pass()));
380 system->event_router()->DispatchEventToExtension(extension_id, event.Pass());
384 void RuntimeEventRouter::DispatchOnBrowserUpdateAvailableEvent(
385 content::BrowserContext* context) {
386 ExtensionSystem* system = ExtensionSystem::Get(context);
390 scoped_ptr<base::ListValue> args(new base::ListValue);
391 DCHECK(system->event_router());
392 scoped_ptr<Event> event(
393 new Event(runtime::OnBrowserUpdateAvailable::kEventName, args.Pass()));
394 system->event_router()->BroadcastEvent(event.Pass());
398 void RuntimeEventRouter::DispatchOnRestartRequiredEvent(
399 content::BrowserContext* context,
400 const std::string& app_id,
401 core_api::runtime::OnRestartRequired::Reason reason) {
402 ExtensionSystem* system = ExtensionSystem::Get(context);
406 scoped_ptr<Event> event(
407 new Event(runtime::OnRestartRequired::kEventName,
408 core_api::runtime::OnRestartRequired::Create(reason)));
410 DCHECK(system->event_router());
411 system->event_router()->DispatchEventToExtension(app_id, event.Pass());
415 void RuntimeEventRouter::OnExtensionUninstalled(
416 content::BrowserContext* context,
417 const std::string& extension_id) {
418 #if defined(ENABLE_EXTENSIONS)
420 GetUninstallURL(ExtensionPrefs::Get(context), extension_id));
422 if (uninstall_url.is_empty())
425 RuntimeAPI::GetFactoryInstance()->Get(context)->OpenURL(uninstall_url);
426 #endif // defined(ENABLE_EXTENSIONS)
429 ExtensionFunction::ResponseAction RuntimeGetBackgroundPageFunction::Run() {
430 ExtensionSystem* system = ExtensionSystem::Get(browser_context());
431 ExtensionHost* host =
432 system->process_manager()->GetBackgroundHostForExtension(extension_id());
433 if (system->lazy_background_task_queue()->ShouldEnqueueTask(browser_context(),
435 system->lazy_background_task_queue()->AddPendingTask(
438 base::Bind(&RuntimeGetBackgroundPageFunction::OnPageLoaded, this));
442 return RespondNow(Error(kNoBackgroundPageError));
445 return RespondLater();
448 void RuntimeGetBackgroundPageFunction::OnPageLoaded(ExtensionHost* host) {
450 Respond(NoArguments());
452 Respond(Error(kPageLoadError));
456 ExtensionFunction::ResponseAction RuntimeSetUninstallURLFunction::Run() {
457 std::string url_string;
458 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &url_string));
460 GURL url(url_string);
461 if (!url.is_valid()) {
463 Error(ErrorUtils::FormatErrorMessage(kInvalidUrlError, url_string)));
466 ExtensionPrefs::Get(browser_context()), extension_id(), url_string);
467 return RespondNow(NoArguments());
470 ExtensionFunction::ResponseAction RuntimeReloadFunction::Run() {
471 RuntimeAPI::GetFactoryInstance()->Get(browser_context())->ReloadExtension(
473 return RespondNow(NoArguments());
476 ExtensionFunction::ResponseAction RuntimeRequestUpdateCheckFunction::Run() {
477 if (!RuntimeAPI::GetFactoryInstance()
478 ->Get(browser_context())
481 base::Bind(&RuntimeRequestUpdateCheckFunction::CheckComplete,
483 return RespondNow(Error(kUpdatesDisabledError));
485 return RespondLater();
488 void RuntimeRequestUpdateCheckFunction::CheckComplete(
489 const RuntimeAPIDelegate::UpdateCheckResult& result) {
490 if (result.success) {
491 base::DictionaryValue* details = new base::DictionaryValue;
492 details->SetString("version", result.version);
493 Respond(TwoArguments(new base::StringValue(result.response), details));
495 // HMM(kalman): Why does !success not imply Error()?
496 Respond(OneArgument(new base::StringValue(result.response)));
500 ExtensionFunction::ResponseAction RuntimeRestartFunction::Run() {
503 RuntimeAPI::GetFactoryInstance()->Get(browser_context())->RestartDevice(
506 return RespondNow(Error(message));
508 return RespondNow(NoArguments());
511 ExtensionFunction::ResponseAction RuntimeGetPlatformInfoFunction::Run() {
512 runtime::PlatformInfo info;
513 if (!RuntimeAPI::GetFactoryInstance()
514 ->Get(browser_context())
515 ->GetPlatformInfo(&info)) {
516 return RespondNow(Error(kPlatformInfoUnavailable));
519 ArgumentList(runtime::GetPlatformInfo::Results::Create(info)));
522 ExtensionFunction::ResponseAction
523 RuntimeGetPackageDirectoryEntryFunction::Run() {
524 fileapi::IsolatedContext* isolated_context =
525 fileapi::IsolatedContext::GetInstance();
526 DCHECK(isolated_context);
528 std::string relative_path = kPackageDirectoryPath;
529 base::FilePath path = extension_->path();
530 std::string filesystem_id = isolated_context->RegisterFileSystemForPath(
531 fileapi::kFileSystemTypeNativeLocal, std::string(), path, &relative_path);
533 int renderer_id = render_view_host_->GetProcess()->GetID();
534 content::ChildProcessSecurityPolicy* policy =
535 content::ChildProcessSecurityPolicy::GetInstance();
536 policy->GrantReadFileSystem(renderer_id, filesystem_id);
537 base::DictionaryValue* dict = new base::DictionaryValue();
538 dict->SetString("fileSystemId", filesystem_id);
539 dict->SetString("baseName", relative_path);
540 return RespondNow(OneArgument(dict));
543 } // namespace extensions