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/path_service.h"
15 #include "base/version.h"
16 #include "content/public/browser/storage_partition.h"
17 #include "content/public/browser/web_contents.h"
18 #include "content/public/browser/web_contents_observer.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/permission_policy_manager.h"
25 #include "xwalk/runtime/browser/runtime_context.h"
26 #include "xwalk/runtime/browser/runtime.h"
27 #include "xwalk/runtime/browser/xwalk_runner.h"
28 #include "xwalk/runtime/common/xwalk_paths.h"
31 #include "xwalk/application/browser/application_tizen.h"
32 #include "xwalk/application/browser/installer/tizen/package_installer.h"
36 namespace application {
40 bool CopyDirectoryContents(const base::FilePath& from,
41 const base::FilePath& to) {
42 base::FileEnumerator iter(from, false,
43 base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES);
44 for (base::FilePath path = iter.Next(); !path.empty(); path = iter.Next()) {
45 if (iter.GetInfo().IsDirectory()) {
46 if (!base::CopyDirectory(path, to, true))
48 } else if (!base::CopyFile(path, to.Append(path.BaseName()))) {
56 void RemoveWidgetStorageFiles(const base::FilePath& storage_path,
57 const std::string& app_id) {
58 base::FileEnumerator iter(storage_path, true,
59 base::FileEnumerator::FILES);
60 for (base::FilePath file = iter.Next(); !file.empty(); file = iter.Next()) {
61 if (file.MaybeAsASCII().find(app_id) != std::string::npos)
62 base::DeleteFile(file, false);
68 const base::FilePath::CharType kApplicationsDir[] =
69 FILE_PATH_LITERAL("applications");
71 ApplicationService::ApplicationService(RuntimeContext* runtime_context,
72 ApplicationStorage* app_storage)
73 : runtime_context_(runtime_context),
74 application_storage_(app_storage),
75 permission_policy_handler_(new PermissionPolicyManager()) {
78 ApplicationService::~ApplicationService() {
81 bool ApplicationService::Install(const base::FilePath& path, std::string* id) {
82 // FIXME(leandro): Installation is not robust enough -- should any step
83 // fail, it can't roll back to a consistent state.
84 if (!base::PathExists(path))
87 const base::FilePath data_dir =
88 runtime_context_->GetPath().Append(kApplicationsDir);
90 // Make sure the kApplicationsDir exists under data_path, otherwise,
91 // the installation will always fail because of moving application
92 // resources into an invalid directory.
93 if (!base::DirectoryExists(data_dir) &&
94 !base::CreateDirectory(data_dir))
98 base::FilePath unpacked_dir;
99 scoped_ptr<Package> package;
100 if (!base::DirectoryExists(path)) {
101 package = Package::Create(path);
102 package->Extract(&unpacked_dir);
103 app_id = package->Id();
109 scoped_refptr<ApplicationData> application_data = LoadApplication(
110 unpacked_dir, app_id, Manifest::COMMAND_LINE,
111 package->type(), &error);
112 if (!application_data) {
113 LOG(ERROR) << "Error during application installation: " << error;
116 if (!permission_policy_handler_->
117 InitApplicationPermission(application_data)) {
118 LOG(ERROR) << "Application permission data is invalid";
122 if (application_storage_->Contains(application_data->ID())) {
123 *id = application_data->ID();
124 LOG(INFO) << "Already installed: " << *id;
128 base::FilePath app_dir = data_dir.AppendASCII(application_data->ID());
129 if (base::DirectoryExists(app_dir)) {
130 if (!base::DeleteFile(app_dir, true))
134 if (!base::CreateDirectory(app_dir))
136 if (!CopyDirectoryContents(unpacked_dir, app_dir))
139 if (!base::Move(unpacked_dir, app_dir))
143 application_data->SetPath(app_dir);
145 if (!application_storage_->AddApplication(application_data)) {
146 LOG(ERROR) << "Application with id " << application_data->ID()
147 << " couldn't be installed.";
151 #if defined(OS_TIZEN)
152 if (!PackageInstaller::InstallApplication(
153 application_data, runtime_context_->GetPath())) {
154 application_storage_->RemoveApplication(application_data->ID());
159 LOG(INFO) << "Application be installed in: " << app_dir.MaybeAsASCII();
160 LOG(INFO) << "Installed application with id: " << application_data->ID()
162 *id = application_data->ID();
164 FOR_EACH_OBSERVER(Observer, observers_,
165 OnApplicationInstalled(application_data->ID()));
170 bool ApplicationService::Update(const std::string& id,
171 const base::FilePath& path) {
172 if (!base::PathExists(path)) {
173 LOG(ERROR) << "The XPK/WGT package file " << path.value() << " is invalid.";
177 if (base::DirectoryExists(path)) {
178 LOG(WARNING) << "Can not update an unpacked XPK/WGT package.";
182 base::FilePath unpacked_dir;
183 base::FilePath origin_dir;
185 scoped_ptr<Package> package = Package::Create(path);
187 LOG(ERROR) << "XPK/WGT file is invalid.";
191 app_id = package->Id();
193 if (app_id.empty()) {
194 LOG(ERROR) << "XPK/WGT file is invalid, and the application id is empty.";
199 id.compare(app_id) != 0) {
200 LOG(ERROR) << "The XPK/WGT file is not the same as expecting.";
204 if (!package->Extract(&unpacked_dir))
208 scoped_refptr<ApplicationData> new_application =
209 LoadApplication(unpacked_dir,
211 Manifest::COMMAND_LINE,
214 if (!new_application) {
215 LOG(ERROR) << "An error occurred during application updating: " << error;
219 scoped_refptr<ApplicationData> old_application =
220 application_storage_->GetApplicationData(app_id);
221 if (!old_application) {
222 LOG(INFO) << "Application haven't installed yet: " << app_id;
226 if (old_application->Version()->CompareTo(
227 *(new_application->Version())) >= 0) {
228 LOG(INFO) << "The version number of new XPK/WGT package "
229 "should be higher than "
230 << old_application->VersionString();
234 const base::FilePath& app_dir = old_application->Path();
235 const base::FilePath tmp_dir(app_dir.value()
236 + FILE_PATH_LITERAL(".tmp"));
238 if (Application* app = GetApplicationByID(app_id)) {
239 LOG(INFO) << "Try to terminate the running application before update.";
243 if (!base::Move(app_dir, tmp_dir) ||
244 !base::Move(unpacked_dir, app_dir))
247 new_application = LoadApplication(app_dir,
249 Manifest::COMMAND_LINE,
252 if (!new_application) {
253 LOG(ERROR) << "Error during loading new package: " << error;
254 base::DeleteFile(app_dir, true);
255 base::Move(tmp_dir, app_dir);
259 if (!application_storage_->UpdateApplication(new_application)) {
260 LOG(ERROR) << "Fail to update application, roll back to the old one.";
261 base::DeleteFile(app_dir, true);
262 base::Move(tmp_dir, app_dir);
266 #if defined(OS_TIZEN)
267 if (!PackageInstaller::UpdateApplication(
268 new_application, runtime_context_->GetPath())) {
269 LOG(ERROR) << "Fail to update package on Tizen, roll back to the old one.";
270 base::DeleteFile(app_dir, true);
271 if (!application_storage_->UpdateApplication(old_application)) {
272 LOG(ERROR) << "Fail to revert old application info, "
273 << "remove the application as a last resort.";
274 application_storage_->RemoveApplication(old_application->ID());
277 base::Move(tmp_dir, app_dir);
282 base::DeleteFile(tmp_dir, true);
284 FOR_EACH_OBSERVER(Observer, observers_,
285 OnApplicationUpdated(app_id));
290 bool ApplicationService::Uninstall(const std::string& id) {
293 scoped_refptr<ApplicationData> application =
294 application_storage_->GetApplicationData(id);
296 LOG(ERROR) << "Cannot uninstall application with id " << id
297 << "; invalid application id";
301 if (Application* app = GetApplicationByID(id)) {
302 LOG(INFO) << "Try to terminate the running application before uninstall.";
306 #if defined(OS_TIZEN)
307 if (!PackageInstaller::UninstallApplication(
308 application, runtime_context_->GetPath()))
312 if (!application_storage_->RemoveApplication(id)) {
313 LOG(ERROR) << "Cannot uninstall application with id " << id
314 << "; application is not installed.";
318 const base::FilePath resources =
319 runtime_context_->GetPath().Append(kApplicationsDir).AppendASCII(id);
320 if (base::DirectoryExists(resources) &&
321 !base::DeleteFile(resources, true)) {
322 LOG(ERROR) << "Error occurred while trying to remove application with id "
323 << id << "; Cannot remove all resources.";
327 content::StoragePartition* partition =
328 content::BrowserContext::GetStoragePartitionForSite(
329 runtime_context_, application->GetBaseURLFromApplicationId(id));
330 partition->ClearDataForOrigin(
331 content::StoragePartition::REMOVE_DATA_MASK_ALL,
332 content::StoragePartition::QUOTA_MANAGED_STORAGE_MASK_ALL,
334 partition->GetURLRequestContext());
337 PathService::Get(xwalk::DIR_WGT_STORAGE_PATH, &path);
338 RemoveWidgetStorageFiles(path, id);
340 FOR_EACH_OBSERVER(Observer, observers_, OnApplicationUninstalled(id));
345 void ApplicationService::ChangeLocale(const std::string& locale) {
346 const ApplicationData::ApplicationDataMap& apps =
347 application_storage_->GetInstalledApplications();
348 ApplicationData::ApplicationDataMap::const_iterator it;
349 for (it = apps.begin(); it != apps.end(); ++it) {
350 base::string16 error;
351 std::string old_name = it->second->Name();
352 if (!it->second->SetApplicationLocale(locale, &error)) {
353 LOG(ERROR) << "Error when set locale " << locale
354 << " to application " << it->second->ID()
355 << "error : " << error;
357 if (old_name != it->second->Name()) {
358 // After we has changed the application locale, we might get a new name in
359 // the new locale, so call all observer for this event.
361 Observer, observers_,
362 OnApplicationNameChanged(it->second->ID(), it->second->Name()));
367 Application* ApplicationService::Launch(
368 scoped_refptr<ApplicationData> application_data,
369 const Application::LaunchParams& launch_params) {
370 if (GetApplicationByID(application_data->ID()) != NULL) {
371 LOG(INFO) << "Application with id: " << application_data->ID()
372 << " is already running.";
373 // FIXME: we need to notify application that it had been attempted
374 // to invoke and let the application to define the further behavior.
378 #if defined(OS_TIZEN)
379 Application* application(new ApplicationTizen(application_data,
380 runtime_context_, this));
382 Application* application(new Application(application_data,
383 runtime_context_, this));
386 ScopedVector<Application>::iterator app_iter =
387 applications_.insert(applications_.end(), application);
389 if (!application->Launch(launch_params)) {
390 applications_.erase(app_iter);
394 FOR_EACH_OBSERVER(Observer, observers_,
395 DidLaunchApplication(application));
400 Application* ApplicationService::Launch(
401 const std::string& id, const Application::LaunchParams& params) {
402 Application* application = NULL;
403 scoped_refptr<ApplicationData> application_data =
404 application_storage_->GetApplicationData(id);
405 if (!application_data) {
406 LOG(ERROR) << "Application with id " << id << " is not installed.";
410 return Launch(application_data, params);
413 Application* ApplicationService::Launch(
414 const base::FilePath& path, const Application::LaunchParams& params) {
415 Application* application = NULL;
416 if (!base::DirectoryExists(path))
420 scoped_refptr<ApplicationData> application_data =
421 LoadApplication(path, Manifest::COMMAND_LINE, &error);
423 if (!application_data) {
424 LOG(ERROR) << "Error occurred while trying to launch application: "
429 return Launch(application_data, params);
434 struct ApplicationRenderHostIDComparator {
435 explicit ApplicationRenderHostIDComparator(int id) : id(id) { }
436 bool operator() (Application* application) {
437 return id == application->GetRenderProcessHostID();
442 struct ApplicationIDComparator {
443 explicit ApplicationIDComparator(const std::string& app_id)
445 bool operator() (Application* application) {
446 return app_id == application->id();
453 Application* ApplicationService::GetApplicationByRenderHostID(int id) const {
454 ApplicationRenderHostIDComparator comparator(id);
455 ScopedVector<Application>::const_iterator found = std::find_if(
456 applications_.begin(), applications_.end(), comparator);
457 if (found != applications_.end())
462 Application* ApplicationService::GetApplicationByID(
463 const std::string& app_id) const {
464 ApplicationIDComparator comparator(app_id);
465 ScopedVector<Application>::const_iterator found = std::find_if(
466 applications_.begin(), applications_.end(), comparator);
467 if (found != applications_.end())
472 void ApplicationService::AddObserver(Observer* observer) {
473 observers_.AddObserver(observer);
476 void ApplicationService::RemoveObserver(Observer* observer) {
477 observers_.RemoveObserver(observer);
480 void ApplicationService::OnApplicationTerminated(
481 Application* application) {
482 ScopedVector<Application>::iterator found = std::find(
483 applications_.begin(), applications_.end(), application);
484 CHECK(found != applications_.end());
485 FOR_EACH_OBSERVER(Observer, observers_,
486 WillDestroyApplication(application));
487 applications_.erase(found);
488 if (!XWalkRunner::GetInstance()->is_running_as_service() &&
489 applications_.empty()) {
490 base::MessageLoop::current()->PostTask(
491 FROM_HERE, base::MessageLoop::QuitClosure());
495 void ApplicationService::CheckAPIAccessControl(const std::string& app_id,
496 const std::string& extension_name,
497 const std::string& api_name, const PermissionCallback& callback) {
498 Application* app = GetApplicationByID(app_id);
500 LOG(ERROR) << "No running application found with ID: "
502 callback.Run(UNDEFINED_RUNTIME_PERM);
505 if (!app->UseExtension(extension_name)) {
506 LOG(ERROR) << "Can not find extension: "
507 << extension_name << " of Application with ID: "
509 callback.Run(UNDEFINED_RUNTIME_PERM);
512 // Permission name should have been registered at extension initialization.
513 std::string permission_name =
514 app->GetRegisteredPermissionName(extension_name, api_name);
515 if (permission_name.empty()) {
516 LOG(ERROR) << "API: " << api_name << " of extension: "
517 << extension_name << " not registered!";
518 callback.Run(UNDEFINED_RUNTIME_PERM);
521 // Okay, since we have the permission name, let's get down to the policies.
522 // First, find out whether the permission is stored for the current session.
523 StoredPermission perm = app->GetPermission(
524 SESSION_PERMISSION, permission_name);
525 if (perm != UNDEFINED_STORED_PERM) {
526 // "PROMPT" should not be in the session storage.
527 DCHECK(perm != PROMPT);
529 callback.Run(ALLOW_SESSION);
533 callback.Run(DENY_SESSION);
538 // Then, query the persistent policy storage.
539 perm = app->GetPermission(PERSISTENT_PERMISSION, permission_name);
540 // Permission not found in persistent permission table, normally this should
541 // not happen because all the permission needed by the application should be
542 // contained in its manifest, so it also means that the application is asking
543 // for something wasn't allowed.
544 if (perm == UNDEFINED_STORED_PERM) {
545 callback.Run(UNDEFINED_RUNTIME_PERM);
548 if (perm == PROMPT) {
549 // TODO(Bai): We needed to pop-up a dialog asking user to chose one from
550 // either allow/deny for session/one shot/forever. Then, we need to update
551 // the session and persistent policy accordingly.
552 callback.Run(UNDEFINED_RUNTIME_PERM);
556 callback.Run(ALLOW_ALWAYS);
560 callback.Run(DENY_ALWAYS);
566 bool ApplicationService::RegisterPermissions(const std::string& app_id,
567 const std::string& extension_name,
568 const std::string& perm_table) {
569 Application* app = GetApplicationByID(app_id);
571 LOG(ERROR) << "No running application found with ID: " << app_id;
574 if (!app->UseExtension(extension_name)) {
575 LOG(ERROR) << "Can not find extension: "
576 << extension_name << " of Application with ID: "
580 return app->RegisterPermissions(extension_name, perm_table);
583 } // namespace application