From bc63334410848dd9cd666037842e2bea999620fa Mon Sep 17 00:00:00 2001 From: WonYoung Choi Date: Thu, 14 May 2015 15:21:37 +0900 Subject: [PATCH] Add functions to support sending messages to runtime from js. - bool extension.sendRuntimeMessage(type, value) - string extension.sendRuntimeSyncMessage(type, value) - bool extension.sendRuntimeAsyncMessage(type, value, callback) Change-Id: I50bfdb9f4b5875aad0fd40b850e3295c8dfc7c1e --- src/bundle/CMakeLists.txt | 1 + src/bundle/extension_module.cc | 109 +++++++++++++++++++++++++++++++ src/bundle/extension_module.h | 6 ++ src/bundle/injected_bundle.cc | 14 +++- src/bundle/runtime_ipc_client.cc | 138 +++++++++++++++++++++++++++++++++++++++ src/bundle/runtime_ipc_client.h | 73 +++++++++++++++++++++ src/runtime/web_application.cc | 72 +++++++++++++++++--- src/runtime/web_application.h | 2 +- src/runtime/web_view.cc | 4 +- src/runtime/web_view.h | 2 +- src/runtime/web_view_impl.cc | 4 +- src/runtime/web_view_impl.h | 2 +- 12 files changed, 407 insertions(+), 20 deletions(-) create mode 100644 src/bundle/runtime_ipc_client.cc create mode 100644 src/bundle/runtime_ipc_client.h diff --git a/src/bundle/CMakeLists.txt b/src/bundle/CMakeLists.txt index 4c636a8..484360a 100644 --- a/src/bundle/CMakeLists.txt +++ b/src/bundle/CMakeLists.txt @@ -30,6 +30,7 @@ SET(TARGET_INJECTED_BUNDLE_SRCS ${BASE_SRCDIR}/bundle/extension_client.cc ${BASE_SRCDIR}/bundle/extension_module.cc ${BASE_SRCDIR}/bundle/module_system.cc + ${BASE_SRCDIR}/bundle/runtime_ipc_client.cc ) INCLUDE_DIRECTORIES(${TARGET_INJECTED_BUNDLE_INCS}) diff --git a/src/bundle/extension_module.cc b/src/bundle/extension_module.cc index ae50939..f4020fb 100644 --- a/src/bundle/extension_module.cc +++ b/src/bundle/extension_module.cc @@ -15,6 +15,7 @@ #include "common/logger.h" #include "bundle/extension_client.h" #include "bundle/module_system.h" +#include "bundle/runtime_ipc_client.h" // The arraysize(arr) macro returns the # of elements in an array arr. // The expression is a compile-time constant, and therefore can be @@ -74,6 +75,18 @@ ExtensionModule::ExtensionModule(ExtensionClient* client, v8::String::NewFromUtf8(isolate, "setMessageListener"), v8::FunctionTemplate::New( isolate, SetMessageListenerCallback, function_data)); + object_template->Set( + v8::String::NewFromUtf8(isolate, "sendRuntimeMessage"), + v8::FunctionTemplate::New( + isolate, SendRuntimeMessageCallback, function_data)); + object_template->Set( + v8::String::NewFromUtf8(isolate, "sendRuntimeSyncMessage"), + v8::FunctionTemplate::New( + isolate, SendRuntimeSyncMessageCallback, function_data)); + object_template->Set( + v8::String::NewFromUtf8(isolate, "sendRuntimeAsyncMessage"), + v8::FunctionTemplate::New( + isolate, SendRuntimeAsyncMessageCallback, function_data)); function_data_.Reset(isolate, function_data); object_template_.Reset(isolate, object_template); @@ -375,6 +388,102 @@ void ExtensionModule::SetMessageListenerCallback( } // static +void ExtensionModule::SendRuntimeMessageCallback( + const v8::FunctionCallbackInfo& info) { + v8::ReturnValue result(info.GetReturnValue()); + ExtensionModule* module = GetExtensionModule(info); + if (!module || info.Length() < 1) { + result.Set(false); + return; + } + + v8::String::Utf8Value type(info[0]->ToString()); + std::string data_str; + if (info.Length() > 1) { + v8::String::Utf8Value data(info[1]->ToString()); + data_str = std::string(*data); + } + + RuntimeIPCClient* rc = RuntimeIPCClient::GetInstance(); + rc->SendMessage(std::string(*type), data_str); + + result.Set(true); +} + +// static +void ExtensionModule::SendRuntimeSyncMessageCallback( + const v8::FunctionCallbackInfo& info) { + v8::Isolate* isolate = info.GetIsolate(); + + v8::ReturnValue result(info.GetReturnValue()); + ExtensionModule* module = GetExtensionModule(info); + if (!module || info.Length() < 1) { + result.SetUndefined(); + return; + } + + v8::String::Utf8Value type(info[0]->ToString()); + std::string data_str; + if (info.Length() > 1) { + v8::String::Utf8Value data(info[1]->ToString()); + data_str = std::string(*data); + } + + RuntimeIPCClient* rc = RuntimeIPCClient::GetInstance(); + std::string reply = rc->SendSyncMessage(std::string(*type), data_str); + + result.Set(v8::String::NewFromUtf8(isolate, reply.c_str())); +} + +// static +void ExtensionModule::SendRuntimeAsyncMessageCallback( + const v8::FunctionCallbackInfo& info) { + v8::Isolate* isolate = info.GetIsolate(); + v8::HandleScope handle_scope(isolate); + + v8::ReturnValue result(info.GetReturnValue()); + ExtensionModule* module = GetExtensionModule(info); + if (!module || info.Length() < 1) { + result.Set(false); + return; + } + + // type + v8::String::Utf8Value type(info[0]->ToString()); + + // value + std::string value_str; + if (info.Length() > 1) { + v8::String::Utf8Value value(info[1]->ToString()); + value_str = std::string(*value); + } + + // callback + RuntimeIPCClient::JSCallback* js_callback = NULL; + if (info.Length() > 2) { + if (info[2]->IsFunction()) { + v8::Handle func = info[2].As(); + js_callback = new RuntimeIPCClient::JSCallback(isolate, func); + } + } + + auto callback = [](const std::string& /*type*/, + const std::string& value, + RuntimeIPCClient::JSCallback* js_callback) -> void { + if (js_callback) { + v8::Handle args[] = { + v8::String::NewFromUtf8(js_callback->isolate(), value.c_str()) }; + js_callback->Call(args); + } + }; + + RuntimeIPCClient* rc = RuntimeIPCClient::GetInstance(); + rc->SendAsyncMessage(std::string(*type), value_str, callback, js_callback); + + result.Set(true); +} + +// static ExtensionModule* ExtensionModule::GetExtensionModule( const v8::FunctionCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); diff --git a/src/bundle/extension_module.h b/src/bundle/extension_module.h index dca58de..6a4eb07 100644 --- a/src/bundle/extension_module.h +++ b/src/bundle/extension_module.h @@ -49,6 +49,12 @@ class ExtensionModule : public ExtensionClient::InstanceHandler { const v8::FunctionCallbackInfo& info); static void SetMessageListenerCallback( const v8::FunctionCallbackInfo& info); + static void SendRuntimeMessageCallback( + const v8::FunctionCallbackInfo& info); + static void SendRuntimeSyncMessageCallback( + const v8::FunctionCallbackInfo& info); + static void SendRuntimeAsyncMessageCallback( + const v8::FunctionCallbackInfo& info); static ExtensionModule* GetExtensionModule( const v8::FunctionCallbackInfo& info); diff --git a/src/bundle/injected_bundle.cc b/src/bundle/injected_bundle.cc index 60e2767..d7fdcab 100755 --- a/src/bundle/injected_bundle.cc +++ b/src/bundle/injected_bundle.cc @@ -10,10 +10,11 @@ #include "common/logger.h" #include "common/string_utils.h" -#include "bundle/extension_renderer_controller.h" #include "common/application_data.h" #include "common/resource_manager.h" #include "common/locale_manager.h" +#include "bundle/runtime_ipc_client.h" +#include "bundle/extension_renderer_controller.h" namespace wrt { class BundleGlobalData { @@ -53,7 +54,7 @@ extern "C" void DynamicSetWidgetInfo(const char* tizen_id) { extern "C" void DynamicPluginStartSession(const char* tizen_id, v8::Handle context, - int /*routing_handle*/, + int routing_handle, double /*scale*/, const char* uuid, const char* /*theme*/, @@ -64,6 +65,11 @@ extern "C" void DynamicPluginStartSession(const char* tizen_id, return; } + // Initialize RuntimeIPCClient + wrt::RuntimeIPCClient* rc = wrt::RuntimeIPCClient::GetInstance(); + rc->set_routing_id(routing_handle); + + // Initialize ExtensionRendererController wrt::ExtensionRendererController& controller = wrt::ExtensionRendererController::GetInstance(); controller.InitializeExtensions(uuid); @@ -95,8 +101,10 @@ extern "C" void DynamicDatabaseAttach(const char* tizen_id) { LOGGER(DEBUG) << "InjectedBundle::DynamicDatabaseAttach !!" << tizen_id; } -extern "C" void DynamicOnIPCMessage(const Ewk_IPC_Wrt_Message_Data& /*data*/) { +extern "C" void DynamicOnIPCMessage(const Ewk_IPC_Wrt_Message_Data& data) { LOGGER(DEBUG) << "InjectedBundle::DynamicOnIPCMessage !!"; + wrt::RuntimeIPCClient* rc = wrt::RuntimeIPCClient::GetInstance(); + rc->HandleMessageFromRuntime(&data); } extern "C" void DynamicPreloading() { diff --git a/src/bundle/runtime_ipc_client.cc b/src/bundle/runtime_ipc_client.cc new file mode 100644 index 0000000..08995ce --- /dev/null +++ b/src/bundle/runtime_ipc_client.cc @@ -0,0 +1,138 @@ +// Copyright 2015 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 "bundle/runtime_ipc_client.h" + +#include "common/logger.h" +#include "common/string_utils.h" + +namespace wrt { + +RuntimeIPCClient::JSCallback::JSCallback(v8::Isolate* isolate, + v8::Handle callback) + : isolate_(isolate) { + callback_.Reset(isolate, callback); +} + +RuntimeIPCClient::JSCallback::~JSCallback() { + callback_.Reset(); +} + +void RuntimeIPCClient::JSCallback::Call(v8::Handle args[]) { + if (!callback_.IsEmpty()) { + v8::HandleScope handle_scope(isolate_); + v8::Handle func = + v8::Local::New(isolate_, callback_); + func->Call(func, 1, args); + } +} + +// static +RuntimeIPCClient* RuntimeIPCClient::GetInstance() { + static RuntimeIPCClient self; + return &self; +} + +RuntimeIPCClient::RuntimeIPCClient() : routing_id_(0) { +} + +void RuntimeIPCClient::SendMessage(const std::string& type, + const std::string& value) { + Ewk_IPC_Wrt_Message_Data* msg = ewk_ipc_wrt_message_data_new(); + ewk_ipc_wrt_message_data_id_set(msg, ""); + ewk_ipc_wrt_message_data_type_set(msg, type.c_str()); + ewk_ipc_wrt_message_data_value_set(msg, value.c_str()); + + if (routing_id_ > 0) { + if (!ewk_ipc_plugins_message_send(routing_id_, msg)) { + LOGGER(ERROR) << "Failed to send message to runtime using ewk_ipc."; + } + } + + ewk_ipc_wrt_message_data_del(msg); +} + +std::string RuntimeIPCClient::SendSyncMessage(const std::string& type, + const std::string& value) { + Ewk_IPC_Wrt_Message_Data* msg = ewk_ipc_wrt_message_data_new(); + ewk_ipc_wrt_message_data_type_set(msg, type.c_str()); + ewk_ipc_wrt_message_data_value_set(msg, value.c_str()); + + if (routing_id_ > 0) { + if (!ewk_ipc_plugins_sync_message_send(routing_id_, msg)) { + LOGGER(ERROR) << "Failed to send message to runtime using ewk_ipc."; + ewk_ipc_wrt_message_data_del(msg); + return std::string(); + } + } + + Eina_Stringshare* msg_value = ewk_ipc_wrt_message_data_value_get(msg); + + std::string result(msg_value); + eina_stringshare_del(msg_value); + ewk_ipc_wrt_message_data_del(msg); + + return result; +} + +void RuntimeIPCClient::SendAsyncMessage(const std::string& type, + const std::string& value, + ReplyCallback callback, + JSCallback* js_callback) { + std::string msg_id = utils::GenerateUUID(); + + Ewk_IPC_Wrt_Message_Data* msg = ewk_ipc_wrt_message_data_new(); + ewk_ipc_wrt_message_data_id_set(msg, msg_id.c_str()); + ewk_ipc_wrt_message_data_type_set(msg, type.c_str()); + ewk_ipc_wrt_message_data_value_set(msg, value.c_str()); + + if (routing_id_ > 0) { + if (!ewk_ipc_plugins_message_send(routing_id_, msg)) { + LOGGER(ERROR) << "Failed to send message to runtime using ewk_ipc."; + ewk_ipc_wrt_message_data_del(msg); + return; + } + } + + callbacks_[msg_id].callback = callback; + callbacks_[msg_id].js_callback = js_callback; + + ewk_ipc_wrt_message_data_del(msg); +} + +void RuntimeIPCClient::HandleMessageFromRuntime( + const Ewk_IPC_Wrt_Message_Data* msg) { + if (msg == NULL) { + LOGGER(ERROR) << "received message is NULL"; + return; + } + + Eina_Stringshare* msg_refid = ewk_ipc_wrt_message_data_reference_id_get(msg); + + if (msg_refid == NULL || !strcmp(msg_refid,"")) { + if (msg_refid) eina_stringshare_del(msg_refid); + LOGGER(ERROR) << "No reference id of received message."; + return; + } + + auto it = callbacks_.find(msg_refid); + if (it == callbacks_.end()) { + eina_stringshare_del(msg_refid); + LOGGER(ERROR) << "No registered callback with reference id : " << msg_refid; + return; + } + + Eina_Stringshare* msg_type = ewk_ipc_wrt_message_data_type_get(msg); + Eina_Stringshare* msg_value = ewk_ipc_wrt_message_data_value_get(msg); + + const AsyncData& async_data = it->second; + async_data.callback(msg_type, msg_value, async_data.js_callback); + callbacks_.erase(it); + + eina_stringshare_del(msg_refid); + eina_stringshare_del(msg_type); + eina_stringshare_del(msg_value); +} + +} // namespace wrt diff --git a/src/bundle/runtime_ipc_client.h b/src/bundle/runtime_ipc_client.h new file mode 100644 index 0000000..91bac59 --- /dev/null +++ b/src/bundle/runtime_ipc_client.h @@ -0,0 +1,73 @@ +// Copyright 2015 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 WRT_BUNDLE_RUNTIME_IPC_CLIENT_H_ +#define WRT_BUNDLE_RUNTIME_IPC_CLIENT_H_ + +#include +#include +#include +#include +#include + +namespace wrt { + +class RuntimeIPCClient { + public: + class JSCallback { + public: + explicit JSCallback(v8::Isolate* isolate, + v8::Handle callback); + ~JSCallback(); + + v8::Isolate* isolate() const { return isolate_; } + + void Call(v8::Handle args[]); + private: + v8::Isolate* isolate_; + v8::Persistent callback_; + }; + + typedef std::function ReplyCallback; + + static RuntimeIPCClient* GetInstance(); + + // Send message to BrowserProcess without reply + void SendMessage(const std::string& type, const std::string& value); + + // Send message to BrowserProcess synchronous with reply + std::string SendSyncMessage(const std::string& type, const std::string& value); + + // Send message to BrowserProcess asynchronous, + // reply message will be passed to callback function. + void SendAsyncMessage(const std::string& type, const std::string& value, + ReplyCallback callback, JSCallback* js_callback); + + void HandleMessageFromRuntime(const Ewk_IPC_Wrt_Message_Data* msg); + + int routing_id() const { return routing_id_; } + void set_routing_id(int routing_id) { routing_id_ = routing_id; } + + private: + class AsyncData { + public: + ~AsyncData() { + if (js_callback) delete js_callback; + } + + ReplyCallback callback; + JSCallback* js_callback; + }; + + RuntimeIPCClient(); + + int routing_id_; + std::map callbacks_; +}; + +} // namespace + +#endif // WRT_BUNDLE_RUNTIME_IPC_CLIENT_H_ \ No newline at end of file diff --git a/src/runtime/web_application.cc b/src/runtime/web_application.cc index d118213..ab6d79c 100755 --- a/src/runtime/web_application.cc +++ b/src/runtime/web_application.cc @@ -164,6 +164,11 @@ static void InitializeNotificationCallback(Ewk_Context* ewk_context, app); } +static Eina_Bool ExitAppIdlerCallback(void* /*data*/) { + elm_exit(); + return ECORE_CALLBACK_CANCEL; +} + } // namespace WebApplication::WebApplication( @@ -315,10 +320,10 @@ void WebApplication::Launch(std::unique_ptr appcontrol) { // TODO(wy80.choi): ewk_send_widget_info should be fixed to receive uuid of // application instead of widget_id. // Currently, uuid is passed as encoded_bundle argument temporarily. - // ewk_send_widget_info(ewk_context_, 1, - // elm_config_scale_get(), - // elm_theme_get(NULL), - // app_uuid_.c_str()); + ewk_send_widget_info(ewk_context_, appid_.c_str(), + elm_config_scale_get(), + elm_theme_get(NULL), + app_uuid_.c_str()); std::unique_ptr res = resource_manager_->GetStartResource(appcontrol.get()); @@ -469,10 +474,55 @@ void WebApplication::OnClosedWebView(WebView * view) { } void WebApplication::OnReceivedWrtMessage( - WebView* /*view*/, - Ewk_IPC_Wrt_Message_Data* /*message*/) { - // TODO(wy80.choi) : Handle messages from injected bundle? - // ex. SendRuntimeMessage to hide / exit application. + WebView* view, + Ewk_IPC_Wrt_Message_Data* msg) { + + Eina_Stringshare* msg_id = ewk_ipc_wrt_message_data_id_get(msg); + Eina_Stringshare* msg_ref_id = ewk_ipc_wrt_message_data_reference_id_get(msg); + Eina_Stringshare* msg_type = ewk_ipc_wrt_message_data_type_get(msg); + Eina_Stringshare* msg_value = ewk_ipc_wrt_message_data_value_get(msg); + + LOGGER(DEBUG) << "RecvMsg: id = " << msg_id; + LOGGER(DEBUG) << "RecvMsg: refid = " << msg_ref_id; + LOGGER(DEBUG) << "RecvMsg: type = " << msg_type; + LOGGER(DEBUG) << "RecvMsg: value = " << msg_value; + + #define TYPE_IS(x) (!strcmp(msg_type, x)) + if (TYPE_IS("tizen://hide")) { + // One Way Message + window_->InActive(); + } else if (TYPE_IS("tizen://exit")) { + // One Way Message + ecore_idler_add(ExitAppIdlerCallback, NULL); + } else if (TYPE_IS("tizen://changeUA")) { + // Async Message + // Change UserAgent of current WebView + bool ret = false; + if (view_stack_.size() > 0 && view_stack_.front() != NULL) { + ret = view_stack_.front()->SetUserAgent(std::string(msg_value)); + } + // Send response + Ewk_IPC_Wrt_Message_Data* ans = ewk_ipc_wrt_message_data_new(); + ewk_ipc_wrt_message_data_type_set(ans, msg_type); + ewk_ipc_wrt_message_data_reference_id_set(ans, msg_id); + if(ret) + ewk_ipc_wrt_message_data_value_set(ans, "success"); + else + ewk_ipc_wrt_message_data_value_set(ans, "failed"); + if (!ewk_ipc_wrt_message_send(ewk_context_, ans)) { + LOGGER(ERROR) << "Failed to send response"; + } + ewk_ipc_wrt_message_data_del(ans); + } else if (TYPE_IS("tizen://test-sync")) { + // TODO(wy80.choi): this type should be removed after finish test + ewk_ipc_wrt_message_data_value_set(msg, "reply!!"); + } + #undef TYPE_IS + + eina_stringshare_del(msg_value); + eina_stringshare_del(msg_type); + eina_stringshare_del(msg_ref_id); + eina_stringshare_del(msg_id); } void WebApplication::OnOrientationLock(WebView* view, @@ -493,7 +543,7 @@ void WebApplication::OnOrientationLock(WebView* view, return; } - if ( lock ) { + if (lock) { window_->SetRotationLock(preferred_rotation); } else { window_->SetAutoRotation(); @@ -520,6 +570,7 @@ void WebApplication::OnLanguageChanged() { void WebApplication::OnConsoleMessage(const std::string& msg, int level) { static bool enabled = (getenv(kConsoleLogEnableKey) != NULL); + enabled = true; if (debug_mode_ || enabled) { int dlog_level = DLOG_DEBUG; switch (level) { @@ -774,7 +825,8 @@ void WebApplication::HandleDBusMethod(GDBusConnection* /*connection*/, if (g_strcmp0(key, "runtime_name") == 0) { value = std::string("wrt"); } else if (g_strcmp0(key, "app_id") == 0) { - // TODO(wy80.choi): TEC requries double quotes, but webapi-plugins is not. + // TODO(wy80.choi): TEC requries double quotes, + // but webapi-plugins doesn't. It should be fixed. value = "\"" + appid_ + "\""; } else if (g_strcmp0(key, "encoded_bundle") == 0) { value = received_appcontrol_->encoded_bundle(); diff --git a/src/runtime/web_application.h b/src/runtime/web_application.h index 2f3c6b6..401b9ec 100755 --- a/src/runtime/web_application.h +++ b/src/runtime/web_application.h @@ -41,7 +41,7 @@ class WebApplication : public WebView::EventListener { virtual void OnCreatedNewWebView(WebView* view, WebView* new_view); virtual void OnClosedWebView(WebView * view); virtual void OnReceivedWrtMessage(WebView* view, - Ewk_IPC_Wrt_Message_Data* message); + Ewk_IPC_Wrt_Message_Data* msg); virtual void OnOrientationLock(WebView* view, bool lock, int preferred_rotation); diff --git a/src/runtime/web_view.cc b/src/runtime/web_view.cc index aa713da..756ea0b 100755 --- a/src/runtime/web_view.cc +++ b/src/runtime/web_view.cc @@ -62,7 +62,7 @@ void WebView::SetAppInfo(const std::string& app_name, impl_->SetAppInfo(app_name, version); } -void WebView::SetUserAgent(const std::string& user_agent) { - impl_->SetUserAgent(user_agent.c_str()); +bool WebView::SetUserAgent(const std::string& user_agent) { + return impl_->SetUserAgent(user_agent.c_str()); } } // namespace wrt diff --git a/src/runtime/web_view.h b/src/runtime/web_view.h index e2d9a78..8608c75 100755 --- a/src/runtime/web_view.h +++ b/src/runtime/web_view.h @@ -84,7 +84,7 @@ class WebView { void SetVisibility(bool show); bool EvalJavascript(const std::string& script); void SetAppInfo(const std::string& app_name, const std::string& version); - void SetUserAgent(const std::string& user_agent); + bool SetUserAgent(const std::string& user_agent); void SetEventListener(EventListener* listener); Evas_Object* evas_object() const; diff --git a/src/runtime/web_view_impl.cc b/src/runtime/web_view_impl.cc index d4066db..90a487a 100755 --- a/src/runtime/web_view_impl.cc +++ b/src/runtime/web_view_impl.cc @@ -741,8 +741,8 @@ void WebViewImpl::SetAppInfo(const std::string& app_name, std::string ua = app_name + "/" + version; ewk_view_application_name_for_user_agent_set(ewk_view_, ua.c_str()); } -void WebViewImpl::SetUserAgent(const std::string& user_agent) { - ewk_view_user_agent_set(ewk_view_, user_agent.c_str()); +bool WebViewImpl::SetUserAgent(const std::string& user_agent) { + return ewk_view_user_agent_set(ewk_view_, user_agent.c_str()); } } // namespace wrt diff --git a/src/runtime/web_view_impl.h b/src/runtime/web_view_impl.h index 728d53c..ce78e8d 100755 --- a/src/runtime/web_view_impl.h +++ b/src/runtime/web_view_impl.h @@ -31,7 +31,7 @@ class WebViewImpl { void SetVisibility(bool show); bool EvalJavascript(const std::string& script); void SetAppInfo(const std::string& app_name, const std::string& version); - void SetUserAgent(const std::string& user_agent); + bool SetUserAgent(const std::string& user_agent); void SetEventListener(WebView::EventListener* listener); Evas_Object* evas_object() const; -- 2.7.4