mirror of
https://github.com/chenasraf/webview_cef.git
synced 2026-05-17 17:48:07 +00:00
Merge pull request #56 from SinyimZhi/main
support cookie manager and jsbridge/jschannel
This commit is contained in:
@@ -101,3 +101,130 @@ CefRefPtr<CefClient> 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<CefJSHandler> 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<CefBrowser> browser, CefRefPtr<CefDictionaryValue> extra_info)
|
||||
{
|
||||
if (!m_render_js_bridge.get())
|
||||
m_render_js_bridge.reset(new CefJSBridge);
|
||||
}
|
||||
|
||||
void WebviewApp::OnBrowserDestroyed(CefRefPtr<CefBrowser> browser)
|
||||
{
|
||||
}
|
||||
|
||||
void WebviewApp::OnContextCreated(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context)
|
||||
{
|
||||
}
|
||||
|
||||
void WebviewApp::OnContextReleased(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context)
|
||||
{
|
||||
if (m_render_js_bridge.get())
|
||||
{
|
||||
m_render_js_bridge->RemoveCallbackFuncWithFrame(frame);
|
||||
}
|
||||
}
|
||||
|
||||
void WebviewApp::OnUncaughtException(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context, CefRefPtr<CefV8Exception> exception, CefRefPtr<CefV8StackTrace> stackTrace)
|
||||
{
|
||||
}
|
||||
|
||||
void WebviewApp::OnFocusedNodeChanged(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefDOMNode> 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<CefProcessMessage> message = CefProcessMessage::Create(kFocusedNodeChangedMessage);
|
||||
|
||||
message->GetArgumentList()->SetBool(0, is_editable);
|
||||
frame->SendProcessMessage(PID_BROWSER, message);
|
||||
}
|
||||
}
|
||||
|
||||
bool WebviewApp::OnProcessMessageReceived(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefProcessId source_process, CefRefPtr<CefProcessMessage> 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;
|
||||
}
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
#ifndef CEF_TESTS_CEFSIMPLE_SIMPLE_APP_H_
|
||||
#define CEF_TESTS_CEFSIMPLE_SIMPLE_APP_H_
|
||||
|
||||
#include "include/cef_app.h"
|
||||
#include <functional>
|
||||
#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<WebviewHandler> handler);
|
||||
|
||||
@@ -18,6 +18,11 @@ public:
|
||||
CefRefPtr<CefBrowserProcessHandler> GetBrowserProcessHandler() override {
|
||||
return this;
|
||||
}
|
||||
|
||||
CefRefPtr<CefRenderProcessHandler> GetRenderProcessHandler() override {
|
||||
return this;
|
||||
}
|
||||
|
||||
void OnBeforeCommandLineProcessing(
|
||||
const CefString& process_type,
|
||||
CefRefPtr<CefCommandLine> 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<CefClient> GetDefaultClient() override;
|
||||
|
||||
// CefRenderProcessHandler methods.
|
||||
void OnWebKitInitialized() override;
|
||||
void OnBrowserCreated(
|
||||
CefRefPtr<CefBrowser> browser,
|
||||
CefRefPtr<CefDictionaryValue> extra_info) override;
|
||||
void OnBrowserDestroyed(CefRefPtr<CefBrowser> browser) override;
|
||||
void OnContextCreated(
|
||||
CefRefPtr<CefBrowser> browser,
|
||||
CefRefPtr<CefFrame> frame,
|
||||
CefRefPtr<CefV8Context> context) override;
|
||||
void OnContextReleased(
|
||||
CefRefPtr<CefBrowser> browser,
|
||||
CefRefPtr<CefFrame> frame,
|
||||
CefRefPtr<CefV8Context> context) override;
|
||||
void OnUncaughtException(
|
||||
CefRefPtr<CefBrowser> browser,
|
||||
CefRefPtr<CefFrame> frame,
|
||||
CefRefPtr<CefV8Context> context,
|
||||
CefRefPtr<CefV8Exception> exception,
|
||||
CefRefPtr<CefV8StackTrace> stackTrace) override;
|
||||
void OnFocusedNodeChanged(
|
||||
CefRefPtr<CefBrowser> browser,
|
||||
CefRefPtr<CefFrame> frame,
|
||||
CefRefPtr<CefDOMNode> node) override;
|
||||
bool OnProcessMessageReceived(
|
||||
CefRefPtr<CefBrowser> browser,
|
||||
CefRefPtr<CefFrame> frame,
|
||||
CefProcessId source_process,
|
||||
CefRefPtr<CefProcessMessage> message) override;
|
||||
|
||||
private:
|
||||
CefRefPtr<WebviewHandler> m_handler;
|
||||
|
||||
std::shared_ptr<CefJSBridge> m_render_js_bridge;
|
||||
bool m_last_node_is_editable = false;
|
||||
|
||||
// Include the default reference counting implementation.
|
||||
IMPLEMENT_REFCOUNTING(WebviewApp);
|
||||
};
|
||||
|
||||
38
common/webview_cookieVisitor.cc
Normal file
38
common/webview_cookieVisitor.cc
Normal file
@@ -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<std::mutex> lock(m_mutexCookieVector);
|
||||
if (count == 0)
|
||||
{
|
||||
m_vecAllCookies.clear();
|
||||
}
|
||||
|
||||
m_vecAllCookies.emplace_back(cookie);
|
||||
}
|
||||
|
||||
return count != total;
|
||||
}
|
||||
|
||||
std::map<std::string, std::map<std::string, std::string>> WebviewCookieVisitor::getVisitedCookies()
|
||||
{
|
||||
std::map<std::string, std::map<std::string, std::string>> 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;
|
||||
}
|
||||
28
common/webview_cookieVisitor.h
Normal file
28
common/webview_cookieVisitor.h
Normal file
@@ -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 <mutex>
|
||||
#include <map>
|
||||
|
||||
class WebviewCookieVisitor : public CefCookieVisitor
|
||||
{
|
||||
public:
|
||||
WebviewCookieVisitor();
|
||||
~WebviewCookieVisitor();
|
||||
|
||||
//CefCookieVisitor
|
||||
bool Visit(const CefCookie& cookie, int count, int total, bool& deleteCookie);
|
||||
|
||||
std::map<std::string, std::map<std::string, std::string>> getVisitedCookies();
|
||||
|
||||
// Include the default reference counting implementation.
|
||||
IMPLEMENT_REFCOUNTING(WebviewCookieVisitor);
|
||||
|
||||
private:
|
||||
std::vector<CefCookie> m_vecAllCookies;
|
||||
std::mutex m_mutexCookieVector;
|
||||
};
|
||||
|
||||
#endif // WEBVIEW_CEF_COOKIE_VISITOR_H_
|
||||
@@ -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<CefBrowser> browser, CefRefPtr<CefFrame> frame,
|
||||
CefProcessId source_process, CefRefPtr<CefProcessMessage> 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<CefBrowser> 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<CefCookieManager> 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<CefCookieManager> manager = CefCookieManager::GetGlobalManager(nullptr);
|
||||
if (manager) {
|
||||
std::string httpDomain = "https://" + domain + "/cookiestorage";
|
||||
manager->DeleteCookies(httpDomain, key, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
bool WebviewHandler::visitAllCookies(){
|
||||
CefRefPtr<CefCookieManager> 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<CefCookieManager> 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<std::string> 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<CefProcessMessage> message = CefProcessMessage::Create(kExecuteJsCallbackMessage);
|
||||
CefRefPtr<CefListValue> 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<CefFrame> 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<CefFrame> frame = (*bit)->GetMainFrame();
|
||||
if (frame) {
|
||||
frame->ExecuteJavaScript(code, frame->GetURL(), 0);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void WebviewHandler::GetViewRect(CefRefPtr<CefBrowser> browser, CefRect &rect) {
|
||||
CEF_REQUIRE_UI_THREAD();
|
||||
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#include <functional>
|
||||
#include <list>
|
||||
|
||||
#include "webview_cookieVisitor.h"
|
||||
|
||||
class WebviewHandler : public CefClient,
|
||||
public CefDisplayHandler,
|
||||
public CefLifeSpanHandler,
|
||||
@@ -19,6 +21,9 @@ public:
|
||||
std::function<void(const void*, int32_t width, int32_t height)> onPaintCallback;
|
||||
std::function<void(std::string url)> onUrlChangedCb;
|
||||
std::function<void(std::string title)> onTitleChangedCb;
|
||||
std::function<void(std::map<std::string, std::map<std::string, std::string>>)> onAllCookieVisitedCb;
|
||||
std::function<void(std::map<std::string, std::map<std::string, std::string>>)> onUrlCookieVisitedCb;
|
||||
std::function<void(std::string channelName, std::string message, std::string js_callback_id, std::string frameId)> onJavaScriptChannelMessage;
|
||||
|
||||
explicit WebviewHandler();
|
||||
~WebviewHandler();
|
||||
@@ -35,6 +40,13 @@ public:
|
||||
}
|
||||
virtual CefRefPtr<CefLoadHandler> GetLoadHandler() override { return this; }
|
||||
virtual CefRefPtr<CefRenderHandler> GetRenderHandler() override { return this; }
|
||||
|
||||
bool OnProcessMessageReceived(
|
||||
CefRefPtr<CefBrowser> browser,
|
||||
CefRefPtr<CefFrame> frame,
|
||||
CefProcessId source_process,
|
||||
CefRefPtr<CefProcessMessage> message) override;
|
||||
|
||||
|
||||
// CefDisplayHandler methods:
|
||||
virtual void OnTitleChange(CefRefPtr<CefBrowser> 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<std::string> 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<WebviewCookieVisitor> m_CookieVisitor;
|
||||
};
|
||||
|
||||
#endif // CEF_TESTS_CEFSIMPLE_SIMPLE_HANDLER_H_
|
||||
|
||||
265
common/webview_js_handler.cc
Normal file
265
common/webview_js_handler.cc
Normal file
@@ -0,0 +1,265 @@
|
||||
#include "webview_js_handler.h"
|
||||
#include <atomic>
|
||||
|
||||
std::atomic_long s_nReqID {1001};
|
||||
|
||||
bool CefJSHandler::Execute(const CefString& name,
|
||||
CefRefPtr<CefV8Value> object,
|
||||
const CefV8ValueList& arguments,
|
||||
CefRefPtr<CefV8Value>& 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<CefV8Value> callback;
|
||||
CefRefPtr<CefV8Value> 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<CefV8Context> context = CefV8Context::GetCurrentContext();
|
||||
if (context)
|
||||
{
|
||||
CefRefPtr<CefFrame> frame = context->GetFrame();
|
||||
if (frame)
|
||||
{
|
||||
CefRefPtr<CefProcessMessage> 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<CefV8Value> callback,
|
||||
CefRefPtr<CefV8Value> rawdata)
|
||||
{
|
||||
auto it = render_callback_.find(js_callback_id_);
|
||||
if (it == render_callback_.cend())
|
||||
{
|
||||
CefRefPtr<CefV8Context> context = CefV8Context::GetCurrentContext();
|
||||
if (context)
|
||||
{
|
||||
CefRefPtr<CefFrame> frame = context->GetFrame();
|
||||
if (frame)
|
||||
{
|
||||
CefRefPtr<CefProcessMessage> 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<CefFrame> 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<CefV8Value> retval = callback->ExecuteFunction(nullptr, arguments);
|
||||
context->Exit();
|
||||
render_callback_.erase(callbackId);
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
50
common/webview_js_handler.h
Normal file
50
common/webview_js_handler.h
Normal file
@@ -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 <functional>
|
||||
#include <memory>
|
||||
|
||||
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<int/* js_callback_id*/, std::pair<CefRefPtr<CefV8Context>/* context*/, std::pair<CefRefPtr<CefV8Value>/* callback*/, CefRefPtr<CefV8Value>/* rawdata*/>>> RenderCallbackMap;
|
||||
typedef std::map<int/* reqId*/, std::pair<CefRefPtr<CefFrame>/* 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<CefV8Value> callback, CefRefPtr<CefV8Value> rawdata);
|
||||
void RemoveCallbackFuncWithFrame(CefRefPtr<CefFrame> 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<CefV8Value> object,
|
||||
const CefV8ValueList& arguments,
|
||||
CefRefPtr<CefV8Value>& retval,
|
||||
CefString& exception) override;
|
||||
void AttachJSBridge(std::shared_ptr<CefJSBridge> js_bridge) { js_bridge_ = js_bridge; }
|
||||
IMPLEMENT_REFCOUNTING(CefJSHandler);
|
||||
private:
|
||||
std::shared_ptr<CefJSBridge> js_bridge_;
|
||||
};
|
||||
|
||||
#endif // WEBVIEW_CEF_JS_HANDLER_H_
|
||||
@@ -41,6 +41,24 @@ class _MyAppState extends State<MyApp> {
|
||||
_textController.text = url;
|
||||
},
|
||||
));
|
||||
|
||||
// ignore: prefer_collection_literals
|
||||
final Set<JavascriptChannel> 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.
|
||||
|
||||
@@ -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> {
|
||||
bool _isDisposed = false;
|
||||
WebviewEventsListener? _listener;
|
||||
|
||||
final Map<String, JavascriptChannel> _javascriptChannels =
|
||||
<String, JavascriptChannel>{};
|
||||
|
||||
Future<void> get ready => _creatingCompleter.future;
|
||||
|
||||
WebViewController() : super(false);
|
||||
@@ -46,6 +50,19 @@ class WebViewController extends ValueNotifier<bool> {
|
||||
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<bool> {
|
||||
await _creatingCompleter.future;
|
||||
if (!_isDisposed) {
|
||||
_isDisposed = true;
|
||||
_javascriptChannels.clear();
|
||||
await _pluginChannel.invokeMethod('dispose', _textureId);
|
||||
}
|
||||
super.dispose();
|
||||
@@ -106,6 +124,71 @@ class WebViewController extends ValueNotifier<bool> {
|
||||
return _pluginChannel.invokeMethod('openDevTools');
|
||||
}
|
||||
|
||||
Future<void> setCookie(String domain, String key, String val) async {
|
||||
if (_isDisposed) {
|
||||
return;
|
||||
}
|
||||
assert(value);
|
||||
return _pluginChannel.invokeMethod('setCookie', [domain, key, val]);
|
||||
}
|
||||
|
||||
Future<void> deleteCookie(String domain, String key) async {
|
||||
if (_isDisposed) {
|
||||
return;
|
||||
}
|
||||
assert(value);
|
||||
return _pluginChannel.invokeMethod('deleteCookie', [domain, key]);
|
||||
}
|
||||
|
||||
Future<void> visitAllCookies() async {
|
||||
if (_isDisposed) {
|
||||
return;
|
||||
}
|
||||
assert(value);
|
||||
return _pluginChannel.invokeMethod('visitAllCookies');
|
||||
}
|
||||
|
||||
Future<void> visitUrlCookies(String domain, bool isHttpOnly) async {
|
||||
if (_isDisposed) {
|
||||
return;
|
||||
}
|
||||
assert(value);
|
||||
return _pluginChannel.invokeMethod('visitUrlCookies', [domain, isHttpOnly]);
|
||||
}
|
||||
|
||||
Future<void> setJavaScriptChannels(Set<JavascriptChannel> channels) async {
|
||||
if (_isDisposed) {
|
||||
return;
|
||||
}
|
||||
assert(value);
|
||||
_assertJavascriptChannelNamesAreUnique(channels);
|
||||
|
||||
channels.forEach((channel) {
|
||||
_javascriptChannels[channel.name] = channel;
|
||||
});
|
||||
|
||||
return _pluginChannel.invokeMethod('setJavaScriptChannels',
|
||||
[_extractJavascriptChannelNames(channels).toList()]);
|
||||
}
|
||||
|
||||
Future<void> sendJavaScriptChannelCallBack(
|
||||
bool error, String result, String callbackId, String frameId) async {
|
||||
if (_isDisposed) {
|
||||
return;
|
||||
}
|
||||
assert(value);
|
||||
return _pluginChannel.invokeMethod(
|
||||
'sendJavaScriptChannelCallBack', [error, result, callbackId, frameId]);
|
||||
}
|
||||
|
||||
Future<void> executeJavaScript(String code) async {
|
||||
if (_isDisposed) {
|
||||
return;
|
||||
}
|
||||
assert(value);
|
||||
return _pluginChannel.invokeMethod('executeJavaScript', [code]);
|
||||
}
|
||||
|
||||
/// Moves the virtual cursor to [position].
|
||||
Future<void> _cursorMove(Offset position) async {
|
||||
if (_isDisposed) {
|
||||
@@ -162,6 +245,30 @@ class WebViewController extends ValueNotifier<bool> {
|
||||
return _pluginChannel
|
||||
.invokeMethod('setSize', [dpi, size.width, size.height]);
|
||||
}
|
||||
|
||||
Set<String> _extractJavascriptChannelNames(Set<JavascriptChannel> channels) {
|
||||
final Set<String> 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<JavascriptChannel>? channels) {
|
||||
if (channels == null || channels.isEmpty) {
|
||||
return;
|
||||
}
|
||||
assert(_extractJavascriptChannelNames(channels).length == channels.length);
|
||||
}
|
||||
}
|
||||
|
||||
class WebView extends StatefulWidget {
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
typedef TitleChangeCb = void Function(String title);
|
||||
typedef UrlChangeCb = void Function(String url);
|
||||
typedef AllCookieVisitedCb = void Function(Map<String, dynamic> cookies);
|
||||
typedef UrlCookieVisitedCb = void Function(Map<String, dynamic> 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});
|
||||
}
|
||||
|
||||
49
lib/src/webview_javascript.dart
Normal file
49
lib/src/webview_javascript.dart
Normal file
@@ -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);
|
||||
@@ -1,2 +1,3 @@
|
||||
export 'src/webview.dart';
|
||||
export 'src/webview_events_listener.dart';
|
||||
export 'src/webview_javascript.dart';
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 <thread>
|
||||
|
||||
@@ -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<std::string, std::map<std::string, std::string>> 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<std::string, std::map<std::string, std::string>> 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<std::string> 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
|
||||
|
||||
@@ -95,6 +95,53 @@
|
||||
[CefWrapper openDevTools];
|
||||
result(nil);
|
||||
}
|
||||
else if([@"setCookie" isEqualToString:call.method]){
|
||||
NSArray<NSString *> *_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<NSString *> *_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<NSString *> *_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<NSArray *> *_arg = call.arguments;
|
||||
NSArray * channels = [_arg objectAtIndex:0];
|
||||
[CefWrapper setJavaScriptChannels:channels];
|
||||
result(nil);
|
||||
}
|
||||
else if ([@"sendJavaScriptChannelCallBack" isEqualToString:call.method]) {
|
||||
NSArray<NSString *> *_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<NSString *> *_arg = call.arguments;
|
||||
NSString * code = [_arg objectAtIndex:0];
|
||||
[CefWrapper executeJavaScript:code];
|
||||
result(nil);
|
||||
}
|
||||
else {
|
||||
result(FlutterMethodNotImplemented);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -12,11 +12,9 @@
|
||||
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include<iostream>
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
|
||||
#include "webview_app.h"
|
||||
|
||||
namespace webview_cef {
|
||||
bool init = false;
|
||||
int64_t texture_id;
|
||||
@@ -38,6 +36,7 @@ namespace webview_cef {
|
||||
CefRefPtr<WebviewHandler> handler(new WebviewHandler());
|
||||
CefRefPtr<WebviewApp> app(new WebviewApp(handler));
|
||||
CefMainArgs mainArgs;
|
||||
|
||||
std::unique_ptr<
|
||||
flutter::MethodChannel<flutter::EncodableValue>,
|
||||
std::default_delete<flutter::MethodChannel<flutter::EncodableValue>>>
|
||||
@@ -98,6 +97,43 @@ namespace webview_cef {
|
||||
channel->InvokeMethod("titleChanged", std::make_unique<flutter::EncodableValue>(title));
|
||||
};
|
||||
|
||||
handler.get()->onAllCookieVisitedCb = [](std::map<std::string, std::map<std::string, std::string>> 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<flutter::EncodableValue>(retMap));
|
||||
};
|
||||
|
||||
handler.get()->onUrlCookieVisitedCb = [](std::map<std::string, std::map<std::string, std::string>> 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<flutter::EncodableValue>(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<flutter::EncodableValue>(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<flutter::EncodableList>(method_call.arguments());
|
||||
const auto domain = *std::get_if<std::string>(&(*list)[0]);
|
||||
const auto key = *std::get_if<std::string>(&(*list)[1]);
|
||||
const auto value = *std::get_if<std::string>(&(*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<flutter::EncodableList>(method_call.arguments());
|
||||
const auto domain = *std::get_if<std::string>(&(*list)[0]);
|
||||
const auto key = *std::get_if<std::string>(&(*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<flutter::EncodableList>(method_call.arguments());
|
||||
const auto domain = *std::get_if<std::string>(&(*list)[0]);
|
||||
const auto isHttpOnly = *std::get_if<bool>(&(*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<flutter::EncodableList>(method_call.arguments());
|
||||
const auto jsChannels = *std::get_if<std::vector<flutter::EncodableValue>>(&(*list)[0]);
|
||||
std::vector<std::string> channels;
|
||||
for (auto& jsChannel : jsChannels) {
|
||||
channels.push_back(*std::get_if<std::string>(&(jsChannel)));
|
||||
}
|
||||
handler.get()->setJavaScriptChannels(channels);
|
||||
result->Success();
|
||||
}
|
||||
else if (method_call.method_name().compare("sendJavaScriptChannelCallBack") == 0) {
|
||||
const flutter::EncodableList* list =
|
||||
std::get_if<flutter::EncodableList>(method_call.arguments());
|
||||
const auto error = *std::get_if<bool>(&(*list)[0]);
|
||||
const auto ret = *std::get_if<std::string>(&(*list)[1]);
|
||||
const auto callbackId = *std::get_if<std::string>(&(*list)[2]);
|
||||
const auto frameId = *std::get_if<std::string>(&(*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<flutter::EncodableList>(method_call.arguments());
|
||||
const auto code = *std::get_if<std::string>(&(*list)[0]);
|
||||
handler.get()->executeJavaScript(code);
|
||||
result->Success();
|
||||
}
|
||||
else {
|
||||
result->NotImplemented();
|
||||
}
|
||||
|
||||
@@ -6,10 +6,13 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "include/cef_app.h"
|
||||
#include "webview_app.h"
|
||||
|
||||
namespace webview_cef {
|
||||
|
||||
//Use WebviewApp for both CefExecuteProcess and CefInitialize.
|
||||
extern CefRefPtr<WebviewApp> app;
|
||||
|
||||
class WebviewCefPlugin : public flutter::Plugin {
|
||||
public:
|
||||
static void RegisterWithRegistrar(flutter::PluginRegistrarWindows *registrar);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <flutter/plugin_registrar_windows.h>
|
||||
|
||||
#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) {
|
||||
|
||||
Reference in New Issue
Block a user