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/application_manifest_constants.h"
25 #include "xwalk/application/common/event_names.h"
26 #include "xwalk/application/common/id_util.h"
27 #include "xwalk/application/common/permission_policy_manager.h"
28 #include "xwalk/runtime/browser/runtime_context.h"
29 #include "xwalk/runtime/browser/runtime.h"
30 #include "xwalk/runtime/browser/xwalk_runner.h"
33 #include "xwalk/application/browser/installer/tizen/package_installer.h"
34 #include "xwalk/application/browser/installer/tizen/service_package_installer.h"
37 using xwalk::RuntimeContext;
41 void CloseMessageLoop() {
42 // FIXME: Quit message loop here at present. This should go away once
43 // we have Application in place.
44 base::MessageLoop::current()->QuitWhenIdle();
47 void WaitForEventAndClose(
48 const std::string& app_id, const std::string& event_name,
49 xwalk::application::ApplicationEventManager* event_manager) {
50 class CloseOnEventArrived : public xwalk::application::EventObserver {
52 static CloseOnEventArrived* Create(const std::string& event_name,
53 xwalk::application::ApplicationEventManager* event_manager) {
54 return new CloseOnEventArrived(event_name, event_manager);
58 const std::string& app_id,
59 scoped_refptr<xwalk::application::Event> event) OVERRIDE {
60 DCHECK(xwalk::application::kOnJavaScriptEventAck == event->name());
61 std::string ack_event_name;
62 event->args()->GetString(0, &ack_event_name);
63 if (ack_event_name != event_name_)
71 const std::string& event_name,
72 xwalk::application::ApplicationEventManager* event_manager)
73 : xwalk::application::EventObserver(event_manager),
74 event_name_(event_name) {}
76 std::string event_name_;
79 DCHECK(event_manager);
80 CloseOnEventArrived* observer =
81 CloseOnEventArrived::Create(event_name, event_manager);
82 event_manager->AttachObserver(app_id,
83 xwalk::application::kOnJavaScriptEventAck, observer);
86 void WaitForFinishLoad(
87 scoped_refptr<xwalk::application::ApplicationData> application,
88 xwalk::application::ApplicationEventManager* event_manager,
89 content::WebContents* contents) {
90 class CloseAfterLoadObserver : public content::WebContentsObserver {
92 CloseAfterLoadObserver(
93 scoped_refptr<xwalk::application::ApplicationData> application,
94 xwalk::application::ApplicationEventManager* event_manager,
95 content::WebContents* contents)
96 : content::WebContentsObserver(contents),
97 application_(application),
98 event_manager_(event_manager) {
100 DCHECK(event_manager_);
103 virtual void DidFinishLoad(
105 const GURL& validate_url,
107 content::RenderViewHost* render_view_host) OVERRIDE {
108 if (!IsEventHandlerRegistered(xwalk::application::kOnInstalled)) {
111 scoped_ptr<base::ListValue> event_args(new base::ListValue);
112 scoped_refptr<xwalk::application::Event> event =
113 xwalk::application::Event::CreateEvent(
114 xwalk::application::kOnInstalled, event_args.Pass());
115 event_manager_->SendEvent(application_->ID(), event);
117 WaitForEventAndClose(
118 application_->ID(), event->name(), event_manager_);
124 bool IsEventHandlerRegistered(const std::string& event_name) const {
125 const std::set<std::string>& events = application_->GetEvents();
126 return events.find(event_name) != events.end();
129 scoped_refptr<xwalk::application::ApplicationData> application_;
130 xwalk::application::ApplicationEventManager* event_manager_;
133 // This object is self-destroyed when an event occurs.
134 new CloseAfterLoadObserver(application, event_manager, contents);
137 void SaveSystemEventsInfo(
138 xwalk::application::ApplicationService* application_service,
139 scoped_refptr<xwalk::application::ApplicationData> application_data,
140 xwalk::application::ApplicationEventManager* event_manager) {
141 // We need to run main document after installation in order to
142 // register system events.
143 if (application_data->HasMainDocument()) {
144 if (xwalk::application::Application* application =
145 application_service->Launch(application_data->ID())) {
146 WaitForFinishLoad(application->data(), event_manager,
147 application->GetMainDocumentRuntime()->web_contents());
152 #if defined(OS_TIZEN)
153 bool InstallPackageOnTizen(xwalk::application::ApplicationService* service,
154 xwalk::application::ApplicationStorage* storage,
155 xwalk::application::ApplicationData* application,
156 const base::FilePath& data_dir) {
157 if (xwalk::XWalkRunner::GetInstance()->is_running_as_service()) {
158 return InstallApplicationForTizen(application, data_dir);
161 scoped_ptr<xwalk::application::PackageInstaller> installer =
162 xwalk::application::PackageInstaller::Create(service, storage,
163 application->ID(), data_dir);
164 if (!installer || !installer->Install()) {
165 LOG(ERROR) << "An error occurred during installation on Tizen.";
171 bool UninstallPackageOnTizen(xwalk::application::ApplicationService* service,
172 xwalk::application::ApplicationStorage* storage,
173 xwalk::application::ApplicationData* application,
174 const base::FilePath& data_dir) {
175 if (xwalk::XWalkRunner::GetInstance()->is_running_as_service()) {
176 return UninstallApplicationForTizen(application, data_dir);
179 scoped_ptr<xwalk::application::PackageInstaller> installer =
180 xwalk::application::PackageInstaller::Create(service, storage,
181 application->ID(), data_dir);
182 if (!installer || !installer->Uninstall()) {
183 LOG(ERROR) << "An error occurred during uninstallation on Tizen.";
190 bool CopyDirectoryContents(const base::FilePath& from,
191 const base::FilePath& to) {
192 base::FileEnumerator iter(from, false,
193 base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES);
194 for (base::FilePath path = iter.Next(); !path.empty(); path = iter.Next()) {
195 if (iter.GetInfo().IsDirectory()) {
196 if (!base::CopyDirectory(path, to, true))
198 } else if (!base::CopyFile(path, to.Append(path.BaseName()))) {
209 namespace application {
211 const base::FilePath::CharType kApplicationsDir[] =
212 FILE_PATH_LITERAL("applications");
214 ApplicationService::ApplicationService(RuntimeContext* runtime_context,
215 ApplicationStorage* app_storage,
216 ApplicationEventManager* event_manager)
217 : runtime_context_(runtime_context),
218 application_storage_(app_storage),
219 event_manager_(event_manager),
220 permission_policy_handler_(new PermissionPolicyManager()) {
221 AddObserver(event_manager);
224 ApplicationService::~ApplicationService() {
227 bool ApplicationService::Install(const base::FilePath& path, std::string* id) {
228 // FIXME(leandro): Installation is not robust enough -- should any step
229 // fail, it can't roll back to a consistent state.
230 if (!base::PathExists(path))
233 const base::FilePath data_dir =
234 runtime_context_->GetPath().Append(kApplicationsDir);
236 // Make sure the kApplicationsDir exists under data_path, otherwise,
237 // the installation will always fail because of moving application
238 // resources into an invalid directory.
239 if (!base::DirectoryExists(data_dir) &&
240 !base::CreateDirectory(data_dir))
244 base::FilePath unpacked_dir;
245 scoped_ptr<Package> package;
246 if (!base::DirectoryExists(path)) {
247 package = Package::Create(path);
248 package->Extract(&unpacked_dir);
249 app_id = package->Id();
255 scoped_refptr<ApplicationData> application_data = LoadApplication(
256 unpacked_dir, app_id, Manifest::COMMAND_LINE, &error);
257 if (!application_data) {
258 LOG(ERROR) << "Error during application installation: " << error;
261 if (!permission_policy_handler_->
262 InitApplicationPermission(application_data)) {
263 LOG(ERROR) << "Application permission data is invalid";
267 if (application_storage_->Contains(application_data->ID())) {
268 *id = application_data->ID();
269 LOG(INFO) << "Already installed: " << *id;
273 base::FilePath app_dir = data_dir.AppendASCII(application_data->ID());
274 if (base::DirectoryExists(app_dir)) {
275 if (!base::DeleteFile(app_dir, true))
279 if (!base::CreateDirectory(app_dir))
281 if (!CopyDirectoryContents(unpacked_dir, app_dir))
284 if (!base::Move(unpacked_dir, app_dir))
288 application_data->SetPath(app_dir);
290 if (!application_storage_->AddApplication(application_data)) {
291 LOG(ERROR) << "Application with id " << application_data->ID()
292 << " couldn't be installed.";
296 #if defined(OS_TIZEN)
297 if (!InstallPackageOnTizen(this, application_storage_,
298 application_data.get(),
299 runtime_context_->GetPath())) {
300 application_storage_->RemoveApplication(application_data->ID());
305 LOG(INFO) << "Application be installed in: " << app_dir.MaybeAsASCII();
306 LOG(INFO) << "Installed application with id: " << application_data->ID()
308 *id = application_data->ID();
310 SaveSystemEventsInfo(this, application_data, event_manager_);
312 FOR_EACH_OBSERVER(Observer, observers_,
313 OnApplicationInstalled(application_data->ID()));
318 bool ApplicationService::Update(const std::string& id,
319 const base::FilePath& path) {
320 if (!base::PathExists(path)) {
321 LOG(ERROR) << "The XPK/WGT package file " << path.value() << " is invalid.";
325 if (base::DirectoryExists(path)) {
326 LOG(WARNING) << "Can not update an unpacked XPK/WGT package.";
330 base::FilePath unpacked_dir;
331 base::FilePath origin_dir;
333 scoped_ptr<Package> package = Package::Create(path);
335 LOG(ERROR) << "XPK/WGT file is invalid.";
339 app_id = package->Id();
341 if (app_id.empty()) {
342 LOG(ERROR) << "XPK/WGT file is invalid, and the application id is empty.";
347 id.compare(app_id) != 0) {
348 LOG(ERROR) << "The XPK/WGT file is not the same as expecting.";
352 if (!package->Extract(&unpacked_dir))
356 scoped_refptr<ApplicationData> new_application =
357 LoadApplication(unpacked_dir,
359 Manifest::COMMAND_LINE,
361 if (!new_application) {
362 LOG(ERROR) << "An error occurred during application updating: " << error;
366 scoped_refptr<ApplicationData> old_application =
367 application_storage_->GetApplicationData(app_id);
368 if (!old_application) {
369 LOG(INFO) << "Application haven't installed yet: " << app_id;
373 if (old_application->Version()->CompareTo(
374 *(new_application->Version())) >= 0) {
375 LOG(INFO) << "The version number of new XPK/WGT package "
376 "should be higher than "
377 << old_application->VersionString();
381 const base::FilePath& app_dir = old_application->Path();
382 const base::FilePath tmp_dir(app_dir.value()
383 + FILE_PATH_LITERAL(".tmp"));
385 // FIXME: Need to terminate the application here if it's running, after
386 // shared runtime process mode has been implemented.
387 if (!base::Move(app_dir, tmp_dir) ||
388 !base::Move(unpacked_dir, app_dir))
391 new_application = LoadApplication(app_dir,
393 Manifest::COMMAND_LINE,
395 if (!new_application) {
396 LOG(ERROR) << "Error during loading new package: " << error;
397 base::DeleteFile(app_dir, true);
398 base::Move(tmp_dir, app_dir);
402 #if defined(OS_TIZEN)
403 if (!UninstallPackageOnTizen(this, application_storage_,
404 old_application.get(),
405 runtime_context_->GetPath())) {
406 base::DeleteFile(app_dir, true);
407 base::Move(tmp_dir, app_dir);
412 if (!application_storage_->UpdateApplication(new_application)) {
413 LOG(ERROR) << "An Error occurred when updating the application.";
414 base::DeleteFile(app_dir, true);
415 base::Move(tmp_dir, app_dir);
416 #if defined(OS_TIZEN)
417 InstallPackageOnTizen(this, application_storage_,
418 old_application.get(),
419 runtime_context_->GetPath());
423 #if defined(OS_TIZEN)
424 if (!InstallPackageOnTizen(this, application_storage_,
425 new_application.get(),
426 runtime_context_->GetPath()))
429 base::DeleteFile(tmp_dir, true);
431 SaveSystemEventsInfo(this, new_application, event_manager_);
433 FOR_EACH_OBSERVER(Observer, observers_,
434 OnApplicationUpdated(app_id));
439 bool ApplicationService::Uninstall(const std::string& id) {
442 scoped_refptr<ApplicationData> application =
443 application_storage_->GetApplicationData(id);
445 LOG(ERROR) << "Cannot uninstall application with id " << id
446 << "; invalid application id";
450 #if defined(OS_TIZEN)
451 if (!UninstallPackageOnTizen(this, application_storage_, application.get(),
452 runtime_context_->GetPath()))
456 if (!application_storage_->RemoveApplication(id)) {
457 LOG(ERROR) << "Cannot uninstall application with id " << id
458 << "; application is not installed.";
462 const base::FilePath resources =
463 runtime_context_->GetPath().Append(kApplicationsDir).AppendASCII(id);
464 if (base::DirectoryExists(resources) &&
465 !base::DeleteFile(resources, true)) {
466 LOG(ERROR) << "Error occurred while trying to remove application with id "
467 << id << "; Cannot remove all resources.";
471 content::StoragePartition* partition =
472 content::BrowserContext::GetStoragePartitionForSite(
473 runtime_context_, application->GetBaseURLFromApplicationId(id));
474 partition->ClearDataForOrigin(
475 content::StoragePartition::REMOVE_DATA_MASK_ALL,
476 content::StoragePartition::QUOTA_MANAGED_STORAGE_MASK_ALL,
478 partition->GetURLRequestContext());
480 FOR_EACH_OBSERVER(Observer, observers_, OnApplicationUninstalled(id));
485 Application* ApplicationService::Launch(
486 const std::string& id, const Application::LaunchParams& params) {
487 scoped_refptr<ApplicationData> application_data =
488 application_storage_->GetApplicationData(id);
489 if (!application_data) {
490 LOG(ERROR) << "Application with id " << id << " is not installed.";
494 return Launch(application_data, params);
497 Application* ApplicationService::Launch(const base::FilePath& path) {
498 if (!base::DirectoryExists(path))
502 scoped_refptr<ApplicationData> application_data =
503 LoadApplication(path, Manifest::COMMAND_LINE, &error);
505 if (!application_data) {
506 LOG(ERROR) << "Error occurred while trying to launch application: "
511 return Launch(application_data, Application::LaunchParams());
514 Application* ApplicationService::Launch(const GURL& url) {
515 namespace keys = xwalk::application_manifest_keys;
517 const std::string& url_spec = url.spec();
518 DCHECK(!url_spec.empty());
519 const std::string& app_id = GenerateId(url_spec);
520 // FIXME: we need to handle hash collisions.
521 DCHECK(!application_storage_->GetApplicationData(app_id));
523 base::DictionaryValue manifest;
524 // FIXME: define permissions!
525 manifest.SetString(keys::kURLKey, url_spec);
526 manifest.SetString(keys::kNameKey, "XWalk Dummy App");
527 manifest.SetString(keys::kVersionKey, "0");
528 manifest.SetInteger(keys::kManifestVersionKey, 1);
530 scoped_refptr<ApplicationData> application_data = ApplicationData::Create(
531 base::FilePath(), Manifest::COMMAND_LINE, manifest, app_id, &error);
532 if (!application_data) {
533 LOG(ERROR) << "Error occurred while trying to launch application: "
538 Application::LaunchParams launch_params;
539 launch_params.entry_points = Application::URLKey;
540 return Launch(application_data, launch_params);
545 struct ApplicationRenderHostIDComparator {
546 explicit ApplicationRenderHostIDComparator(int id) : id(id) { }
547 bool operator() (Application* application) {
548 return id == application->GetRenderProcessHostID();
553 struct ApplicationIDComparator {
554 explicit ApplicationIDComparator(const std::string& app_id)
556 bool operator() (Application* application) {
557 return app_id == application->id();
564 Application* ApplicationService::GetApplicationByRenderHostID(int id) const {
565 ApplicationRenderHostIDComparator comparator(id);
566 ScopedVector<Application>::const_iterator found = std::find_if(
567 applications_.begin(), applications_.end(), comparator);
568 if (found != applications_.end())
573 Application* ApplicationService::GetApplicationByID(
574 const std::string& app_id) const {
575 ApplicationIDComparator comparator(app_id);
576 ScopedVector<Application>::const_iterator found = std::find_if(
577 applications_.begin(), applications_.end(), comparator);
578 if (found != applications_.end())
583 void ApplicationService::AddObserver(Observer* observer) {
584 observers_.AddObserver(observer);
587 void ApplicationService::RemoveObserver(Observer* observer) {
588 observers_.RemoveObserver(observer);
591 void ApplicationService::OnApplicationTerminated(
592 Application* application) {
593 ScopedVector<Application>::iterator found = std::find(
594 applications_.begin(), applications_.end(), application);
595 CHECK(found != applications_.end());
596 FOR_EACH_OBSERVER(Observer, observers_,
597 WillDestroyApplication(application));
598 applications_.erase(found);
599 if (applications_.empty()) {
600 base::MessageLoop::current()->PostTask(
601 FROM_HERE, base::MessageLoop::QuitClosure());
605 Application* ApplicationService::Launch(
606 scoped_refptr<ApplicationData> application_data,
607 const Application::LaunchParams& launch_params) {
608 if (GetApplicationByID(application_data->ID()) != NULL) {
609 LOG(INFO) << "Application with id: " << application_data->ID()
610 << " is already running.";
611 // FIXME: we need to notify application that it had been attempted
612 // to invoke and let the application to define the further behavior.
616 event_manager_->AddEventRouterForApp(application_data);
617 Application* application(new Application(application_data,
620 ScopedVector<Application>::iterator app_iter =
621 applications_.insert(applications_.end(), application);
623 if (!application->Launch(launch_params)) {
624 event_manager_->RemoveEventRouterForApp(application_data);
625 applications_.erase(app_iter);
629 FOR_EACH_OBSERVER(Observer, observers_,
630 DidLaunchApplication(application));
635 void ApplicationService::CheckAPIAccessControl(const std::string& app_id,
636 const std::string& extension_name,
637 const std::string& api_name, const PermissionCallback& callback) {
638 Application* app = GetApplicationByID(app_id);
640 LOG(ERROR) << "No running application found with ID: "
642 callback.Run(UNDEFINED_RUNTIME_PERM);
645 if (!app->UseExtension(extension_name)) {
646 LOG(ERROR) << "Can not find extension: "
647 << extension_name << " of Application with ID: "
649 callback.Run(UNDEFINED_RUNTIME_PERM);
652 // Permission name should have been registered at extension initialization.
653 std::string permission_name =
654 app->GetRegisteredPermissionName(extension_name, api_name);
655 if (permission_name.empty()) {
656 LOG(ERROR) << "API: " << api_name << " of extension: "
657 << extension_name << " not registered!";
658 callback.Run(UNDEFINED_RUNTIME_PERM);
661 // Okay, since we have the permission name, let's get down to the policies.
662 // First, find out whether the permission is stored for the current session.
663 StoredPermission perm = app->GetPermission(
664 SESSION_PERMISSION, permission_name);
665 if (perm != UNDEFINED_STORED_PERM) {
666 // "PROMPT" should not be in the session storage.
667 DCHECK(perm != PROMPT);
669 callback.Run(ALLOW_SESSION);
673 callback.Run(DENY_SESSION);
678 // Then, query the persistent policy storage.
679 perm = app->GetPermission(PERSISTENT_PERMISSION, permission_name);
680 // Permission not found in persistent permission table, normally this should
681 // not happen because all the permission needed by the application should be
682 // contained in its manifest, so it also means that the application is asking
683 // for something wasn't allowed.
684 if (perm == UNDEFINED_STORED_PERM) {
685 callback.Run(UNDEFINED_RUNTIME_PERM);
688 if (perm == PROMPT) {
689 // TODO(Bai): We needed to pop-up a dialog asking user to chose one from
690 // either allow/deny for session/one shot/forever. Then, we need to update
691 // the session and persistent policy accordingly.
692 callback.Run(UNDEFINED_RUNTIME_PERM);
696 callback.Run(ALLOW_ALWAYS);
700 callback.Run(DENY_ALWAYS);
706 bool ApplicationService::RegisterPermissions(const std::string& app_id,
707 const std::string& extension_name,
708 const std::string& perm_table) {
709 Application* app = GetApplicationByID(app_id);
711 LOG(ERROR) << "No running application found with ID: " << app_id;
714 if (!app->UseExtension(extension_name)) {
715 LOG(ERROR) << "Can not find extension: "
716 << extension_name << " of Application with ID: "
720 return app->RegisterPermissions(extension_name, perm_table);
723 } // namespace application