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 "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_process_host.h"
19 #include "content/public/browser/render_view_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"
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 std::string
GetUninstallURL(ExtensionPrefs
* prefs
,
121 const std::string
& extension_id
) {
122 std::string url_string
;
123 prefs
->ReadPrefAsString(extension_id
, kUninstallUrl
, &url_string
);
129 ///////////////////////////////////////////////////////////////////////////////
131 static base::LazyInstance
<BrowserContextKeyedAPIFactory
<RuntimeAPI
> >
132 g_factory
= LAZY_INSTANCE_INITIALIZER
;
135 BrowserContextKeyedAPIFactory
<RuntimeAPI
>* RuntimeAPI::GetFactoryInstance() {
136 return g_factory
.Pointer();
140 void BrowserContextKeyedAPIFactory
<RuntimeAPI
>::DeclareFactoryDependencies() {
141 DependsOn(ProcessManagerFactory::GetInstance());
144 RuntimeAPI::RuntimeAPI(content::BrowserContext
* context
)
145 : browser_context_(context
),
146 dispatch_chrome_updated_event_(false),
147 extension_registry_observer_(this),
148 process_manager_observer_(this) {
149 // RuntimeAPI is redirected in incognito, so |browser_context_| is never
151 DCHECK(!browser_context_
->IsOffTheRecord());
154 extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED
,
155 content::Source
<BrowserContext
>(context
));
156 extension_registry_observer_
.Add(ExtensionRegistry::Get(browser_context_
));
157 process_manager_observer_
.Add(ProcessManager::Get(browser_context_
));
159 delegate_
= ExtensionsBrowserClient::Get()->CreateRuntimeAPIDelegate(
162 // Check if registered events are up-to-date. We can only do this once
163 // per browser context, since it updates internal state when called.
164 dispatch_chrome_updated_event_
=
165 ExtensionsBrowserClient::Get()->DidVersionUpdate(browser_context_
);
168 RuntimeAPI::~RuntimeAPI() {
171 void RuntimeAPI::Observe(int type
,
172 const content::NotificationSource
& source
,
173 const content::NotificationDetails
& details
) {
174 DCHECK_EQ(extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED
, type
);
175 // We're done restarting Chrome after an update.
176 dispatch_chrome_updated_event_
= false;
178 delegate_
->AddUpdateObserver(this);
181 void RuntimeAPI::OnExtensionLoaded(content::BrowserContext
* browser_context
,
182 const Extension
* extension
) {
183 if (!dispatch_chrome_updated_event_
)
186 // Dispatch the onInstalled event with reason "chrome_update".
187 base::MessageLoop::current()->PostTask(
189 base::Bind(&RuntimeEventRouter::DispatchOnInstalledEvent
,
196 void RuntimeAPI::OnExtensionWillBeInstalled(
197 content::BrowserContext
* browser_context
,
198 const Extension
* extension
,
201 const std::string
& old_name
) {
202 // Ephemeral apps are not considered to be installed and do not receive
203 // the onInstalled() event.
204 if (util::IsEphemeralApp(extension
->id(), browser_context_
))
207 Version old_version
= delegate_
->GetPreviousExtensionVersion(extension
);
209 // Dispatch the onInstalled event.
210 base::MessageLoop::current()->PostTask(
212 base::Bind(&RuntimeEventRouter::DispatchOnInstalledEvent
,
219 void RuntimeAPI::OnExtensionUninstalled(
220 content::BrowserContext
* browser_context
,
221 const Extension
* extension
,
222 UninstallReason reason
) {
223 // Ephemeral apps are not considered to be installed, so the uninstall URL
224 // is not invoked when they are removed.
225 if (util::IsEphemeralApp(extension
->id(), browser_context_
))
228 RuntimeEventRouter::OnExtensionUninstalled(
229 browser_context_
, extension
->id(), reason
);
232 void RuntimeAPI::Shutdown() {
233 delegate_
->RemoveUpdateObserver(this);
236 void RuntimeAPI::OnAppUpdateAvailable(const Extension
* extension
) {
237 RuntimeEventRouter::DispatchOnUpdateAvailableEvent(
238 browser_context_
, extension
->id(), extension
->manifest()->value());
241 void RuntimeAPI::OnChromeUpdateAvailable() {
242 RuntimeEventRouter::DispatchOnBrowserUpdateAvailableEvent(browser_context_
);
245 void RuntimeAPI::OnBackgroundHostStartup(const Extension
* extension
) {
246 RuntimeEventRouter::DispatchOnStartupEvent(browser_context_
, extension
->id());
249 void RuntimeAPI::ReloadExtension(const std::string
& extension_id
) {
250 delegate_
->ReloadExtension(extension_id
);
253 bool RuntimeAPI::CheckForUpdates(
254 const std::string
& extension_id
,
255 const RuntimeAPIDelegate::UpdateCheckCallback
& callback
) {
256 return delegate_
->CheckForUpdates(extension_id
, callback
);
259 void RuntimeAPI::OpenURL(const GURL
& update_url
) {
260 delegate_
->OpenURL(update_url
);
263 bool RuntimeAPI::GetPlatformInfo(runtime::PlatformInfo
* info
) {
264 return delegate_
->GetPlatformInfo(info
);
267 bool RuntimeAPI::RestartDevice(std::string
* error_message
) {
268 return delegate_
->RestartDevice(error_message
);
271 ///////////////////////////////////////////////////////////////////////////////
274 void RuntimeEventRouter::DispatchOnStartupEvent(
275 content::BrowserContext
* context
,
276 const std::string
& extension_id
) {
277 DispatchOnStartupEventImpl(context
, extension_id
, true, NULL
);
281 void RuntimeEventRouter::DispatchOnInstalledEvent(
282 content::BrowserContext
* context
,
283 const std::string
& extension_id
,
284 const Version
& old_version
,
285 bool chrome_updated
) {
286 if (!ExtensionsBrowserClient::Get()->IsValidContext(context
))
288 ExtensionSystem
* system
= ExtensionSystem::Get(context
);
292 scoped_ptr
<base::ListValue
> event_args(new base::ListValue());
293 base::DictionaryValue
* info
= new base::DictionaryValue();
294 event_args
->Append(info
);
295 if (old_version
.IsValid()) {
296 info
->SetString(kInstallReason
, kInstallReasonUpdate
);
297 info
->SetString(kInstallPreviousVersion
, old_version
.GetString());
298 } else if (chrome_updated
) {
299 info
->SetString(kInstallReason
, kInstallReasonChromeUpdate
);
301 info
->SetString(kInstallReason
, kInstallReasonInstall
);
303 DCHECK(system
->event_router());
304 scoped_ptr
<Event
> event(
305 new Event(runtime::OnInstalled::kEventName
, event_args
.Pass()));
306 system
->event_router()->DispatchEventWithLazyListener(extension_id
,
309 if (old_version
.IsValid()) {
310 const Extension
* extension
=
311 ExtensionRegistry::Get(context
)->enabled_extensions().GetByID(
313 if (extension
&& SharedModuleInfo::IsSharedModule(extension
)) {
314 scoped_ptr
<ExtensionSet
> dependents
=
315 system
->GetDependentExtensions(extension
);
316 for (ExtensionSet::const_iterator i
= dependents
->begin();
317 i
!= dependents
->end();
319 scoped_ptr
<base::ListValue
> sm_event_args(new base::ListValue());
320 base::DictionaryValue
* sm_info
= new base::DictionaryValue();
321 sm_event_args
->Append(sm_info
);
322 sm_info
->SetString(kInstallReason
, kInstallReasonSharedModuleUpdate
);
323 sm_info
->SetString(kInstallPreviousVersion
, old_version
.GetString());
324 sm_info
->SetString(kInstallId
, extension_id
);
325 scoped_ptr
<Event
> sm_event(
326 new Event(runtime::OnInstalled::kEventName
, sm_event_args
.Pass()));
327 system
->event_router()->DispatchEventWithLazyListener((*i
)->id(),
335 void RuntimeEventRouter::DispatchOnUpdateAvailableEvent(
336 content::BrowserContext
* context
,
337 const std::string
& extension_id
,
338 const base::DictionaryValue
* manifest
) {
339 ExtensionSystem
* system
= ExtensionSystem::Get(context
);
343 scoped_ptr
<base::ListValue
> args(new base::ListValue
);
344 args
->Append(manifest
->DeepCopy());
345 DCHECK(system
->event_router());
346 scoped_ptr
<Event
> event(
347 new Event(runtime::OnUpdateAvailable::kEventName
, args
.Pass()));
348 system
->event_router()->DispatchEventToExtension(extension_id
, event
.Pass());
352 void RuntimeEventRouter::DispatchOnBrowserUpdateAvailableEvent(
353 content::BrowserContext
* context
) {
354 ExtensionSystem
* system
= ExtensionSystem::Get(context
);
358 scoped_ptr
<base::ListValue
> args(new base::ListValue
);
359 DCHECK(system
->event_router());
360 scoped_ptr
<Event
> event(
361 new Event(runtime::OnBrowserUpdateAvailable::kEventName
, args
.Pass()));
362 system
->event_router()->BroadcastEvent(event
.Pass());
366 void RuntimeEventRouter::DispatchOnRestartRequiredEvent(
367 content::BrowserContext
* context
,
368 const std::string
& app_id
,
369 core_api::runtime::OnRestartRequired::Reason reason
) {
370 ExtensionSystem
* system
= ExtensionSystem::Get(context
);
374 scoped_ptr
<Event
> event(
375 new Event(runtime::OnRestartRequired::kEventName
,
376 core_api::runtime::OnRestartRequired::Create(reason
)));
378 DCHECK(system
->event_router());
379 system
->event_router()->DispatchEventToExtension(app_id
, event
.Pass());
383 void RuntimeEventRouter::OnExtensionUninstalled(
384 content::BrowserContext
* context
,
385 const std::string
& extension_id
,
386 UninstallReason reason
) {
387 if (!(reason
== UNINSTALL_REASON_USER_INITIATED
||
388 reason
== UNINSTALL_REASON_MANAGEMENT_API
)) {
393 GetUninstallURL(ExtensionPrefs::Get(context
), extension_id
));
395 if (uninstall_url
.is_empty())
398 RuntimeAPI::GetFactoryInstance()->Get(context
)->OpenURL(uninstall_url
);
401 ExtensionFunction::ResponseAction
RuntimeGetBackgroundPageFunction::Run() {
402 ExtensionSystem
* system
= ExtensionSystem::Get(browser_context());
403 ExtensionHost
* host
= ProcessManager::Get(browser_context())
404 ->GetBackgroundHostForExtension(extension_id());
405 if (system
->lazy_background_task_queue()->ShouldEnqueueTask(browser_context(),
407 system
->lazy_background_task_queue()->AddPendingTask(
410 base::Bind(&RuntimeGetBackgroundPageFunction::OnPageLoaded
, this));
414 return RespondNow(Error(kNoBackgroundPageError
));
417 return RespondLater();
420 void RuntimeGetBackgroundPageFunction::OnPageLoaded(ExtensionHost
* host
) {
422 Respond(NoArguments());
424 Respond(Error(kPageLoadError
));
428 ExtensionFunction::ResponseAction
RuntimeSetUninstallURLFunction::Run() {
429 std::string url_string
;
430 EXTENSION_FUNCTION_VALIDATE(args_
->GetString(0, &url_string
));
432 GURL
url(url_string
);
433 if (!url
.is_valid()) {
435 Error(ErrorUtils::FormatErrorMessage(kInvalidUrlError
, url_string
)));
438 ExtensionPrefs::Get(browser_context()), extension_id(), url_string
);
439 return RespondNow(NoArguments());
442 ExtensionFunction::ResponseAction
RuntimeReloadFunction::Run() {
443 RuntimeAPI::GetFactoryInstance()->Get(browser_context())->ReloadExtension(
445 return RespondNow(NoArguments());
448 ExtensionFunction::ResponseAction
RuntimeRequestUpdateCheckFunction::Run() {
449 if (!RuntimeAPI::GetFactoryInstance()
450 ->Get(browser_context())
453 base::Bind(&RuntimeRequestUpdateCheckFunction::CheckComplete
,
455 return RespondNow(Error(kUpdatesDisabledError
));
457 return RespondLater();
460 void RuntimeRequestUpdateCheckFunction::CheckComplete(
461 const RuntimeAPIDelegate::UpdateCheckResult
& result
) {
462 if (result
.success
) {
463 base::DictionaryValue
* details
= new base::DictionaryValue
;
464 details
->SetString("version", result
.version
);
465 Respond(TwoArguments(new base::StringValue(result
.response
), details
));
467 // HMM(kalman): Why does !success not imply Error()?
468 Respond(OneArgument(new base::StringValue(result
.response
)));
472 ExtensionFunction::ResponseAction
RuntimeRestartFunction::Run() {
475 RuntimeAPI::GetFactoryInstance()->Get(browser_context())->RestartDevice(
478 return RespondNow(Error(message
));
480 return RespondNow(NoArguments());
483 ExtensionFunction::ResponseAction
RuntimeGetPlatformInfoFunction::Run() {
484 runtime::PlatformInfo info
;
485 if (!RuntimeAPI::GetFactoryInstance()
486 ->Get(browser_context())
487 ->GetPlatformInfo(&info
)) {
488 return RespondNow(Error(kPlatformInfoUnavailable
));
491 ArgumentList(runtime::GetPlatformInfo::Results::Create(info
)));
494 ExtensionFunction::ResponseAction
495 RuntimeGetPackageDirectoryEntryFunction::Run() {
496 storage::IsolatedContext
* isolated_context
=
497 storage::IsolatedContext::GetInstance();
498 DCHECK(isolated_context
);
500 std::string relative_path
= kPackageDirectoryPath
;
501 base::FilePath path
= extension_
->path();
502 std::string filesystem_id
= isolated_context
->RegisterFileSystemForPath(
503 storage::kFileSystemTypeNativeLocal
, std::string(), path
, &relative_path
);
505 int renderer_id
= render_view_host_
->GetProcess()->GetID();
506 content::ChildProcessSecurityPolicy
* policy
=
507 content::ChildProcessSecurityPolicy::GetInstance();
508 policy
->GrantReadFileSystem(renderer_id
, filesystem_id
);
509 base::DictionaryValue
* dict
= new base::DictionaryValue();
510 dict
->SetString("fileSystemId", filesystem_id
);
511 dict
->SetString("baseName", relative_path
);
512 return RespondNow(OneArgument(dict
));
515 } // namespace extensions