Revert of Fix missing GN dependencies. (patchset #4 id:60001 of https://codereview...
[chromium-blink-merge.git] / extensions / browser / api / runtime / runtime_api.cc
blobcc90d3732c2e5824609fc4a5bcd590550cc02628
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 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 system->lazy_background_task_queue()->ShouldEnqueueTask(browser_context,
99 extension)) {
100 system->lazy_background_task_queue()->AddPendingTask(
101 browser_context,
102 extension_id,
103 base::Bind(
104 &DispatchOnStartupEventImpl, browser_context, extension_id, false));
105 return;
108 scoped_ptr<base::ListValue> event_args(new base::ListValue());
109 scoped_ptr<Event> event(
110 new Event(runtime::OnStartup::kEventName, event_args.Pass()));
111 system->event_router()->DispatchEventToExtension(extension_id, event.Pass());
114 void SetUninstallURL(ExtensionPrefs* prefs,
115 const std::string& extension_id,
116 const std::string& url_string) {
117 prefs->UpdateExtensionPref(
118 extension_id, kUninstallUrl, new base::StringValue(url_string));
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);
125 return url_string;
128 } // namespace
130 ///////////////////////////////////////////////////////////////////////////////
132 static base::LazyInstance<BrowserContextKeyedAPIFactory<RuntimeAPI> >
133 g_factory = LAZY_INSTANCE_INITIALIZER;
135 // static
136 BrowserContextKeyedAPIFactory<RuntimeAPI>* RuntimeAPI::GetFactoryInstance() {
137 return g_factory.Pointer();
140 template <>
141 void BrowserContextKeyedAPIFactory<RuntimeAPI>::DeclareFactoryDependencies() {
142 DependsOn(ProcessManagerFactory::GetInstance());
145 RuntimeAPI::RuntimeAPI(content::BrowserContext* context)
146 : browser_context_(context),
147 dispatch_chrome_updated_event_(false),
148 extension_registry_observer_(this),
149 process_manager_observer_(this) {
150 // RuntimeAPI is redirected in incognito, so |browser_context_| is never
151 // incognito.
152 DCHECK(!browser_context_->IsOffTheRecord());
154 registrar_.Add(this,
155 extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED,
156 content::Source<BrowserContext>(context));
157 extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_));
158 process_manager_observer_.Add(ProcessManager::Get(browser_context_));
160 delegate_ = ExtensionsBrowserClient::Get()->CreateRuntimeAPIDelegate(
161 browser_context_);
163 // Check if registered events are up-to-date. We can only do this once
164 // per browser context, since it updates internal state when called.
165 dispatch_chrome_updated_event_ =
166 ExtensionsBrowserClient::Get()->DidVersionUpdate(browser_context_);
169 RuntimeAPI::~RuntimeAPI() {
172 void RuntimeAPI::Observe(int type,
173 const content::NotificationSource& source,
174 const content::NotificationDetails& details) {
175 DCHECK_EQ(extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED, type);
176 // We're done restarting Chrome after an update.
177 dispatch_chrome_updated_event_ = false;
179 delegate_->AddUpdateObserver(this);
182 void RuntimeAPI::OnExtensionLoaded(content::BrowserContext* browser_context,
183 const Extension* extension) {
184 if (!dispatch_chrome_updated_event_)
185 return;
187 // Dispatch the onInstalled event with reason "chrome_update".
188 base::MessageLoop::current()->PostTask(
189 FROM_HERE,
190 base::Bind(&RuntimeEventRouter::DispatchOnInstalledEvent,
191 browser_context_,
192 extension->id(),
193 Version(),
194 true));
197 void RuntimeAPI::OnExtensionWillBeInstalled(
198 content::BrowserContext* browser_context,
199 const Extension* extension,
200 bool is_update,
201 bool from_ephemeral,
202 const std::string& old_name) {
203 // Ephemeral apps are not considered to be installed and do not receive
204 // the onInstalled() event.
205 if (util::IsEphemeralApp(extension->id(), browser_context_))
206 return;
208 Version old_version = delegate_->GetPreviousExtensionVersion(extension);
210 // Dispatch the onInstalled event.
211 base::MessageLoop::current()->PostTask(
212 FROM_HERE,
213 base::Bind(&RuntimeEventRouter::DispatchOnInstalledEvent,
214 browser_context_,
215 extension->id(),
216 old_version,
217 false));
220 void RuntimeAPI::OnExtensionUninstalled(
221 content::BrowserContext* browser_context,
222 const Extension* extension,
223 UninstallReason reason) {
224 // Ephemeral apps are not considered to be installed, so the uninstall URL
225 // is not invoked when they are removed.
226 if (util::IsEphemeralApp(extension->id(), browser_context_))
227 return;
229 RuntimeEventRouter::OnExtensionUninstalled(
230 browser_context_, extension->id(), reason);
233 void RuntimeAPI::Shutdown() {
234 delegate_->RemoveUpdateObserver(this);
237 void RuntimeAPI::OnAppUpdateAvailable(const Extension* extension) {
238 RuntimeEventRouter::DispatchOnUpdateAvailableEvent(
239 browser_context_, extension->id(), extension->manifest()->value());
242 void RuntimeAPI::OnChromeUpdateAvailable() {
243 RuntimeEventRouter::DispatchOnBrowserUpdateAvailableEvent(browser_context_);
246 void RuntimeAPI::OnBackgroundHostStartup(const Extension* extension) {
247 RuntimeEventRouter::DispatchOnStartupEvent(browser_context_, extension->id());
250 void RuntimeAPI::ReloadExtension(const std::string& extension_id) {
251 delegate_->ReloadExtension(extension_id);
254 bool RuntimeAPI::CheckForUpdates(
255 const std::string& extension_id,
256 const RuntimeAPIDelegate::UpdateCheckCallback& callback) {
257 return delegate_->CheckForUpdates(extension_id, callback);
260 void RuntimeAPI::OpenURL(const GURL& update_url) {
261 delegate_->OpenURL(update_url);
264 bool RuntimeAPI::GetPlatformInfo(runtime::PlatformInfo* info) {
265 return delegate_->GetPlatformInfo(info);
268 bool RuntimeAPI::RestartDevice(std::string* error_message) {
269 return delegate_->RestartDevice(error_message);
272 bool RuntimeAPI::OpenOptionsPage(const Extension* extension) {
273 return delegate_->OpenOptionsPage(extension);
276 ///////////////////////////////////////////////////////////////////////////////
278 // static
279 void RuntimeEventRouter::DispatchOnStartupEvent(
280 content::BrowserContext* context,
281 const std::string& extension_id) {
282 DispatchOnStartupEventImpl(context, extension_id, true, NULL);
285 // static
286 void RuntimeEventRouter::DispatchOnInstalledEvent(
287 content::BrowserContext* context,
288 const std::string& extension_id,
289 const Version& old_version,
290 bool chrome_updated) {
291 if (!ExtensionsBrowserClient::Get()->IsValidContext(context))
292 return;
293 ExtensionSystem* system = ExtensionSystem::Get(context);
294 if (!system)
295 return;
297 scoped_ptr<base::ListValue> event_args(new base::ListValue());
298 base::DictionaryValue* info = new base::DictionaryValue();
299 event_args->Append(info);
300 if (old_version.IsValid()) {
301 info->SetString(kInstallReason, kInstallReasonUpdate);
302 info->SetString(kInstallPreviousVersion, old_version.GetString());
303 } else if (chrome_updated) {
304 info->SetString(kInstallReason, kInstallReasonChromeUpdate);
305 } else {
306 info->SetString(kInstallReason, kInstallReasonInstall);
308 DCHECK(system->event_router());
309 scoped_ptr<Event> event(
310 new Event(runtime::OnInstalled::kEventName, event_args.Pass()));
311 system->event_router()->DispatchEventWithLazyListener(extension_id,
312 event.Pass());
314 if (old_version.IsValid()) {
315 const Extension* extension =
316 ExtensionRegistry::Get(context)->enabled_extensions().GetByID(
317 extension_id);
318 if (extension && SharedModuleInfo::IsSharedModule(extension)) {
319 scoped_ptr<ExtensionSet> dependents =
320 system->GetDependentExtensions(extension);
321 for (ExtensionSet::const_iterator i = dependents->begin();
322 i != dependents->end();
323 i++) {
324 scoped_ptr<base::ListValue> sm_event_args(new base::ListValue());
325 base::DictionaryValue* sm_info = new base::DictionaryValue();
326 sm_event_args->Append(sm_info);
327 sm_info->SetString(kInstallReason, kInstallReasonSharedModuleUpdate);
328 sm_info->SetString(kInstallPreviousVersion, old_version.GetString());
329 sm_info->SetString(kInstallId, extension_id);
330 scoped_ptr<Event> sm_event(
331 new Event(runtime::OnInstalled::kEventName, sm_event_args.Pass()));
332 system->event_router()->DispatchEventWithLazyListener((*i)->id(),
333 sm_event.Pass());
339 // static
340 void RuntimeEventRouter::DispatchOnUpdateAvailableEvent(
341 content::BrowserContext* context,
342 const std::string& extension_id,
343 const base::DictionaryValue* manifest) {
344 ExtensionSystem* system = ExtensionSystem::Get(context);
345 if (!system)
346 return;
348 scoped_ptr<base::ListValue> args(new base::ListValue);
349 args->Append(manifest->DeepCopy());
350 DCHECK(system->event_router());
351 scoped_ptr<Event> event(
352 new Event(runtime::OnUpdateAvailable::kEventName, args.Pass()));
353 system->event_router()->DispatchEventToExtension(extension_id, event.Pass());
356 // static
357 void RuntimeEventRouter::DispatchOnBrowserUpdateAvailableEvent(
358 content::BrowserContext* context) {
359 ExtensionSystem* system = ExtensionSystem::Get(context);
360 if (!system)
361 return;
363 scoped_ptr<base::ListValue> args(new base::ListValue);
364 DCHECK(system->event_router());
365 scoped_ptr<Event> event(
366 new Event(runtime::OnBrowserUpdateAvailable::kEventName, args.Pass()));
367 system->event_router()->BroadcastEvent(event.Pass());
370 // static
371 void RuntimeEventRouter::DispatchOnRestartRequiredEvent(
372 content::BrowserContext* context,
373 const std::string& app_id,
374 core_api::runtime::OnRestartRequiredReason reason) {
375 ExtensionSystem* system = ExtensionSystem::Get(context);
376 if (!system)
377 return;
379 scoped_ptr<Event> event(
380 new Event(runtime::OnRestartRequired::kEventName,
381 core_api::runtime::OnRestartRequired::Create(reason)));
383 DCHECK(system->event_router());
384 system->event_router()->DispatchEventToExtension(app_id, event.Pass());
387 // static
388 void RuntimeEventRouter::OnExtensionUninstalled(
389 content::BrowserContext* context,
390 const std::string& extension_id,
391 UninstallReason reason) {
392 if (!(reason == UNINSTALL_REASON_USER_INITIATED ||
393 reason == UNINSTALL_REASON_MANAGEMENT_API)) {
394 return;
397 GURL uninstall_url(
398 GetUninstallURL(ExtensionPrefs::Get(context), extension_id));
400 if (uninstall_url.is_empty())
401 return;
403 RuntimeAPI::GetFactoryInstance()->Get(context)->OpenURL(uninstall_url);
406 ExtensionFunction::ResponseAction RuntimeGetBackgroundPageFunction::Run() {
407 ExtensionSystem* system = ExtensionSystem::Get(browser_context());
408 ExtensionHost* host = ProcessManager::Get(browser_context())
409 ->GetBackgroundHostForExtension(extension_id());
410 if (system->lazy_background_task_queue()->ShouldEnqueueTask(browser_context(),
411 extension())) {
412 system->lazy_background_task_queue()->AddPendingTask(
413 browser_context(),
414 extension_id(),
415 base::Bind(&RuntimeGetBackgroundPageFunction::OnPageLoaded, this));
416 } else if (host) {
417 OnPageLoaded(host);
418 } else {
419 return RespondNow(Error(kNoBackgroundPageError));
422 return RespondLater();
425 void RuntimeGetBackgroundPageFunction::OnPageLoaded(ExtensionHost* host) {
426 if (host) {
427 Respond(NoArguments());
428 } else {
429 Respond(Error(kPageLoadError));
433 ExtensionFunction::ResponseAction RuntimeOpenOptionsPageFunction::Run() {
434 RuntimeAPI* api = RuntimeAPI::GetFactoryInstance()->Get(browser_context());
435 return RespondNow(api->OpenOptionsPage(extension())
436 ? NoArguments()
437 : Error(kFailedToCreateOptionsPage));
440 ExtensionFunction::ResponseAction RuntimeSetUninstallURLFunction::Run() {
441 std::string url_string;
442 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &url_string));
444 GURL url(url_string);
445 if (!url.is_valid()) {
446 return RespondNow(
447 Error(ErrorUtils::FormatErrorMessage(kInvalidUrlError, url_string)));
449 SetUninstallURL(
450 ExtensionPrefs::Get(browser_context()), extension_id(), url_string);
451 return RespondNow(NoArguments());
454 ExtensionFunction::ResponseAction RuntimeReloadFunction::Run() {
455 RuntimeAPI::GetFactoryInstance()->Get(browser_context())->ReloadExtension(
456 extension_id());
457 return RespondNow(NoArguments());
460 ExtensionFunction::ResponseAction RuntimeRequestUpdateCheckFunction::Run() {
461 if (!RuntimeAPI::GetFactoryInstance()
462 ->Get(browser_context())
463 ->CheckForUpdates(
464 extension_id(),
465 base::Bind(&RuntimeRequestUpdateCheckFunction::CheckComplete,
466 this))) {
467 return RespondNow(Error(kUpdatesDisabledError));
469 return RespondLater();
472 void RuntimeRequestUpdateCheckFunction::CheckComplete(
473 const RuntimeAPIDelegate::UpdateCheckResult& result) {
474 if (result.success) {
475 base::DictionaryValue* details = new base::DictionaryValue;
476 details->SetString("version", result.version);
477 Respond(TwoArguments(new base::StringValue(result.response), details));
478 } else {
479 // HMM(kalman): Why does !success not imply Error()?
480 Respond(OneArgument(new base::StringValue(result.response)));
484 ExtensionFunction::ResponseAction RuntimeRestartFunction::Run() {
485 std::string message;
486 bool result =
487 RuntimeAPI::GetFactoryInstance()->Get(browser_context())->RestartDevice(
488 &message);
489 if (!result) {
490 return RespondNow(Error(message));
492 return RespondNow(NoArguments());
495 ExtensionFunction::ResponseAction RuntimeGetPlatformInfoFunction::Run() {
496 runtime::PlatformInfo info;
497 if (!RuntimeAPI::GetFactoryInstance()
498 ->Get(browser_context())
499 ->GetPlatformInfo(&info)) {
500 return RespondNow(Error(kPlatformInfoUnavailable));
502 return RespondNow(
503 ArgumentList(runtime::GetPlatformInfo::Results::Create(info)));
506 ExtensionFunction::ResponseAction
507 RuntimeGetPackageDirectoryEntryFunction::Run() {
508 storage::IsolatedContext* isolated_context =
509 storage::IsolatedContext::GetInstance();
510 DCHECK(isolated_context);
512 std::string relative_path = kPackageDirectoryPath;
513 base::FilePath path = extension_->path();
514 std::string filesystem_id = isolated_context->RegisterFileSystemForPath(
515 storage::kFileSystemTypeNativeLocal, std::string(), path, &relative_path);
517 int renderer_id = render_view_host_->GetProcess()->GetID();
518 content::ChildProcessSecurityPolicy* policy =
519 content::ChildProcessSecurityPolicy::GetInstance();
520 policy->GrantReadFileSystem(renderer_id, filesystem_id);
521 base::DictionaryValue* dict = new base::DictionaryValue();
522 dict->SetString("fileSystemId", filesystem_id);
523 dict->SetString("baseName", relative_path);
524 return RespondNow(OneArgument(dict));
527 } // namespace extensions