831 lines
31 KiB
C++
831 lines
31 KiB
C++
#include "webview_plugin.h"
|
|
|
|
#ifdef OS_MAC
|
|
#include <include/wrapper/cef_library_loader.h>
|
|
#endif
|
|
|
|
#include <math.h>
|
|
#include <filesystem>
|
|
#include <memory>
|
|
#include <math.h>
|
|
#include <memory>
|
|
#include <thread>
|
|
#include <iostream>
|
|
#include <unordered_map>
|
|
|
|
namespace webview_cef {
|
|
CefMainArgs mainArgs;
|
|
CefRefPtr<WebviewApp> app;
|
|
CefString userAgent;
|
|
CefString rootCachePath;
|
|
// JJ_MKVMaker: 프록시 (JJ_PATCH.md 6)
|
|
CefString proxyServer;
|
|
bool isCefInitialized = false;
|
|
#ifdef OS_MAC
|
|
std::string g_macSubprocessPath;
|
|
std::string g_macFrameworkDirPath;
|
|
std::string g_macMainBundlePath;
|
|
|
|
void setMacCEFPaths(const std::string& subprocessPath,
|
|
const std::string& frameworkDirPath,
|
|
const std::string& mainBundlePath) {
|
|
g_macSubprocessPath = subprocessPath;
|
|
g_macFrameworkDirPath = frameworkDirPath;
|
|
g_macMainBundlePath = mainBundlePath;
|
|
}
|
|
#endif
|
|
|
|
WebviewPlugin::WebviewPlugin() {
|
|
m_handler = new WebviewHandler();
|
|
}
|
|
|
|
WebviewPlugin::~WebviewPlugin() {
|
|
uninitCallback();
|
|
m_handler->CloseAllBrowsers(true);
|
|
m_handler = nullptr;
|
|
if(!m_renderers.empty()){
|
|
m_renderers.clear();
|
|
}
|
|
}
|
|
|
|
void WebviewPlugin::initCallback() {
|
|
if (!m_init)
|
|
{
|
|
m_handler->onPaintCallback = [=, this](int browserId, const void* buffer, int32_t width, int32_t height) {
|
|
if (m_renderers.find(browserId) != m_renderers.end() && m_renderers[browserId] != nullptr) {
|
|
m_renderers[browserId]->onFrame(buffer, width, height);
|
|
}
|
|
};
|
|
|
|
m_handler->onAcceleratedPaintCallback = [=, this](int browserId, const void* sharedHandle, int32_t width, int32_t height, int32_t format) {
|
|
if (m_renderers.find(browserId) != m_renderers.end() && m_renderers[browserId] != nullptr) {
|
|
m_renderers[browserId]->onAcceleratedFrame(sharedHandle, width, height, format);
|
|
}
|
|
};
|
|
|
|
m_handler->onTooltipEvent = [=, this](int browserId, std::string text) {
|
|
if (m_invokeFunc) {
|
|
WValue* bId = webview_value_new_int(browserId);
|
|
WValue* wText = webview_value_new_string(const_cast<char*>(text.c_str()));
|
|
WValue* retMap = webview_value_new_map();
|
|
webview_value_set_string(retMap, "browserId", bId);
|
|
webview_value_set_string(retMap, "text", wText);
|
|
m_invokeFunc("onTooltip", retMap);
|
|
webview_value_unref(bId);
|
|
webview_value_unref(wText);
|
|
webview_value_unref(retMap);
|
|
}
|
|
};
|
|
|
|
m_handler->onCursorChangedEvent = [=, this](int browserId, int type) {
|
|
if(m_invokeFunc){
|
|
WValue* bId = webview_value_new_int(browserId);
|
|
WValue* wType = webview_value_new_int(type);
|
|
WValue* retMap = webview_value_new_map();
|
|
webview_value_set_string(retMap, "browserId", bId);
|
|
webview_value_set_string(retMap, "type", wType);
|
|
m_invokeFunc("onCursorChanged", retMap);
|
|
webview_value_unref(bId);
|
|
webview_value_unref(wType);
|
|
webview_value_unref(retMap);
|
|
}
|
|
};
|
|
|
|
m_handler->onConsoleMessageEvent = [=, this](int browserId, int level, std::string message, std::string source, int line){
|
|
if(m_invokeFunc){
|
|
WValue* bId = webview_value_new_int(browserId);
|
|
WValue* wLevel = webview_value_new_int(level);
|
|
WValue* wMessage = webview_value_new_string(const_cast<char*>(message.c_str()));
|
|
WValue* wSource = webview_value_new_string(const_cast<char*>(source.c_str()));
|
|
WValue* wLine = webview_value_new_int(line);
|
|
WValue* retMap = webview_value_new_map();
|
|
webview_value_set_string(retMap, "browserId", bId);
|
|
webview_value_set_string(retMap, "level", wLevel);
|
|
webview_value_set_string(retMap, "message", wMessage);
|
|
webview_value_set_string(retMap, "source", wSource);
|
|
webview_value_set_string(retMap, "line", wLine);
|
|
m_invokeFunc("onConsoleMessage", retMap);
|
|
webview_value_unref(bId);
|
|
webview_value_unref(wLevel);
|
|
webview_value_unref(wMessage);
|
|
webview_value_unref(wSource);
|
|
webview_value_unref(wLine);
|
|
webview_value_unref(retMap);
|
|
}
|
|
};
|
|
|
|
m_handler->onUrlChangedEvent = [=, this](int browserId, std::string url)
|
|
{
|
|
if (m_invokeFunc)
|
|
{
|
|
WValue* bId = webview_value_new_int(browserId);
|
|
WValue* wUrl = webview_value_new_string(const_cast<char*>(url.c_str()));
|
|
WValue* retMap = webview_value_new_map();
|
|
webview_value_set_string(retMap, "browserId", bId);
|
|
webview_value_set_string(retMap, "url", wUrl);
|
|
m_invokeFunc("urlChanged", retMap);
|
|
webview_value_unref(bId);
|
|
webview_value_unref(wUrl);
|
|
webview_value_unref(retMap);
|
|
}
|
|
};
|
|
|
|
m_handler->onTitleChangedEvent = [=, this](int browserId, std::string title)
|
|
{
|
|
if (m_invokeFunc)
|
|
{
|
|
WValue* bId = webview_value_new_int(browserId);
|
|
WValue* wTitle = webview_value_new_string(const_cast<char*>(title.c_str()));
|
|
WValue* retMap = webview_value_new_map();
|
|
webview_value_set_string(retMap, "browserId", bId);
|
|
webview_value_set_string(retMap, "title", wTitle);
|
|
m_invokeFunc("titleChanged", retMap);
|
|
webview_value_unref(bId);
|
|
webview_value_unref(wTitle);
|
|
webview_value_unref(retMap);
|
|
}
|
|
};
|
|
|
|
m_handler->onJavaScriptChannelMessage = [=, this](std::string channelName, std::string message, std::string callbackId, int browserId, std::string frameId)
|
|
{
|
|
if (m_invokeFunc)
|
|
{
|
|
WValue* retMap = webview_value_new_map();
|
|
WValue* channel = webview_value_new_string(const_cast<char*>(channelName.c_str()));
|
|
WValue* msg = webview_value_new_string(const_cast<char*>(message.c_str()));
|
|
WValue* cbId = webview_value_new_string(const_cast<char*>(callbackId.c_str()));
|
|
WValue* bId = webview_value_new_int(browserId);
|
|
WValue* fId = webview_value_new_string(const_cast<char*>(frameId.c_str()));
|
|
webview_value_set_string(retMap, "channel", channel);
|
|
webview_value_set_string(retMap, "message", msg);
|
|
webview_value_set_string(retMap, "callbackId", cbId);
|
|
webview_value_set_string(retMap, "browserId", bId);
|
|
webview_value_set_string(retMap, "frameId", fId);
|
|
m_invokeFunc("javascriptChannelMessage", retMap);
|
|
webview_value_unref(retMap);
|
|
webview_value_unref(channel);
|
|
webview_value_unref(msg);
|
|
webview_value_unref(cbId);
|
|
webview_value_unref(bId);
|
|
webview_value_unref(fId);
|
|
}
|
|
};
|
|
|
|
m_handler->onFocusedNodeChangeMessage = [=, this](int nBrowserId, bool bEditable)
|
|
{
|
|
// Track editable focus per browser so the platform layer can route
|
|
// raw character keys to the OS IME while a web input is focused
|
|
// (the IME/delta path handles text). Focus moving to a new node
|
|
// also ends any prior composition for that browser.
|
|
auto rit = m_renderers.find(nBrowserId);
|
|
if (rit != m_renderers.end() && rit->second) {
|
|
rit->second->editableFocused = bEditable;
|
|
rit->second->composing = false;
|
|
}
|
|
if (m_invokeFunc)
|
|
{
|
|
WValue* bId = webview_value_new_int(int64_t(nBrowserId));
|
|
WValue* editable = webview_value_new_bool(bEditable);
|
|
WValue* retMap = webview_value_new_map();
|
|
webview_value_set_string(retMap, "browserId", bId);
|
|
webview_value_set_string(retMap, "editable", editable);
|
|
m_invokeFunc("onFocusedNodeChangeMessage", retMap);
|
|
webview_value_unref(bId);
|
|
webview_value_unref(editable);
|
|
webview_value_unref(retMap);
|
|
}
|
|
};
|
|
|
|
m_handler->onImeCompositionRangeChangedMessage = [=, this](int nBrowserId, int32_t x, int32_t y, int32_t height)
|
|
{
|
|
if (m_invokeFunc)
|
|
{
|
|
WValue* bId = webview_value_new_int(nBrowserId);
|
|
WValue* retMap = webview_value_new_map();
|
|
WValue* xValue = webview_value_new_int(x);
|
|
WValue* yValue = webview_value_new_int(y);
|
|
WValue* hValue = webview_value_new_int(height);
|
|
webview_value_set_string(retMap, "browserId", bId);
|
|
webview_value_set_string(retMap, "x", xValue);
|
|
webview_value_set_string(retMap, "y", yValue);
|
|
webview_value_set_string(retMap, "height", hValue);
|
|
m_invokeFunc("onImeCompositionRangeChangedMessage", retMap);
|
|
webview_value_unref(bId);
|
|
webview_value_unref(xValue);
|
|
webview_value_unref(yValue);
|
|
webview_value_unref(hValue);
|
|
webview_value_unref(retMap);
|
|
}
|
|
};
|
|
|
|
|
|
m_handler->onLoadStart = [=, this](int nBrowserId, std::string urlId)
|
|
{
|
|
if (m_invokeFunc)
|
|
{
|
|
WValue* bId = webview_value_new_int(nBrowserId);
|
|
WValue* uId = webview_value_new_string(const_cast<char*>(urlId.c_str()));
|
|
WValue* retMap = webview_value_new_map();
|
|
webview_value_set_string(retMap, "browserId", bId);
|
|
webview_value_set_string(retMap, "urlId", uId);
|
|
m_invokeFunc("onLoadStart", retMap);
|
|
webview_value_unref(bId);
|
|
webview_value_unref(uId);
|
|
webview_value_unref(retMap);
|
|
}
|
|
};
|
|
|
|
m_handler->onLoadEnd = [=, this](int nBrowserId, std::string urlId)
|
|
{
|
|
if (m_invokeFunc)
|
|
{
|
|
WValue* bId = webview_value_new_int(nBrowserId);
|
|
WValue* uId = webview_value_new_string(const_cast<char*>(urlId.c_str()));
|
|
WValue* retMap = webview_value_new_map();
|
|
webview_value_set_string(retMap, "browserId", bId);
|
|
webview_value_set_string(retMap, "urlId", uId);
|
|
m_invokeFunc("onLoadEnd", retMap);
|
|
webview_value_unref(bId);
|
|
webview_value_unref(uId);
|
|
webview_value_unref(retMap);
|
|
}
|
|
};
|
|
|
|
m_init = true;
|
|
}
|
|
}
|
|
|
|
void WebviewPlugin::uninitCallback(){
|
|
m_handler->onPaintCallback = nullptr;
|
|
m_handler->onAcceleratedPaintCallback = nullptr;
|
|
m_handler->onTooltipEvent = nullptr;
|
|
m_handler->onCursorChangedEvent = nullptr;
|
|
m_handler->onConsoleMessageEvent = nullptr;
|
|
m_handler->onUrlChangedEvent = nullptr;
|
|
m_handler->onTitleChangedEvent = nullptr;
|
|
m_handler->onJavaScriptChannelMessage = nullptr;
|
|
m_handler->onFocusedNodeChangeMessage = nullptr;
|
|
m_handler->onImeCompositionRangeChangedMessage = nullptr;
|
|
m_init = false;
|
|
}
|
|
|
|
|
|
void WebviewPlugin::HandleMethodCall(std::string name, WValue* values, std::function<void(int ,WValue*)> result) {
|
|
if (name.compare("init") == 0){
|
|
if(!isCefInitialized){
|
|
if(values != nullptr){
|
|
// A plain string is the user agent, a map carries the settings by name
|
|
if(webview_value_get_type(values) == Webview_Value_Type_Map){
|
|
WValue* agent = webview_value_get_by_string(values, "userAgent");
|
|
if(agent != nullptr && webview_value_get_type(agent) == Webview_Value_Type_String){
|
|
userAgent = CefString(webview_value_get_string(agent));
|
|
}
|
|
WValue* cachePath = webview_value_get_by_string(values, "rootCachePath");
|
|
if(cachePath != nullptr && webview_value_get_type(cachePath) == Webview_Value_Type_String){
|
|
rootCachePath = CefString(webview_value_get_string(cachePath));
|
|
}
|
|
WValue* proxy = webview_value_get_by_string(values, "proxyServer");
|
|
if(proxy != nullptr && webview_value_get_type(proxy) == Webview_Value_Type_String){
|
|
proxyServer = CefString(webview_value_get_string(proxy));
|
|
}
|
|
// JJ_MKVMaker (JJ_PATCH.md 7): 프록시 인증
|
|
WValue* pu = webview_value_get_by_string(values, "proxyUser");
|
|
WValue* pp = webview_value_get_by_string(values, "proxyPassword");
|
|
if(pu != nullptr && pp != nullptr && webview_value_get_type(pu) == Webview_Value_Type_String && webview_value_get_type(pp) == Webview_Value_Type_String){
|
|
webview_cef_jj::proxyUser = webview_value_get_string(pu);
|
|
webview_cef_jj::proxyPassword = webview_value_get_string(pp);
|
|
}
|
|
}
|
|
else{
|
|
userAgent = CefString(webview_value_get_string(values));
|
|
}
|
|
}
|
|
startCEF();
|
|
}
|
|
initCallback();
|
|
result(1, nullptr);
|
|
}
|
|
else if (name.compare("quit") == 0) {
|
|
//only call this method when you want to quit the app
|
|
stopCEF();
|
|
result(1, nullptr);
|
|
}
|
|
else if (name.compare("create") == 0) {
|
|
// 주소만 오거나, {url, javascript} 묶음 (JJ_PATCH.md 5)
|
|
std::string url;
|
|
bool javascript = true;
|
|
if (webview_value_get_type(values) == Webview_Value_Type_Map) {
|
|
WValue* u = webview_value_get_by_string(values, "url");
|
|
if (u != nullptr && webview_value_get_type(u) == Webview_Value_Type_String) url = webview_value_get_string(u);
|
|
WValue* js = webview_value_get_by_string(values, "javascript");
|
|
if (js != nullptr && webview_value_get_type(js) == Webview_Value_Type_Bool) javascript = webview_value_get_bool(js);
|
|
} else {
|
|
url = webview_value_get_string(values);
|
|
}
|
|
m_handler->createBrowser(url, javascript, [=, this](int browserId) {
|
|
std::shared_ptr<WebviewTexture> renderer = m_createTextureFunc();
|
|
m_renderers[browserId] = renderer;
|
|
WValue *response = webview_value_new_list();
|
|
webview_value_append(response, webview_value_new_int(browserId));
|
|
webview_value_append(response, webview_value_new_int(renderer->textureId));
|
|
result(1, response);
|
|
webview_value_unref(response);
|
|
});
|
|
}
|
|
else if (name.compare("close") == 0) {
|
|
int browserId = int(webview_value_get_int(values));
|
|
m_handler->closeBrowser(browserId);
|
|
if(m_renderers.find(browserId) != m_renderers.end() && m_renderers[browserId] != nullptr) {
|
|
m_renderers[browserId].reset();
|
|
}
|
|
result(1, nullptr);
|
|
}
|
|
else if (name.compare("loadUrl") == 0) {
|
|
int browserId = int(webview_value_get_int(webview_value_get_list_value(values, 0)));
|
|
const auto url = webview_value_get_string(webview_value_get_list_value(values, 1));
|
|
if(url != nullptr){
|
|
m_handler->loadUrl(browserId, url);
|
|
result(1, nullptr);
|
|
}
|
|
}
|
|
else if (name.compare("setSize") == 0) {
|
|
int browserId = int(webview_value_get_int(webview_value_get_list_value(values, 0)));
|
|
const auto dpi = webview_value_get_double(webview_value_get_list_value(values, 1));
|
|
const auto width = webview_value_get_double(webview_value_get_list_value(values, 2));
|
|
const auto height = webview_value_get_double(webview_value_get_list_value(values, 3));
|
|
m_handler->changeSize(browserId, (float)dpi, (int)std::round(width), (int)std::round(height));
|
|
result(1, nullptr);
|
|
}
|
|
else if (name.compare("cursorClickDown") == 0
|
|
|| name.compare("cursorClickUp") == 0
|
|
|| name.compare("cursorMove") == 0
|
|
|| name.compare("cursorDragging") == 0) {
|
|
result(cursorAction(values, name), nullptr);
|
|
}
|
|
else if (name.compare("setScrollDelta") == 0) {
|
|
int browserId = int(webview_value_get_int(webview_value_get_list_value(values, 0)));
|
|
auto x = webview_value_get_int(webview_value_get_list_value(values, 1));
|
|
auto y = webview_value_get_int(webview_value_get_list_value(values, 2));
|
|
auto deltaX = webview_value_get_int(webview_value_get_list_value(values, 3));
|
|
auto deltaY = webview_value_get_int(webview_value_get_list_value(values, 4));
|
|
m_handler->sendScrollEvent(browserId, (int)x, (int)y, (int)deltaX, (int)deltaY);
|
|
result(1, nullptr);
|
|
}
|
|
else if (name.compare("goForward") == 0) {
|
|
int browserId = int(webview_value_get_int(values));
|
|
m_handler->goForward(browserId);
|
|
result(1, nullptr);
|
|
}
|
|
else if (name.compare("goBack") == 0) {
|
|
int browserId = int(webview_value_get_int(values));
|
|
m_handler->goBack(browserId);
|
|
result(1, nullptr);
|
|
}
|
|
else if (name.compare("reload") == 0) {
|
|
int browserId = int(webview_value_get_int(values));
|
|
m_handler->reload(browserId);
|
|
result(1, nullptr);
|
|
}
|
|
else if (name.compare("openDevTools") == 0) {
|
|
int browserId = int(webview_value_get_int(values));
|
|
m_handler->openDevTools(browserId);
|
|
result(1, nullptr);
|
|
}
|
|
else if (name.compare("imeSetComposition") == 0) {
|
|
int browserId = int(webview_value_get_int(webview_value_get_list_value(values, 0)));
|
|
const auto text = webview_value_get_string(webview_value_get_list_value(values, 1));
|
|
// Non-empty preedit → composition active; empty preedit means the IME
|
|
// cleared it (e.g. the user deleted the last composing letter).
|
|
setComposingForBrowser(browserId, text != nullptr && text[0] != '\0');
|
|
m_handler->imeSetComposition(browserId, text);
|
|
result(1, nullptr);
|
|
}
|
|
else if (name.compare("imeCommitText") == 0) {
|
|
int browserId = int(webview_value_get_int(webview_value_get_list_value(values, 0)));
|
|
const auto text = webview_value_get_string(webview_value_get_list_value(values, 1));
|
|
// Commit ends the active composition.
|
|
setComposingForBrowser(browserId, false);
|
|
m_handler->imeCommitText(browserId, text);
|
|
result(1, nullptr);
|
|
}
|
|
else if (name.compare("setClientFocus") == 0) {
|
|
int browserId = int(webview_value_get_int(webview_value_get_list_value(values, 0)));
|
|
if (m_renderers.find(browserId) != m_renderers.end() && m_renderers[browserId] != nullptr) {
|
|
bool focused = webview_value_get_bool(webview_value_get_list_value(values, 1));
|
|
m_renderers[browserId].get()->isFocused = focused;
|
|
// Losing focus ends any composition: the OS IME drops marked text,
|
|
// but CEF's SetFocus(false) does not blur the DOM node, so no
|
|
// FocusedNodeChanged fires to clear it — a stale flag would then
|
|
// mis-route the first keys after the webview is refocused.
|
|
if (!focused) {
|
|
m_renderers[browserId].get()->composing = false;
|
|
}
|
|
m_handler->setClientFocus(browserId, focused);
|
|
}
|
|
result(1, nullptr);
|
|
}
|
|
else if(name.compare("setCookie") == 0){
|
|
const auto domain = webview_value_get_string(webview_value_get_list_value(values, 0));
|
|
const auto key = webview_value_get_string(webview_value_get_list_value(values, 1));
|
|
const auto value = webview_value_get_string(webview_value_get_list_value(values, 2));
|
|
m_handler->setCookie(domain, key, value);
|
|
result(1, nullptr);
|
|
}
|
|
else if (name.compare("deleteCookie") == 0) {
|
|
const auto domain = webview_value_get_string(webview_value_get_list_value(values, 0));
|
|
const auto key = webview_value_get_string(webview_value_get_list_value(values, 1));
|
|
m_handler->deleteCookie(domain, key);
|
|
result(1, nullptr);
|
|
}
|
|
else if (name.compare("visitAllCookies") == 0) {
|
|
m_handler->visitAllCookies([=](std::map<std::string, std::map<std::string, std::string>> cookies){
|
|
WValue* retMap = webview_value_new_map();
|
|
for (auto &cookie : cookies)
|
|
{
|
|
WValue* tempMap = webview_value_new_map();
|
|
for (auto &c : cookie.second)
|
|
{
|
|
WValue * val = webview_value_new_string(const_cast<char *>(c.second.c_str()));
|
|
webview_value_set_string(tempMap, c.first.c_str(), val);
|
|
webview_value_unref(val);
|
|
}
|
|
webview_value_set_string(retMap, cookie.first.c_str(), tempMap);
|
|
webview_value_unref(tempMap);
|
|
}
|
|
result(1, retMap);
|
|
webview_value_unref(retMap);
|
|
});
|
|
}
|
|
else if (name.compare("visitUrlCookies") == 0) {
|
|
const auto domain = webview_value_get_string(webview_value_get_list_value(values, 0));
|
|
const auto isHttpOnly = webview_value_get_bool(webview_value_get_list_value(values, 1));
|
|
m_handler->visitUrlCookies(domain, isHttpOnly,[=](std::map<std::string, std::map<std::string, std::string>> cookies){
|
|
WValue* retMap = webview_value_new_map();
|
|
for (auto &cookie : cookies)
|
|
{
|
|
WValue* tempMap = webview_value_new_map();
|
|
for (auto &c : cookie.second)
|
|
{
|
|
WValue * val = webview_value_new_string(const_cast<char *>(c.second.c_str()));
|
|
webview_value_set_string(tempMap, c.first.c_str(), val);
|
|
webview_value_unref(val);
|
|
}
|
|
webview_value_set_string(retMap, cookie.first.c_str(), tempMap);
|
|
webview_value_unref(tempMap);
|
|
}
|
|
result(1, retMap);
|
|
webview_value_unref(retMap);
|
|
});
|
|
}
|
|
else if(name.compare("setJavaScriptChannels") == 0){
|
|
int browserId = int(webview_value_get_int(webview_value_get_list_value(values, 0)));
|
|
WValue *list = webview_value_get_list_value(values, 1);
|
|
auto len = webview_value_get_len(list);
|
|
std::vector<std::string> channels;
|
|
for(size_t i = 0; i < len; i++){
|
|
auto channel = webview_value_get_string(webview_value_get_list_value(list, i));
|
|
channels.push_back(channel);
|
|
}
|
|
m_handler->setJavaScriptChannels(browserId, channels);
|
|
result(1, nullptr);
|
|
}
|
|
else if (name.compare("sendJavaScriptChannelCallBack") == 0) {
|
|
const auto error = webview_value_get_bool(webview_value_get_list_value(values, 0));
|
|
const auto ret = webview_value_get_string(webview_value_get_list_value(values, 1));
|
|
const auto callbackId = webview_value_get_string(webview_value_get_list_value(values, 2));
|
|
const auto browserId = int(webview_value_get_int(webview_value_get_list_value(values, 3)));
|
|
const auto frameId = webview_value_get_string(webview_value_get_list_value(values, 4));
|
|
m_handler->sendJavaScriptChannelCallBack(error, ret, callbackId, browserId, frameId);
|
|
result(1, nullptr);
|
|
}
|
|
else if (name.compare("sendKeyEvent") == 0) {
|
|
int type = int(webview_value_get_int(webview_value_get_list_value(values, 1)));
|
|
int keyCode = int(webview_value_get_int(webview_value_get_list_value(values, 2)));
|
|
int modifiers = int(webview_value_get_int(webview_value_get_list_value(values, 3)));
|
|
int character = int(webview_value_get_int(webview_value_get_list_value(values, 4)));
|
|
int unmodifiedCharacter = int(webview_value_get_int(webview_value_get_list_value(values, 5)));
|
|
|
|
CefKeyEvent keyEvent;
|
|
keyEvent.type = static_cast<cef_key_event_type_t>(type);
|
|
keyEvent.windows_key_code = keyCode;
|
|
keyEvent.modifiers = modifiers;
|
|
keyEvent.character = static_cast<char16_t>(character);
|
|
keyEvent.unmodified_character = static_cast<char16_t>(unmodifiedCharacter);
|
|
|
|
sendKeyEvent(keyEvent);
|
|
result(1, nullptr);
|
|
}
|
|
else if(name.compare("hasNativeKeySupport") == 0) {
|
|
#if defined(HAS_GTK) || defined(OS_WIN) || defined(OS_MAC)
|
|
// Desktop Linux (GTK via processKeyEventForCEF), Windows (WM_KEYDOWN)
|
|
// and macOS (NSEventMaskKeyDown) all deliver keys to CEF natively.
|
|
bool hasNativeKeySupport = true;
|
|
#else
|
|
// eLinux (no GTK): use Dart-side handling.
|
|
bool hasNativeKeySupport = false;
|
|
#endif
|
|
WValue* ret = webview_value_new_bool(hasNativeKeySupport);
|
|
result(1, ret);
|
|
webview_value_unref(ret);
|
|
}
|
|
else if(name.compare("executeJavaScript") == 0){
|
|
int browserId = int(webview_value_get_int(webview_value_get_list_value(values, 0)));
|
|
const auto code = webview_value_get_string(webview_value_get_list_value(values, 1));
|
|
m_handler->executeJavaScript(browserId, code);
|
|
result(1, nullptr);
|
|
}
|
|
else if(name.compare("evaluateJavascript") == 0){
|
|
int browserId = int(webview_value_get_int(webview_value_get_list_value(values, 0)));
|
|
const auto code = webview_value_get_string(webview_value_get_list_value(values, 1));
|
|
m_handler->executeJavaScript(browserId, code, [=](CefRefPtr<CefValue> values){
|
|
WValue* retValue;
|
|
|
|
if (values == nullptr) {
|
|
result(1, nullptr);
|
|
return;
|
|
}
|
|
|
|
switch(values->GetType()) {
|
|
case VTYPE_BOOL:
|
|
retValue = webview_value_new_bool(values->GetBool());
|
|
break;
|
|
case VTYPE_DOUBLE:
|
|
retValue = webview_value_new_double(values->GetDouble());
|
|
break;
|
|
case VTYPE_INT:
|
|
retValue = webview_value_new_int(values->GetInt());
|
|
break;
|
|
case VTYPE_STRING:
|
|
retValue = webview_value_new_string(values->GetString().ToString().c_str());
|
|
break;
|
|
case VTYPE_LIST: {
|
|
retValue = webview_value_new_list();
|
|
CefRefPtr<CefListValue> list = values->GetList();
|
|
|
|
if (list) {
|
|
for (size_t i = 0; i < list->GetSize(); ++i) {
|
|
CefValueType type = list->GetType(i);
|
|
CefRefPtr<CefValue> listItem = list->GetValue(i);
|
|
|
|
if (type == VTYPE_INT) {
|
|
webview_value_append(retValue, webview_value_new_int(listItem->GetInt()));
|
|
} else if (type == VTYPE_BOOL) {
|
|
webview_value_append(retValue, webview_value_new_bool(listItem->GetBool()));
|
|
} else if (type == VTYPE_STRING) {
|
|
webview_value_append(retValue, webview_value_new_string(listItem->GetString().ToString().c_str()));
|
|
} else if (type == VTYPE_DOUBLE) {
|
|
webview_value_append(retValue, webview_value_new_double(listItem->GetDouble()));
|
|
} else {
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
// Return null as fallback
|
|
result(1, nullptr);
|
|
return;
|
|
}
|
|
|
|
result(1, retValue);
|
|
webview_value_unref(retValue);
|
|
});
|
|
}
|
|
else {
|
|
result = 0;
|
|
}
|
|
}
|
|
|
|
void WebviewPlugin::imeSetCompositionNative(const std::wstring& text, int cursor)
|
|
{
|
|
m_handler->imeSetCompositionNative(text, cursor);
|
|
}
|
|
|
|
void WebviewPlugin::imeCommitTextNative(const std::wstring& text)
|
|
{
|
|
m_handler->imeCommitTextNative(text);
|
|
}
|
|
|
|
void WebviewPlugin::imeFinishCompositionNative()
|
|
{
|
|
m_handler->imeFinishComposition();
|
|
}
|
|
|
|
void WebviewPlugin::sendKeyEvent(CefKeyEvent& ev)
|
|
{
|
|
m_handler->sendKeyEvent(ev);
|
|
if(ev.type == KEYEVENT_RAWKEYDOWN && ev.windows_key_code == 0x7B && (ev.modifiers & EVENTFLAG_CONTROL_DOWN) != 0){
|
|
for(auto render : m_renderers){
|
|
if(render.second.get()->isFocused){
|
|
m_handler->openDevTools(render.first);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void WebviewPlugin::setInvokeMethodFunc(std::function<void(std::string, WValue*)> func){
|
|
m_invokeFunc = func;
|
|
}
|
|
|
|
void WebviewPlugin::setCreateTextureFunc(std::function<std::shared_ptr<WebviewTexture>()> func)
|
|
{
|
|
m_createTextureFunc = func;
|
|
}
|
|
|
|
bool WebviewPlugin::getAnyBrowserFocused(){
|
|
for(auto render : m_renderers){
|
|
if(render.second != nullptr && render.second.get()->isFocused){
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int WebviewPlugin::focusedBrowserId(){
|
|
for(auto& render : m_renderers){
|
|
if(render.second != nullptr && render.second->isFocused){
|
|
return render.first;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
bool WebviewPlugin::isEditableFocused(){
|
|
int id = focusedBrowserId();
|
|
if(id < 0){
|
|
return false;
|
|
}
|
|
auto it = m_renderers.find(id);
|
|
return it != m_renderers.end() && it->second && it->second->editableFocused;
|
|
}
|
|
|
|
bool WebviewPlugin::isComposing(){
|
|
int id = focusedBrowserId();
|
|
if(id < 0){
|
|
return false;
|
|
}
|
|
auto it = m_renderers.find(id);
|
|
return it != m_renderers.end() && it->second && it->second->composing;
|
|
}
|
|
|
|
void WebviewPlugin::setComposingForBrowser(int browserId, bool composing){
|
|
auto it = m_renderers.find(browserId);
|
|
if(it != m_renderers.end() && it->second){
|
|
it->second->composing = composing;
|
|
}
|
|
}
|
|
|
|
void WebviewPlugin::tickBeginFrame(){
|
|
if (m_handler) {
|
|
m_handler->sendExternalBeginFrame();
|
|
}
|
|
}
|
|
|
|
int WebviewPlugin::cursorAction(WValue *args, std::string name) {
|
|
if (!args || webview_value_get_len(args) != 3) {
|
|
return 0;
|
|
}
|
|
int browserId = int(webview_value_get_int(webview_value_get_list_value(args, 0)));
|
|
int x = int(webview_value_get_int(webview_value_get_list_value(args, 1)));
|
|
int y = int(webview_value_get_int(webview_value_get_list_value(args, 2)));
|
|
if (!x && !y) {
|
|
return 0;
|
|
}
|
|
if (name.compare("cursorClickDown") == 0) {
|
|
m_handler->cursorClick(browserId, x, y, false);
|
|
}
|
|
else if (name.compare("cursorClickUp") == 0) {
|
|
m_handler->cursorClick(browserId, x, y, true);
|
|
}
|
|
else if (name.compare("cursorMove") == 0) {
|
|
m_handler->cursorMove(browserId, x, y, false);
|
|
}
|
|
else if (name.compare("cursorDragging") == 0) {
|
|
m_handler->cursorMove(browserId, x, y, true);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int initCEFProcesses(CefMainArgs args)
|
|
{
|
|
mainArgs = args;
|
|
return initCEFProcesses();
|
|
}
|
|
|
|
int initCEFProcesses()
|
|
{
|
|
#ifdef OS_MAC
|
|
CefScopedLibraryLoader loader;
|
|
if(!loader.LoadInMain()) {
|
|
printf("load cef err");
|
|
}
|
|
#endif
|
|
// handler = new WebviewHandler();
|
|
app = new WebviewApp();
|
|
#ifdef OS_MAC
|
|
// No bundled helper resolved → fall back to single-process so consumers
|
|
// that haven't added the CEF helper apps keep working (mode 3 appends
|
|
// the "single-process" switch in OnBeforeCommandLineProcessing).
|
|
if (g_macSubprocessPath.empty()) {
|
|
app->SetProcessMode(3);
|
|
}
|
|
#endif
|
|
return CefExecuteProcess(mainArgs, app, nullptr);
|
|
}
|
|
|
|
void startCEF()
|
|
{
|
|
CefSettings cefs;
|
|
cefs.windowless_rendering_enabled = true;
|
|
cefs.no_sandbox = true;
|
|
if(!userAgent.empty()){
|
|
CefString(&cefs.user_agent_product) = userAgent;
|
|
}
|
|
// Without a root cache path CEF uses the platform default (~/.config/cef_user_data on
|
|
// Linux) and keeps its process singleton lock there, see the warning CEF logs about it.
|
|
// A lock left behind by an app that was killed keeps the next start from opening a
|
|
// browser, and a home the app may not write to stops CEF right away. CEF expects the
|
|
// directory to exist.
|
|
if(!rootCachePath.empty()){
|
|
// The narrow string of CefString is UTF-8, but on Windows std::filesystem::path decodes
|
|
// narrow strings in the active code page, so the wide string is used there
|
|
#ifdef OS_WIN
|
|
std::filesystem::path cachePath(rootCachePath.ToWString());
|
|
#else
|
|
std::filesystem::path cachePath(rootCachePath.ToString());
|
|
#endif
|
|
// CEF requires an absolute path and clears a relative one while normalizing its
|
|
// settings, which would put the cache back into the default directory unnoticed
|
|
std::error_code error;
|
|
cachePath = std::filesystem::absolute(cachePath, error).lexically_normal();
|
|
if(!error){
|
|
std::filesystem::create_directories(cachePath, error);
|
|
}
|
|
|
|
if(error){
|
|
// The path given by the embedder is logged, converting the resolved one back to a
|
|
// narrow string would run through the code page conversion again
|
|
std::cout << "webview_cef: could not prepare root cache path " << rootCachePath.ToString()
|
|
<< " (" << error.message() << "), falling back to the CEF default" << std::endl;
|
|
}
|
|
else{
|
|
#ifdef OS_WIN
|
|
CefString(&cefs.root_cache_path) = cachePath.wstring();
|
|
#else
|
|
CefString(&cefs.root_cache_path) = cachePath.string();
|
|
#endif
|
|
}
|
|
}
|
|
//locale language setting
|
|
//CefString(&cefs.locale) = "zh-CN";
|
|
#ifdef OS_MAC
|
|
//cef message loop handle by MainApplication on mac
|
|
cefs.external_message_pump = true;
|
|
// Multi-process: point CEF at the bundled helper executable and the
|
|
// framework. The platform layer fills these from the app bundle.
|
|
if (!g_macSubprocessPath.empty()) {
|
|
CefString(&cefs.browser_subprocess_path) = g_macSubprocessPath;
|
|
}
|
|
if (!g_macFrameworkDirPath.empty()) {
|
|
CefString(&cefs.framework_dir_path) = g_macFrameworkDirPath;
|
|
}
|
|
if (!g_macMainBundlePath.empty()) {
|
|
CefString(&cefs.main_bundle_path) = g_macMainBundlePath;
|
|
}
|
|
#else
|
|
//cef message run in another thread on windows/linux
|
|
cefs.multi_threaded_message_loop = true;
|
|
#endif
|
|
if(!proxyServer.empty()){
|
|
app->SetProxyServer(proxyServer.ToString());
|
|
}
|
|
CefInitialize(mainArgs, cefs, app.get(), nullptr);
|
|
}
|
|
|
|
void doMessageLoopWork(){
|
|
CefDoMessageLoopWork();
|
|
}
|
|
|
|
void SwapBufferFromBgraToRgba(void* _dest, const void* _src, int width, int height) {
|
|
int32_t* dest = (int32_t*)_dest;
|
|
int32_t* src = (int32_t*)_src;
|
|
int32_t rgba;
|
|
int32_t bgra;
|
|
int length = width * height;
|
|
for (int i = 0; i < length; i++) {
|
|
bgra = src[i];
|
|
// BGRA in hex = 0xAARRGGBB.
|
|
rgba = (bgra & 0x00ff0000) >> 16 // Red >> Blue.
|
|
| (bgra & 0xff00ff00) // Green Alpha.
|
|
| (bgra & 0x000000ff) << 16; // Blue >> Red.
|
|
dest[i] = rgba;
|
|
}
|
|
}
|
|
|
|
void stopCEF()
|
|
{
|
|
CefShutdown();
|
|
}
|
|
}
|