Add testing/scripts/OWNERS
[chromium-blink-merge.git] / extensions / browser / api / runtime / runtime_api.cc
blob51e8ec9bcf771dfcd5fe4e89eeaa52edff2d2108
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_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"
37 #include "url/gurl.h"
39 using content::BrowserContext;
41 namespace extensions {
43 namespace runtime = core_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 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,
71 bool first_call,
72 ExtensionHost* host) {
73 // A NULL host from the LazyBackgroundTaskQueue means the page failed to
74 // load. Give up.
75 if (!host && !first_call)
76 return;
78 // Don't send onStartup events to incognito browser contexts.
79 if (browser_context->IsOffTheRecord())
80 return;
82 if (ExtensionsBrowserClient::Get()->IsShuttingDown() ||
83 !ExtensionsBrowserClient::Get()->IsValidContext(browser_context))
84 return;
85 ExtensionSystem* system = ExtensionSystem::Get(browser_context);
86 if (!system)
87 return;
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(
94 extension_id);
95 if (extension && BackgroundInfo::HasPersistentBackgroundPage(extension) &&
96 first_call &&
97 system->lazy_background_task_queue()->ShouldEnqueueTask(browser_context,
98 extension)) {
99 system->lazy_background_task_queue()->AddPendingTask(
100 browser_context,
101 extension_id,
102 base::Bind(
103 &DispatchOnStartupEventImpl, browser_context, extension_id, false));
104 return;
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);
124 return url_string;
127 } // namespace
129 ///////////////////////////////////////////////////////////////////////////////
131 static base::LazyInstance<BrowserContextKeyedAPIFactory<RuntimeAPI> >
132 g_factory = LAZY_INSTANCE_INITIALIZER;
134 // static
135 BrowserContextKeyedAPIFactory<RuntimeAPI>* RuntimeAPI::GetFactoryInstance() {
136 return g_factory.Pointer();
139 template <>
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
150 // incognito.
151 DCHECK(!browser_context_->IsOffTheRecord());
153 registrar_.Add(this,
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(
160 browser_context_);
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_)
184 return;
186 // Dispatch the onInstalled event with reason "chrome_update".
187 base::MessageLoop::current()->PostTask(
188 FROM_HERE,
189 base::Bind(&RuntimeEventRouter::DispatchOnInstalledEvent,
190 browser_context_,
191 extension->id(),
192 Version(),
193 true));
196 void RuntimeAPI::OnExtensionWillBeInstalled(
197 content::BrowserContext* browser_context,
198 const Extension* extension,
199 bool is_update,
200 bool from_ephemeral,
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_))
205 return;
207 Version old_version = delegate_->GetPreviousExtensionVersion(extension);
209 // Dispatch the onInstalled event.
210 base::MessageLoop::current()->PostTask(
211 FROM_HERE,
212 base::Bind(&RuntimeEventRouter::DispatchOnInstalledEvent,
213 browser_context_,
214 extension->id(),
215 old_version,
216 false));
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_))
226 return;
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 ///////////////////////////////////////////////////////////////////////////////
273 // static
274 void RuntimeEventRouter::DispatchOnStartupEvent(
275 content::BrowserContext* context,
276 const std::string& extension_id) {
277 DispatchOnStartupEventImpl(context, extension_id, true, NULL);
280 // static
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))
287 return;
288 ExtensionSystem* system = ExtensionSystem::Get(context);
289 if (!system)
290 return;
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);
300 } else {
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,
307 event.Pass());
309 if (old_version.IsValid()) {
310 const Extension* extension =
311 ExtensionRegistry::Get(context)->enabled_extensions().GetByID(
312 extension_id);
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();
318 i++) {
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(),
328 sm_event.Pass());
334 // static
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);
340 if (!system)
341 return;
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());
351 // static
352 void RuntimeEventRouter::DispatchOnBrowserUpdateAvailableEvent(
353 content::BrowserContext* context) {
354 ExtensionSystem* system = ExtensionSystem::Get(context);
355 if (!system)
356 return;
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());
365 // static
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);
371 if (!system)
372 return;
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());
382 // static
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)) {
389 return;
392 GURL uninstall_url(
393 GetUninstallURL(ExtensionPrefs::Get(context), extension_id));
395 if (uninstall_url.is_empty())
396 return;
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(),
406 extension())) {
407 system->lazy_background_task_queue()->AddPendingTask(
408 browser_context(),
409 extension_id(),
410 base::Bind(&RuntimeGetBackgroundPageFunction::OnPageLoaded, this));
411 } else if (host) {
412 OnPageLoaded(host);
413 } else {
414 return RespondNow(Error(kNoBackgroundPageError));
417 return RespondLater();
420 void RuntimeGetBackgroundPageFunction::OnPageLoaded(ExtensionHost* host) {
421 if (host) {
422 Respond(NoArguments());
423 } else {
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()) {
434 return RespondNow(
435 Error(ErrorUtils::FormatErrorMessage(kInvalidUrlError, url_string)));
437 SetUninstallURL(
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(
444 extension_id());
445 return RespondNow(NoArguments());
448 ExtensionFunction::ResponseAction RuntimeRequestUpdateCheckFunction::Run() {
449 if (!RuntimeAPI::GetFactoryInstance()
450 ->Get(browser_context())
451 ->CheckForUpdates(
452 extension_id(),
453 base::Bind(&RuntimeRequestUpdateCheckFunction::CheckComplete,
454 this))) {
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));
466 } else {
467 // HMM(kalman): Why does !success not imply Error()?
468 Respond(OneArgument(new base::StringValue(result.response)));
472 ExtensionFunction::ResponseAction RuntimeRestartFunction::Run() {
473 std::string message;
474 bool result =
475 RuntimeAPI::GetFactoryInstance()->Get(browser_context())->RestartDevice(
476 &message);
477 if (!result) {
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));
490 return RespondNow(
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