#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/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_file_util.h"
#include "xwalk/application/common/application_manifest_constants.h"
-#include "xwalk/application/common/event_names.h"
+#include "xwalk/application/common/application_file_util.h"
#include "xwalk/application/common/id_util.h"
#include "xwalk/runtime/browser/runtime_context.h"
#include "xwalk/runtime/browser/runtime.h"
-#include "xwalk/runtime/browser/xwalk_runner.h"
+#include "xwalk/runtime/common/xwalk_paths.h"
-#if defined(OS_TIZEN_MOBILE)
-#include "xwalk/application/browser/installer/tizen/package_installer.h"
+#if defined(OS_TIZEN)
+#include "xwalk/application/browser/application_service_tizen.h"
#endif
-using xwalk::RuntimeContext;
-
-namespace {
-
-void CloseMessageLoop() {
- // FIXME: Quit message loop here at present. This should go away once
- // we have Application in place.
- base::MessageLoop::current()->QuitWhenIdle();
-}
-
-void WaitForEventAndClose(
- const std::string& app_id, const std::string& event_name,
- xwalk::application::ApplicationEventManager* event_manager) {
- class CloseOnEventArrived : public xwalk::application::EventObserver {
- public:
- static CloseOnEventArrived* Create(const std::string& event_name,
- xwalk::application::ApplicationEventManager* event_manager) {
- return new CloseOnEventArrived(event_name, event_manager);
- }
-
- virtual void Observe(
- const std::string& app_id,
- scoped_refptr<xwalk::application::Event> event) OVERRIDE {
- DCHECK(xwalk::application::kOnJavaScriptEventAck == event->name());
- std::string ack_event_name;
- event->args()->GetString(0, &ack_event_name);
- if (ack_event_name != event_name_)
- return;
- CloseMessageLoop();
- delete this;
- }
-
- private:
- CloseOnEventArrived(
- const std::string& event_name,
- xwalk::application::ApplicationEventManager* event_manager)
- : xwalk::application::EventObserver(event_manager),
- event_name_(event_name) {}
-
- std::string event_name_;
- };
-
- DCHECK(event_manager);
- CloseOnEventArrived* observer =
- CloseOnEventArrived::Create(event_name, event_manager);
- event_manager->AttachObserver(app_id,
- xwalk::application::kOnJavaScriptEventAck, observer);
-}
-
-void WaitForFinishLoad(
- scoped_refptr<xwalk::application::ApplicationData> application,
- xwalk::application::ApplicationEventManager* event_manager,
- content::WebContents* contents) {
- class CloseAfterLoadObserver : public content::WebContentsObserver {
- public:
- CloseAfterLoadObserver(
- scoped_refptr<xwalk::application::ApplicationData> application,
- xwalk::application::ApplicationEventManager* event_manager,
- content::WebContents* contents)
- : content::WebContentsObserver(contents),
- application_(application),
- event_manager_(event_manager) {
- DCHECK(application_);
- DCHECK(event_manager_);
- }
-
- virtual void DidFinishLoad(
- int64 frame_id,
- const GURL& validate_url,
- bool is_main_frame,
- content::RenderViewHost* render_view_host) OVERRIDE {
- if (!IsEventHandlerRegistered(xwalk::application::kOnInstalled)) {
- CloseMessageLoop();
- } else {
- scoped_ptr<base::ListValue> event_args(new base::ListValue);
- scoped_refptr<xwalk::application::Event> event =
- xwalk::application::Event::CreateEvent(
- xwalk::application::kOnInstalled, event_args.Pass());
- event_manager_->SendEvent(application_->ID(), event);
-
- WaitForEventAndClose(
- application_->ID(), event->name(), event_manager_);
- }
- delete this;
- }
-
- private:
- bool IsEventHandlerRegistered(const std::string& event_name) const {
- const std::set<std::string>& events = application_->GetEvents();
- return events.find(event_name) != events.end();
- }
-
- scoped_refptr<xwalk::application::ApplicationData> application_;
- xwalk::application::ApplicationEventManager* event_manager_;
- };
-
- // This object is self-destroyed when an event occurs.
- new CloseAfterLoadObserver(application, event_manager, contents);
-}
-
-#if defined(OS_TIZEN_MOBILE)
-bool InstallPackageOnTizen(xwalk::application::ApplicationService* service,
- xwalk::application::ApplicationStorage* storage,
- const std::string& app_id,
- const base::FilePath& data_dir) {
- // FIXME(cmarcelo): The Tizen-specific steps of installation in
- // service mode are not supported yet. Remove when this is fixed.
- if (xwalk::XWalkRunner::GetInstance()->is_running_as_service())
- return true;
-
- scoped_ptr<xwalk::application::PackageInstaller> installer =
- xwalk::application::PackageInstaller::Create(service, storage,
- app_id, data_dir);
- if (!installer || !installer->Install()) {
- LOG(ERROR) << "An error occurred during installation on Tizen.";
- return false;
- }
- return true;
-}
-
-bool UninstallPackageOnTizen(xwalk::application::ApplicationService* service,
- xwalk::application::ApplicationStorage* storage,
- const std::string& app_id,
- const base::FilePath& data_dir) {
- // FIXME(cmarcelo): The Tizen-specific steps of installation in
- // service mode are not supported yet. Remove when this is fixed.
- if (xwalk::XWalkRunner::GetInstance()->is_running_as_service())
- return true;
-
- scoped_ptr<xwalk::application::PackageInstaller> installer =
- xwalk::application::PackageInstaller::Create(service, storage,
- app_id, data_dir);
- if (!installer || !installer->Uninstall()) {
- LOG(ERROR) << "An error occurred during uninstallation on Tizen.";
- return false;
- }
- return true;
-}
-#endif // OS_TIZEN_MOBILE
-
-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;
-}
-
-} // namespace
-
namespace xwalk {
+
namespace application {
-const base::FilePath::CharType kApplicationsDir[] =
- FILE_PATH_LITERAL("applications");
+ApplicationService::ApplicationService(RuntimeContext* runtime_context)
+ : runtime_context_(runtime_context) {
+}
-ApplicationService::ApplicationService(RuntimeContext* runtime_context,
- ApplicationStorage* app_storage,
- ApplicationEventManager* event_manager)
- : runtime_context_(runtime_context),
- application_storage_(app_storage),
- event_manager_(event_manager) {
- AddObserver(event_manager);
+scoped_ptr<ApplicationService> ApplicationService::Create(
+ RuntimeContext* runtime_context) {
+#if defined(OS_TIZEN)
+ return make_scoped_ptr<ApplicationService>(
+ new ApplicationServiceTizen(runtime_context));
+#else
+ return make_scoped_ptr(new ApplicationService(runtime_context));
+#endif
}
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) &&
- !file_util::CreateDirectory(data_dir))
- return false;
-
- base::FilePath unpacked_dir;
- scoped_ptr<Package> package;
- if (!base::DirectoryExists(path)) {
- package = Package::Create(path);
- package->Extract(&unpacked_dir);
- } else {
- unpacked_dir = path;
- }
-
- std::string error;
- scoped_refptr<ApplicationData> application_data = LoadApplication(
- unpacked_dir, Manifest::COMMAND_LINE, &error);
- if (!application_data) {
- LOG(ERROR) << "Error during application installation: " << error;
- return false;
+Application* ApplicationService::Launch(
+ scoped_refptr<ApplicationData> application_data,
+ const Application::LaunchParams& launch_params) {
+ if (GetApplicationByID(application_data->ID()) != NULL) {
+ LOG(INFO) << "Application with id: " << application_data->ID()
+ << " is already running.";
+ // FIXME: we need to notify application that it had been attempted
+ // to invoke and let the application to define the further behavior.
+ return NULL;
}
- if (application_storage_->Contains(application_data->ID())) {
- *id = application_data->ID();
- LOG(INFO) << "Already installed: " << id;
- return false;
- }
+ Application* application = Application::Create(application_data,
+ runtime_context_).release();
+ ScopedVector<Application>::iterator app_iter =
+ applications_.insert(applications_.end(), application);
- 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 (!file_util::CreateDirectory(app_dir))
- return false;
- if (!CopyDirectoryContents(unpacked_dir, app_dir))
- return false;
- } else {
- if (!base::Move(unpacked_dir, app_dir))
- return false;
+ if (!application->Launch(launch_params)) {
+ applications_.erase(app_iter);
+ return NULL;
}
- 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_MOBILE)
- if (!InstallPackageOnTizen(this, application_storage_,
- application_data->ID(),
- runtime_context_->GetPath()))
- 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();
+ application->set_observer(this);
FOR_EACH_OBSERVER(Observer, observers_,
- OnApplicationInstalled(application_data->ID()));
-
- // We need to run main document after installation in order to
- // register system events.
- if (application_data->HasMainDocument()) {
- if (Application* application = Launch(application_data->ID())) {
- WaitForFinishLoad(application->data(), event_manager_,
- application->GetMainDocumentRuntime()->web_contents());
- }
- }
+ DidLaunchApplication(application));
- return true;
+ return application;
}
-bool ApplicationService::Uninstall(const std::string& id) {
-#if defined(OS_TIZEN_MOBILE)
- if (!UninstallPackageOnTizen(this, application_storage_, id,
- runtime_context_->GetPath()))
- return false;
-#endif
-
- if (!application_storage_->RemoveApplication(id)) {
- LOG(ERROR) << "Cannot uninstall application with id " << id
- << "; application is not installed.";
- return false;
+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;
}
- 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.";
- return false;
- }
+ base::FilePath app_path = path.DirName();
+ LOG(ERROR) << "Loading app from " << app_path.MaybeAsASCII();
- FOR_EACH_OBSERVER(Observer, observers_, OnApplicationUninstalled(id));
+ 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 true;
+ return Launch(application_data, params);
}
-Application* ApplicationService::Launch(const std::string& id) {
- scoped_refptr<ApplicationData> application_data =
- application_storage_->GetApplicationData(id);
- if (!application_data) {
- LOG(ERROR) << "Application with id " << id << " is not installed.";
+Application* ApplicationService::LaunchFromPackagePath(
+ const base::FilePath& path, const Application::LaunchParams& params) {
+ scoped_ptr<Package> package = Package::Create(path);
+ if (!package || !package->IsValid()) {
+ LOG(ERROR) << "Failed to obtain valid package from "
+ << path.AsUTF8Unsafe();
return NULL;
}
- return Launch(application_data);
-}
+ base::FilePath tmp_dir, target_dir;
+ if (!GetTempDir(&tmp_dir)) {
+ LOG(ERROR) << "Failed to obtain system temp directory.";
+ return NULL;
+ }
-Application* ApplicationService::Launch(const base::FilePath& path) {
- if (!base::DirectoryExists(path))
+#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(path, Manifest::COMMAND_LINE, &error);
-
- if (!application_data) {
- LOG(ERROR) << "Error occurred while trying to launch application: "
+ 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;
}
- return Launch(application_data);
+ return Launch(application_data, params);
}
-Application* ApplicationService::Launch(const GURL& url) {
- namespace keys = xwalk::application_manifest_keys;
-
+// 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();
- DCHECK(!url_spec.empty());
+ if (url_spec.empty()) {
+ LOG(ERROR) << "Failed to launch application from the URL: " << url;
+ return NULL;
+ }
+
const std::string& app_id = GenerateId(url_spec);
- // FIXME: we need to handle hash collisions.
- DCHECK(!application_storage_->GetApplicationData(app_id));
- base::DictionaryValue manifest;
+ scoped_ptr<base::DictionaryValue> settings(new base::DictionaryValue());
// FIXME: define permissions!
- manifest.SetString(keys::kLaunchWebURLKey, url_spec);
- manifest.SetString(keys::kNameKey, "XWalk Browser");
- manifest.SetString(keys::kVersionKey, "0");
- manifest.SetInteger(keys::kManifestVersionKey, 1);
+ 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> application_data = ApplicationData::Create(
- base::FilePath(), Manifest::COMMAND_LINE, manifest, app_id, &error);
- if (!application_data) {
- LOG(ERROR) << "Error occurred while trying to launch application: "
- << error;
- return NULL;
- }
+ scoped_refptr<ApplicationData> app_data =
+ ApplicationData::Create(base::FilePath(), app_id,
+ ApplicationData::EXTERNAL_URL, manifest.Pass(), &error);
+ DCHECK(app_data.get());
- return Launch(application_data, Application::LaunchWebURLKey);
+ 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 (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(
+ runtime_context_,
+ make_scoped_ptr(new base::hash_set<base::FilePath>()),
+ base::Bind(&base::DoNothing));
+ }
+
+#if !defined(SHARED_PROCESS_MODE)
if (applications_.empty()) {
base::MessageLoop::current()->PostTask(
FROM_HERE, base::MessageLoop::QuitClosure());
}
+#endif
}
-Application* ApplicationService::Launch(
- scoped_refptr<ApplicationData> application_data,
- Application::LaunchEntryPoints entry_points) {
- if (GetApplicationByID(application_data->ID()) != NULL) {
- LOG(INFO) << "Application with id: " << application_data->ID()
- << " is already running.";
- // FIXME: we need to notify application that it had been attempted
- // to invoke and let the application to define the further behavior.
- return NULL;
+void ApplicationService::CheckAPIAccessControl(const std::string& app_id,
+ const std::string& extension_name,
+ const std::string& api_name, const PermissionCallback& callback) {
+ Application* app = GetApplicationByID(app_id);
+ if (!app) {
+ LOG(ERROR) << "No running application found with ID: "
+ << app_id;
+ callback.Run(UNDEFINED_RUNTIME_PERM);
+ return;
}
-
- event_manager_->AddEventRouterForApp(application_data);
- Application* application(new Application(application_data,
- runtime_context_,
- this));
- application->set_entry_points(entry_points);
- ScopedVector<Application>::iterator app_iter =
- applications_.insert(applications_.end(), application);
-
- if (!application->Launch()) {
- event_manager_->RemoveEventRouterForApp(application_data);
- applications_.erase(app_iter);
- return NULL;
+ if (!app->UseExtension(extension_name)) {
+ LOG(ERROR) << "Can not find extension: "
+ << extension_name << " of Application with ID: "
+ << app_id;
+ callback.Run(UNDEFINED_RUNTIME_PERM);
+ return;
}
+ // Permission name should have been registered at extension initialization.
+ std::string permission_name =
+ app->GetRegisteredPermissionName(extension_name, api_name);
+ if (permission_name.empty()) {
+ LOG(ERROR) << "API: " << api_name << " of extension: "
+ << extension_name << " not registered!";
+ callback.Run(UNDEFINED_RUNTIME_PERM);
+ return;
+ }
+ // Okay, since we have the permission name, let's get down to the policies.
+ // First, find out whether the permission is stored for the current session.
+ StoredPermission perm = app->GetPermission(
+ SESSION_PERMISSION, permission_name);
+ if (perm != UNDEFINED_STORED_PERM) {
+ // "PROMPT" should not be in the session storage.
+ DCHECK(perm != PROMPT);
+ if (perm == ALLOW) {
+ callback.Run(ALLOW_SESSION);
+ return;
+ }
+ if (perm == DENY) {
+ callback.Run(DENY_SESSION);
+ return;
+ }
+ NOTREACHED();
+ }
+ // Then, query the persistent policy storage.
+ perm = app->GetPermission(PERSISTENT_PERMISSION, permission_name);
+ // Permission not found in persistent permission table, normally this should
+ // not happen because all the permission needed by the application should be
+ // contained in its manifest, so it also means that the application is asking
+ // for something wasn't allowed.
+ if (perm == UNDEFINED_STORED_PERM) {
+ callback.Run(UNDEFINED_RUNTIME_PERM);
+ return;
+ }
+ if (perm == PROMPT) {
+ // TODO(Bai): We needed to pop-up a dialog asking user to chose one from
+ // either allow/deny for session/one shot/forever. Then, we need to update
+ // the session and persistent policy accordingly.
+ callback.Run(UNDEFINED_RUNTIME_PERM);
+ return;
+ }
+ if (perm == ALLOW) {
+ callback.Run(ALLOW_ALWAYS);
+ return;
+ }
+ if (perm == DENY) {
+ callback.Run(DENY_ALWAYS);
+ return;
+ }
+ NOTREACHED();
+}
- FOR_EACH_OBSERVER(Observer, observers_,
- DidLaunchApplication(application));
-
- return application;
+bool ApplicationService::RegisterPermissions(const std::string& app_id,
+ const std::string& extension_name,
+ const std::string& perm_table) {
+ Application* app = GetApplicationByID(app_id);
+ if (!app) {
+ LOG(ERROR) << "No running application found with ID: " << app_id;
+ return false;
+ }
+ if (!app->UseExtension(extension_name)) {
+ LOG(ERROR) << "Can not find extension: "
+ << extension_name << " of Application with ID: "
+ << app_id;
+ return false;
+ }
+ return app->RegisterPermissions(extension_name, perm_table);
}
} // namespace application