Upstream version 8.37.180.0
[platform/framework/web/crosswalk.git] / src / extensions / browser / api / runtime / runtime_api.cc
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.
4
5 #include "extensions/browser/api/runtime/runtime_api.h"
6
7 #include <utility>
8
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"
36 #include "url/gurl.h"
37 #include "webkit/browser/fileapi/isolated_context.h"
38
39 using content::BrowserContext;
40
41 namespace extensions {
42
43 namespace runtime = core_api::runtime;
44
45 namespace {
46
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.";
58
59 const char kUpdatesDisabledError[] = "Autoupdate is not enabled.";
60
61 // A preference key storing the url loaded when an extension is uninstalled.
62 const char kUninstallUrl[] = "uninstall_url";
63
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";
68
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;
77
78   // Don't send onStartup events to incognito browser contexts.
79   if (browser_context->IsOffTheRecord())
80     return;
81
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;
88
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;
105   }
106
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());
111 }
112
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));
118 }
119
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);
125   return url_string;
126 }
127 #endif  // defined(ENABLE_EXTENSIONS)
128
129 }  // namespace
130
131 ///////////////////////////////////////////////////////////////////////////////
132
133 static base::LazyInstance<BrowserContextKeyedAPIFactory<RuntimeAPI> >
134     g_factory = LAZY_INSTANCE_INITIALIZER;
135
136 // static
137 BrowserContextKeyedAPIFactory<RuntimeAPI>* RuntimeAPI::GetFactoryInstance() {
138   return g_factory.Pointer();
139 }
140
141 RuntimeAPI::RuntimeAPI(content::BrowserContext* context)
142     : browser_context_(context), dispatch_chrome_updated_event_(false) {
143   registrar_.Add(this,
144                  chrome::NOTIFICATION_EXTENSIONS_READY,
145                  content::Source<BrowserContext>(context));
146   registrar_.Add(this,
147                  chrome::NOTIFICATION_EXTENSION_LOADED_DEPRECATED,
148                  content::Source<BrowserContext>(context));
149   registrar_.Add(this,
150                  chrome::NOTIFICATION_EXTENSION_INSTALLED_DEPRECATED,
151                  content::Source<BrowserContext>(context));
152   registrar_.Add(this,
153                  chrome::NOTIFICATION_EXTENSION_UNINSTALLED_DEPRECATED,
154                  content::Source<BrowserContext>(context));
155
156   delegate_ = ExtensionsBrowserClient::Get()->CreateRuntimeAPIDelegate(
157       browser_context_);
158
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_);
163 }
164
165 RuntimeAPI::~RuntimeAPI() {
166   delegate_->RemoveUpdateObserver(this);
167 }
168
169 void RuntimeAPI::Observe(int type,
170                          const content::NotificationSource& source,
171                          const content::NotificationDetails& details) {
172   switch (type) {
173     case chrome::NOTIFICATION_EXTENSIONS_READY: {
174       OnExtensionsReady();
175       break;
176     }
177     case chrome::NOTIFICATION_EXTENSION_LOADED_DEPRECATED: {
178       const Extension* extension =
179           content::Details<const Extension>(details).ptr();
180       OnExtensionLoaded(extension);
181       break;
182     }
183     case chrome::NOTIFICATION_EXTENSION_INSTALLED_DEPRECATED: {
184       const Extension* extension =
185           content::Details<const InstalledExtensionInfo>(details)->extension;
186       OnExtensionInstalled(extension);
187       break;
188     }
189     case chrome::NOTIFICATION_EXTENSION_UNINSTALLED_DEPRECATED: {
190       const Extension* extension =
191           content::Details<const Extension>(details).ptr();
192       OnExtensionUninstalled(extension);
193       break;
194     }
195     default:
196       NOTREACHED();
197       break;
198   }
199 }
200
201 void RuntimeAPI::OnExtensionsReady() {
202   // We're done restarting Chrome after an update.
203   dispatch_chrome_updated_event_ = false;
204
205   delegate_->AddUpdateObserver(this);
206
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);
215 }
216
217 void RuntimeAPI::OnExtensionLoaded(const Extension* extension) {
218   if (!dispatch_chrome_updated_event_)
219     return;
220
221   // Dispatch the onInstalled event with reason "chrome_update".
222   base::MessageLoop::current()->PostTask(
223       FROM_HERE,
224       base::Bind(&RuntimeEventRouter::DispatchOnInstalledEvent,
225                  browser_context_,
226                  extension->id(),
227                  Version(),
228                  true));
229 }
230
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_))
235     return;
236
237   Version old_version = delegate_->GetPreviousExtensionVersion(extension);
238
239   // Dispatch the onInstalled event.
240   base::MessageLoop::current()->PostTask(
241       FROM_HERE,
242       base::Bind(&RuntimeEventRouter::DispatchOnInstalledEvent,
243                  browser_context_,
244                  extension->id(),
245                  old_version,
246                  false));
247 }
248
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_))
253     return;
254
255   RuntimeEventRouter::OnExtensionUninstalled(browser_context_, extension->id());
256 }
257
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.
264   if (process_manager)
265     process_manager->RemoveObserver(this);
266 }
267
268 void RuntimeAPI::OnAppUpdateAvailable(const Extension* extension) {
269   RuntimeEventRouter::DispatchOnUpdateAvailableEvent(
270       browser_context_, extension->id(), extension->manifest()->value());
271 }
272
273 void RuntimeAPI::OnChromeUpdateAvailable() {
274   RuntimeEventRouter::DispatchOnBrowserUpdateAvailableEvent(browser_context_);
275 }
276
277 void RuntimeAPI::OnBackgroundHostStartup(const Extension* extension) {
278   RuntimeEventRouter::DispatchOnStartupEvent(browser_context_, extension->id());
279 }
280
281 void RuntimeAPI::ReloadExtension(const std::string& extension_id) {
282   delegate_->ReloadExtension(extension_id);
283 }
284
285 bool RuntimeAPI::CheckForUpdates(
286     const std::string& extension_id,
287     const RuntimeAPIDelegate::UpdateCheckCallback& callback) {
288   return delegate_->CheckForUpdates(extension_id, callback);
289 }
290
291 void RuntimeAPI::OpenURL(const GURL& update_url) {
292   delegate_->OpenURL(update_url);
293 }
294
295 bool RuntimeAPI::GetPlatformInfo(runtime::PlatformInfo* info) {
296   return delegate_->GetPlatformInfo(info);
297 }
298
299 bool RuntimeAPI::RestartDevice(std::string* error_message) {
300   return delegate_->RestartDevice(error_message);
301 }
302
303 ///////////////////////////////////////////////////////////////////////////////
304
305 // static
306 void RuntimeEventRouter::DispatchOnStartupEvent(
307     content::BrowserContext* context,
308     const std::string& extension_id) {
309   DispatchOnStartupEventImpl(context, extension_id, true, NULL);
310 }
311
312 // static
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))
319     return;
320   ExtensionSystem* system = ExtensionSystem::Get(context);
321   if (!system)
322     return;
323
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);
332   } else {
333     info->SetString(kInstallReason, kInstallReasonInstall);
334   }
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,
339                                                         event.Pass());
340
341   if (old_version.IsValid()) {
342     const Extension* extension =
343         ExtensionRegistry::Get(context)->enabled_extensions().GetByID(
344             extension_id);
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();
350            i++) {
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(),
360                                                               sm_event.Pass());
361       }
362     }
363   }
364 }
365
366 // static
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);
372   if (!system)
373     return;
374
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());
381 }
382
383 // static
384 void RuntimeEventRouter::DispatchOnBrowserUpdateAvailableEvent(
385     content::BrowserContext* context) {
386   ExtensionSystem* system = ExtensionSystem::Get(context);
387   if (!system)
388     return;
389
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());
395 }
396
397 // static
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);
403   if (!system)
404     return;
405
406   scoped_ptr<Event> event(
407       new Event(runtime::OnRestartRequired::kEventName,
408                 core_api::runtime::OnRestartRequired::Create(reason)));
409
410   DCHECK(system->event_router());
411   system->event_router()->DispatchEventToExtension(app_id, event.Pass());
412 }
413
414 // static
415 void RuntimeEventRouter::OnExtensionUninstalled(
416     content::BrowserContext* context,
417     const std::string& extension_id) {
418 #if defined(ENABLE_EXTENSIONS)
419   GURL uninstall_url(
420       GetUninstallURL(ExtensionPrefs::Get(context), extension_id));
421
422   if (uninstall_url.is_empty())
423     return;
424
425   RuntimeAPI::GetFactoryInstance()->Get(context)->OpenURL(uninstall_url);
426 #endif  // defined(ENABLE_EXTENSIONS)
427 }
428
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(),
434                                                               GetExtension())) {
435     system->lazy_background_task_queue()->AddPendingTask(
436         browser_context(),
437         extension_id(),
438         base::Bind(&RuntimeGetBackgroundPageFunction::OnPageLoaded, this));
439   } else if (host) {
440     OnPageLoaded(host);
441   } else {
442     return RespondNow(Error(kNoBackgroundPageError));
443   }
444
445   return RespondLater();
446 }
447
448 void RuntimeGetBackgroundPageFunction::OnPageLoaded(ExtensionHost* host) {
449   if (host) {
450     Respond(NoArguments());
451   } else {
452     Respond(Error(kPageLoadError));
453   }
454 }
455
456 ExtensionFunction::ResponseAction RuntimeSetUninstallURLFunction::Run() {
457   std::string url_string;
458   EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &url_string));
459
460   GURL url(url_string);
461   if (!url.is_valid()) {
462     return RespondNow(
463         Error(ErrorUtils::FormatErrorMessage(kInvalidUrlError, url_string)));
464   }
465   SetUninstallURL(
466       ExtensionPrefs::Get(browser_context()), extension_id(), url_string);
467   return RespondNow(NoArguments());
468 }
469
470 ExtensionFunction::ResponseAction RuntimeReloadFunction::Run() {
471   RuntimeAPI::GetFactoryInstance()->Get(browser_context())->ReloadExtension(
472       extension_id());
473   return RespondNow(NoArguments());
474 }
475
476 ExtensionFunction::ResponseAction RuntimeRequestUpdateCheckFunction::Run() {
477   if (!RuntimeAPI::GetFactoryInstance()
478            ->Get(browser_context())
479            ->CheckForUpdates(
480                extension_id(),
481                base::Bind(&RuntimeRequestUpdateCheckFunction::CheckComplete,
482                           this))) {
483     return RespondNow(Error(kUpdatesDisabledError));
484   }
485   return RespondLater();
486 }
487
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));
494   } else {
495     // HMM(kalman): Why does !success not imply Error()?
496     Respond(OneArgument(new base::StringValue(result.response)));
497   }
498 }
499
500 ExtensionFunction::ResponseAction RuntimeRestartFunction::Run() {
501   std::string message;
502   bool result =
503       RuntimeAPI::GetFactoryInstance()->Get(browser_context())->RestartDevice(
504           &message);
505   if (!result) {
506     return RespondNow(Error(message));
507   }
508   return RespondNow(NoArguments());
509 }
510
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));
517   }
518   return RespondNow(
519       ArgumentList(runtime::GetPlatformInfo::Results::Create(info)));
520 }
521
522 ExtensionFunction::ResponseAction
523 RuntimeGetPackageDirectoryEntryFunction::Run() {
524   fileapi::IsolatedContext* isolated_context =
525       fileapi::IsolatedContext::GetInstance();
526   DCHECK(isolated_context);
527
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);
532
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));
541 }
542
543 }  // namespace extensions