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.
5 #include "xwalk/application/browser/application_service.h"
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"
31 #include "xwalk/application/browser/installer/tizen/service_package_installer.h"
35 namespace application {
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();
45 void WaitForEventAndClose(
46 const std::string& app_id, const std::string& event_name,
47 ApplicationEventManager* event_manager) {
48 class CloseOnEventArrived : public EventObserver {
50 static CloseOnEventArrived* Create(const std::string& event_name,
51 ApplicationEventManager* event_manager) {
52 return new CloseOnEventArrived(event_name, event_manager);
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_)
69 const std::string& event_name,
70 ApplicationEventManager* event_manager)
71 : EventObserver(event_manager),
72 event_name_(event_name) {}
74 std::string event_name_;
77 DCHECK(event_manager);
78 CloseOnEventArrived* observer =
79 CloseOnEventArrived::Create(event_name, event_manager);
80 event_manager->AttachObserver(app_id,
81 kOnJavaScriptEventAck, observer);
84 void WaitForFinishLoad(
85 scoped_refptr<ApplicationData> application,
86 ApplicationEventManager* event_manager,
87 content::WebContents* contents) {
88 class CloseAfterLoadObserver : public content::WebContentsObserver {
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) {
98 DCHECK(event_manager_);
101 virtual void DidFinishLoad(
103 const GURL& validate_url,
105 content::RenderViewHost* render_view_host) OVERRIDE {
106 if (!IsEventHandlerRegistered(kOnInstalled)) {
109 scoped_ptr<base::ListValue> event_args(new base::ListValue);
110 scoped_refptr<Event> event =
112 kOnInstalled, event_args.Pass());
113 event_manager_->SendEvent(application_->ID(), event);
115 WaitForEventAndClose(
116 application_->ID(), event->name(), event_manager_);
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();
127 scoped_refptr<ApplicationData> application_;
128 ApplicationEventManager* event_manager_;
131 // This object is self-destroyed when an event occurs.
132 new CloseAfterLoadObserver(application, event_manager, contents);
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());
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.";
159 return InstallApplicationForTizen(application_data, data_dir);
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.";
170 return UninstallApplicationForTizen(application_data, data_dir);
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))
182 } else if (!base::CopyFile(path, to.Append(path.BaseName()))) {
192 const base::FilePath::CharType kApplicationsDir[] =
193 FILE_PATH_LITERAL("applications");
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);
205 ApplicationService::~ApplicationService() {
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))
214 const base::FilePath data_dir =
215 runtime_context_->GetPath().Append(kApplicationsDir);
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))
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();
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;
242 if (!permission_policy_handler_->
243 InitApplicationPermission(application_data)) {
244 LOG(ERROR) << "Application permission data is invalid";
248 if (application_storage_->Contains(application_data->ID())) {
249 *id = application_data->ID();
250 LOG(INFO) << "Already installed: " << *id;
254 base::FilePath app_dir = data_dir.AppendASCII(application_data->ID());
255 if (base::DirectoryExists(app_dir)) {
256 if (!base::DeleteFile(app_dir, true))
260 if (!base::CreateDirectory(app_dir))
262 if (!CopyDirectoryContents(unpacked_dir, app_dir))
265 if (!base::Move(unpacked_dir, app_dir))
269 application_data->SetPath(app_dir);
271 if (!application_storage_->AddApplication(application_data)) {
272 LOG(ERROR) << "Application with id " << application_data->ID()
273 << " couldn't be installed.";
277 #if defined(OS_TIZEN)
278 if (!InstallPackageOnTizen(application_data,
279 runtime_context_->GetPath())) {
280 application_storage_->RemoveApplication(application_data->ID());
285 LOG(INFO) << "Application be installed in: " << app_dir.MaybeAsASCII();
286 LOG(INFO) << "Installed application with id: " << application_data->ID()
288 *id = application_data->ID();
290 SaveSystemEventsInfo(this, application_data, event_manager_);
292 FOR_EACH_OBSERVER(Observer, observers_,
293 OnApplicationInstalled(application_data->ID()));
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.";
305 if (base::DirectoryExists(path)) {
306 LOG(WARNING) << "Can not update an unpacked XPK/WGT package.";
310 base::FilePath unpacked_dir;
311 base::FilePath origin_dir;
313 scoped_ptr<Package> package = Package::Create(path);
315 LOG(ERROR) << "XPK/WGT file is invalid.";
319 app_id = package->Id();
321 if (app_id.empty()) {
322 LOG(ERROR) << "XPK/WGT file is invalid, and the application id is empty.";
327 id.compare(app_id) != 0) {
328 LOG(ERROR) << "The XPK/WGT file is not the same as expecting.";
332 if (!package->Extract(&unpacked_dir))
336 scoped_refptr<ApplicationData> new_application =
337 LoadApplication(unpacked_dir,
339 Manifest::COMMAND_LINE,
341 if (!new_application) {
342 LOG(ERROR) << "An error occurred during application updating: " << error;
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;
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();
361 const base::FilePath& app_dir = old_application->Path();
362 const base::FilePath tmp_dir(app_dir.value()
363 + FILE_PATH_LITERAL(".tmp"));
365 if (Application* app = GetApplicationByID(app_id)) {
366 LOG(INFO) << "Try to terminate the running application before update.";
367 app->Terminate(Application::Immediate);
370 if (!base::Move(app_dir, tmp_dir) ||
371 !base::Move(unpacked_dir, app_dir))
374 new_application = LoadApplication(app_dir,
376 Manifest::COMMAND_LINE,
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);
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);
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());
404 #if defined(OS_TIZEN)
405 if (!InstallPackageOnTizen(new_application,
406 runtime_context_->GetPath()))
409 base::DeleteFile(tmp_dir, true);
411 SaveSystemEventsInfo(this, new_application, event_manager_);
413 FOR_EACH_OBSERVER(Observer, observers_,
414 OnApplicationUpdated(app_id));
419 bool ApplicationService::Uninstall(const std::string& id) {
422 scoped_refptr<ApplicationData> application =
423 application_storage_->GetApplicationData(id);
425 LOG(ERROR) << "Cannot uninstall application with id " << id
426 << "; invalid application id";
430 if (Application* app = GetApplicationByID(id)) {
431 LOG(INFO) << "Try to terminate the running application before uninstall.";
432 app->Terminate(Application::Immediate);
435 #if defined(OS_TIZEN)
436 if (!UninstallPackageOnTizen(application,
437 runtime_context_->GetPath()))
441 if (!application_storage_->RemoveApplication(id)) {
442 LOG(ERROR) << "Cannot uninstall application with id " << id
443 << "; application is not installed.";
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.";
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,
463 partition->GetURLRequestContext());
465 FOR_EACH_OBSERVER(Observer, observers_, OnApplicationUninstalled(id));
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.
481 event_manager_->AddEventRouterForApp(application_data);
482 Application* application(new Application(application_data,
485 ScopedVector<Application>::iterator app_iter =
486 applications_.insert(applications_.end(), application);
488 if (!application->Launch(launch_params)) {
489 event_manager_->RemoveEventRouterForApp(application_data);
490 applications_.erase(app_iter);
494 FOR_EACH_OBSERVER(Observer, observers_,
495 DidLaunchApplication(application));
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.";
509 return Launch(application_data, params);
512 Application* ApplicationService::Launch(
513 const base::FilePath& path, const Application::LaunchParams& params) {
514 if (!base::DirectoryExists(path))
518 scoped_refptr<ApplicationData> application_data =
519 LoadApplication(path, Manifest::COMMAND_LINE, &error);
521 if (!application_data) {
522 LOG(ERROR) << "Error occurred while trying to launch application: "
527 return Launch(application_data, params);
532 struct ApplicationRenderHostIDComparator {
533 explicit ApplicationRenderHostIDComparator(int id) : id(id) { }
534 bool operator() (Application* application) {
535 return id == application->GetRenderProcessHostID();
540 struct ApplicationIDComparator {
541 explicit ApplicationIDComparator(const std::string& app_id)
543 bool operator() (Application* application) {
544 return app_id == application->id();
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())
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())
570 void ApplicationService::AddObserver(Observer* observer) {
571 observers_.AddObserver(observer);
574 void ApplicationService::RemoveObserver(Observer* observer) {
575 observers_.RemoveObserver(observer);
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());
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);
597 LOG(ERROR) << "No running application found with ID: "
599 callback.Run(UNDEFINED_RUNTIME_PERM);
602 if (!app->UseExtension(extension_name)) {
603 LOG(ERROR) << "Can not find extension: "
604 << extension_name << " of Application with ID: "
606 callback.Run(UNDEFINED_RUNTIME_PERM);
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);
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);
626 callback.Run(ALLOW_SESSION);
630 callback.Run(DENY_SESSION);
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);
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);
653 callback.Run(ALLOW_ALWAYS);
657 callback.Run(DENY_ALWAYS);
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);
668 LOG(ERROR) << "No running application found with ID: " << app_id;
671 if (!app->UseExtension(extension_name)) {
672 LOG(ERROR) << "Can not find extension: "
673 << extension_name << " of Application with ID: "
677 return app->RegisterPermissions(extension_name, perm_table);
680 } // namespace application