#include "xwalk/application/browser/application_service.h"
+#if defined(OS_MACOSX)
+#include <ext/hash_set>
+#else
+#include <hash_set>
+#endif
#include <set>
#include <string>
+#include <vector>
-#include "base/files/file_enumerator.h"
-#include "base/file_util.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/message_loop/message_loop.h"
-#include "base/path_service.h"
-#include "base/version.h"
-#include "content/public/browser/storage_partition.h"
+#include "base/files/file_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "content/public/browser/browser_thread.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_observer.h"
-#include "xwalk/application/browser/application_event_manager.h"
#include "xwalk/application/browser/application.h"
-#include "xwalk/application/browser/application_storage.h"
-#include "xwalk/application/browser/application_system.h"
-#include "xwalk/application/browser/installer/package.h"
+#include "xwalk/application/common/application_manifest_constants.h"
#include "xwalk/application/common/application_file_util.h"
-#include "xwalk/application/common/event_names.h"
-#include "xwalk/application/common/permission_policy_manager.h"
-#include "xwalk/runtime/browser/runtime_context.h"
+#include "xwalk/application/common/id_util.h"
#include "xwalk/runtime/browser/runtime.h"
+#include "xwalk/runtime/browser/xwalk_browser_context.h"
#include "xwalk/runtime/browser/xwalk_runner.h"
#include "xwalk/runtime/common/xwalk_paths.h"
#if defined(OS_TIZEN)
-#include "xwalk/application/browser/application_tizen.h"
-#include "xwalk/application/browser/installer/tizen/service_package_installer.h"
+#include "xwalk/application/browser/application_service_tizen.h"
#endif
namespace xwalk {
-namespace application {
-
-namespace {
-
-void WaitForEventAndClose(
- const std::string& app_id,
- const std::string& event_name,
- ApplicationService* application_service,
- ApplicationEventManager* event_manager) {
-
- class CloseOnEventArrived : public EventObserver {
- public:
- CloseOnEventArrived(
- const std::string& event_name,
- ApplicationService* application_service,
- ApplicationEventManager* event_manager)
- : EventObserver(event_manager),
- event_name_(event_name),
- application_service_(application_service) {}
-
- virtual void Observe(
- const std::string& app_id,
- scoped_refptr<Event> event) OVERRIDE {
- DCHECK(kOnJavaScriptEventAck == event->name());
- std::string ack_event_name;
- event->args()->GetString(0, &ack_event_name);
- if (ack_event_name != event_name_)
- return;
-
- if (Application* app = application_service_->GetApplicationByID(app_id))
- app->Terminate(Application::Immediate);
-
- delete this;
- }
-
- private:
- std::string event_name_;
- ApplicationService* application_service_;
- };
-
- DCHECK(event_manager);
- CloseOnEventArrived* observer =
- new CloseOnEventArrived(event_name, application_service, event_manager);
- event_manager->AttachObserver(app_id,
- kOnJavaScriptEventAck, observer);
-}
-
-void WaitForFinishLoad(
- const std::string& app_id,
- ApplicationService* application_service,
- ApplicationEventManager* event_manager,
- content::WebContents* contents) {
- class CloseAfterLoadObserver : public content::WebContentsObserver {
- public:
- CloseAfterLoadObserver(
- const std::string& app_id,
- ApplicationService* application_service,
- ApplicationEventManager* event_manager,
- content::WebContents* contents)
- : content::WebContentsObserver(contents),
- id_(app_id),
- application_service_(application_service),
- event_manager_(event_manager) {
- DCHECK(application_service_);
- DCHECK(event_manager_);
- }
-
- virtual void DidFinishLoad(
- int64 frame_id,
- const GURL& validate_url,
- bool is_main_frame,
- content::RenderViewHost* render_view_host) OVERRIDE {
- Application* app = application_service_->GetApplicationByID(id_);
- if (!app) {
- delete this;
- return;
- }
-
- if (!IsEventHandlerRegistered(app->data(), kOnInstalled)) {
- app->Terminate(Application::Immediate);
- } else {
- scoped_ptr<base::ListValue> event_args(new base::ListValue);
- scoped_refptr<Event> event =
- Event::CreateEvent(kOnInstalled, event_args.Pass());
- event_manager_->SendEvent(id_, event);
-
- WaitForEventAndClose(
- id_, event->name(), application_service_, event_manager_);
- }
- delete this;
- }
-
- private:
- bool IsEventHandlerRegistered(scoped_refptr<ApplicationData> app_data,
- const std::string& event_name) const {
- const std::set<std::string>& events = app_data->GetEvents();
- return events.find(event_name) != events.end();
- }
-
- std::string id_;
- ApplicationService* application_service_;
- ApplicationEventManager* event_manager_;
- };
-
- // This object is self-destroyed when an event occurs.
- new CloseAfterLoadObserver(
- app_id, application_service, event_manager, contents);
-}
-
-void SaveSystemEventsInfo(
- scoped_refptr<ApplicationData> application_data,
- ApplicationService* application_service,
- ApplicationEventManager* event_manager) {
- // We need to run main document after installation in order to
- // register system events.
- if (application_data->HasMainDocument()) {
- if (Application* application =
- application_service->Launch(application_data->ID())) {
- WaitForFinishLoad(application->id(), application_service, event_manager,
- application->GetMainDocumentRuntime()->web_contents());
- }
- }
-}
-
-#if defined(OS_TIZEN)
-bool InstallPackageOnTizen(ApplicationData* application_data,
- const base::FilePath& data_dir) {
- if (!XWalkRunner::GetInstance()->is_running_as_service()) {
- LOG(ERROR) << "Installation on Tizen is only possible in"
- << "service mode via 'xwalkctl' utility.";
- return false;
- }
-
- return InstallApplicationForTizen(application_data, data_dir);
-}
-
-bool UninstallPackageOnTizen(ApplicationData* application_data,
- const base::FilePath& data_dir) {
- if (!XWalkRunner::GetInstance()->is_running_as_service()) {
- LOG(ERROR) << "Uninstallation on Tizen is only possible in"
- << "service mode using 'xwalkctl' utility.";
- return false;
- }
-
- return UninstallApplicationForTizen(application_data, data_dir);
-}
-#endif // OS_TIZEN
-
-bool CopyDirectoryContents(const base::FilePath& from,
- const base::FilePath& to) {
- base::FileEnumerator iter(from, false,
- base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES);
- for (base::FilePath path = iter.Next(); !path.empty(); path = iter.Next()) {
- if (iter.GetInfo().IsDirectory()) {
- if (!base::CopyDirectory(path, to, true))
- return false;
- } else if (!base::CopyFile(path, to.Append(path.BaseName()))) {
- return false;
- }
- }
-
- return true;
-}
-
-void RemoveWidgetStorageFiles(const base::FilePath& storage_path,
- const std::string& app_id) {
- base::FileEnumerator iter(storage_path, true,
- base::FileEnumerator::FILES);
- for (base::FilePath file = iter.Next(); !file.empty(); file = iter.Next()) {
- if (file.MaybeAsASCII().find(app_id) != std::string::npos)
- base::DeleteFile(file, false);
- }
-}
-
-} // namespace
-
-const base::FilePath::CharType kApplicationsDir[] =
- FILE_PATH_LITERAL("applications");
-
-ApplicationService::ApplicationService(RuntimeContext* runtime_context,
- ApplicationStorage* app_storage,
- ApplicationEventManager* event_manager)
- : runtime_context_(runtime_context),
- application_storage_(app_storage),
- event_manager_(event_manager),
- permission_policy_handler_(new PermissionPolicyManager()) {
- AddObserver(event_manager);
-}
-
-ApplicationService::~ApplicationService() {
-}
-bool ApplicationService::Install(const base::FilePath& path, std::string* id) {
- // FIXME(leandro): Installation is not robust enough -- should any step
- // fail, it can't roll back to a consistent state.
- if (!base::PathExists(path))
- return false;
-
- const base::FilePath data_dir =
- runtime_context_->GetPath().Append(kApplicationsDir);
-
- // Make sure the kApplicationsDir exists under data_path, otherwise,
- // the installation will always fail because of moving application
- // resources into an invalid directory.
- if (!base::DirectoryExists(data_dir) &&
- !base::CreateDirectory(data_dir))
- return false;
-
- std::string app_id;
- base::FilePath unpacked_dir;
- scoped_ptr<Package> package;
- if (!base::DirectoryExists(path)) {
- package = Package::Create(path);
- package->Extract(&unpacked_dir);
- app_id = package->Id();
- } else {
- unpacked_dir = path;
- }
-
- std::string error;
- scoped_refptr<ApplicationData> application_data = LoadApplication(
- unpacked_dir, app_id, Manifest::COMMAND_LINE, &error);
- if (!application_data) {
- LOG(ERROR) << "Error during application installation: " << error;
- return false;
- }
- if (!permission_policy_handler_->
- InitApplicationPermission(application_data)) {
- LOG(ERROR) << "Application permission data is invalid";
- return false;
- }
-
- if (application_storage_->Contains(application_data->ID())) {
- *id = application_data->ID();
- LOG(INFO) << "Already installed: " << *id;
- return false;
- }
-
- base::FilePath app_dir = data_dir.AppendASCII(application_data->ID());
- if (base::DirectoryExists(app_dir)) {
- if (!base::DeleteFile(app_dir, true))
- return false;
- }
- if (!package) {
- if (!base::CreateDirectory(app_dir))
- return false;
- if (!CopyDirectoryContents(unpacked_dir, app_dir))
- return false;
- } else {
- if (!base::Move(unpacked_dir, app_dir))
- return false;
- }
-
- application_data->SetPath(app_dir);
-
- if (!application_storage_->AddApplication(application_data)) {
- LOG(ERROR) << "Application with id " << application_data->ID()
- << " couldn't be installed.";
- return false;
- }
-
-#if defined(OS_TIZEN)
- if (!InstallPackageOnTizen(application_data,
- runtime_context_->GetPath())) {
- application_storage_->RemoveApplication(application_data->ID());
- return false;
- }
-#endif
-
- LOG(INFO) << "Application be installed in: " << app_dir.MaybeAsASCII();
- LOG(INFO) << "Installed application with id: " << application_data->ID()
- << " successfully.";
- *id = application_data->ID();
-
- SaveSystemEventsInfo(application_data, this, event_manager_);
-
- FOR_EACH_OBSERVER(Observer, observers_,
- OnApplicationInstalled(application_data->ID()));
-
- return true;
-}
-
-bool ApplicationService::Update(const std::string& id,
- const base::FilePath& path) {
- if (!base::PathExists(path)) {
- LOG(ERROR) << "The XPK/WGT package file " << path.value() << " is invalid.";
- return false;
- }
-
- if (base::DirectoryExists(path)) {
- LOG(WARNING) << "Can not update an unpacked XPK/WGT package.";
- return false;
- }
-
- base::FilePath unpacked_dir;
- base::FilePath origin_dir;
- std::string app_id;
- scoped_ptr<Package> package = Package::Create(path);
- if (!package) {
- LOG(ERROR) << "XPK/WGT file is invalid.";
- return false;
- }
-
- app_id = package->Id();
-
- if (app_id.empty()) {
- LOG(ERROR) << "XPK/WGT file is invalid, and the application id is empty.";
- return false;
- }
-
- if (id.empty() ||
- id.compare(app_id) != 0) {
- LOG(ERROR) << "The XPK/WGT file is not the same as expecting.";
- return false;
- }
-
- if (!package->Extract(&unpacked_dir))
- return false;
-
- std::string error;
- scoped_refptr<ApplicationData> new_application =
- LoadApplication(unpacked_dir,
- app_id,
- Manifest::COMMAND_LINE,
- &error);
- if (!new_application) {
- LOG(ERROR) << "An error occurred during application updating: " << error;
- return false;
- }
-
- scoped_refptr<ApplicationData> old_application =
- application_storage_->GetApplicationData(app_id);
- if (!old_application) {
- LOG(INFO) << "Application haven't installed yet: " << app_id;
- return false;
- }
-
- if (old_application->Version()->CompareTo(
- *(new_application->Version())) >= 0) {
- LOG(INFO) << "The version number of new XPK/WGT package "
- "should be higher than "
- << old_application->VersionString();
- return false;
- }
-
- const base::FilePath& app_dir = old_application->Path();
- const base::FilePath tmp_dir(app_dir.value()
- + FILE_PATH_LITERAL(".tmp"));
-
- if (Application* app = GetApplicationByID(app_id)) {
- LOG(INFO) << "Try to terminate the running application before update.";
- app->Terminate(Application::Immediate);
- }
-
- if (!base::Move(app_dir, tmp_dir) ||
- !base::Move(unpacked_dir, app_dir))
- return false;
-
- new_application = LoadApplication(app_dir,
- app_id,
- Manifest::COMMAND_LINE,
- &error);
- if (!new_application) {
- LOG(ERROR) << "Error during loading new package: " << error;
- base::DeleteFile(app_dir, true);
- base::Move(tmp_dir, app_dir);
- return false;
- }
-
-#if defined(OS_TIZEN)
- if (!UninstallPackageOnTizen(old_application,
- runtime_context_->GetPath())) {
- base::DeleteFile(app_dir, true);
- base::Move(tmp_dir, app_dir);
- return false;
- }
-#endif
-
- if (!application_storage_->UpdateApplication(new_application)) {
- LOG(ERROR) << "An Error occurred when updating the application.";
- base::DeleteFile(app_dir, true);
- base::Move(tmp_dir, app_dir);
-#if defined(OS_TIZEN)
- InstallPackageOnTizen(old_application,
- runtime_context_->GetPath());
-#endif
- return false;
- }
-#if defined(OS_TIZEN)
- if (!InstallPackageOnTizen(new_application,
- runtime_context_->GetPath()))
- return false;
-#endif
- base::DeleteFile(tmp_dir, true);
-
- SaveSystemEventsInfo(new_application, this, event_manager_);
-
- FOR_EACH_OBSERVER(Observer, observers_,
- OnApplicationUpdated(app_id));
+namespace application {
- return true;
+ApplicationService::ApplicationService(XWalkBrowserContext* browser_context)
+ : browser_context_(browser_context) {
}
-bool ApplicationService::Uninstall(const std::string& id) {
- bool result = true;
-
- scoped_refptr<ApplicationData> application =
- application_storage_->GetApplicationData(id);
- if (!application) {
- LOG(ERROR) << "Cannot uninstall application with id " << id
- << "; invalid application id";
- return false;
- }
-
- if (Application* app = GetApplicationByID(id)) {
- LOG(INFO) << "Try to terminate the running application before uninstall.";
- app->Terminate(Application::Immediate);
- }
-
+scoped_ptr<ApplicationService> ApplicationService::Create(
+ XWalkBrowserContext* browser_context) {
#if defined(OS_TIZEN)
- if (!UninstallPackageOnTizen(application,
- runtime_context_->GetPath()))
- result = false;
+ return make_scoped_ptr<ApplicationService>(
+ new ApplicationServiceTizen(browser_context));
+#else
+ return make_scoped_ptr(new ApplicationService(browser_context));
#endif
-
- if (!application_storage_->RemoveApplication(id)) {
- LOG(ERROR) << "Cannot uninstall application with id " << id
- << "; application is not installed.";
- result = false;
- }
-
- const base::FilePath resources =
- runtime_context_->GetPath().Append(kApplicationsDir).AppendASCII(id);
- if (base::DirectoryExists(resources) &&
- !base::DeleteFile(resources, true)) {
- LOG(ERROR) << "Error occurred while trying to remove application with id "
- << id << "; Cannot remove all resources.";
- result = false;
- }
-
- content::StoragePartition* partition =
- content::BrowserContext::GetStoragePartitionForSite(
- runtime_context_, application->GetBaseURLFromApplicationId(id));
- partition->ClearDataForOrigin(
- content::StoragePartition::REMOVE_DATA_MASK_ALL,
- content::StoragePartition::QUOTA_MANAGED_STORAGE_MASK_ALL,
- application->URL(),
- partition->GetURLRequestContext());
-
- base::FilePath path;
- PathService::Get(xwalk::DIR_WGT_STORAGE_PATH, &path);
- RemoveWidgetStorageFiles(path, id);
-
- FOR_EACH_OBSERVER(Observer, observers_, OnApplicationUninstalled(id));
-
- return result;
}
-void ApplicationService::ChangeLocale(const std::string& locale) {
- const ApplicationData::ApplicationDataMap& apps =
- application_storage_->GetInstalledApplications();
- ApplicationData::ApplicationDataMap::const_iterator it;
- for (it = apps.begin(); it != apps.end(); ++it) {
- base::string16 error;
- std::string old_name = it->second->Name();
- if (!it->second->SetApplicationLocale(locale, &error)) {
- LOG(ERROR) << "Error when set locale " << locale
- << " to application " << it->second->ID()
- << "error : " << error;
- }
- if (old_name != it->second->Name()) {
- // After we has changed the application locale, we might get a new name in
- // the new locale, so call all observer for this event.
- FOR_EACH_OBSERVER(
- Observer, observers_,
- OnApplicationNameChanged(it->second->ID(), it->second->Name()));
- }
- }
+ApplicationService::~ApplicationService() {
}
Application* ApplicationService::Launch(
return NULL;
}
- event_manager_->AddEventRouterForApp(application_data);
-
-#if defined(OS_TIZEN)
- Application* application(new ApplicationTizen(application_data,
- runtime_context_, this));
-#else
- Application* application(new Application(application_data,
- runtime_context_, this));
-#endif
-
+ Application* application = Application::Create(application_data,
+ browser_context_).release();
ScopedVector<Application>::iterator app_iter =
applications_.insert(applications_.end(), application);
if (!application->Launch(launch_params)) {
- event_manager_->RemoveEventRouterForApp(application_data);
applications_.erase(app_iter);
return NULL;
}
+ application->set_observer(this);
+
FOR_EACH_OBSERVER(Observer, observers_,
DidLaunchApplication(application));
return application;
}
-Application* ApplicationService::Launch(
- const std::string& id, const Application::LaunchParams& params) {
- Application* application = NULL;
- scoped_refptr<ApplicationData> application_data =
- application_storage_->GetApplicationData(id);
- if (!application_data) {
- LOG(ERROR) << "Application with id " << id << " is not installed.";
+Application* ApplicationService::LaunchFromManifestPath(
+ const base::FilePath& path, Manifest::Type manifest_type,
+ const Application::LaunchParams& params) {
+ std::string error;
+ scoped_ptr<Manifest> manifest = LoadManifest(path, manifest_type, &error);
+ if (!manifest) {
+ LOG(ERROR) << "Failed to load manifest.";
return NULL;
}
- if ((application = Launch(application_data, params))) {
- scoped_refptr<Event> event = Event::CreateEvent(
- kOnLaunched, scoped_ptr<base::ListValue>(new base::ListValue));
- event_manager_->SendEvent(application->id(), event);
+ base::FilePath app_path = path.DirName();
+ LOG(ERROR) << "Loading app from " << app_path.MaybeAsASCII();
+
+ scoped_refptr<ApplicationData> application_data = ApplicationData::Create(
+ app_path, std::string(), ApplicationData::LOCAL_DIRECTORY,
+ manifest.Pass(), &error);
+ if (!application_data.get()) {
+ LOG(ERROR) << "Error occurred while trying to load application: "
+ << error;
+ return NULL;
}
- return application;
+
+ return Launch(application_data, params);
}
-Application* ApplicationService::Launch(
+Application* ApplicationService::LaunchFromPackagePath(
const base::FilePath& path, const Application::LaunchParams& params) {
- Application* application = NULL;
- if (!base::DirectoryExists(path))
+ scoped_ptr<Package> package = Package::Create(path);
+ if (!package || !package->IsValid()) {
+ LOG(ERROR) << "Failed to obtain valid package from "
+ << path.AsUTF8Unsafe();
return NULL;
+ }
- std::string error;
- scoped_refptr<ApplicationData> application_data =
- LoadApplication(path, Manifest::COMMAND_LINE, &error);
+ base::FilePath tmp_dir, target_dir;
+ if (!GetTempDir(&tmp_dir)) {
+ LOG(ERROR) << "Failed to obtain system temp directory.";
+ return NULL;
+ }
- if (!application_data) {
- LOG(ERROR) << "Error occurred while trying to launch application: "
+#if defined (OS_WIN)
+ base::CreateTemporaryDirInDir(tmp_dir,
+ base::UTF8ToWide(package->name()), &target_dir);
+#else
+ base::CreateTemporaryDirInDir(tmp_dir, package->name(), &target_dir);
+#endif
+ if (!package->ExtractTo(target_dir)) {
+ LOG(ERROR) << "Failed to unpack to a temporary directory: "
+ << target_dir.MaybeAsASCII();
+ return NULL;
+ }
+
+ std::string error;
+ scoped_refptr<ApplicationData> application_data = LoadApplication(
+ target_dir, std::string(), ApplicationData::TEMP_DIRECTORY,
+ package->manifest_type(), &error);
+ if (!application_data.get()) {
+ LOG(ERROR) << "Error occurred while trying to load application: "
<< error;
return NULL;
}
- if ((application = Launch(application_data, params))) {
- scoped_refptr<Event> event = Event::CreateEvent(
- kOnLaunched, scoped_ptr<base::ListValue>(new base::ListValue));
- event_manager_->SendEvent(application->id(), event);
+ return Launch(application_data, params);
+}
+
+// Launch an application created from arbitrary url.
+// FIXME: This application should have the same strict permissions
+// as common browser apps.
+Application* ApplicationService::LaunchHostedURL(
+ const GURL& url, const Application::LaunchParams& params) {
+ const std::string& url_spec = url.spec();
+ if (url_spec.empty()) {
+ LOG(ERROR) << "Failed to launch application from the URL: " << url;
+ return NULL;
}
- return application;
+
+ const std::string& app_id = GenerateId(url_spec);
+
+ scoped_ptr<base::DictionaryValue> settings(new base::DictionaryValue());
+ // FIXME: define permissions!
+ settings->SetString(application_manifest_keys::kStartURLKey, url_spec);
+ // FIXME: Why use URL as name?
+ settings->SetString(application_manifest_keys::kNameKey, url_spec);
+ settings->SetString(application_manifest_keys::kXWalkVersionKey, "0");
+
+ scoped_ptr<Manifest> manifest(
+ new Manifest(settings.Pass(), Manifest::TYPE_MANIFEST));
+
+ std::string error;
+ scoped_refptr<ApplicationData> app_data =
+ ApplicationData::Create(base::FilePath(), app_id,
+ ApplicationData::EXTERNAL_URL, manifest.Pass(), &error);
+ DCHECK(app_data.get());
+
+ return Launch(app_data, params);
}
namespace {
CHECK(found != applications_.end());
FOR_EACH_OBSERVER(Observer, observers_,
WillDestroyApplication(application));
+ scoped_refptr<ApplicationData> app_data = application->data();
applications_.erase(found);
- if (!XWalkRunner::GetInstance()->is_running_as_service() &&
- applications_.empty()) {
- base::MessageLoop::current()->PostTask(
+
+ if (app_data->source_type() == ApplicationData::TEMP_DIRECTORY) {
+ LOG(INFO) << "Deleting the app temporary directory "
+ << app_data->path().AsUTF8Unsafe();
+ content::BrowserThread::PostTask(content::BrowserThread::FILE,
+ FROM_HERE, base::Bind(base::IgnoreResult(&base::DeleteFile),
+ app_data->path(), true /*recursive*/));
+ // FIXME: So far we simply clean up all the app persistent data,
+ // further we need to add an appropriate logic to handle it.
+ content::BrowserContext::GarbageCollectStoragePartitions(
+ browser_context_,
+ make_scoped_ptr(new base::hash_set<base::FilePath>()),
+ base::Bind(&base::DoNothing));
+ }
+
+ if (!XWalkRunner::GetInstance()->shared_process_mode_enabled()) {
+ if (applications_.empty()) {
+ base::MessageLoop::current()->PostTask(
FROM_HERE, base::MessageLoop::QuitClosure());
+ }
}
}