a2ee3f08dcb7898377573612d9be9f639babc2ad
[platform/framework/web/crosswalk.git] / src / xwalk / application / browser / application_service.cc
1 // Copyright (c) 2013 Intel Corporation. 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 "xwalk/application/browser/application_service.h"
6
7 #include <set>
8 #include <string>
9
10 #include "base/files/file_enumerator.h"
11 #include "base/file_util.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/version.h"
15 #include "content/public/browser/storage_partition.h"
16 #include "content/public/browser/web_contents.h"
17 #include "content/public/browser/web_contents_observer.h"
18 #include "xwalk/application/browser/application_event_manager.h"
19 #include "xwalk/application/browser/application.h"
20 #include "xwalk/application/browser/application_storage.h"
21 #include "xwalk/application/browser/application_system.h"
22 #include "xwalk/application/browser/installer/package.h"
23 #include "xwalk/application/common/application_file_util.h"
24 #include "xwalk/application/common/event_names.h"
25 #include "xwalk/application/common/permission_policy_manager.h"
26 #include "xwalk/runtime/browser/runtime_context.h"
27 #include "xwalk/runtime/browser/runtime.h"
28 #include "xwalk/runtime/browser/xwalk_runner.h"
29
30 #if defined(OS_TIZEN)
31 #include "xwalk/application/browser/installer/tizen/service_package_installer.h"
32 #endif
33
34 namespace xwalk {
35 namespace application {
36
37 namespace {
38
39 void CloseMessageLoop() {
40   // FIXME: Quit message loop here at present. This should go away once
41   // we have Application in place.
42   base::MessageLoop::current()->QuitWhenIdle();
43 }
44
45 void WaitForEventAndClose(
46     const std::string& app_id, const std::string& event_name,
47     ApplicationEventManager* event_manager) {
48   class CloseOnEventArrived : public EventObserver {
49    public:
50     static CloseOnEventArrived* Create(const std::string& event_name,
51         ApplicationEventManager* event_manager) {
52       return new CloseOnEventArrived(event_name, event_manager);
53     }
54
55     virtual void Observe(
56         const std::string& app_id,
57         scoped_refptr<Event> event) OVERRIDE {
58       DCHECK(kOnJavaScriptEventAck == event->name());
59       std::string ack_event_name;
60       event->args()->GetString(0, &ack_event_name);
61       if (ack_event_name != event_name_)
62         return;
63       CloseMessageLoop();
64       delete this;
65     }
66
67    private:
68     CloseOnEventArrived(
69         const std::string& event_name,
70         ApplicationEventManager* event_manager)
71         : EventObserver(event_manager),
72           event_name_(event_name) {}
73
74     std::string event_name_;
75   };
76
77   DCHECK(event_manager);
78   CloseOnEventArrived* observer =
79       CloseOnEventArrived::Create(event_name, event_manager);
80   event_manager->AttachObserver(app_id,
81       kOnJavaScriptEventAck, observer);
82 }
83
84 void WaitForFinishLoad(
85     scoped_refptr<ApplicationData> application,
86     ApplicationEventManager* event_manager,
87     content::WebContents* contents) {
88   class CloseAfterLoadObserver : public content::WebContentsObserver {
89    public:
90     CloseAfterLoadObserver(
91         scoped_refptr<ApplicationData> application,
92         ApplicationEventManager* event_manager,
93         content::WebContents* contents)
94         : content::WebContentsObserver(contents),
95           application_(application),
96           event_manager_(event_manager) {
97       DCHECK(application_);
98       DCHECK(event_manager_);
99     }
100
101     virtual void DidFinishLoad(
102         int64 frame_id,
103         const GURL& validate_url,
104         bool is_main_frame,
105         content::RenderViewHost* render_view_host) OVERRIDE {
106       if (!IsEventHandlerRegistered(kOnInstalled)) {
107         CloseMessageLoop();
108       } else {
109         scoped_ptr<base::ListValue> event_args(new base::ListValue);
110         scoped_refptr<Event> event =
111             Event::CreateEvent(
112                 kOnInstalled, event_args.Pass());
113         event_manager_->SendEvent(application_->ID(), event);
114
115         WaitForEventAndClose(
116             application_->ID(), event->name(), event_manager_);
117       }
118       delete this;
119     }
120
121    private:
122     bool IsEventHandlerRegistered(const std::string& event_name) const {
123       const std::set<std::string>& events = application_->GetEvents();
124       return events.find(event_name) != events.end();
125     }
126
127     scoped_refptr<ApplicationData> application_;
128     ApplicationEventManager* event_manager_;
129   };
130
131   // This object is self-destroyed when an event occurs.
132   new CloseAfterLoadObserver(application, event_manager, contents);
133 }
134
135 void SaveSystemEventsInfo(
136     ApplicationService* application_service,
137     scoped_refptr<ApplicationData> application_data,
138     ApplicationEventManager* event_manager) {
139   // We need to run main document after installation in order to
140   // register system events.
141   if (application_data->HasMainDocument()) {
142     if (Application* application =
143         application_service->Launch(application_data->ID())) {
144       WaitForFinishLoad(application->data(), event_manager,
145                         application->GetMainDocumentRuntime()->web_contents());
146     }
147   }
148 }
149
150 #if defined(OS_TIZEN)
151 bool InstallPackageOnTizen(ApplicationData* application_data,
152                            const base::FilePath& data_dir) {
153   if (!XWalkRunner::GetInstance()->is_running_as_service()) {
154     LOG(ERROR) << "Installation on Tizen is only possible in"
155                << "service mode via 'xwalkctl' utility.";
156     return false;
157   }
158
159   return InstallApplicationForTizen(application_data, data_dir);
160 }
161
162 bool UninstallPackageOnTizen(ApplicationData* application_data,
163                              const base::FilePath& data_dir) {
164   if (!XWalkRunner::GetInstance()->is_running_as_service()) {
165     LOG(ERROR) << "Uninstallation on Tizen is only possible in"
166                << "service mode using 'xwalkctl' utility.";
167     return false;
168   }
169
170   return UninstallApplicationForTizen(application_data, data_dir);
171 }
172 #endif  // OS_TIZEN
173
174 bool CopyDirectoryContents(const base::FilePath& from,
175     const base::FilePath& to) {
176   base::FileEnumerator iter(from, false,
177       base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES);
178   for (base::FilePath path = iter.Next(); !path.empty(); path = iter.Next()) {
179     if (iter.GetInfo().IsDirectory()) {
180       if (!base::CopyDirectory(path, to, true))
181         return false;
182     } else if (!base::CopyFile(path, to.Append(path.BaseName()))) {
183         return false;
184     }
185   }
186
187   return true;
188 }
189
190 }  // namespace
191
192 const base::FilePath::CharType kApplicationsDir[] =
193     FILE_PATH_LITERAL("applications");
194
195 ApplicationService::ApplicationService(RuntimeContext* runtime_context,
196                                        ApplicationStorage* app_storage,
197                                        ApplicationEventManager* event_manager)
198     : runtime_context_(runtime_context),
199       application_storage_(app_storage),
200       event_manager_(event_manager),
201       permission_policy_handler_(new PermissionPolicyManager()) {
202   AddObserver(event_manager);
203 }
204
205 ApplicationService::~ApplicationService() {
206 }
207
208 bool ApplicationService::Install(const base::FilePath& path, std::string* id) {
209   // FIXME(leandro): Installation is not robust enough -- should any step
210   // fail, it can't roll back to a consistent state.
211   if (!base::PathExists(path))
212     return false;
213
214   const base::FilePath data_dir =
215       runtime_context_->GetPath().Append(kApplicationsDir);
216
217   // Make sure the kApplicationsDir exists under data_path, otherwise,
218   // the installation will always fail because of moving application
219   // resources into an invalid directory.
220   if (!base::DirectoryExists(data_dir) &&
221       !base::CreateDirectory(data_dir))
222     return false;
223
224   std::string app_id;
225   base::FilePath unpacked_dir;
226   scoped_ptr<Package> package;
227   if (!base::DirectoryExists(path)) {
228     package = Package::Create(path);
229     package->Extract(&unpacked_dir);
230     app_id = package->Id();
231   } else {
232     unpacked_dir = path;
233   }
234
235   std::string error;
236   scoped_refptr<ApplicationData> application_data = LoadApplication(
237       unpacked_dir, app_id, Manifest::COMMAND_LINE, &error);
238   if (!application_data) {
239     LOG(ERROR) << "Error during application installation: " << error;
240     return false;
241   }
242   if (!permission_policy_handler_->
243       InitApplicationPermission(application_data)) {
244     LOG(ERROR) << "Application permission data is invalid";
245     return false;
246   }
247
248   if (application_storage_->Contains(application_data->ID())) {
249     *id = application_data->ID();
250     LOG(INFO) << "Already installed: " << *id;
251     return false;
252   }
253
254   base::FilePath app_dir = data_dir.AppendASCII(application_data->ID());
255   if (base::DirectoryExists(app_dir)) {
256     if (!base::DeleteFile(app_dir, true))
257       return false;
258   }
259   if (!package) {
260     if (!base::CreateDirectory(app_dir))
261       return false;
262     if (!CopyDirectoryContents(unpacked_dir, app_dir))
263       return false;
264   } else {
265     if (!base::Move(unpacked_dir, app_dir))
266       return false;
267   }
268
269   application_data->SetPath(app_dir);
270
271   if (!application_storage_->AddApplication(application_data)) {
272     LOG(ERROR) << "Application with id " << application_data->ID()
273                << " couldn't be installed.";
274     return false;
275   }
276
277 #if defined(OS_TIZEN)
278   if (!InstallPackageOnTizen(application_data,
279                              runtime_context_->GetPath())) {
280     application_storage_->RemoveApplication(application_data->ID());
281     return false;
282   }
283 #endif
284
285   LOG(INFO) << "Application be installed in: " << app_dir.MaybeAsASCII();
286   LOG(INFO) << "Installed application with id: " << application_data->ID()
287             << " successfully.";
288   *id = application_data->ID();
289
290   SaveSystemEventsInfo(this, application_data, event_manager_);
291
292   FOR_EACH_OBSERVER(Observer, observers_,
293                     OnApplicationInstalled(application_data->ID()));
294
295   return true;
296 }
297
298 bool ApplicationService::Update(const std::string& id,
299                                 const base::FilePath& path) {
300   if (!base::PathExists(path)) {
301     LOG(ERROR) << "The XPK/WGT package file " << path.value() << " is invalid.";
302     return false;
303   }
304
305   if (base::DirectoryExists(path)) {
306     LOG(WARNING) << "Can not update an unpacked XPK/WGT package.";
307     return false;
308   }
309
310   base::FilePath unpacked_dir;
311   base::FilePath origin_dir;
312   std::string app_id;
313   scoped_ptr<Package> package = Package::Create(path);
314   if (!package) {
315     LOG(ERROR) << "XPK/WGT file is invalid.";
316     return false;
317   }
318
319   app_id = package->Id();
320
321   if (app_id.empty()) {
322     LOG(ERROR) << "XPK/WGT file is invalid, and the application id is empty.";
323     return false;
324   }
325
326   if (id.empty() ||
327       id.compare(app_id) != 0) {
328     LOG(ERROR) << "The XPK/WGT file is not the same as expecting.";
329     return false;
330   }
331
332   if (!package->Extract(&unpacked_dir))
333     return false;
334
335   std::string error;
336   scoped_refptr<ApplicationData> new_application =
337       LoadApplication(unpacked_dir,
338                       app_id,
339                       Manifest::COMMAND_LINE,
340                       &error);
341   if (!new_application) {
342     LOG(ERROR) << "An error occurred during application updating: " << error;
343     return false;
344   }
345
346   scoped_refptr<ApplicationData> old_application =
347       application_storage_->GetApplicationData(app_id);
348   if (!old_application) {
349     LOG(INFO) << "Application haven't installed yet: " << app_id;
350     return false;
351   }
352
353   if (old_application->Version()->CompareTo(
354           *(new_application->Version())) >= 0) {
355     LOG(INFO) << "The version number of new XPK/WGT package "
356                  "should be higher than "
357               << old_application->VersionString();
358     return false;
359   }
360
361   const base::FilePath& app_dir = old_application->Path();
362   const base::FilePath tmp_dir(app_dir.value()
363                                + FILE_PATH_LITERAL(".tmp"));
364
365   if (Application* app = GetApplicationByID(app_id)) {
366     LOG(INFO) << "Try to terminate the running application before update.";
367     app->Terminate(Application::Immediate);
368   }
369
370   if (!base::Move(app_dir, tmp_dir) ||
371       !base::Move(unpacked_dir, app_dir))
372     return false;
373
374   new_application = LoadApplication(app_dir,
375                                     app_id,
376                                     Manifest::COMMAND_LINE,
377                                     &error);
378   if (!new_application) {
379     LOG(ERROR) << "Error during loading new package: " << error;
380     base::DeleteFile(app_dir, true);
381     base::Move(tmp_dir, app_dir);
382     return false;
383   }
384
385 #if defined(OS_TIZEN)
386   if (!UninstallPackageOnTizen(old_application,
387                                runtime_context_->GetPath())) {
388     base::DeleteFile(app_dir, true);
389     base::Move(tmp_dir, app_dir);
390     return false;
391   }
392 #endif
393
394   if (!application_storage_->UpdateApplication(new_application)) {
395     LOG(ERROR) << "An Error occurred when updating the application.";
396     base::DeleteFile(app_dir, true);
397     base::Move(tmp_dir, app_dir);
398 #if defined(OS_TIZEN)
399     InstallPackageOnTizen(old_application,
400                           runtime_context_->GetPath());
401 #endif
402     return false;
403   }
404 #if defined(OS_TIZEN)
405   if (!InstallPackageOnTizen(new_application,
406                              runtime_context_->GetPath()))
407     return false;
408 #endif
409   base::DeleteFile(tmp_dir, true);
410
411   SaveSystemEventsInfo(this, new_application, event_manager_);
412
413   FOR_EACH_OBSERVER(Observer, observers_,
414                     OnApplicationUpdated(app_id));
415
416   return true;
417 }
418
419 bool ApplicationService::Uninstall(const std::string& id) {
420   bool result = true;
421
422   scoped_refptr<ApplicationData> application =
423       application_storage_->GetApplicationData(id);
424   if (!application) {
425     LOG(ERROR) << "Cannot uninstall application with id " << id
426                << "; invalid application id";
427     return false;
428   }
429
430   if (Application* app = GetApplicationByID(id)) {
431     LOG(INFO) << "Try to terminate the running application before uninstall.";
432     app->Terminate(Application::Immediate);
433   }
434
435 #if defined(OS_TIZEN)
436   if (!UninstallPackageOnTizen(application,
437                                runtime_context_->GetPath()))
438     result = false;
439 #endif
440
441   if (!application_storage_->RemoveApplication(id)) {
442     LOG(ERROR) << "Cannot uninstall application with id " << id
443                << "; application is not installed.";
444     result = false;
445   }
446
447   const base::FilePath resources =
448       runtime_context_->GetPath().Append(kApplicationsDir).AppendASCII(id);
449   if (base::DirectoryExists(resources) &&
450       !base::DeleteFile(resources, true)) {
451     LOG(ERROR) << "Error occurred while trying to remove application with id "
452                << id << "; Cannot remove all resources.";
453     result = false;
454   }
455
456   content::StoragePartition* partition =
457       content::BrowserContext::GetStoragePartitionForSite(
458           runtime_context_, application->GetBaseURLFromApplicationId(id));
459   partition->ClearDataForOrigin(
460       content::StoragePartition::REMOVE_DATA_MASK_ALL,
461       content::StoragePartition::QUOTA_MANAGED_STORAGE_MASK_ALL,
462       application->URL(),
463       partition->GetURLRequestContext());
464
465   FOR_EACH_OBSERVER(Observer, observers_, OnApplicationUninstalled(id));
466
467   return result;
468 }
469
470 Application* ApplicationService::Launch(
471     scoped_refptr<ApplicationData> application_data,
472     const Application::LaunchParams& launch_params) {
473   if (GetApplicationByID(application_data->ID()) != NULL) {
474     LOG(INFO) << "Application with id: " << application_data->ID()
475               << " is already running.";
476     // FIXME: we need to notify application that it had been attempted
477     // to invoke and let the application to define the further behavior.
478     return NULL;
479   }
480
481   event_manager_->AddEventRouterForApp(application_data);
482   Application* application(new Application(application_data,
483                                            runtime_context_,
484                                            this));
485   ScopedVector<Application>::iterator app_iter =
486       applications_.insert(applications_.end(), application);
487
488   if (!application->Launch(launch_params)) {
489     event_manager_->RemoveEventRouterForApp(application_data);
490     applications_.erase(app_iter);
491     return NULL;
492   }
493
494   FOR_EACH_OBSERVER(Observer, observers_,
495                     DidLaunchApplication(application));
496
497   return application;
498 }
499
500 Application* ApplicationService::Launch(
501     const std::string& id, const Application::LaunchParams& params) {
502   scoped_refptr<ApplicationData> application_data =
503     application_storage_->GetApplicationData(id);
504   if (!application_data) {
505     LOG(ERROR) << "Application with id " << id << " is not installed.";
506     return NULL;
507   }
508
509   return Launch(application_data, params);
510 }
511
512 Application* ApplicationService::Launch(
513     const base::FilePath& path, const Application::LaunchParams& params) {
514   if (!base::DirectoryExists(path))
515     return NULL;
516
517   std::string error;
518   scoped_refptr<ApplicationData> application_data =
519       LoadApplication(path, Manifest::COMMAND_LINE, &error);
520
521   if (!application_data) {
522     LOG(ERROR) << "Error occurred while trying to launch application: "
523                << error;
524     return NULL;
525   }
526
527   return Launch(application_data, params);
528 }
529
530 namespace {
531
532 struct ApplicationRenderHostIDComparator {
533     explicit ApplicationRenderHostIDComparator(int id) : id(id) { }
534     bool operator() (Application* application) {
535       return id == application->GetRenderProcessHostID();
536     }
537     int id;
538 };
539
540 struct ApplicationIDComparator {
541     explicit ApplicationIDComparator(const std::string& app_id)
542       : app_id(app_id) { }
543     bool operator() (Application* application) {
544       return app_id == application->id();
545     }
546     std::string app_id;
547 };
548
549 }  // namespace
550
551 Application* ApplicationService::GetApplicationByRenderHostID(int id) const {
552   ApplicationRenderHostIDComparator comparator(id);
553   ScopedVector<Application>::const_iterator found = std::find_if(
554       applications_.begin(), applications_.end(), comparator);
555   if (found != applications_.end())
556     return *found;
557   return NULL;
558 }
559
560 Application* ApplicationService::GetApplicationByID(
561     const std::string& app_id) const {
562   ApplicationIDComparator comparator(app_id);
563   ScopedVector<Application>::const_iterator found = std::find_if(
564       applications_.begin(), applications_.end(), comparator);
565   if (found != applications_.end())
566     return *found;
567   return NULL;
568 }
569
570 void ApplicationService::AddObserver(Observer* observer) {
571   observers_.AddObserver(observer);
572 }
573
574 void ApplicationService::RemoveObserver(Observer* observer) {
575   observers_.RemoveObserver(observer);
576 }
577
578 void ApplicationService::OnApplicationTerminated(
579                                       Application* application) {
580   ScopedVector<Application>::iterator found = std::find(
581       applications_.begin(), applications_.end(), application);
582   CHECK(found != applications_.end());
583   FOR_EACH_OBSERVER(Observer, observers_,
584                     WillDestroyApplication(application));
585   applications_.erase(found);
586   if (applications_.empty()) {
587     base::MessageLoop::current()->PostTask(
588             FROM_HERE, base::MessageLoop::QuitClosure());
589   }
590 }
591
592 void ApplicationService::CheckAPIAccessControl(const std::string& app_id,
593     const std::string& extension_name,
594     const std::string& api_name, const PermissionCallback& callback) {
595   Application* app = GetApplicationByID(app_id);
596   if (!app) {
597     LOG(ERROR) << "No running application found with ID: "
598       << app_id;
599     callback.Run(UNDEFINED_RUNTIME_PERM);
600     return;
601   }
602   if (!app->UseExtension(extension_name)) {
603     LOG(ERROR) << "Can not find extension: "
604       << extension_name << " of Application with ID: "
605       << app_id;
606     callback.Run(UNDEFINED_RUNTIME_PERM);
607     return;
608   }
609   // Permission name should have been registered at extension initialization.
610   std::string permission_name =
611       app->GetRegisteredPermissionName(extension_name, api_name);
612   if (permission_name.empty()) {
613     LOG(ERROR) << "API: " << api_name << " of extension: "
614       << extension_name << " not registered!";
615     callback.Run(UNDEFINED_RUNTIME_PERM);
616     return;
617   }
618   // Okay, since we have the permission name, let's get down to the policies.
619   // First, find out whether the permission is stored for the current session.
620   StoredPermission perm = app->GetPermission(
621       SESSION_PERMISSION, permission_name);
622   if (perm != UNDEFINED_STORED_PERM) {
623     // "PROMPT" should not be in the session storage.
624     DCHECK(perm != PROMPT);
625     if (perm == ALLOW) {
626       callback.Run(ALLOW_SESSION);
627       return;
628     }
629     if (perm == DENY) {
630       callback.Run(DENY_SESSION);
631       return;
632     }
633     NOTREACHED();
634   }
635   // Then, query the persistent policy storage.
636   perm = app->GetPermission(PERSISTENT_PERMISSION, permission_name);
637   // Permission not found in persistent permission table, normally this should
638   // not happen because all the permission needed by the application should be
639   // contained in its manifest, so it also means that the application is asking
640   // for something wasn't allowed.
641   if (perm == UNDEFINED_STORED_PERM) {
642     callback.Run(UNDEFINED_RUNTIME_PERM);
643     return;
644   }
645   if (perm == PROMPT) {
646     // TODO(Bai): We needed to pop-up a dialog asking user to chose one from
647     // either allow/deny for session/one shot/forever. Then, we need to update
648     // the session and persistent policy accordingly.
649     callback.Run(UNDEFINED_RUNTIME_PERM);
650     return;
651   }
652   if (perm == ALLOW) {
653     callback.Run(ALLOW_ALWAYS);
654     return;
655   }
656   if (perm == DENY) {
657     callback.Run(DENY_ALWAYS);
658     return;
659   }
660   NOTREACHED();
661 }
662
663 bool ApplicationService::RegisterPermissions(const std::string& app_id,
664     const std::string& extension_name,
665     const std::string& perm_table) {
666   Application* app = GetApplicationByID(app_id);
667   if (!app) {
668     LOG(ERROR) << "No running application found with ID: " << app_id;
669     return false;
670   }
671   if (!app->UseExtension(extension_name)) {
672     LOG(ERROR) << "Can not find extension: "
673                << extension_name << " of Application with ID: "
674                << app_id;
675     return false;
676   }
677   return app->RegisterPermissions(extension_name, perm_table);
678 }
679
680 }  // namespace application
681 }  // namespace xwalk