diff --git a/common/webview_app.cc b/common/webview_app.cc index 0261c17..54c8dc5 100644 --- a/common/webview_app.cc +++ b/common/webview_app.cc @@ -101,3 +101,130 @@ CefRefPtr WebviewApp::GetDefaultClient() { // Called when a new browser window is created via the Chrome runtime UI. return WebviewHandler::GetInstance(); } + +void WebviewApp::OnWebKitInitialized() +{ + //inject js function for jssdk + std::string extensionCode = R"( + var external = {}; + var clientSdk = {}; + (() => { + clientSdk.jsCmd = (functionName, arg1, arg2, arg3) => { + if (typeof arg1 === 'function') { + native function jsCmd(functionName, arg1); + return jsCmd(functionName, arg1); + } + else if (typeof arg2 === 'function') { + jsonString = arg1; + if (typeof arg1 !== 'string'){ + jsonString = JSON.stringify(arg1); + } + native function jsCmd(functionName, jsonString, arg2); + return jsCmd(functionName, jsonString, arg2); + } + else if (typeof arg3 === 'function') { + jsonString = arg1; + if (typeof arg1 !== 'string'){ + jsonString = JSON.stringify(arg1); + } + native function jsCmd(functionName, jsonString, arg2, arg3); + return jsCmd(functionName, jsonString, arg2, arg3); + }else { + + } + }; + + external.JavaScriptChannel = (n,e,r) => { + var a; + null == r ? a = '' : (a = '_' + new Date + (1e3 + Math.floor(8999 * Math.random())), window[a] = function (n, e) { + return function () { + try { + e && e.call && e.call(null, arguments[1]) + } finally { + delete window[n] + } + } + }(a, r)); + try { + external.StartRequest(external.GetNextReqID(), n, a, JSON.stringify(e || {}), '') + } catch (l) { + console.log('messeage send') + } + } + + external.StartRequest = (nReqID, strCmd, strCallBack, strArgs, strLog) => { + native function StartRequest(); + StartRequest(nReqID, strCmd, strCallBack, strArgs, strLog); + }; + external.GetNextReqID = () => { + native function GetNextReqID(); + return GetNextReqID(); + }; + })(); + )"; + + CefRefPtr handler = new CefJSHandler(); + + if (!m_render_js_bridge.get()) + m_render_js_bridge.reset(new CefJSBridge); + handler->AttachJSBridge(m_render_js_bridge); + + CefRegisterExtension("v8/extern", extensionCode, handler); +} + +void WebviewApp::OnBrowserCreated(CefRefPtr browser, CefRefPtr extra_info) +{ + if (!m_render_js_bridge.get()) + m_render_js_bridge.reset(new CefJSBridge); +} + +void WebviewApp::OnBrowserDestroyed(CefRefPtr browser) +{ +} + +void WebviewApp::OnContextCreated(CefRefPtr browser, CefRefPtr frame, CefRefPtr context) +{ +} + +void WebviewApp::OnContextReleased(CefRefPtr browser, CefRefPtr frame, CefRefPtr context) +{ + if (m_render_js_bridge.get()) + { + m_render_js_bridge->RemoveCallbackFuncWithFrame(frame); + } +} + +void WebviewApp::OnUncaughtException(CefRefPtr browser, CefRefPtr frame, CefRefPtr context, CefRefPtr exception, CefRefPtr stackTrace) +{ +} + +void WebviewApp::OnFocusedNodeChanged(CefRefPtr browser, CefRefPtr frame, CefRefPtr node) +{ + bool is_editable = (node.get() && node->IsEditable()); + if (is_editable != m_last_node_is_editable) + { + // Notify the browser of the change in focused element type. + m_last_node_is_editable = is_editable; + CefRefPtr message = CefProcessMessage::Create(kFocusedNodeChangedMessage); + + message->GetArgumentList()->SetBool(0, is_editable); + frame->SendProcessMessage(PID_BROWSER, message); + } +} + +bool WebviewApp::OnProcessMessageReceived(CefRefPtr browser, CefRefPtr frame, CefProcessId source_process, CefRefPtr message) +{ + const CefString& message_name = message->GetName(); + if (message_name == kExecuteJsCallbackMessage) + { + int callbackId = message->GetArgumentList()->GetInt(0); + bool error = message->GetArgumentList()->GetBool(1); + CefString result = message->GetArgumentList()->GetString(2); + if (m_render_js_bridge.get()) + { + m_render_js_bridge->ExecuteJSCallbackFunc(callbackId, error, result); + } + } + + return false; +} diff --git a/common/webview_app.h b/common/webview_app.h index be59e27..3c40709 100644 --- a/common/webview_app.h +++ b/common/webview_app.h @@ -5,12 +5,12 @@ #ifndef CEF_TESTS_CEFSIMPLE_SIMPLE_APP_H_ #define CEF_TESTS_CEFSIMPLE_SIMPLE_APP_H_ -#include "include/cef_app.h" #include #include "webview_handler.h" +#include "webview_js_handler.h" // Implement application-level callbacks for the browser process. -class WebviewApp : public CefApp, public CefBrowserProcessHandler { +class WebviewApp : public CefApp, public CefBrowserProcessHandler, public CefRenderProcessHandler{ public: WebviewApp(CefRefPtr handler); @@ -18,6 +18,11 @@ public: CefRefPtr GetBrowserProcessHandler() override { return this; } + + CefRefPtr GetRenderProcessHandler() override { + return this; + } + void OnBeforeCommandLineProcessing( const CefString& process_type, CefRefPtr command_line) override { @@ -27,14 +32,52 @@ public: command_line->AppendSwitch("use-mock-keychain"); command_line->AppendSwitch("single-process"); #endif + #ifdef _DEBUG + command_line->AppendSwitch("renderer-startup-dialog"); + command_line->AppendSwitch("no-sandbox"); + #endif } // CefBrowserProcessHandler methods: void OnContextInitialized() override; CefRefPtr GetDefaultClient() override; + + // CefRenderProcessHandler methods. + void OnWebKitInitialized() override; + void OnBrowserCreated( + CefRefPtr browser, + CefRefPtr extra_info) override; + void OnBrowserDestroyed(CefRefPtr browser) override; + void OnContextCreated( + CefRefPtr browser, + CefRefPtr frame, + CefRefPtr context) override; + void OnContextReleased( + CefRefPtr browser, + CefRefPtr frame, + CefRefPtr context) override; + void OnUncaughtException( + CefRefPtr browser, + CefRefPtr frame, + CefRefPtr context, + CefRefPtr exception, + CefRefPtr stackTrace) override; + void OnFocusedNodeChanged( + CefRefPtr browser, + CefRefPtr frame, + CefRefPtr node) override; + bool OnProcessMessageReceived( + CefRefPtr browser, + CefRefPtr frame, + CefProcessId source_process, + CefRefPtr message) override; private: CefRefPtr m_handler; + + std::shared_ptr m_render_js_bridge; + bool m_last_node_is_editable = false; + // Include the default reference counting implementation. IMPLEMENT_REFCOUNTING(WebviewApp); }; diff --git a/common/webview_cookieVisitor.cc b/common/webview_cookieVisitor.cc new file mode 100644 index 0000000..d91c9d9 --- /dev/null +++ b/common/webview_cookieVisitor.cc @@ -0,0 +1,38 @@ +#include "webview_cookieVisitor.h" + +WebviewCookieVisitor::WebviewCookieVisitor() +{ +} + +WebviewCookieVisitor::~WebviewCookieVisitor() +{ +} + +bool WebviewCookieVisitor::Visit(const CefCookie &cookie, int count, int total, bool &deleteCookie) +{ + { + std::unique_lock lock(m_mutexCookieVector); + if (count == 0) + { + m_vecAllCookies.clear(); + } + + m_vecAllCookies.emplace_back(cookie); + } + + return count != total; +} + +std::map> WebviewCookieVisitor::getVisitedCookies() +{ + std::map> ret; + for (auto &cookie : m_vecAllCookies) + { + ; + std::string domain = CefString(cookie.domain.str).ToString(); + std::string name = CefString(cookie.name.str).ToString(); + std::string value = CefString(cookie.value.str).ToString(); + ret[domain][name] = value; + } + return ret; +} diff --git a/common/webview_cookieVisitor.h b/common/webview_cookieVisitor.h new file mode 100644 index 0000000..fe6d91d --- /dev/null +++ b/common/webview_cookieVisitor.h @@ -0,0 +1,28 @@ +#ifndef WEBVIEW_CEF_COOKIE_VISITOR_H_ +#define WEBVIEW_CEF_COOKIE_VISITOR_H_ + +#include "include/cef_base.h" +#include "include/cef_cookie.h" +#include +#include + +class WebviewCookieVisitor : public CefCookieVisitor +{ +public: + WebviewCookieVisitor(); + ~WebviewCookieVisitor(); + + //CefCookieVisitor + bool Visit(const CefCookie& cookie, int count, int total, bool& deleteCookie); + + std::map> getVisitedCookies(); + + // Include the default reference counting implementation. + IMPLEMENT_REFCOUNTING(WebviewCookieVisitor); + +private: + std::vector m_vecAllCookies; + std::mutex m_mutexCookieVector; +}; + +#endif // WEBVIEW_CEF_COOKIE_VISITOR_H_ \ No newline at end of file diff --git a/common/webview_handler.cc b/common/webview_handler.cc index 1395ef4..87cd672 100644 --- a/common/webview_handler.cc +++ b/common/webview_handler.cc @@ -16,6 +16,8 @@ #include "include/wrapper/cef_closure_task.h" #include "include/wrapper/cef_helpers.h" +#include "webview_js_handler.h" + namespace { WebviewHandler* g_instance = nullptr; @@ -43,6 +45,28 @@ WebviewHandler* WebviewHandler::GetInstance() { return g_instance; } +bool WebviewHandler::OnProcessMessageReceived( + CefRefPtr browser, CefRefPtr frame, + CefProcessId source_process, CefRefPtr message) +{ + std::string message_name = message->GetName(); + + if(message_name == kJSCallCppFunctionMessage) + { + CefString fun_name = message->GetArgumentList()->GetString(0); + CefString param = message->GetArgumentList()->GetString(1); + int js_callback_id = message->GetArgumentList()->GetInt(2); + + if (fun_name.empty() || !(browser.get())) { + return false; + } + + onJavaScriptChannelMessage( + fun_name,param,std::to_string(js_callback_id),std::to_string(frame->GetIdentifier())); + } + return false; +} + void WebviewHandler::OnTitleChange(CefRefPtr browser, const CefString& title) { //todo: title change @@ -291,6 +315,138 @@ void WebviewHandler::openDevTools() { } } +void WebviewHandler::setCookie(const std::string& domain, const std::string& key, const std::string& value){ + CefRefPtr manager = CefCookieManager::GetGlobalManager(nullptr); + if(manager){ + CefCookie cookie; + CefString(&cookie.path).FromASCII("/"); + CefString(&cookie.name).FromString(key.c_str()); + CefString(&cookie.value).FromString(value.c_str()); + + if (!domain.empty()) { + CefString(&cookie.domain).FromString(domain.c_str()); + } + + cookie.httponly = true; + cookie.secure = false; + std::string httpDomain = "https://" + domain + "/cookiestorage"; + manager->SetCookie(httpDomain, cookie, nullptr); + } +} + +void WebviewHandler::deleteCookie(const std::string& domain, const std::string& key) +{ + CefRefPtr manager = CefCookieManager::GetGlobalManager(nullptr); + if (manager) { + std::string httpDomain = "https://" + domain + "/cookiestorage"; + manager->DeleteCookies(httpDomain, key, nullptr); + } +} + +bool WebviewHandler::visitAllCookies(){ + CefRefPtr manager = CefCookieManager::GetGlobalManager(nullptr); + if (!manager) + { + return false; + } + + if(!m_CookieVisitor.get()) + { + m_CookieVisitor = new WebviewCookieVisitor(); + if (!m_CookieVisitor.get()) + { + return false; + } + } + + if (manager->VisitAllCookies(m_CookieVisitor)) + { + if (onAllCookieVisitedCb) { + onAllCookieVisitedCb(m_CookieVisitor->getVisitedCookies()); + return true; + } + } + return false; +} + +bool WebviewHandler::visitUrlCookies(const std::string& domain, const bool& isHttpOnly){ + CefRefPtr manager = CefCookieManager::GetGlobalManager(nullptr); + if (!manager) + { + return false; + } + + if(!m_CookieVisitor.get()) + { + m_CookieVisitor = new WebviewCookieVisitor(); + if (!m_CookieVisitor.get()) + { + return false; + } + } + + std::string httpDomain = "https://" + domain + "/cookiestorage"; + + if (manager->VisitUrlCookies(httpDomain, isHttpOnly, m_CookieVisitor)) + { + if (onUrlCookieVisitedCb) { + onUrlCookieVisitedCb(m_CookieVisitor->getVisitedCookies()); + return true; + } + } + return false; +} + +bool WebviewHandler::setJavaScriptChannels(const std::vector channels) +{ + std::string extensionCode = ""; + for(auto& channel : channels) + { + extensionCode += channel; + extensionCode += " = (e,r) => {external.JavaScriptChannel('"; + extensionCode += channel; + extensionCode += "',e,r)};"; + } + return executeJavaScript(extensionCode); +} + +bool WebviewHandler::sendJavaScriptChannelCallBack(const bool error, const std::string result, const std::string callbackId, const std::string frameId) +{ + CefRefPtr message = CefProcessMessage::Create(kExecuteJsCallbackMessage); + CefRefPtr args = message->GetArgumentList(); + args->SetInt(0, atoi(callbackId.c_str())); + args->SetBool(1, error); + args->SetString(2, result); + BrowserList::iterator bit = browser_list_.begin(); + for (; bit != browser_list_.end(); ++bit) { + CefRefPtr frame = (*bit)->GetMainFrame(); + if (frame->GetIdentifier() == atoll(frameId.c_str())) + { + frame->SendProcessMessage(PID_RENDERER, message); + return true; + } + } + return false; +} + +bool WebviewHandler::executeJavaScript(const std::string code) +{ + if(!code.empty()) + { + BrowserList::iterator bit = browser_list_.begin(); + for (; bit != browser_list_.end(); ++bit) { + if ((*bit).get()) { + CefRefPtr frame = (*bit)->GetMainFrame(); + if (frame) { + frame->ExecuteJavaScript(code, frame->GetURL(), 0); + return true; + } + } + } + } + return false; +} + void WebviewHandler::GetViewRect(CefRefPtr browser, CefRect &rect) { CEF_REQUIRE_UI_THREAD(); diff --git a/common/webview_handler.h b/common/webview_handler.h index 84b6530..0086528 100644 --- a/common/webview_handler.h +++ b/common/webview_handler.h @@ -10,6 +10,8 @@ #include #include +#include "webview_cookieVisitor.h" + class WebviewHandler : public CefClient, public CefDisplayHandler, public CefLifeSpanHandler, @@ -19,6 +21,9 @@ public: std::function onPaintCallback; std::function onUrlChangedCb; std::function onTitleChangedCb; + std::function>)> onAllCookieVisitedCb; + std::function>)> onUrlCookieVisitedCb; + std::function onJavaScriptChannelMessage; explicit WebviewHandler(); ~WebviewHandler(); @@ -35,6 +40,13 @@ public: } virtual CefRefPtr GetLoadHandler() override { return this; } virtual CefRefPtr GetRenderHandler() override { return this; } + + bool OnProcessMessageReceived( + CefRefPtr browser, + CefRefPtr frame, + CefProcessId source_process, + CefRefPtr message) override; + // CefDisplayHandler methods: virtual void OnTitleChange(CefRefPtr browser, @@ -94,6 +106,15 @@ public: void reload(); void openDevTools(); + void setCookie(const std::string& domain, const std::string& key, const std::string& value); + void deleteCookie(const std::string& domain, const std::string& key); + bool visitAllCookies(); + bool visitUrlCookies(const std::string& domain, const bool& isHttpOnly); + + bool setJavaScriptChannels(const std::vector channels); + bool sendJavaScriptChannelCallBack(const bool error, const std::string result, const std::string callbackId, const std::string frameId); + bool executeJavaScript(const std::string code); + private: uint32_t width = 1; uint32_t height = 1; @@ -106,6 +127,8 @@ private: // Include the default reference counting implementation. IMPLEMENT_REFCOUNTING(WebviewHandler); + + CefRefPtr m_CookieVisitor; }; #endif // CEF_TESTS_CEFSIMPLE_SIMPLE_HANDLER_H_ diff --git a/common/webview_js_handler.cc b/common/webview_js_handler.cc new file mode 100644 index 0000000..543de08 --- /dev/null +++ b/common/webview_js_handler.cc @@ -0,0 +1,265 @@ +#include "webview_js_handler.h" +#include + +std::atomic_long s_nReqID {1001}; + +bool CefJSHandler::Execute(const CefString& name, + CefRefPtr object, + const CefV8ValueList& arguments, + CefRefPtr& retval, + CefString& exception) +{ + if (name == "jsCmd") + { + if (arguments.size() < 2) { + exception = "Invalid arguments."; + return true; + } + //the first param is function name,the last param is callback function,and allow most 2 custom params between them. + CefString function_name = arguments[0]->GetStringValue(); + CefString params = ""; + CefRefPtr callback; + CefRefPtr rawdata; + if (arguments[0]->IsString() && arguments[1]->IsFunction()) + { + callback = arguments[1]; + } + else if (arguments[0]->IsString() && arguments[1]->IsString() && arguments[2]->IsFunction()) + { + params = arguments[1]->GetStringValue(); + callback = arguments[2]; + } + else if (arguments[0]->IsString() && arguments[1]->IsString() && arguments[3]->IsFunction()) + { + params = arguments[1]->GetStringValue(); + rawdata = arguments[2]; + callback = arguments[3]; + } + else + { + exception = "Invalid arguments."; + return true; + } + + //call c++ funtion + if (!js_bridge_->CallCppFunction(function_name, params, callback, rawdata)) + { + std::ostringstream strStream; + strStream << "Failed to call function: " << function_name.c_str() << "."; + strStream.flush(); + + exception = strStream.str(); + } + + } + else if (name == "StartRequest") + { + if (arguments.size() < 5) { + exception = "Invalid arguments."; + return true; + } + // args[0] + int reqId = (int)arguments[0]->GetIntValue(); + + // args[1] + CefString strCmd = arguments[1]->GetStringValue(); + + //// args[2] + CefString strCallback = arguments[2]->GetStringValue(); + + //// args[3] + CefString strArgs = arguments[3]->GetStringValue(); + + // call c++ function + if (!js_bridge_->StartRequest(reqId, strCmd, strCallback, strArgs)) + { + std::ostringstream strStream; + strStream << "Failed to call function: " << strCmd.c_str() << "."; + strStream.flush(); + + exception = strStream.str(); + } + + } + else if (name == "GetNextReqID") + { + int reqID = CefJSBridge::GetNextReqID(); + retval = CefV8Value::CreateInt(reqID); + } + else { + exception = "NativeHost no this fun."; + } + + return true; +} + +bool CefJSBridge::StartRequest(int reqId, + const CefString& strCmd, + const CefString& strCallback, + const CefString& strArgs) +{ + if (reqId > 0) { + reqId *= -1; + } + + auto it = startRequest_callback_.find(reqId); + if (it == startRequest_callback_.cend()) + { + CefRefPtr context = CefV8Context::GetCurrentContext(); + if (context) + { + CefRefPtr frame = context->GetFrame(); + if (frame) + { + CefRefPtr message = CefProcessMessage::Create(kJSCallCppFunctionMessage); + message->GetArgumentList()->SetString(0, strCmd); + message->GetArgumentList()->SetString(1, strArgs); + message->GetArgumentList()->SetInt(2, reqId); + startRequest_callback_.emplace(reqId, std::make_pair(frame, strCallback)); + frame->SendProcessMessage(PID_BROWSER, message); + return true; + } + } + } + + return false; +} + +int CefJSBridge::GetNextReqID() +{ + long nRet = ++s_nReqID; + if (nRet < 0) + { + nRet = 0; + } + + while (nRet == 0) + { + nRet = ++s_nReqID; + } + + return nRet; +} + +bool CefJSBridge::CallCppFunction(const CefString& function_name, + const CefString& params, + CefRefPtr callback, + CefRefPtr rawdata) +{ + auto it = render_callback_.find(js_callback_id_); + if (it == render_callback_.cend()) + { + CefRefPtr context = CefV8Context::GetCurrentContext(); + if (context) + { + CefRefPtr frame = context->GetFrame(); + if (frame) + { + CefRefPtr message = CefProcessMessage::Create(kJSCallCppFunctionMessage); + message->GetArgumentList()->SetString(0, function_name); + message->GetArgumentList()->SetString(1, params); + message->GetArgumentList()->SetInt(2, js_callback_id_); + render_callback_.emplace(js_callback_id_++, std::make_pair(context, std::make_pair(callback, rawdata))); + frame->SendProcessMessage(PID_BROWSER, message); + return true; + } + } + } + + return false; +} + + +void CefJSBridge::RemoveCallbackFuncWithFrame(CefRefPtr frame) +{ + if (!render_callback_.empty()) { + for (auto it = render_callback_.begin(); it != render_callback_.end();) { + if (it->second.first->IsSame(frame->GetV8Context())) { + it = render_callback_.erase(it); + } + else { + ++it; + } + } + } + + if (!startRequest_callback_.empty()) { + for (auto it = startRequest_callback_.begin(); it != startRequest_callback_.end();) { + if (it->second.first->GetIdentifier() == frame->GetIdentifier()) { + it = startRequest_callback_.erase(it); + } + else { + ++it; + } + } + } +} + +bool CefJSBridge::ExecuteJSCallbackFunc(int callbackId, bool error, const CefString& result) +{ + if (callbackId < 0) + { + auto it = startRequest_callback_.find(callbackId); + if (it != startRequest_callback_.cend()) + { + auto frame = it->second.first; + CefString callback = it->second.second; + + if (callback != "" && frame.get()) + { + std::ostringstream strStream; + strStream <<"window['" << callback.ToString() << "'](" << callbackId * -1 << ", " << result.ToString() << ");"; + strStream.flush(); + + CefString strCode = strStream.str(); + frame->ExecuteJavaScript(strCode, frame->GetURL(), 0); + startRequest_callback_.erase(callbackId); + + return true; + } + else + { + return false; + } + } + } + else + { + auto it = render_callback_.find(callbackId); + if (it != render_callback_.cend()) + { + auto context = it->second.first; + auto callback = it->second.second.first; + auto rawdata = it->second.second.second; + if (context.get() && callback.get()) + { + context->Enter(); + + CefV8ValueList arguments; + + //the first param marks whether the function execution result was successful + arguments.push_back(CefV8Value::CreateBool(error)); + + // the second prarm take the return data + arguments.push_back(CefV8Value::CreateString(result)); + if (rawdata.get()) { + arguments.push_back(rawdata); + } + + // call js function + CefRefPtr retval = callback->ExecuteFunction(nullptr, arguments); + context->Exit(); + render_callback_.erase(callbackId); + + return true; + } + else + { + return false; + } + } + + } + + return false; +} diff --git a/common/webview_js_handler.h b/common/webview_js_handler.h new file mode 100644 index 0000000..6ec7945 --- /dev/null +++ b/common/webview_js_handler.h @@ -0,0 +1,50 @@ +#pragma once +#ifndef WEBVIEW_CEF_JS_HANDLER_H_ +#define WEBVIEW_CEF_JS_HANDLER_H_ +#include "include/cef_base.h" +#include "include/cef_app.h" + +#include +#include + +static const char kJSCallCppFunctionMessage[] = "JSCallCppFunction"; //js call c++ message +static const char kExecuteJsCallbackMessage[] = "ExecuteJsCallback"; //c++ call js message +static const char kFocusedNodeChangedMessage[] = "FocusedNodeChanged"; //elements that capture focus in web pages changed message + +class CefJSBridge +{ + typedef std::map/* context*/, std::pair/* callback*/, CefRefPtr/* rawdata*/>>> RenderCallbackMap; + typedef std::map/* frame*/, CefString /* callback*/>> StartRequestCallbackMap; + +public: + CefJSBridge() {}; + ~CefJSBridge() {}; +public: + static int GetNextReqID(); + bool StartRequest(int reqId, const CefString& strCmd, const CefString& strCallback, const CefString& strArgs); + + bool CallCppFunction(const CefString& function_name, const CefString& params, CefRefPtr callback, CefRefPtr rawdata); + void RemoveCallbackFuncWithFrame(CefRefPtr frame); + bool ExecuteJSCallbackFunc(int js_callback_id, bool has_error, const CefString& json_result); +private: + uint32 js_callback_id_ = 0; + RenderCallbackMap render_callback_; + StartRequestCallbackMap startRequest_callback_; +}; + +class CefJSHandler : public CefV8Handler +{ +public: + CefJSHandler() {} + virtual bool Execute(const CefString& name, + CefRefPtr object, + const CefV8ValueList& arguments, + CefRefPtr& retval, + CefString& exception) override; + void AttachJSBridge(std::shared_ptr js_bridge) { js_bridge_ = js_bridge; } + IMPLEMENT_REFCOUNTING(CefJSHandler); +private: + std::shared_ptr js_bridge_; +}; + +#endif // WEBVIEW_CEF_JS_HANDLER_H_ diff --git a/example/lib/main.dart b/example/lib/main.dart index f2d1a42..817286a 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -41,6 +41,24 @@ class _MyAppState extends State { _textController.text = url; }, )); + + // ignore: prefer_collection_literals + final Set jsChannels = [ + JavascriptChannel( + name: 'Print', + onMessageReceived: (JavascriptMessage message) { + print(message.message); + _controller.sendJavaScriptChannelCallBack( + false, + "{'code':'200','message':'print succeed!'}", + message.callbackId, + message.frameId); + }), + ].toSet(); + //normal JavaScriptChannels + await _controller.setJavaScriptChannels(jsChannels); + //also you can build your own jssdk by execute JavaScript code to CEF + await _controller.executeJavaScript("function abc(e){console.log(e)}"); // If the widget was removed from the tree while the asynchronous platform // message was in flight, we want to discard the reply rather than calling // setState to update our non-existent appearance. diff --git a/lib/src/webview.dart b/lib/src/webview.dart index df489a7..fbf28c9 100644 --- a/lib/src/webview.dart +++ b/lib/src/webview.dart @@ -6,6 +6,7 @@ import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'webview_events_listener.dart'; +import 'webview_javascript.dart'; const MethodChannel _pluginChannel = MethodChannel("webview_cef"); @@ -15,6 +16,9 @@ class WebViewController extends ValueNotifier { bool _isDisposed = false; WebviewEventsListener? _listener; + final Map _javascriptChannels = + {}; + Future get ready => _creatingCompleter.future; WebViewController() : super(false); @@ -46,6 +50,19 @@ class WebViewController extends ValueNotifier { case "titleChanged": _listener?.onTitleChanged?.call(call.arguments); return; + case "allCookiesVisited": + _listener?.onAllCookiesVisited?.call(Map.from(call.arguments)); + return; + case "urlCookiesVisited": + _listener?.onUrlCookiesVisited?.call(Map.from(call.arguments)); + return; + case 'javascriptChannelMessage': + _handleJavascriptChannelMessage( + call.arguments['channel'], + call.arguments['message'], + call.arguments['callbackId'], + call.arguments['frameId']); + break; default: } } @@ -59,6 +76,7 @@ class WebViewController extends ValueNotifier { await _creatingCompleter.future; if (!_isDisposed) { _isDisposed = true; + _javascriptChannels.clear(); await _pluginChannel.invokeMethod('dispose', _textureId); } super.dispose(); @@ -106,6 +124,71 @@ class WebViewController extends ValueNotifier { return _pluginChannel.invokeMethod('openDevTools'); } + Future setCookie(String domain, String key, String val) async { + if (_isDisposed) { + return; + } + assert(value); + return _pluginChannel.invokeMethod('setCookie', [domain, key, val]); + } + + Future deleteCookie(String domain, String key) async { + if (_isDisposed) { + return; + } + assert(value); + return _pluginChannel.invokeMethod('deleteCookie', [domain, key]); + } + + Future visitAllCookies() async { + if (_isDisposed) { + return; + } + assert(value); + return _pluginChannel.invokeMethod('visitAllCookies'); + } + + Future visitUrlCookies(String domain, bool isHttpOnly) async { + if (_isDisposed) { + return; + } + assert(value); + return _pluginChannel.invokeMethod('visitUrlCookies', [domain, isHttpOnly]); + } + + Future setJavaScriptChannels(Set channels) async { + if (_isDisposed) { + return; + } + assert(value); + _assertJavascriptChannelNamesAreUnique(channels); + + channels.forEach((channel) { + _javascriptChannels[channel.name] = channel; + }); + + return _pluginChannel.invokeMethod('setJavaScriptChannels', + [_extractJavascriptChannelNames(channels).toList()]); + } + + Future sendJavaScriptChannelCallBack( + bool error, String result, String callbackId, String frameId) async { + if (_isDisposed) { + return; + } + assert(value); + return _pluginChannel.invokeMethod( + 'sendJavaScriptChannelCallBack', [error, result, callbackId, frameId]); + } + + Future executeJavaScript(String code) async { + if (_isDisposed) { + return; + } + assert(value); + return _pluginChannel.invokeMethod('executeJavaScript', [code]); + } + /// Moves the virtual cursor to [position]. Future _cursorMove(Offset position) async { if (_isDisposed) { @@ -162,6 +245,30 @@ class WebViewController extends ValueNotifier { return _pluginChannel .invokeMethod('setSize', [dpi, size.width, size.height]); } + + Set _extractJavascriptChannelNames(Set channels) { + final Set channelNames = + channels.map((JavascriptChannel channel) => channel.name).toSet(); + return channelNames; + } + + void _handleJavascriptChannelMessage(final String channelName, + final String message, final String callbackId, final String frameId) { + if (_javascriptChannels.containsKey(channelName)) { + _javascriptChannels[channelName]! + .onMessageReceived(JavascriptMessage(message, callbackId, frameId)); + } else { + print('Channel "$channelName" is not exstis'); + } + } + + void _assertJavascriptChannelNamesAreUnique( + final Set? channels) { + if (channels == null || channels.isEmpty) { + return; + } + assert(_extractJavascriptChannelNames(channels).length == channels.length); + } } class WebView extends StatefulWidget { diff --git a/lib/src/webview_events_listener.dart b/lib/src/webview_events_listener.dart index 0512a7a..cfad12c 100644 --- a/lib/src/webview_events_listener.dart +++ b/lib/src/webview_events_listener.dart @@ -1,9 +1,17 @@ typedef TitleChangeCb = void Function(String title); typedef UrlChangeCb = void Function(String url); +typedef AllCookieVisitedCb = void Function(Map cookies); +typedef UrlCookieVisitedCb = void Function(Map cookies); class WebviewEventsListener { TitleChangeCb? onTitleChanged; UrlChangeCb? onUrlChanged; + AllCookieVisitedCb? onAllCookiesVisited; + UrlCookieVisitedCb? onUrlCookiesVisited; - WebviewEventsListener({this.onTitleChanged, this.onUrlChanged}); + WebviewEventsListener( + {this.onTitleChanged, + this.onUrlChanged, + this.onAllCookiesVisited, + this.onUrlCookiesVisited}); } diff --git a/lib/src/webview_javascript.dart b/lib/src/webview_javascript.dart new file mode 100644 index 0000000..4fdc8ea --- /dev/null +++ b/lib/src/webview_javascript.dart @@ -0,0 +1,49 @@ +/// A message that was sent by JavaScript code running in a [WebView]. + +class JavascriptMessage { + /// Constructs a JavaScript message object. + /// + /// The `message` parameter must not be null. + const JavascriptMessage(this.message, this.callbackId, this.frameId) + : assert(message != null && frameId != null); + + /// The contents of the message that was sent by the JavaScript code. + final String message; + + // The callbackId of the JavaScript code + final String callbackId; + // the frameId of the webview frame + final String frameId; +} + +final RegExp _validChannelNames = RegExp('^[a-zA-Z_][a-zA-Z0-9]*\$'); + +/// A named channel for receiving messaged from JavaScript code running inside a web view. +class JavascriptChannel { + /// Constructs a Javascript channel. + /// + /// The parameters `name` and `onMessageReceived` must not be null. + JavascriptChannel({ + required this.name, + required this.onMessageReceived, + }) : assert(_validChannelNames.hasMatch(name)); + + /// The channel's name. + /// + /// Passing this channel object as part of a [WebView.javascriptChannels] adds a channel object to + /// the Javascript window object's property named `name`. + /// + /// The name must start with a letter or underscore(_), followed by any combination of those + /// characters plus digits. + /// + /// Note that any JavaScript existing `window` property with this name will be overriden. + /// + /// See also [WebView.javascriptChannels] for more details on the channel registration mechanism. + final String name; + + /// A callback that's invoked when a message is received through the channel. + final JavascriptMessageHandler onMessageReceived; +} + +/// Callback type for handling messages sent from Javascript running in a web view. +typedef void JavascriptMessageHandler(JavascriptMessage message); diff --git a/lib/webview_cef.dart b/lib/webview_cef.dart index cb83c38..f8a76e5 100644 --- a/lib/webview_cef.dart +++ b/lib/webview_cef.dart @@ -1,2 +1,3 @@ export 'src/webview.dart'; export 'src/webview_events_listener.dart'; +export 'src/webview_javascript.dart'; diff --git a/macos/Classes/CefWrapper.h b/macos/Classes/CefWrapper.h index 2223d15..9df84f2 100644 --- a/macos/Classes/CefWrapper.h +++ b/macos/Classes/CefWrapper.h @@ -41,6 +41,20 @@ extern int64_t textureId; + (void) setMethodChannel: (FlutterMethodChannel*)channel; ++ (void) setCookie: (NSString *)domain key:(NSString *) key value:(NSString *)value; + ++ (void) deleteCookie: (NSString *)domain key:(NSString *) key; + ++ (void) visitAllCookies; + ++ (void) visitUrlCookies: (NSString *)domain isHttpOnly:(bool)isHttpOnly; + ++ (void) setJavaScriptChannels: (NSArray *)channels; + ++ (void) sendJavaScriptChannelCallBack: (bool)error result:(NSString *)result callbackId:(NSString *)callbackId frameId:(NSString *)frameId; + ++ (void) executeJavaScript: (NSString *)code; + @end #endif /* CefWrapper_h */ diff --git a/macos/Classes/CefWrapper.mm b/macos/Classes/CefWrapper.mm index 0f49c8d..573ce87 100644 --- a/macos/Classes/CefWrapper.mm +++ b/macos/Classes/CefWrapper.mm @@ -11,6 +11,8 @@ #import "include/cef_app.h" #import "../../common/webview_app.h" #import "../../common/webview_handler.h" +#import "../../common/webview_cookieVisitor.h" +#import "../../common/webview_js_handler.h" #include @@ -93,7 +95,52 @@ FlutterMethodChannel* f_channel; handler.get()->onTitleChangedCb = [](std::string title) { [f_channel invokeMethod:@"titleChanged" arguments:[NSString stringWithCString:title.c_str() encoding:NSUTF8StringEncoding]]; }; + //allcookie visited cb + handler.get()->onAllCookieVisitedCb = [](std::map> cookies) { + NSMutableDictionary * dict = [NSMutableDictionary dictionary]; + for(auto &cookie : cookies) + { + NSString * domain = [NSString stringWithCString:cookie.first.c_str() encoding:NSUTF8StringEncoding]; + NSMutableDictionary * tempdict = [NSMutableDictionary dictionary]; + for(auto &c : cookie.second) + { + NSString * key = [NSString stringWithCString:c.first.c_str() encoding:NSUTF8StringEncoding]; + NSString * val = [NSString stringWithCString:c.second.c_str() encoding:NSUTF8StringEncoding]; + tempdict[key] = val; + } + dict[domain] = tempdict; + } + [f_channel invokeMethod:@"allCookiesVisited" arguments:dict]; + }; + //urlcookie visited cb + handler.get()->onUrlCookieVisitedCb = [](std::map> cookies) { + NSMutableDictionary * dict = [NSMutableDictionary dictionary]; + for(auto &cookie : cookies) + { + NSString * domain = [NSString stringWithCString:cookie.first.c_str() encoding:NSUTF8StringEncoding]; + NSMutableDictionary * tempdict = [NSMutableDictionary dictionary]; + for(auto &c : cookie.second) + { + NSString * key = [NSString stringWithCString:c.first.c_str() encoding:NSUTF8StringEncoding]; + NSString * val = [NSString stringWithCString:c.second.c_str() encoding:NSUTF8StringEncoding]; + tempdict[key] = val; + } + dict[domain] = tempdict; + } + [f_channel invokeMethod:@"urlCookiesVisited" arguments:dict]; + }; + + //JavaScriptChannel called + handler.get()->onJavaScriptChannelMessage = [](std::string channelName, std::string message, std::string callbackId, std::string frameId) { + NSMutableDictionary * dict = [NSMutableDictionary dictionary]; + dict[@"channel"] = [NSString stringWithCString:channelName.c_str() encoding:NSUTF8StringEncoding]; + dict[@"message"] = [NSString stringWithCString:message.c_str() encoding:NSUTF8StringEncoding]; + dict[@"callbackId"] = [NSString stringWithCString:callbackId.c_str() encoding:NSUTF8StringEncoding]; + dict[@"frameId"] = [NSString stringWithCString:frameId.c_str() encoding:NSUTF8StringEncoding]; + [f_channel invokeMethod:@"javascriptChannelMessage" arguments:dict]; + }; + CefSettings settings; settings.windowless_rendering_enabled = true; settings.external_message_pump = true; @@ -248,4 +295,38 @@ FlutterMethodChannel* f_channel; f_channel = channel; } ++ (void)setCookie: (NSString *)domain key:(NSString *) key value:(NSString *)value { + handler.get()->setCookie(std::string([domain cStringUsingEncoding:NSUTF8StringEncoding]), std::string([key cStringUsingEncoding:NSUTF8StringEncoding]), std::string([value cStringUsingEncoding:NSUTF8StringEncoding])); +} + ++ (void)deleteCookie: (NSString *)domain key:(NSString *) key { + handler.get()->deleteCookie(std::string([domain cStringUsingEncoding:NSUTF8StringEncoding]), std::string([key cStringUsingEncoding:NSUTF8StringEncoding])); +} + ++ (void)visitAllCookies { + handler.get()->visitAllCookies(); +} + ++ (void)visitUrlCookies: (NSString *)domain isHttpOnly:(bool)isHttpOnly { + handler.get()->visitUrlCookies(std::string([domain cStringUsingEncoding:NSUTF8StringEncoding]), isHttpOnly); +} + ++ (void) setJavaScriptChannels: (NSArray *)channels { + std::vector stdChannels; + NSEnumerator * enumerator = [channels objectEnumerator]; + NSString * value; + while (value = [enumerator nextObject]) { + stdChannels.push_back(std::string([value cStringUsingEncoding:NSUTF8StringEncoding])); + } + handler.get()->setJavaScriptChannels(stdChannels); +} + ++ (void) sendJavaScriptChannelCallBack: (bool)error result:(NSString *)result callbackId:(NSString *)callbackId frameId:(NSString *)frameId { + handler.get()->sendJavaScriptChannelCallBack(error, std::string([result cStringUsingEncoding:NSUTF8StringEncoding]), + std::string([callbackId cStringUsingEncoding:NSUTF8StringEncoding]), std::string([frameId cStringUsingEncoding:NSUTF8StringEncoding])); +} + ++ (void) executeJavaScript: (NSString *)code { + handler.get()->executeJavaScript(std::string([code cStringUsingEncoding:NSUTF8StringEncoding])); +} @end diff --git a/macos/Classes/WebviewCefPlugin.m b/macos/Classes/WebviewCefPlugin.m index 2e2a66d..a3c8967 100644 --- a/macos/Classes/WebviewCefPlugin.m +++ b/macos/Classes/WebviewCefPlugin.m @@ -95,6 +95,53 @@ [CefWrapper openDevTools]; result(nil); } + else if([@"setCookie" isEqualToString:call.method]){ + NSArray *_arg = call.arguments; + NSString * domain = [_arg objectAtIndex:0]; + NSString * key = [_arg objectAtIndex:1]; + NSString * value = [_arg objectAtIndex:2]; + [CefWrapper setCookie:domain key:key value:value]; + result(nil); + } + else if ([@"deleteCookie" isEqualToString:call.method]) { + NSArray *_arg = call.arguments; + NSString * domain = [_arg objectAtIndex:0]; + NSString * key = [_arg objectAtIndex:1]; + [CefWrapper deleteCookie:domain key:key]; + result(nil); + } + else if ([@"visitAllCookies" isEqualToString:call.method]) { + [CefWrapper visitAllCookies]; + result(nil); + } + else if ([@"visitUrlCookies" isEqualToString:call.method]) { + NSArray *_arg = call.arguments; + NSString * domain = [_arg objectAtIndex:0]; + NSString * isHttpOnly = [_arg objectAtIndex:1]; + [CefWrapper visitUrlCookies:domain isHttpOnly:[isHttpOnly boolValue]]; + result(nil); + } + else if ([@"setJavaScriptChannels" isEqualToString:call.method]) { + NSArray *_arg = call.arguments; + NSArray * channels = [_arg objectAtIndex:0]; + [CefWrapper setJavaScriptChannels:channels]; + result(nil); + } + else if ([@"sendJavaScriptChannelCallBack" isEqualToString:call.method]) { + NSArray *_arg = call.arguments; + NSString * error = [_arg objectAtIndex:0]; + NSString * ret = [_arg objectAtIndex:1]; + NSString * callbackId = [_arg objectAtIndex:2]; + NSString * frameId = [_arg objectAtIndex:3]; + [CefWrapper sendJavaScriptChannelCallBack:[error boolValue] result:ret callbackId:callbackId frameId:frameId]; + result(nil); + } + else if ([@"executeJavaScript" isEqualToString:call.method]) { + NSArray *_arg = call.arguments; + NSString * code = [_arg objectAtIndex:0]; + [CefWrapper executeJavaScript:code]; + result(nil); + } else { result(FlutterMethodNotImplemented); } diff --git a/macos/Classes/cef_bridge.cc b/macos/Classes/cef_bridge.cc index 239b952..7f6b2fd 100644 --- a/macos/Classes/cef_bridge.cc +++ b/macos/Classes/cef_bridge.cc @@ -4,5 +4,7 @@ #include "../../common/webview_app.cc" #include "../../common/webview_handler.cc" +#include "../../common/webview_cookieVisitor.cc" +#include "../../common/webview_js_handler.cc" #endif diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index 88d09c1..cb2c4ef 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -27,6 +27,10 @@ list(APPEND PLUGIN_SOURCES "${CMAKE_CURRENT_LIST_DIR}/../common/webview_app.h" "${CMAKE_CURRENT_LIST_DIR}/../common/webview_handler.cc" "${CMAKE_CURRENT_LIST_DIR}/../common/webview_handler.h" + "${CMAKE_CURRENT_LIST_DIR}/../common/webview_js_handler.cc" + "${CMAKE_CURRENT_LIST_DIR}/../common/webview_js_handler.h" + "${CMAKE_CURRENT_LIST_DIR}/../common/webview_cookieVisitor.cc" + "${CMAKE_CURRENT_LIST_DIR}/../common/webview_cookieVisitor.h" ) # Define the plugin library target. Its name must not be changed (see comment diff --git a/windows/webview_cef_plugin.cpp b/windows/webview_cef_plugin.cpp index 1c26f96..d944987 100644 --- a/windows/webview_cef_plugin.cpp +++ b/windows/webview_cef_plugin.cpp @@ -12,11 +12,9 @@ #include #include -#include +#include #include -#include "webview_app.h" - namespace webview_cef { bool init = false; int64_t texture_id; @@ -38,6 +36,7 @@ namespace webview_cef { CefRefPtr handler(new WebviewHandler()); CefRefPtr app(new WebviewApp(handler)); CefMainArgs mainArgs; + std::unique_ptr< flutter::MethodChannel, std::default_delete>> @@ -98,6 +97,43 @@ namespace webview_cef { channel->InvokeMethod("titleChanged", std::make_unique(title)); }; + handler.get()->onAllCookieVisitedCb = [](std::map> cookies) { + flutter::EncodableMap retMap; + for (auto& cookie : cookies) + { + flutter::EncodableMap tempMap; + for (auto& c : cookie.second) + { + tempMap[flutter::EncodableValue(c.first)] = flutter::EncodableValue(c.second); + } + retMap[flutter::EncodableValue(cookie.first)] = flutter::EncodableValue(tempMap); + } + channel->InvokeMethod("allCookiesVisited", std::make_unique(retMap)); + }; + + handler.get()->onUrlCookieVisitedCb = [](std::map> cookies) { + flutter::EncodableMap retMap; + for (auto& cookie : cookies) + { + flutter::EncodableMap tempMap; + for (auto& c : cookie.second) + { + tempMap[flutter::EncodableValue(c.first)] = flutter::EncodableValue(c.second); + } + retMap[flutter::EncodableValue(cookie.first)] = flutter::EncodableValue(tempMap); + } + channel->InvokeMethod("urlCookiesVisited", std::make_unique(retMap)); + }; + + handler.get()->onJavaScriptChannelMessage = [](std::string channelName, std::string message, std::string callbackId, std::string frameId) { + flutter::EncodableMap retMap; + retMap[flutter::EncodableValue("channel")] = flutter::EncodableValue(channelName); + retMap[flutter::EncodableValue("message")] = flutter::EncodableValue(message); + retMap[flutter::EncodableValue("callbackId")] = flutter::EncodableValue(callbackId); + retMap[flutter::EncodableValue("frameId")] = flutter::EncodableValue(frameId); + channel->InvokeMethod("javascriptChannelMessage", std::make_unique(retMap)); + }; + CefSettings cefs; cefs.windowless_rendering_enabled = true; CefInitialize(mainArgs, cefs, app.get(), nullptr); @@ -233,6 +269,63 @@ namespace webview_cef { handler.get()->openDevTools(); result->Success(); } + else if(method_call.method_name().compare("setCookie") == 0){ + const flutter::EncodableList* list = + std::get_if(method_call.arguments()); + const auto domain = *std::get_if(&(*list)[0]); + const auto key = *std::get_if(&(*list)[1]); + const auto value = *std::get_if(&(*list)[2]); + handler.get()->setCookie(domain, key, value); + result->Success(); + } + else if (method_call.method_name().compare("deleteCookie") == 0) { + const flutter::EncodableList* list = + std::get_if(method_call.arguments()); + const auto domain = *std::get_if(&(*list)[0]); + const auto key = *std::get_if(&(*list)[1]); + handler.get()->deleteCookie(domain, key); + result->Success(); + } + else if (method_call.method_name().compare("visitAllCookies") == 0) { + handler.get()->visitAllCookies(); + result->Success(); + } + else if (method_call.method_name().compare("visitUrlCookies") == 0) { + const flutter::EncodableList* list = + std::get_if(method_call.arguments()); + const auto domain = *std::get_if(&(*list)[0]); + const auto isHttpOnly = *std::get_if(&(*list)[1]); + handler.get()->visitUrlCookies(domain, isHttpOnly); + result->Success(); + } + else if(method_call.method_name().compare("setJavaScriptChannels") == 0){ + const flutter::EncodableList* list = + std::get_if(method_call.arguments()); + const auto jsChannels = *std::get_if>(&(*list)[0]); + std::vector channels; + for (auto& jsChannel : jsChannels) { + channels.push_back(*std::get_if(&(jsChannel))); + } + handler.get()->setJavaScriptChannels(channels); + result->Success(); + } + else if (method_call.method_name().compare("sendJavaScriptChannelCallBack") == 0) { + const flutter::EncodableList* list = + std::get_if(method_call.arguments()); + const auto error = *std::get_if(&(*list)[0]); + const auto ret = *std::get_if(&(*list)[1]); + const auto callbackId = *std::get_if(&(*list)[2]); + const auto frameId = *std::get_if(&(*list)[3]); + handler.get()->sendJavaScriptChannelCallBack(error, ret,callbackId,frameId); + result->Success(); + } + else if(method_call.method_name().compare("executeJavaScript") == 0){ + const flutter::EncodableList* list = + std::get_if(method_call.arguments()); + const auto code = *std::get_if(&(*list)[0]); + handler.get()->executeJavaScript(code); + result->Success(); + } else { result->NotImplemented(); } diff --git a/windows/webview_cef_plugin.h b/windows/webview_cef_plugin.h index dff658b..0a6c7ba 100644 --- a/windows/webview_cef_plugin.h +++ b/windows/webview_cef_plugin.h @@ -6,10 +6,13 @@ #include -#include "include/cef_app.h" +#include "webview_app.h" namespace webview_cef { +//Use WebviewApp for both CefExecuteProcess and CefInitialize. +extern CefRefPtr app; + class WebviewCefPlugin : public flutter::Plugin { public: static void RegisterWithRegistrar(flutter::PluginRegistrarWindows *registrar); diff --git a/windows/webview_cef_plugin_c_api.cpp b/windows/webview_cef_plugin_c_api.cpp index fa93868..03bff3f 100644 --- a/windows/webview_cef_plugin_c_api.cpp +++ b/windows/webview_cef_plugin_c_api.cpp @@ -3,7 +3,7 @@ #include #include "webview_cef_plugin.h" -#include "include/cef_app.h" +//#include "include/cef_app.h" void WebviewCefPluginCApiRegisterWithRegistrar( FlutterDesktopPluginRegistrarRef registrar) { @@ -14,7 +14,8 @@ void WebviewCefPluginCApiRegisterWithRegistrar( FLUTTER_PLUGIN_EXPORT void initCEFProcesses() { CefMainArgs mainArgs; - CefExecuteProcess(mainArgs, nullptr, nullptr); + //Post class WebviewApp can run the override functions which support JSBridge/JSSDK + CefExecuteProcess(mainArgs, webview_cef::app, nullptr); } bool IsKeyDown(WPARAM wparam) {