diff --git a/common/webview_app.cc b/common/webview_app.cc index 45de898..9b5b174 100644 --- a/common/webview_app.cc +++ b/common/webview_app.cc @@ -150,6 +150,11 @@ void WebviewApp::OnWebKitInitialized() } } + external.EvaluateCallback = (nReqID, result) => { + native function EvaluateCallback(); + EvaluateCallback(nReqID, result); + } + external.StartRequest = (nReqID, strCmd, strCallBack, strArgs, strLog) => { native function StartRequest(); StartRequest(nReqID, strCmd, strCallBack, strArgs, strLog); diff --git a/common/webview_handler.cc b/common/webview_handler.cc index 8b1fc04..6eba259 100644 --- a/common/webview_handler.cc +++ b/common/webview_handler.cc @@ -7,6 +7,7 @@ #include #include #include +#include #include "include/base/cef_callback.h" #include "include/cef_app.h" @@ -64,6 +65,17 @@ bool WebviewHandler::OnProcessMessageReceived( onJavaScriptChannelMessage( fun_name,param,std::to_string(js_callback_id),std::to_string(frame->GetIdentifier())); } + else if(message_name == kEvaluateCallbackMessage){ + CefString callbackId = message->GetArgumentList()->GetString(0); + CefString param = message->GetArgumentList()->GetString(1); + if(!callbackId.empty() && !param.empty()){ + auto it = js_callbacks_.find(callbackId.ToString()); + if(it != js_callbacks_.end()){ + it->second(param.ToString()); + js_callbacks_.erase(it); + } + } + } return false; } @@ -373,24 +385,24 @@ void WebviewHandler::deleteCookie(const std::string& domain, const std::string& } } -bool WebviewHandler::visitAllCookies(std::function>)> callback){ +void WebviewHandler::visitAllCookies(std::function>)> callback){ CefRefPtr manager = CefCookieManager::GetGlobalManager(nullptr); if (!manager) { - return false; + return; } CefRefPtr cookieVisitor = new WebviewCookieVisitor(); cookieVisitor->setOnVisitComplete(callback); - return manager->VisitAllCookies(cookieVisitor); + manager->VisitAllCookies(cookieVisitor); } -bool WebviewHandler::visitUrlCookies(const std::string& domain, const bool& isHttpOnly, std::function>)> callback){ +void WebviewHandler::visitUrlCookies(const std::string& domain, const bool& isHttpOnly, std::function>)> callback){ CefRefPtr manager = CefCookieManager::GetGlobalManager(nullptr); if (!manager) { - return false; + return; } CefRefPtr cookieVisitor = new WebviewCookieVisitor(); @@ -398,10 +410,10 @@ bool WebviewHandler::visitUrlCookies(const std::string& domain, const bool& isHt std::string httpDomain = "https://" + domain + "/cookiestorage"; - return manager->VisitUrlCookies(httpDomain, isHttpOnly, cookieVisitor); + manager->VisitUrlCookies(httpDomain, isHttpOnly, cookieVisitor); } -bool WebviewHandler::setJavaScriptChannels(const std::vector channels) +void WebviewHandler::setJavaScriptChannels(const std::vector channels) { std::string extensionCode = ""; for(auto& channel : channels) @@ -411,10 +423,10 @@ bool WebviewHandler::setJavaScriptChannels(const std::vector channe extensionCode += channel; extensionCode += "',e,r)};"; } - return executeJavaScript(extensionCode); + executeJavaScript(extensionCode); } -bool WebviewHandler::sendJavaScriptChannelCallBack(const bool error, const std::string result, const std::string callbackId, const std::string frameId) +void 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(); @@ -427,13 +439,18 @@ bool WebviewHandler::sendJavaScriptChannelCallBack(const bool error, const std:: if (frame->GetIdentifier() == atoll(frameId.c_str())) { frame->SendProcessMessage(PID_RENDERER, message); - return true; } } - return false; } -bool WebviewHandler::executeJavaScript(const std::string code) +static std::string GetCallbackId() +{ + auto time = std::chrono::time_point_cast(std::chrono::system_clock::now()); + time_t timestamp = time.time_since_epoch().count(); + return std::to_string(timestamp); +} + +void WebviewHandler::executeJavaScript(const std::string code, std::function callback) { if(!code.empty()) { @@ -442,13 +459,21 @@ bool WebviewHandler::executeJavaScript(const std::string code) if ((*bit).get()) { CefRefPtr frame = (*bit)->GetMainFrame(); if (frame) { - frame->ExecuteJavaScript(code, frame->GetURL(), 0); - return true; + std::string finalCode = code; + if(callback != nullptr){ + std::string callbackId = GetCallbackId(); + finalCode = "external.EvaluateCallback('"; + finalCode += callbackId; + finalCode += "',(function(){return "; + finalCode += code; + finalCode += "})());"; + js_callbacks_[callbackId] = callback; + } + frame->ExecuteJavaScript(finalCode, frame->GetURL(), 0); } } } } - return false; } void WebviewHandler::GetViewRect(CefRefPtr browser, CefRect &rect) { diff --git a/common/webview_handler.h b/common/webview_handler.h index f3cd019..666a35d 100644 --- a/common/webview_handler.h +++ b/common/webview_handler.h @@ -9,6 +9,7 @@ #include #include +#include #include "webview_cookieVisitor.h" @@ -122,12 +123,12 @@ public CefRenderHandler{ 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(std::function>)> callback); - bool visitUrlCookies(const std::string& domain, const bool& isHttpOnly, std::function>)> callback); + void visitAllCookies(std::function>)> callback); + void visitUrlCookies(const std::string& domain, const bool& isHttpOnly, std::function>)> callback); - 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); + void setJavaScriptChannels(const std::vector channels); + void sendJavaScriptChannelCallBack(const bool error, const std::string result, const std::string callbackId, const std::string frameId); + void executeJavaScript(const std::string code, std::function callback = nullptr); private: uint32_t width = 1; @@ -138,6 +139,8 @@ public CefRenderHandler{ // List of existing browser windows. Only accessed on the CEF UI thread. typedef std::list> BrowserList; BrowserList browser_list_; + + std::unordered_map> js_callbacks_; // Include the default reference counting implementation. IMPLEMENT_REFCOUNTING(WebviewHandler); diff --git a/common/webview_js_handler.cc b/common/webview_js_handler.cc index 543de08..6916546 100644 --- a/common/webview_js_handler.cc +++ b/common/webview_js_handler.cc @@ -86,6 +86,17 @@ bool CefJSHandler::Execute(const CefString& name, int reqID = CefJSBridge::GetNextReqID(); retval = CefV8Value::CreateInt(reqID); } + else if (name == "EvaluateCallback") { + CefString callbackId = arguments[0]->GetStringValue(); + CefString result = arguments[1]->GetStringValue(); + if (!js_bridge_->EvaluateCallback(callbackId, result)) { + std::ostringstream strStream; + strStream << "Failed to callback: " << callbackId.c_str() << "."; + strStream.flush(); + + exception = strStream.str(); + } + } else { exception = "NativeHost no this fun."; } @@ -125,6 +136,22 @@ bool CefJSBridge::StartRequest(int reqId, return false; } +bool CefJSBridge::EvaluateCallback(const CefString& callbackId, const CefString& result){ + CefRefPtr context = CefV8Context::GetCurrentContext(); + if(context){ + CefRefPtr frame = context->GetFrame(); + if(frame){ + CefRefPtr message = CefProcessMessage::Create(kEvaluateCallbackMessage); + message->GetArgumentList()->SetString(0, callbackId); + message->GetArgumentList()->SetString(1, result); + frame->SendProcessMessage(PID_BROWSER, message); + return true; + } + } + return false; +} + + int CefJSBridge::GetNextReqID() { long nRet = ++s_nReqID; diff --git a/common/webview_js_handler.h b/common/webview_js_handler.h index 6ec7945..3c94293 100644 --- a/common/webview_js_handler.h +++ b/common/webview_js_handler.h @@ -9,6 +9,7 @@ static const char kJSCallCppFunctionMessage[] = "JSCallCppFunction"; //js call c++ message static const char kExecuteJsCallbackMessage[] = "ExecuteJsCallback"; //c++ call js message +static const char kEvaluateCallbackMessage[] = "EvaluateCallback"; //js callback c++ message static const char kFocusedNodeChangedMessage[] = "FocusedNodeChanged"; //elements that capture focus in web pages changed message class CefJSBridge @@ -22,6 +23,7 @@ class CefJSBridge public: static int GetNextReqID(); bool StartRequest(int reqId, const CefString& strCmd, const CefString& strCallback, const CefString& strArgs); + bool EvaluateCallback(const CefString& callbackId, const CefString& result); bool CallCppFunction(const CefString& function_name, const CefString& params, CefRefPtr callback, CefRefPtr rawdata); void RemoveCallbackFuncWithFrame(CefRefPtr frame); diff --git a/common/webview_plugin.cc b/common/webview_plugin.cc index e9df407..56d8112 100644 --- a/common/webview_plugin.cc +++ b/common/webview_plugin.cc @@ -219,6 +219,14 @@ namespace webview_cef { handler.get()->executeJavaScript(code); result(1, nullptr); } + else if(name.compare("evaluateJavascript") == 0){ + const auto code = webview_value_get_string(webview_value_get_list_value(values, 0)); + handler.get()->executeJavaScript(code, [=](std::string values){ + WValue* retValue = webview_value_new_string(values.c_str()); + result(1, retValue); + webview_value_unref(retValue); + }); + } else { result = 0; } diff --git a/example/lib/main.dart b/example/lib/main.dart index 4afaf6c..ee0f9ee 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -59,7 +59,8 @@ class _MyAppState extends State { //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)}"); + await _controller.executeJavaScript("function abc(e){return 'abc:'+ e}"); + _controller.evaluateJavascript("abc('test')").then((value) => print(value)); // 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 ec14499..410171f 100644 --- a/lib/src/webview.dart +++ b/lib/src/webview.dart @@ -194,7 +194,7 @@ class WebViewController extends ValueNotifier { 'sendJavaScriptChannelCallBack', [error, result, callbackId, frameId]); } - Future executeJavaScript(String code) async { + Future executeJavaScript(String code) async { if (_isDisposed) { return; } @@ -202,6 +202,14 @@ class WebViewController extends ValueNotifier { return _pluginChannel.invokeMethod('executeJavaScript', [code]); } + Future evaluateJavascript(String code) async { + if (_isDisposed) { + return; + } + assert(value); + return _pluginChannel.invokeMethod('evaluateJavascript', [code]); + } + /// Moves the virtual cursor to [position]. Future _cursorMove(Offset position) async { if (_isDisposed) { diff --git a/macos/Classes/CefWrapper.mm b/macos/Classes/CefWrapper.mm index 4c67cc7..b32f79f 100644 --- a/macos/Classes/CefWrapper.mm +++ b/macos/Classes/CefWrapper.mm @@ -288,9 +288,9 @@ + (void) handleMethodCallWrapper: (FlutterMethodCall*)call result:(FlutterResult result([NSNumber numberWithLong:textureId]); }else{ WValue *encodeArgs = [self encode_flvalue_to_wvalue:call.arguments]; - int ret = webview_cef::HandleMethodCall(name, encodeArgs, [=](int ret, WValue* args){ + webview_cef::HandleMethodCall(name, encodeArgs, [=](int ret, WValue* args){ if(ret != 0){ - result([self encode_wvalue_to_flvalue:args]) + result([self encode_wvalue_to_flvalue:args]); } else{ result(nil);