From: Jakub Izydorczyk Date: Thu, 31 Jul 2014 11:35:08 +0000 (+0200) Subject: WebSetting plugin implementation X-Git-Tag: accepted/tizen/common/20140924.140331~12^2 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=15da3b3aa4e3f25c4eebbad24def410250d25aa2;p=platform%2Fframework%2Fweb%2Ftizen-extensions-crosswalk.git WebSetting plugin implementation The Web setting extension defines APIs that manages the setting states of the Web view in Web application. Properties of the Web view that can be managed via Web setting API: - Delete all cookies saved for the web view in the Web application - Set a custom user agent string of the web view in Web application This commit includes implementation of API for Web application to set custom user agent and remove all application cookies. Result of running tct-tizen-websetting tests: 22/22 pass. BUG=XWALK-1063 --- diff --git a/tizen-wrt.gyp b/tizen-wrt.gyp index 03f2ed4..87bdc1a 100644 --- a/tizen-wrt.gyp +++ b/tizen-wrt.gyp @@ -21,6 +21,7 @@ 'time/time.gyp:*', 'tizen/tizen.gyp:*', 'utils/utils.gyp:*', + 'web_setting/web_setting.gyp:*', ], 'conditions': [ [ 'tizen == 1', { diff --git a/web_setting/web_setting.cc b/web_setting/web_setting.cc new file mode 100644 index 0000000..41df642 --- /dev/null +++ b/web_setting/web_setting.cc @@ -0,0 +1,100 @@ +// Copyright (c) 2014 Samsung Electronics Co., Ltd. All Rights Reserved +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "web_setting/web_setting.h" + +#include +#include +#include + +#include "web_setting/web_setting_extension_utils.h" + +namespace { + +const char kRuntimeServiceName[] = "org.crosswalkproject.Runtime1"; +const char kRuntimeRunningManagerPath[] = "/running1"; +const char kRuntimeRunningAppInterface[] = + "org.crosswalkproject.Running.Application1"; + +// The runtime process exports object for each running app on the session bus. +GDBusProxy* CreateRunningAppProxy(const std::string& app_id) { + GError* error = NULL; + GDBusConnection* connection = + g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, &error); + if (!connection) { + std::cerr << "Couldn't get the session bus connection: " + << error->message << std::endl; + g_error_free(error); + return NULL; + } + + std::string path = + std::string(kRuntimeRunningManagerPath) + "/" + app_id; + // Every application id contains '.' character and since object path + // is created from application id it also contains '.' character. + // The d-bus proxy doesn't accept '.' character in object path + // And that is why the substantiation is needed here. + std::replace(path.begin(), path.end(), '.', '_'); + GDBusProxy* proxy = g_dbus_proxy_new_sync( + connection, G_DBUS_PROXY_FLAGS_NONE, NULL, kRuntimeServiceName, + path.c_str(), kRuntimeRunningAppInterface, NULL, &error); + if (!proxy) { + std::cerr << "Couldn't create proxy for " << kRuntimeRunningAppInterface + << ": " << error->message << std::endl; + g_error_free(error); + return NULL; + } + + return proxy; +} + +} // namespace + +WebSetting::WebSetting(const std::string& app_id) + : app_id_(app_id), + running_app_proxy_(NULL) { +} + +WebSetting::~WebSetting() { + if (running_app_proxy_) + g_object_unref(running_app_proxy_); +} + +std::unique_ptr WebSetting::RemoveAllCookies() { + if (!running_app_proxy_) { + if (!(running_app_proxy_ = CreateRunningAppProxy(app_id_))) + return CreateResultMessage(WebApiAPIErrors::UNKNOWN_ERR); + } + GError* error = NULL; + GVariant* result = g_dbus_proxy_call_sync( + running_app_proxy_, "RemoveAllCookies", NULL, + G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); + if (!result) { + std::cerr << "Fail to call 'RemoveuserAgentAllCookies':" + << error->message << std::endl; + g_error_free(error); + return CreateResultMessage(WebApiAPIErrors::UNKNOWN_ERR); + } + return CreateResultMessage(); +} + +std::unique_ptr WebSetting::SetUserAgentString( + const std::string& user_agent) { + if (!running_app_proxy_) { + if (!(running_app_proxy_ = CreateRunningAppProxy(app_id_))) + return CreateResultMessage(WebApiAPIErrors::UNKNOWN_ERR); + } + GError* error = NULL; + GVariant* result = g_dbus_proxy_call_sync( + running_app_proxy_, "SetUserAgentString", + g_variant_new("(s)", user_agent.c_str()), + G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); + if (!result) { + std::cerr << "Fail to call 'SetUserAgentString':" + << error->message << std::endl; + g_error_free(error); + return CreateResultMessage(WebApiAPIErrors::UNKNOWN_ERR); + } + return CreateResultMessage(); +} diff --git a/web_setting/web_setting.gyp b/web_setting/web_setting.gyp new file mode 100644 index 0000000..fe4bc14 --- /dev/null +++ b/web_setting/web_setting.gyp @@ -0,0 +1,29 @@ +{ + 'includes':[ + '../common/common.gypi', + ], + 'targets': [ + { + 'target_name': 'tizen_websetting', + 'type': 'loadable_module', + 'sources': [ + 'web_setting.cc', + 'web_setting.h', + 'web_setting_api.js', + 'web_setting_extension.cc', + 'web_setting_extension.h', + 'web_setting_extension_utils.h', + 'web_setting_instance.cc', + 'web_setting_instance.h', + ], + 'includes': [ + '../common/pkg-config.gypi', + ], + 'variables': { + 'packages': [ + 'glib-2.0', + ] + }, + }, + ], +} diff --git a/web_setting/web_setting.h b/web_setting/web_setting.h new file mode 100644 index 0000000..bcaddd8 --- /dev/null +++ b/web_setting/web_setting.h @@ -0,0 +1,27 @@ +// Copyright (c) 2014 Samsung Electronics Co., Ltd. All Rights Reserved +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef WEB_SETTING_WEB_SETTING_H_ +#define WEB_SETTING_WEB_SETTING_H_ + +#include +#include +#include + +#include "common/picojson.h" + +class WebSetting { + public: + explicit WebSetting(const std::string& app_id); + ~WebSetting(); + + std::unique_ptr RemoveAllCookies(); + std::unique_ptr SetUserAgentString(const std::string& user_agent); + + private: + std::string app_id_; + GDBusProxy* running_app_proxy_; +}; + +#endif // WEB_SETTING_WEB_SETTING_H_ diff --git a/web_setting/web_setting_api.js b/web_setting/web_setting_api.js new file mode 100644 index 0000000..cdcba26 --- /dev/null +++ b/web_setting/web_setting_api.js @@ -0,0 +1,96 @@ +var asyncCallbacks = { + _next_id: 0, + _callbacks: {}, + key: '_callback', + + // Return a callback ID number which will be contained by the native message. + setup: function(callback) { + var id = ++this._next_id; + this._callbacks[id] = callback; + return id; + }, + + dispatch: function(m) { + var id = m[this.key]; + var callback = this._callbacks[id]; + callback.call(null, m); + delete this._callbacks[id]; + } +}; + +extension.setMessageListener(function(msg) { + var m = JSON.parse(msg); + if (typeof m[asyncCallbacks.key] === 'number') { + asyncCallbacks.dispatch(m); + } else { + if (m[asyncCallbacks.key] === appInfoEventCallbacks.key) + appInfoEventCallbacks.dispatch(m); + else + console.error('unexpected message received' + msg); + } +}); + +// Post async message to extension with callbackId saved. The extension will return +// a message with the same callbackId to the callback set in setMessageListener. +function postMessage(msg, callbackId) { + msg[asyncCallbacks.key] = callbackId; + extension.postMessage(JSON.stringify(msg)); +} + +function defineReadOnlyProperty(object, key, value) { + Object.defineProperty(object, key, { + enumerable: true, + writable: false, + value: value + }); +} + +exports.setUserAgentString = function(userAgent, + successCallback, errorCallback) { + if (arguments.length > 0 && typeof userAgent !== 'string' || + arguments.length > 1 && (successCallback !== null && + typeof successCallback !== 'function') || arguments.length > 2 && + (errorCallback !== null && typeof errorCallback !== 'function')) { + throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR); + } + + var callbackId = asyncCallbacks.setup(function(result) { + if (result.error !== null) { + if (!errorCallback) { + return; + } + return errorCallback(new tizen.WebAPIError(result.error)); + } + if (successCallback !== null) { + return successCallback(result.data); + } + return; + }); + + var msg = { cmd: 'SetUserAgentString', userAgentStr: userAgent }; + postMessage(msg, callbackId); +}; + +exports.removeAllCookies = function(successCallback, errorCallback) { + if (arguments.length > 0 && (successCallback !== null && + typeof successCallback !== 'function') || arguments.length > 1 && + (errorCallback !== null && typeof errorCallback !== 'function')) { + throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR); + } + + var callbackId = asyncCallbacks.setup(function(result) { + if (result.error !== null) { + if (!errorCallback) { + return; + } + return errorCallback(new tizen.WebAPIError(result.error)); + } + if (successCallback !== null) { + return successCallback(result.data); + } + return; + }); + + var msg = { cmd: 'RemoveAllCookies' }; + postMessage(msg, callbackId); +}; diff --git a/web_setting/web_setting_extension.cc b/web_setting/web_setting_extension.cc new file mode 100644 index 0000000..108c769 --- /dev/null +++ b/web_setting/web_setting_extension.cc @@ -0,0 +1,34 @@ +// Copyright (c) 2014 Samsung Electronics Co., Ltd. All Rights Reserved +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "web_setting/web_setting_extension.h" + +#include + +#include "web_setting/web_setting.h" +#include "web_setting/web_setting_instance.h" + +extern const char kSource_web_setting_api[]; + +common::Extension* CreateExtension() { + std::string env_app_id = common::Extension::GetRuntimeVariable("app_id", 64); + std::string app_id = env_app_id.substr(1, env_app_id.rfind('"') - 1); + if (app_id.empty()) { + std::cerr << "Got invalid application ID." << std::endl; + return nullptr; + } + return new WebSettingExtension(app_id); +} + +WebSettingExtension::WebSettingExtension(const std::string& app_id) { + current_app_.reset(new WebSetting(app_id)); + SetExtensionName("tizen.websetting"); + SetJavaScriptAPI(kSource_web_setting_api); +} + +WebSettingExtension::~WebSettingExtension() {} + +common::Instance* WebSettingExtension::CreateInstance() { + return new WebSettingInstance(this); +} diff --git a/web_setting/web_setting_extension.h b/web_setting/web_setting_extension.h new file mode 100644 index 0000000..f7e3aaf --- /dev/null +++ b/web_setting/web_setting_extension.h @@ -0,0 +1,25 @@ +// Copyright (c) 2014 Samsung Electronics Co., Ltd. All Rights Reserved +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef WEB_SETTING_WEB_SETTING_EXTENSION_H_ +#define WEB_SETTING_WEB_SETTING_EXTENSION_H_ + +#include +#include +#include "common/extension.h" +#include "web_setting/web_setting.h" + +class WebSettingExtension : public common::Extension { + public: + explicit WebSettingExtension(const std::string& app_id); + virtual ~WebSettingExtension(); + + WebSetting* current_app() { return current_app_.get(); } + private: + virtual common::Instance* CreateInstance(); + + std::unique_ptr current_app_; +}; + +#endif // WEB_SETTING_WEB_SETTING_EXTENSION_H_ diff --git a/web_setting/web_setting_extension_utils.h b/web_setting/web_setting_extension_utils.h new file mode 100644 index 0000000..03a92a0 --- /dev/null +++ b/web_setting/web_setting_extension_utils.h @@ -0,0 +1,47 @@ +// Copyright (c) 2014 Samsung Electronics Co., Ltd. All Rights Reserved +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef WEB_SETTING_WEB_SETTING_EXTENSION_UTILS_H_ +#define WEB_SETTING_WEB_SETTING_EXTENSION_UTILS_H_ + +#include + +#include "common/picojson.h" +#include "tizen/tizen.h" + +std::unique_ptr CreateResultMessage() { + picojson::object obj; + obj["error"] = picojson::value(); + return std::unique_ptr(new picojson::value(obj)); +} + +std::unique_ptr CreateResultMessage( + WebApiAPIErrors error) { + picojson::object obj; + obj["error"] = picojson::value(static_cast(error)); + return std::unique_ptr(new picojson::value(obj)); +} + +std::unique_ptr CreateResultMessage( + const picojson::object& data) { + picojson::object obj; + obj["data"] = picojson::value(data); + return std::unique_ptr(new picojson::value(obj)); +} + +std::unique_ptr CreateResultMessage( + const picojson::array& data) { + picojson::object obj; + obj["data"] = picojson::value(data); + return std::unique_ptr(new picojson::value(obj)); +} + +std::unique_ptr CreateResultMessage( + const picojson::value& data) { + picojson::object obj; + obj["data"] = data; + return std::unique_ptr(new picojson::value(obj)); +} + +#endif // WEB_SETTING_WEB_SETTING_EXTENSION_UTILS_H_ diff --git a/web_setting/web_setting_instance.cc b/web_setting/web_setting_instance.cc new file mode 100644 index 0000000..08f3070 --- /dev/null +++ b/web_setting/web_setting_instance.cc @@ -0,0 +1,72 @@ +// Copyright (c) 2014 Samsung Electronics Co., Ltd. All Rights Reserved +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "web_setting/web_setting_instance.h" + +#include +#include +#include "common/picojson.h" + +namespace { + +const char kJSCallbackKey[] = "_callback"; + +double GetJSCallbackId(const picojson::value& msg) { + assert(msg.contains(kJSCallbackKey)); + const picojson::value& id_value = msg.get(kJSCallbackKey); + return id_value.get(); +} + +void SetJSCallbackId(picojson::value& msg, double id) { + assert(msg.is()); + msg.get()[kJSCallbackKey] = picojson::value(id); +} + +} // namespace + +WebSettingInstance::WebSettingInstance(WebSettingExtension* extension) + : extension_(extension) { +} + +WebSettingInstance::~WebSettingInstance() {} + +void WebSettingInstance::HandleMessage(const char* message) { + picojson::value v; + + std::string err; + picojson::parse(v, message, message + strlen(message), &err); + if (!err.empty()) { + std::cerr << "Error during parsing message: " << err.c_str(); + return; + } + + std::string cmd = v.get("cmd").to_str(); + if (cmd == "SetUserAgentString") + HandleSetUserAgentString(v); + else if (cmd == "RemoveAllCookies") + HandleRemoveAllCookies(v); + else + std::cerr << "ASSERT NOT REACHED. \n"; +} + +void WebSettingInstance::HandleSetUserAgentString(const picojson::value &msg) { + std::string userAgent = msg.get("userAgentStr").to_str(); + picojson::value *result = extension_->current_app()-> + SetUserAgentString(userAgent).release(); + ReturnMessageAsync(GetJSCallbackId(msg), *result); + delete result; +} + +void WebSettingInstance::HandleRemoveAllCookies(const picojson::value& msg) { + picojson::value *result = extension_->current_app()-> + RemoveAllCookies().release(); + ReturnMessageAsync(GetJSCallbackId(msg), *result); + delete result; +} + +void WebSettingInstance::ReturnMessageAsync(double callback_id, + picojson::value& value) { + SetJSCallbackId(value, callback_id); + PostMessage(value.serialize().c_str()); +} diff --git a/web_setting/web_setting_instance.h b/web_setting/web_setting_instance.h new file mode 100644 index 0000000..e887541 --- /dev/null +++ b/web_setting/web_setting_instance.h @@ -0,0 +1,30 @@ +// Copyright (c) 2014 Samsung Electronics Co., Ltd. All Rights Reserved +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef WEB_SETTING_WEB_SETTING_INSTANCE_H_ +#define WEB_SETTING_WEB_SETTING_INSTANCE_H_ + +#include "common/extension.h" +#include "common/picojson.h" +#include "tizen/tizen.h" + +#include "web_setting/web_setting_extension.h" + +class WebSettingInstance : public common::Instance { + public: + explicit WebSettingInstance(WebSettingExtension* extension); + virtual ~WebSettingInstance(); + + private: + void HandleMessage(const char* message); + + void HandleSetUserAgentString(const picojson::value& msg); + void HandleRemoveAllCookies(const picojson::value& msg); + + void ReturnMessageAsync(double callback_id, picojson::value& value); + + WebSettingExtension* extension_; +}; + +#endif // WEB_SETTING_WEB_SETTING_INSTANCE_H_