Upstream version 10.39.225.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 "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.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"
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 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;
125 }
126
127 }  // namespace
128
129 ///////////////////////////////////////////////////////////////////////////////
130
131 static base::LazyInstance<BrowserContextKeyedAPIFactory<RuntimeAPI> >
132     g_factory = LAZY_INSTANCE_INITIALIZER;
133
134 // static
135 BrowserContextKeyedAPIFactory<RuntimeAPI>* RuntimeAPI::GetFactoryInstance() {
136   return g_factory.Pointer();
137 }
138
139 RuntimeAPI::RuntimeAPI(content::BrowserContext* context)
140     : browser_context_(context),
141       dispatch_chrome_updated_event_(false),
142       extension_registry_observer_(this) {
143   registrar_.Add(this,
144                  extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED,
145                  content::Source<BrowserContext>(context));
146   extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_));
147
148   delegate_ = ExtensionsBrowserClient::Get()->CreateRuntimeAPIDelegate(
149       browser_context_);
150
151   // Check if registered events are up-to-date. We can only do this once
152   // per browser context, since it updates internal state when called.
153   dispatch_chrome_updated_event_ =
154       ExtensionsBrowserClient::Get()->DidVersionUpdate(browser_context_);
155 }
156
157 RuntimeAPI::~RuntimeAPI() {
158   delegate_->RemoveUpdateObserver(this);
159 }
160
161 void RuntimeAPI::Observe(int type,
162                          const content::NotificationSource& source,
163                          const content::NotificationDetails& details) {
164   DCHECK_EQ(extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED, type);
165   // We're done restarting Chrome after an update.
166   dispatch_chrome_updated_event_ = false;
167
168   delegate_->AddUpdateObserver(this);
169
170   // RuntimeAPI is redirected in incognito, so |browser_context_| is never
171   // incognito. We don't observe incognito ProcessManagers but that is OK
172   // because we don't send onStartup events to incognito browser contexts.
173   DCHECK(!browser_context_->IsOffTheRecord());
174   // Some tests use partially constructed Profiles without a process manager.
175   ExtensionSystem* extension_system = ExtensionSystem::Get(browser_context_);
176   if (extension_system->process_manager())
177     extension_system->process_manager()->AddObserver(this);
178 }
179
180 void RuntimeAPI::OnExtensionLoaded(content::BrowserContext* browser_context,
181                                    const Extension* extension) {
182   if (!dispatch_chrome_updated_event_)
183     return;
184
185   // Dispatch the onInstalled event with reason "chrome_update".
186   base::MessageLoop::current()->PostTask(
187       FROM_HERE,
188       base::Bind(&RuntimeEventRouter::DispatchOnInstalledEvent,
189                  browser_context_,
190                  extension->id(),
191                  Version(),
192                  true));
193 }
194
195 void RuntimeAPI::OnExtensionWillBeInstalled(
196     content::BrowserContext* browser_context,
197     const Extension* extension,
198     bool is_update,
199     bool from_ephemeral,
200     const std::string& old_name) {
201   // Ephemeral apps are not considered to be installed and do not receive
202   // the onInstalled() event.
203   if (util::IsEphemeralApp(extension->id(), browser_context_))
204     return;
205
206   Version old_version = delegate_->GetPreviousExtensionVersion(extension);
207
208   // Dispatch the onInstalled event.
209   base::MessageLoop::current()->PostTask(
210       FROM_HERE,
211       base::Bind(&RuntimeEventRouter::DispatchOnInstalledEvent,
212                  browser_context_,
213                  extension->id(),
214                  old_version,
215                  false));
216 }
217
218 void RuntimeAPI::OnExtensionUninstalled(
219     content::BrowserContext* browser_context,
220     const Extension* extension,
221     UninstallReason reason) {
222   // Ephemeral apps are not considered to be installed, so the uninstall URL
223   // is not invoked when they are removed.
224   if (util::IsEphemeralApp(extension->id(), browser_context_))
225     return;
226
227   RuntimeEventRouter::OnExtensionUninstalled(
228       browser_context_, extension->id(), reason);
229 }
230
231 void RuntimeAPI::Shutdown() {
232   // ExtensionSystem deletes its ProcessManager during the Shutdown() phase, so
233   // the observer must be removed here and not in the RuntimeAPI destructor.
234   ProcessManager* process_manager =
235       ExtensionSystem::Get(browser_context_)->process_manager();
236   // Some tests use partially constructed Profiles without a process manager.
237   if (process_manager)
238     process_manager->RemoveObserver(this);
239 }
240
241 void RuntimeAPI::OnAppUpdateAvailable(const Extension* extension) {
242   RuntimeEventRouter::DispatchOnUpdateAvailableEvent(
243       browser_context_, extension->id(), extension->manifest()->value());
244 }
245
246 void RuntimeAPI::OnChromeUpdateAvailable() {
247   RuntimeEventRouter::DispatchOnBrowserUpdateAvailableEvent(browser_context_);
248 }
249
250 void RuntimeAPI::OnBackgroundHostStartup(const Extension* extension) {
251   RuntimeEventRouter::DispatchOnStartupEvent(browser_context_, extension->id());
252 }
253
254 void RuntimeAPI::ReloadExtension(const std::string& extension_id) {
255   delegate_->ReloadExtension(extension_id);
256 }
257
258 bool RuntimeAPI::CheckForUpdates(
259     const std::string& extension_id,
260     const RuntimeAPIDelegate::UpdateCheckCallback& callback) {
261   return delegate_->CheckForUpdates(extension_id, callback);
262 }
263
264 void RuntimeAPI::OpenURL(const GURL& update_url) {
265   delegate_->OpenURL(update_url);
266 }
267
268 bool RuntimeAPI::GetPlatformInfo(runtime::PlatformInfo* info) {
269   return delegate_->GetPlatformInfo(info);
270 }
271
272 bool RuntimeAPI::RestartDevice(std::string* error_message) {
273   return delegate_->RestartDevice(error_message);
274 }
275
276 ///////////////////////////////////////////////////////////////////////////////
277
278 // static
279 void RuntimeEventRouter::DispatchOnStartupEvent(
280     content::BrowserContext* context,
281     const std::string& extension_id) {
282   DispatchOnStartupEventImpl(context, extension_id, true, NULL);
283 }
284
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;
296
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);
307   }
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());
313
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());
334       }
335     }
336   }
337 }
338
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;
347
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());
354 }
355
356 // static
357 void RuntimeEventRouter::DispatchOnBrowserUpdateAvailableEvent(
358     content::BrowserContext* context) {
359   ExtensionSystem* system = ExtensionSystem::Get(context);
360   if (!system)
361     return;
362
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());
368 }
369
370 // static
371 void RuntimeEventRouter::DispatchOnRestartRequiredEvent(
372     content::BrowserContext* context,
373     const std::string& app_id,
374     core_api::runtime::OnRestartRequired::Reason reason) {
375   ExtensionSystem* system = ExtensionSystem::Get(context);
376   if (!system)
377     return;
378
379   scoped_ptr<Event> event(
380       new Event(runtime::OnRestartRequired::kEventName,
381                 core_api::runtime::OnRestartRequired::Create(reason)));
382
383   DCHECK(system->event_router());
384   system->event_router()->DispatchEventToExtension(app_id, event.Pass());
385 }
386
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;
395   }
396
397   GURL uninstall_url(
398       GetUninstallURL(ExtensionPrefs::Get(context), extension_id));
399
400   if (uninstall_url.is_empty())
401     return;
402
403   RuntimeAPI::GetFactoryInstance()->Get(context)->OpenURL(uninstall_url);
404 }
405
406 ExtensionFunction::ResponseAction RuntimeGetBackgroundPageFunction::Run() {
407   ExtensionSystem* system = ExtensionSystem::Get(browser_context());
408   ExtensionHost* host =
409       system->process_manager()->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));
420   }
421
422   return RespondLater();
423 }
424
425 void RuntimeGetBackgroundPageFunction::OnPageLoaded(ExtensionHost* host) {
426   if (host) {
427     Respond(NoArguments());
428   } else {
429     Respond(Error(kPageLoadError));
430   }
431 }
432
433 ExtensionFunction::ResponseAction RuntimeSetUninstallURLFunction::Run() {
434   std::string url_string;
435   EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &url_string));
436
437   GURL url(url_string);
438   if (!url.is_valid()) {
439     return RespondNow(
440         Error(ErrorUtils::FormatErrorMessage(kInvalidUrlError, url_string)));
441   }
442   SetUninstallURL(
443       ExtensionPrefs::Get(browser_context()), extension_id(), url_string);
444   return RespondNow(NoArguments());
445 }
446
447 ExtensionFunction::ResponseAction RuntimeReloadFunction::Run() {
448   RuntimeAPI::GetFactoryInstance()->Get(browser_context())->ReloadExtension(
449       extension_id());
450   return RespondNow(NoArguments());
451 }
452
453 ExtensionFunction::ResponseAction RuntimeRequestUpdateCheckFunction::Run() {
454   if (!RuntimeAPI::GetFactoryInstance()
455            ->Get(browser_context())
456            ->CheckForUpdates(
457                extension_id(),
458                base::Bind(&RuntimeRequestUpdateCheckFunction::CheckComplete,
459                           this))) {
460     return RespondNow(Error(kUpdatesDisabledError));
461   }
462   return RespondLater();
463 }
464
465 void RuntimeRequestUpdateCheckFunction::CheckComplete(
466     const RuntimeAPIDelegate::UpdateCheckResult& result) {
467   if (result.success) {
468     base::DictionaryValue* details = new base::DictionaryValue;
469     details->SetString("version", result.version);
470     Respond(TwoArguments(new base::StringValue(result.response), details));
471   } else {
472     // HMM(kalman): Why does !success not imply Error()?
473     Respond(OneArgument(new base::StringValue(result.response)));
474   }
475 }
476
477 ExtensionFunction::ResponseAction RuntimeRestartFunction::Run() {
478   std::string message;
479   bool result =
480       RuntimeAPI::GetFactoryInstance()->Get(browser_context())->RestartDevice(
481           &message);
482   if (!result) {
483     return RespondNow(Error(message));
484   }
485   return RespondNow(NoArguments());
486 }
487
488 ExtensionFunction::ResponseAction RuntimeGetPlatformInfoFunction::Run() {
489   runtime::PlatformInfo info;
490   if (!RuntimeAPI::GetFactoryInstance()
491            ->Get(browser_context())
492            ->GetPlatformInfo(&info)) {
493     return RespondNow(Error(kPlatformInfoUnavailable));
494   }
495   return RespondNow(
496       ArgumentList(runtime::GetPlatformInfo::Results::Create(info)));
497 }
498
499 ExtensionFunction::ResponseAction
500 RuntimeGetPackageDirectoryEntryFunction::Run() {
501   storage::IsolatedContext* isolated_context =
502       storage::IsolatedContext::GetInstance();
503   DCHECK(isolated_context);
504
505   std::string relative_path = kPackageDirectoryPath;
506   base::FilePath path = extension_->path();
507   std::string filesystem_id = isolated_context->RegisterFileSystemForPath(
508       storage::kFileSystemTypeNativeLocal, std::string(), path, &relative_path);
509
510   int renderer_id = render_view_host_->GetProcess()->GetID();
511   content::ChildProcessSecurityPolicy* policy =
512       content::ChildProcessSecurityPolicy::GetInstance();
513   policy->GrantReadFileSystem(renderer_id, filesystem_id);
514   base::DictionaryValue* dict = new base::DictionaryValue();
515   dict->SetString("fileSystemId", filesystem_id);
516   dict->SetString("baseName", relative_path);
517   return RespondNow(OneArgument(dict));
518 }
519
520 }  // namespace extensions