plusui-native-core 0.1.4 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Core/CMakeLists.txt +190 -7
- package/Core/Features/App/app.cpp +129 -0
- package/Core/Features/App/app.ts +126 -0
- package/Core/Features/Browser/browser.cpp +181 -0
- package/Core/Features/Browser/browser.ts +182 -0
- package/Core/Features/Clipboard/clipboard.cpp +234 -0
- package/Core/Features/Clipboard/clipboard.ts +113 -0
- package/Core/Features/Display/display.cpp +209 -0
- package/Core/Features/Display/display.ts +104 -0
- package/Core/Features/Event/Events.ts +166 -0
- package/Core/Features/Event/events.cpp +200 -0
- package/Core/Features/Keyboard/keyboard.cpp +186 -0
- package/Core/Features/Keyboard/keyboard.ts +175 -0
- package/Core/Features/Menu/context-menu.css +293 -0
- package/Core/Features/Menu/menu.cpp +481 -0
- package/Core/Features/Menu/menu.ts +439 -0
- package/Core/Features/Tray/tray.cpp +310 -0
- package/Core/Features/Tray/tray.ts +68 -0
- package/Core/Features/WebGPU/webgpu.cpp +939 -0
- package/Core/Features/WebGPU/webgpu.ts +1013 -0
- package/Core/Features/WebView/webview.cpp +1052 -0
- package/Core/Features/WebView/webview.ts +510 -0
- package/Core/Features/Window/window.cpp +664 -0
- package/Core/Features/Window/window.ts +142 -0
- package/Core/Features/WindowManager/window_manager.cpp +341 -0
- package/Core/include/plusui/app.hpp +73 -0
- package/Core/include/plusui/browser.hpp +66 -0
- package/Core/include/plusui/clipboard.hpp +41 -0
- package/Core/include/plusui/events.hpp +58 -0
- package/Core/include/{keyboard.hpp → plusui/keyboard.hpp} +21 -44
- package/Core/include/plusui/menu.hpp +153 -0
- package/Core/include/plusui/tray.hpp +93 -0
- package/Core/include/plusui/webgpu.hpp +434 -0
- package/Core/include/plusui/webview.hpp +142 -0
- package/Core/include/plusui/window.hpp +111 -0
- package/Core/include/plusui/window_manager.hpp +57 -0
- package/Core/vendor/WebView2EnvironmentOptions.h +406 -0
- package/Core/vendor/stb_image.h +7988 -0
- package/Core/vendor/webview.h +618 -510
- package/Core/vendor/webview2.h +52079 -0
- package/README.md +19 -0
- package/package.json +12 -15
- package/Core/include/app.hpp +0 -121
- package/Core/include/menu.hpp +0 -79
- package/Core/include/tray.hpp +0 -81
- package/Core/include/window.hpp +0 -106
- package/Core/src/app.cpp +0 -311
- package/Core/src/display.cpp +0 -424
- package/Core/src/tray.cpp +0 -275
- package/Core/src/window.cpp +0 -528
- package/dist/index.d.ts +0 -205
- package/dist/index.js +0 -198
- package/src/index.ts +0 -574
- /package/Core/include/{display.hpp → plusui/display.hpp} +0 -0
package/Core/vendor/webview.h
CHANGED
|
@@ -1,365 +1,362 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* MIT License
|
|
3
|
-
*
|
|
4
|
-
* Copyright (c) 2017 Serge Zaitsev
|
|
5
|
-
*/
|
|
6
|
-
#ifndef WEBVIEW_H
|
|
7
|
-
#define WEBVIEW_H
|
|
8
|
-
|
|
9
|
-
#ifndef WEBVIEW_API
|
|
10
|
-
#define WEBVIEW_API extern
|
|
11
|
-
#endif
|
|
12
|
-
|
|
13
|
-
#ifdef __cplusplus
|
|
14
|
-
extern "C" {
|
|
15
|
-
#endif
|
|
16
|
-
|
|
17
|
-
typedef void *webview_t;
|
|
18
|
-
|
|
19
|
-
WEBVIEW_API webview_t webview_create(int debug, void *window);
|
|
20
|
-
WEBVIEW_API void webview_destroy(webview_t w);
|
|
21
|
-
WEBVIEW_API void webview_run(webview_t w);
|
|
22
|
-
WEBVIEW_API void webview_terminate(webview_t w);
|
|
23
|
-
WEBVIEW_API void webview_dispatch(webview_t w, void (*fn)(webview_t w, void *arg), void *arg);
|
|
24
|
-
WEBVIEW_API void *webview_get_window(webview_t w);
|
|
25
|
-
WEBVIEW_API void webview_set_title(webview_t w, const char *title);
|
|
26
|
-
|
|
27
|
-
#define WEBVIEW_HINT_NONE 0
|
|
28
|
-
#define WEBVIEW_HINT_MIN 1
|
|
29
|
-
#define WEBVIEW_HINT_MAX 2
|
|
30
|
-
#define WEBVIEW_HINT_FIXED 3
|
|
31
|
-
|
|
32
|
-
WEBVIEW_API void webview_set_size(webview_t w, int width, int height, int hints);
|
|
33
|
-
WEBVIEW_API void webview_navigate(webview_t w, const char *url);
|
|
34
|
-
WEBVIEW_API void webview_init(webview_t w, const char *js);
|
|
35
|
-
WEBVIEW_API void webview_eval(webview_t w, const char *js);
|
|
36
|
-
WEBVIEW_API void webview_bind(webview_t w, const char *name, void (*fn)(const char *seq, const char *req, void *arg), void *arg);
|
|
37
|
-
WEBVIEW_API void webview_return(webview_t w, const char *seq, int status, const char *result);
|
|
38
|
-
|
|
39
|
-
#ifdef __cplusplus
|
|
40
|
-
}
|
|
41
|
-
#endif
|
|
42
|
-
|
|
43
|
-
#ifndef WEBVIEW_HEADER
|
|
44
|
-
|
|
45
|
-
#if !defined(WEBVIEW_GTK) && !defined(WEBVIEW_COCOA) && !defined(WEBVIEW_EDGE)
|
|
46
|
-
#if defined(__linux__)
|
|
47
|
-
#define WEBVIEW_GTK
|
|
48
|
-
#elif defined(__APPLE__)
|
|
49
|
-
#define WEBVIEW_COCOA
|
|
50
|
-
#elif defined(_WIN32)
|
|
51
|
-
#define WEBVIEW_EDGE
|
|
52
|
-
#else
|
|
53
|
-
#error "please, specify webview backend"
|
|
54
|
-
#endif
|
|
55
|
-
#endif
|
|
56
|
-
|
|
57
|
-
#include <atomic>
|
|
58
|
-
#include <functional>
|
|
59
|
-
#include <future>
|
|
60
|
-
#include <map>
|
|
61
|
-
#include <string>
|
|
62
|
-
#include <utility>
|
|
63
|
-
#include <vector>
|
|
64
|
-
#include <cstring>
|
|
65
|
-
|
|
66
|
-
namespace webview {
|
|
67
|
-
using dispatch_fn_t = std::function<void()>;
|
|
68
|
-
|
|
69
|
-
inline std::string url_encode(const std::string s) {
|
|
70
|
-
std::string encoded;
|
|
71
|
-
for (unsigned int i = 0; i < s.length(); i++) {
|
|
72
|
-
auto c = s[i];
|
|
73
|
-
if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') {
|
|
74
|
-
encoded = encoded + c;
|
|
75
|
-
} else {
|
|
76
|
-
char hex[4];
|
|
77
|
-
snprintf(hex, sizeof(hex), "%%%02x", c);
|
|
78
|
-
encoded = encoded + hex;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
return encoded;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
inline std::string url_decode(const std::string s) {
|
|
85
|
-
std::string decoded;
|
|
86
|
-
for (unsigned int i = 0; i < s.length(); i++) {
|
|
87
|
-
if (s[i] == '%') {
|
|
88
|
-
int n;
|
|
89
|
-
n = std::stoul(s.substr(i + 1, 2), nullptr, 16);
|
|
90
|
-
decoded = decoded + static_cast<char>(n);
|
|
91
|
-
i = i + 2;
|
|
92
|
-
} else if (s[i] == '+') {
|
|
93
|
-
decoded = decoded + ' ';
|
|
94
|
-
} else {
|
|
95
|
-
decoded = decoded + s[i];
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
return decoded;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
inline std::string html_from_uri(const std::string s) {
|
|
102
|
-
if (s.substr(0, 15) == "data:text/html,") {
|
|
103
|
-
return url_decode(s.substr(15));
|
|
104
|
-
}
|
|
105
|
-
return "";
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
inline int json_parse_c(const char *s, size_t sz, const char *key, size_t keysz,
|
|
109
|
-
const char **value, size_t *valuesz) {
|
|
110
|
-
enum { JSON_STATE_VALUE, JSON_STATE_LITERAL, JSON_STATE_STRING, JSON_STATE_ESCAPE, JSON_STATE_UTF8 } state = JSON_STATE_VALUE;
|
|
111
|
-
const char *k = NULL;
|
|
112
|
-
int index = 1;
|
|
113
|
-
int depth = 0;
|
|
114
|
-
int utf8_bytes = 0;
|
|
115
|
-
|
|
116
|
-
if (key == NULL) { index = keysz; keysz = 0; }
|
|
117
|
-
*value = NULL;
|
|
118
|
-
*valuesz = 0;
|
|
119
|
-
|
|
120
|
-
for (; sz > 0; s++, sz--) {
|
|
121
|
-
enum { JSON_ACTION_NONE, JSON_ACTION_START, JSON_ACTION_END, JSON_ACTION_START_STRUCT, JSON_ACTION_END_STRUCT } action = JSON_ACTION_NONE;
|
|
122
|
-
unsigned char c = *s;
|
|
123
|
-
switch (state) {
|
|
124
|
-
case JSON_STATE_VALUE:
|
|
125
|
-
if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == ',' || c == ':') { continue; }
|
|
126
|
-
else if (c == '"') { action = JSON_ACTION_START; state = JSON_STATE_STRING; }
|
|
127
|
-
else if (c == '{' || c == '[') { action = JSON_ACTION_START_STRUCT; }
|
|
128
|
-
else if (c == '}' || c == ']') { action = JSON_ACTION_END_STRUCT; }
|
|
129
|
-
else if (c == 't' || c == 'f' || c == 'n' || c == '-' || (c >= '0' && c <= '9')) { action = JSON_ACTION_START; state = JSON_STATE_LITERAL; }
|
|
130
|
-
else { return -1; }
|
|
131
|
-
break;
|
|
132
|
-
case JSON_STATE_LITERAL:
|
|
133
|
-
if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == ']' || c == '}' || c == ':') { state = JSON_STATE_VALUE; s--; sz++; action = JSON_ACTION_END; }
|
|
134
|
-
else if (c < 32 || c > 126) { return -1; }
|
|
135
|
-
case JSON_STATE_STRING:
|
|
136
|
-
if (c < 32 || (c > 126 && c < 192)) { return -1; }
|
|
137
|
-
else if (c == '"') { action = JSON_ACTION_END; state = JSON_STATE_VALUE; }
|
|
138
|
-
else if (c == '\\') { state = JSON_STATE_ESCAPE; }
|
|
139
|
-
else if (c >= 192 && c < 224) { utf8_bytes = 1; state = JSON_STATE_UTF8; }
|
|
140
|
-
else if (c >= 224 && c < 240) { utf8_bytes = 2; state = JSON_STATE_UTF8; }
|
|
141
|
-
else if (c >= 240 && c < 247) { utf8_bytes = 3; state = JSON_STATE_UTF8; }
|
|
142
|
-
else if (c >= 128 && c < 192) { return -1; }
|
|
143
|
-
break;
|
|
144
|
-
case JSON_STATE_ESCAPE:
|
|
145
|
-
if (c == '"' || c == '\\' || c == '/' || c == 'b' || c == 'f' || c == 'n' || c == 'r' || c == 't' || c == 'u') { state = JSON_STATE_STRING; }
|
|
146
|
-
else { return -1; }
|
|
147
|
-
break;
|
|
148
|
-
case JSON_STATE_UTF8:
|
|
149
|
-
if (c < 128 || c > 191) { return -1; }
|
|
150
|
-
utf8_bytes--;
|
|
151
|
-
if (utf8_bytes == 0) { state = JSON_STATE_STRING; }
|
|
152
|
-
break;
|
|
153
|
-
default: return -1;
|
|
154
|
-
}
|
|
155
|
-
if (action == JSON_ACTION_END_STRUCT) { depth--; }
|
|
156
|
-
if (depth == 1) {
|
|
157
|
-
if (action == JSON_ACTION_START || action == JSON_ACTION_START_STRUCT) {
|
|
158
|
-
if (index == 0) { *value = s; }
|
|
159
|
-
else if (keysz > 0 && index == 1) { k = s; }
|
|
160
|
-
else { index--; }
|
|
161
|
-
} else if (action == JSON_ACTION_END || action == JSON_ACTION_END_STRUCT) {
|
|
162
|
-
if (*value != NULL && index == 0) { *valuesz = (size_t)(s + 1 - *value); return 0; }
|
|
163
|
-
else if (keysz > 0 && k != NULL) {
|
|
164
|
-
if (keysz == (size_t)(s - k - 1) && memcmp(key, k + 1, keysz) == 0) { index = 0; }
|
|
165
|
-
else { index = 2; }
|
|
166
|
-
k = NULL;
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
if (action == JSON_ACTION_START_STRUCT) { depth++; }
|
|
171
|
-
}
|
|
172
|
-
return -1;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
inline std::string json_escape(std::string s) { return '"' + s + '"'; }
|
|
176
|
-
|
|
177
|
-
inline int json_unescape(const char *s, size_t n, char *out) {
|
|
178
|
-
int r = 0;
|
|
179
|
-
if (*s++ != '"') { return -1; }
|
|
180
|
-
while (n > 2) {
|
|
181
|
-
char c = *s;
|
|
182
|
-
if (c == '\\') { s++; n--; switch (*s) {
|
|
183
|
-
case 'b': c = '\b'; break;
|
|
184
|
-
case 'f': c = '\f'; break;
|
|
185
|
-
case 'n': c = '\n'; break;
|
|
186
|
-
case 'r': c = '\r'; break;
|
|
187
|
-
case 't': c = '\t'; break;
|
|
188
|
-
case '\\': c = '\\'; break;
|
|
189
|
-
case '/': c = '/'; break;
|
|
190
|
-
case '\"': c = '\"'; break;
|
|
191
|
-
default: return -1;
|
|
192
|
-
}}
|
|
193
|
-
if (out != NULL) { *out++ = c; }
|
|
194
|
-
s++; n--; r++;
|
|
195
|
-
}
|
|
196
|
-
if (*s != '"') { return -1; }
|
|
197
|
-
if (out != NULL) { *out = '\0'; }
|
|
198
|
-
return r;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
inline std::string json_parse(const std::string s, const std::string key, const int index) {
|
|
202
|
-
const char *value; size_t value_sz;
|
|
203
|
-
if (key == "") { json_parse_c(s.c_str(), s.length(), nullptr, index, &value, &value_sz); }
|
|
204
|
-
else { json_parse_c(s.c_str(), s.length(), key.c_str(), key.length(), &value, &value_sz); }
|
|
205
|
-
if (value != nullptr) {
|
|
206
|
-
if (value[0] != '"') { return std::string(value, value_sz); }
|
|
207
|
-
int n = json_unescape(value, value_sz, nullptr);
|
|
208
|
-
if (n > 0) { char *decoded = new char[n + 1]; json_unescape(value, value_sz, decoded); std::string result(decoded, n); delete[] decoded; return result; }
|
|
209
|
-
}
|
|
210
|
-
return "";
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
} // namespace webview
|
|
214
|
-
|
|
215
|
-
#if defined(WEBVIEW_GTK)
|
|
216
|
-
#include <JavaScriptCore/JavaScript.h>
|
|
217
|
-
#include <gtk/gtk.h>
|
|
218
|
-
#include <webkit2/webkit2.h>
|
|
219
|
-
namespace webview {
|
|
220
|
-
class gtk_webkit_engine {
|
|
221
|
-
public:
|
|
222
|
-
gtk_webkit_engine(bool debug, void *window) : m_window(static_cast<GtkWidget *>(window)) {
|
|
223
|
-
gtk_init_check(0, NULL);
|
|
224
|
-
m_window = static_cast<GtkWidget *>(window);
|
|
225
|
-
if (m_window == nullptr) { m_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); }
|
|
226
|
-
g_signal_connect(G_OBJECT(m_window), "destroy", G_CALLBACK(+[](GtkWidget *, gpointer arg) { static_cast<gtk_webkit_engine *>(arg)->terminate(); }), this);
|
|
227
|
-
m_webview = webkit_web_view_new();
|
|
228
|
-
WebKitUserContentManager *manager = webkit_web_view_get_user_content_manager(WEBKIT_WEB_VIEW(m_webview));
|
|
229
|
-
g_signal_connect(manager, "script-message-received::external", G_CALLBACK(+[](WebKitUserContentManager *, WebKitJavascriptResult *r, gpointer arg) {
|
|
230
|
-
auto *w = static_cast<gtk_webkit_engine *>(arg);
|
|
231
|
-
#if WEBKIT_MAJOR_VERSION >= 2 && WEBKIT_MINOR_VERSION >= 22
|
|
232
|
-
JSCValue *value = webkit_javascript_result_get_js_value(r);
|
|
233
|
-
char *s = jsc_value_to_string(value);
|
|
234
|
-
#else
|
|
235
|
-
JSGlobalContextRef ctx = webkit_javascript_result_get_global_context(r);
|
|
236
|
-
JSValueRef value = webkit_javascript_result_get_value(r);
|
|
237
|
-
JSStringRef js = JSValueToStringCopy(ctx, value, NULL);
|
|
238
|
-
size_t n = JSStringGetMaximumUTF8CStringSize(js);
|
|
239
|
-
char *s = g_new(char, n);
|
|
240
|
-
JSStringGetUTF8CString(js, s, n);
|
|
241
|
-
JSStringRelease(js);
|
|
242
|
-
#endif
|
|
243
|
-
w->on_message(s);
|
|
244
|
-
g_free(s);
|
|
245
|
-
}), this);
|
|
246
|
-
webkit_user_content_manager_register_script_message_handler(manager, "external");
|
|
247
|
-
init("window.external={invoke:function(s){window.webkit.messageHandlers.external.postMessage(s);}}");
|
|
248
|
-
gtk_container_add(GTK_CONTAINER(m_window), GTK_WIDGET(m_webview));
|
|
249
|
-
gtk_widget_grab_focus(GTK_WIDGET(m_webview));
|
|
250
|
-
if (debug) {
|
|
251
|
-
WebKitSettings *settings = webkit_web_view_get_settings(WEBKIT_WEB_VIEW(m_webview));
|
|
252
|
-
webkit_settings_set_enable_write_console_messages_to_stdout(settings, true);
|
|
253
|
-
webkit_settings_set_enable_developer_extras(settings, true);
|
|
254
|
-
}
|
|
255
|
-
gtk_widget_show_all(m_window);
|
|
256
|
-
}
|
|
257
|
-
void *window() { return (void *)m_window; }
|
|
258
|
-
void run() { gtk_main(); }
|
|
259
|
-
void terminate() { gtk_main_quit(); }
|
|
260
|
-
void dispatch(std::function<void()> f) { g_idle_add_full(G_PRIORITY_HIGH_IDLE, (GSourceFunc)([](void *f) -> int { (*static_cast<dispatch_fn_t *>(f))(); return G_SOURCE_REMOVE; }), new std::function<void()>(f), [](void *f) { delete static_cast<dispatch_fn_t *>(f); }); }
|
|
261
|
-
void set_title(const std::string title) { gtk_window_set_title(GTK_WINDOW(m_window), title.c_str()); }
|
|
262
|
-
void set_size(int width, int height, int hints) {
|
|
263
|
-
gtk_window_set_resizable(GTK_WINDOW(m_window), hints != WEBVIEW_HINT_FIXED);
|
|
264
|
-
if (hints == WEBVIEW_HINT_NONE) { gtk_window_resize(GTK_WINDOW(m_window), width, height); }
|
|
265
|
-
else if (hints == WEBVIEW_HINT_FIXED) { gtk_widget_set_size_request(m_window, width, height); }
|
|
266
|
-
else { GdkGeometry g; g.min_width = g.max_width = width; g.min_height = g.max_height = height; GdkWindowHints h = (hints == WEBVIEW_HINT_MIN ? GDK_HINT_MIN_SIZE : GDK_HINT_MAX_SIZE); gtk_window_set_geometry_hints(GTK_WINDOW(m_window), nullptr, &g, h); }
|
|
267
|
-
}
|
|
268
|
-
void navigate(const std::string url) { webkit_web_view_load_uri(WEBKIT_WEB_VIEW(m_webview), url.c_str()); }
|
|
269
|
-
void init(const std::string js) {
|
|
270
|
-
WebKitUserContentManager *manager = webkit_web_view_get_user_content_manager(WEBKIT_WEB_VIEW(m_webview));
|
|
271
|
-
webkit_user_content_manager_add_script(manager, webkit_user_script_new(js.c_str(), WEBKIT_USER_CONTENT_INJECT_TOP_FRAME, WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_START, NULL, NULL));
|
|
272
|
-
}
|
|
273
|
-
void eval(const std::string js) { webkit_web_view_run_javascript(WEBKIT_WEB_VIEW(m_webview), js.c_str(), NULL, NULL, NULL); }
|
|
274
|
-
private:
|
|
275
|
-
virtual void on_message(const std::string msg) = 0;
|
|
276
|
-
GtkWidget *m_window;
|
|
277
|
-
GtkWidget *m_webview;
|
|
278
|
-
};
|
|
279
|
-
using browser_engine = gtk_webkit_engine;
|
|
280
|
-
} // namespace webview
|
|
281
|
-
|
|
282
|
-
#elif defined(WEBVIEW_COCOA)
|
|
283
|
-
#define OBJC_OLD_DISPATCH_PROTOTYPES 1
|
|
284
|
-
#include <CoreGraphics/CoreGraphics.h>
|
|
285
|
-
#include <objc/objc-runtime.h>
|
|
286
|
-
#define NSBackingStoreBuffered 2
|
|
287
|
-
#define NSWindowStyleMaskResizable 8
|
|
288
|
-
#define NSWindowStyleMaskMiniaturizable 4
|
|
289
|
-
#define NSWindowStyleMaskTitled 1
|
|
290
|
-
#define NSWindowStyleMaskClosable 2
|
|
291
|
-
#define NSApplicationActivationPolicyRegular 0
|
|
292
|
-
#define WKUserScriptInjectionTimeAtDocumentStart 0
|
|
293
|
-
namespace webview {
|
|
294
|
-
id operator"" _cls(const char *s, std::size_t) { return (id)objc_getClass(s); }
|
|
295
|
-
SEL operator"" _sel(const char *s, std::size_t) { return sel_registerName(s); }
|
|
296
|
-
id operator"" _str(const char *s, std::size_t) { return objc_msgSend("NSString"_cls, "stringWithUTF8String:"_sel, s); }
|
|
297
|
-
class cocoa_wkwebview_engine {
|
|
298
|
-
public:
|
|
299
|
-
cocoa_wkwebview_engine(bool debug, void *window) {
|
|
300
|
-
id app = objc_msgSend("NSApplication"_cls, "sharedApplication"_sel);
|
|
301
|
-
objc_msgSend(app, "setActivationPolicy:"_sel, NSApplicationActivationPolicyRegular);
|
|
302
|
-
auto cls = objc_allocateClassPair((Class)"NSResponder"_cls, "AppDelegate", 0);
|
|
303
|
-
class_addProtocol(cls, objc_getProtocol("NSTouchBarProvider"));
|
|
304
|
-
class_addMethod(cls, "applicationShouldTerminateAfterLastWindowClosed:"_sel, (IMP)(+[](id, SEL, id) -> BOOL { return 1; }), "c@:@");
|
|
305
|
-
class_addMethod(cls, "userContentController:didReceiveScriptMessage:"_sel, (IMP)(+[](id self, SEL, id, id msg) {
|
|
306
|
-
auto w = (cocoa_wkwebview_engine *)objc_getAssociatedObject(self, "webview");
|
|
307
|
-
w->on_message((const char *)objc_msgSend(objc_msgSend(msg, "body"_sel), "UTF8String"_sel));
|
|
308
|
-
}), "v@:@@");
|
|
309
|
-
objc_registerClassPair(cls);
|
|
310
|
-
auto delegate = objc_msgSend((id)cls, "new"_sel);
|
|
311
|
-
objc_setAssociatedObject(delegate, "webview", (id)this, OBJC_ASSOCIATION_ASSIGN);
|
|
312
|
-
objc_msgSend(app, sel_registerName("setDelegate:"), delegate);
|
|
313
|
-
if (window == nullptr) {
|
|
314
|
-
m_window = objc_msgSend("NSWindow"_cls, "alloc"_sel);
|
|
315
|
-
m_window = objc_msgSend(m_window, "initWithContentRect:styleMask:backing:defer:"_sel, CGRectMake(0, 0, 0, 0), 0, NSBackingStoreBuffered, 0);
|
|
316
|
-
} else { m_window = (id)window; }
|
|
317
|
-
auto config = objc_msgSend("WKWebViewConfiguration"_cls, "new"_sel);
|
|
318
|
-
m_manager = objc_msgSend(config, "userContentController"_sel);
|
|
319
|
-
m_webview = objc_msgSend("WKWebView"_cls, "alloc"_sel);
|
|
320
|
-
if (debug) { objc_msgSend(objc_msgSend(config, "preferences"_sel), "setValue:forKey:"_sel, objc_msgSend("NSNumber"_cls, "numberWithBool:"_sel, 1), "developerExtrasEnabled"_str); }
|
|
321
|
-
objc_msgSend(m_webview, "initWithFrame:configuration:"_sel, CGRectMake(0, 0, 0, 0), config);
|
|
322
|
-
objc_msgSend(m_manager, "addScriptMessageHandler:name:"_sel, delegate, "external"_str);
|
|
323
|
-
init(R"script(window.external = { invoke: function(s) { window.webkit.messageHandlers.external.postMessage(s); }, };)script");
|
|
324
|
-
objc_msgSend(m_window, "setContentView:"_sel, m_webview);
|
|
325
|
-
objc_msgSend(m_window, "makeKeyAndOrderFront:"_sel, nullptr);
|
|
326
|
-
}
|
|
327
|
-
~cocoa_wkwebview_engine() { close(); }
|
|
328
|
-
void *window() { return (void *)m_window; }
|
|
329
|
-
void terminate() { close(); objc_msgSend("NSApp"_cls, "terminate:"_sel, nullptr); }
|
|
330
|
-
void run() { id app = objc_msgSend("NSApplication"_cls, "sharedApplication"_sel); dispatch([&]() { objc_msgSend(app, "activateIgnoringOtherApps:"_sel, 1); }); objc_msgSend(app, "run"_sel); }
|
|
331
|
-
void dispatch(std::function<void()> f) { dispatch_async_f(dispatch_get_main_queue(), new dispatch_fn_t(f), (dispatch_function_t)([](void *arg) { auto f = static_cast<dispatch_fn_t *>(arg); (*f)(); delete f; })); }
|
|
332
|
-
void set_title(const std::string title) { objc_msgSend(m_window, "setTitle:"_sel, objc_msgSend("NSString"_cls, "stringWithUTF8String:"_sel, title.c_str())); }
|
|
333
|
-
void set_size(int width, int height, int hints) {
|
|
334
|
-
auto style = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniurizable;
|
|
335
|
-
if (hints != WEBVIEW_HINT_FIXED) { style = style | NSWindowStyleMaskResizable; }
|
|
336
|
-
objc_msgSend(m_window, "setStyleMask:"_sel, style);
|
|
337
|
-
struct { CGFloat width; CGFloat height; } size;
|
|
338
|
-
if (hints == WEBVIEW_HINT_MIN) { size.width = width; size.height = height; objc_msgSend(m_window, "setContentMinSize:"_sel, size); }
|
|
339
|
-
else if (hints == WEBVIEW_HINT_MAX) { size.width = width; size.height = height; objc_msgSend(m_window, "setContentMaxSize:"_sel, size); }
|
|
340
|
-
else { objc_msgSend(m_window, "setFrame:display:animate:"_sel, CGRectMake(0, 0, width, height), 1, 0); }
|
|
341
|
-
}
|
|
342
|
-
void navigate(const std::string url) { auto nsurl = objc_msgSend("NSURL"_cls, "URLWithString:"_sel, objc_msgSend("NSString"_cls, "stringWithUTF8String:"_sel, url.c_str())); objc_msgSend(m_webview, "loadRequest:"_sel, objc_msgSend("NSURLRequest"_cls, "requestWithURL:"_sel, nsurl)); }
|
|
343
|
-
void init(const std::string js) { objc_msgSend(m_manager, "addUserScript:"_sel, objc_msgSend(objc_msgSend("WKUserScript"_cls, "alloc"_sel), "initWithSource:injectionTime:forMainFrameOnly:"_sel, objc_msgSend("NSString"_cls, "stringWithUTF8String:"_sel, js.c_str()), WKUserScriptInjectionTimeAtDocumentStart, 1)); }
|
|
344
|
-
void eval(const std::string js) { objc_msgSend(m_webview, "evaluateJavaScript:completionHandler:"_sel, objc_msgSend("NSString"_cls, "stringWithUTF8String:"_sel, js.c_str()), nullptr); }
|
|
345
|
-
private:
|
|
346
|
-
virtual void on_message(const std::string msg) = 0;
|
|
347
|
-
void close() { objc_msgSend(m_window, "close"_sel); }
|
|
348
|
-
id m_window;
|
|
349
|
-
id m_webview;
|
|
350
|
-
id m_manager;
|
|
351
|
-
};
|
|
352
|
-
using browser_engine = cocoa_wkwebview_engine;
|
|
353
|
-
} // namespace webview
|
|
354
|
-
|
|
1
|
+
/*
|
|
2
|
+
* MIT License
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) 2017 Serge Zaitsev
|
|
5
|
+
*/
|
|
6
|
+
#ifndef WEBVIEW_H
|
|
7
|
+
#define WEBVIEW_H
|
|
8
|
+
|
|
9
|
+
#ifndef WEBVIEW_API
|
|
10
|
+
#define WEBVIEW_API extern
|
|
11
|
+
#endif
|
|
12
|
+
|
|
13
|
+
#ifdef __cplusplus
|
|
14
|
+
extern "C" {
|
|
15
|
+
#endif
|
|
16
|
+
|
|
17
|
+
typedef void *webview_t;
|
|
18
|
+
|
|
19
|
+
WEBVIEW_API webview_t webview_create(int debug, void *window);
|
|
20
|
+
WEBVIEW_API void webview_destroy(webview_t w);
|
|
21
|
+
WEBVIEW_API void webview_run(webview_t w);
|
|
22
|
+
WEBVIEW_API void webview_terminate(webview_t w);
|
|
23
|
+
WEBVIEW_API void webview_dispatch(webview_t w, void (*fn)(webview_t w, void *arg), void *arg);
|
|
24
|
+
WEBVIEW_API void *webview_get_window(webview_t w);
|
|
25
|
+
WEBVIEW_API void webview_set_title(webview_t w, const char *title);
|
|
26
|
+
|
|
27
|
+
#define WEBVIEW_HINT_NONE 0
|
|
28
|
+
#define WEBVIEW_HINT_MIN 1
|
|
29
|
+
#define WEBVIEW_HINT_MAX 2
|
|
30
|
+
#define WEBVIEW_HINT_FIXED 3
|
|
31
|
+
|
|
32
|
+
WEBVIEW_API void webview_set_size(webview_t w, int width, int height, int hints);
|
|
33
|
+
WEBVIEW_API void webview_navigate(webview_t w, const char *url);
|
|
34
|
+
WEBVIEW_API void webview_init(webview_t w, const char *js);
|
|
35
|
+
WEBVIEW_API void webview_eval(webview_t w, const char *js);
|
|
36
|
+
WEBVIEW_API void webview_bind(webview_t w, const char *name, void (*fn)(const char *seq, const char *req, void *arg), void *arg);
|
|
37
|
+
WEBVIEW_API void webview_return(webview_t w, const char *seq, int status, const char *result);
|
|
38
|
+
|
|
39
|
+
#ifdef __cplusplus
|
|
40
|
+
}
|
|
41
|
+
#endif
|
|
42
|
+
|
|
43
|
+
#ifndef WEBVIEW_HEADER
|
|
44
|
+
|
|
45
|
+
#if !defined(WEBVIEW_GTK) && !defined(WEBVIEW_COCOA) && !defined(WEBVIEW_EDGE)
|
|
46
|
+
#if defined(__linux__)
|
|
47
|
+
#define WEBVIEW_GTK
|
|
48
|
+
#elif defined(__APPLE__)
|
|
49
|
+
#define WEBVIEW_COCOA
|
|
50
|
+
#elif defined(_WIN32)
|
|
51
|
+
#define WEBVIEW_EDGE
|
|
52
|
+
#else
|
|
53
|
+
#error "please, specify webview backend"
|
|
54
|
+
#endif
|
|
55
|
+
#endif
|
|
56
|
+
|
|
57
|
+
#include <atomic>
|
|
58
|
+
#include <functional>
|
|
59
|
+
#include <future>
|
|
60
|
+
#include <map>
|
|
61
|
+
#include <string>
|
|
62
|
+
#include <utility>
|
|
63
|
+
#include <vector>
|
|
64
|
+
#include <cstring>
|
|
65
|
+
|
|
66
|
+
namespace webview {
|
|
67
|
+
using dispatch_fn_t = std::function<void()>;
|
|
68
|
+
|
|
69
|
+
inline std::string url_encode(const std::string s) {
|
|
70
|
+
std::string encoded;
|
|
71
|
+
for (unsigned int i = 0; i < s.length(); i++) {
|
|
72
|
+
auto c = s[i];
|
|
73
|
+
if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') {
|
|
74
|
+
encoded = encoded + c;
|
|
75
|
+
} else {
|
|
76
|
+
char hex[4];
|
|
77
|
+
snprintf(hex, sizeof(hex), "%%%02x", c);
|
|
78
|
+
encoded = encoded + hex;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return encoded;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
inline std::string url_decode(const std::string s) {
|
|
85
|
+
std::string decoded;
|
|
86
|
+
for (unsigned int i = 0; i < s.length(); i++) {
|
|
87
|
+
if (s[i] == '%') {
|
|
88
|
+
int n;
|
|
89
|
+
n = std::stoul(s.substr(i + 1, 2), nullptr, 16);
|
|
90
|
+
decoded = decoded + static_cast<char>(n);
|
|
91
|
+
i = i + 2;
|
|
92
|
+
} else if (s[i] == '+') {
|
|
93
|
+
decoded = decoded + ' ';
|
|
94
|
+
} else {
|
|
95
|
+
decoded = decoded + s[i];
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return decoded;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
inline std::string html_from_uri(const std::string s) {
|
|
102
|
+
if (s.substr(0, 15) == "data:text/html,") {
|
|
103
|
+
return url_decode(s.substr(15));
|
|
104
|
+
}
|
|
105
|
+
return "";
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
inline int json_parse_c(const char *s, size_t sz, const char *key, size_t keysz,
|
|
109
|
+
const char **value, size_t *valuesz) {
|
|
110
|
+
enum { JSON_STATE_VALUE, JSON_STATE_LITERAL, JSON_STATE_STRING, JSON_STATE_ESCAPE, JSON_STATE_UTF8 } state = JSON_STATE_VALUE;
|
|
111
|
+
const char *k = NULL;
|
|
112
|
+
int index = 1;
|
|
113
|
+
int depth = 0;
|
|
114
|
+
int utf8_bytes = 0;
|
|
115
|
+
|
|
116
|
+
if (key == NULL) { index = keysz; keysz = 0; }
|
|
117
|
+
*value = NULL;
|
|
118
|
+
*valuesz = 0;
|
|
119
|
+
|
|
120
|
+
for (; sz > 0; s++, sz--) {
|
|
121
|
+
enum { JSON_ACTION_NONE, JSON_ACTION_START, JSON_ACTION_END, JSON_ACTION_START_STRUCT, JSON_ACTION_END_STRUCT } action = JSON_ACTION_NONE;
|
|
122
|
+
unsigned char c = *s;
|
|
123
|
+
switch (state) {
|
|
124
|
+
case JSON_STATE_VALUE:
|
|
125
|
+
if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == ',' || c == ':') { continue; }
|
|
126
|
+
else if (c == '"') { action = JSON_ACTION_START; state = JSON_STATE_STRING; }
|
|
127
|
+
else if (c == '{' || c == '[') { action = JSON_ACTION_START_STRUCT; }
|
|
128
|
+
else if (c == '}' || c == ']') { action = JSON_ACTION_END_STRUCT; }
|
|
129
|
+
else if (c == 't' || c == 'f' || c == 'n' || c == '-' || (c >= '0' && c <= '9')) { action = JSON_ACTION_START; state = JSON_STATE_LITERAL; }
|
|
130
|
+
else { return -1; }
|
|
131
|
+
break;
|
|
132
|
+
case JSON_STATE_LITERAL:
|
|
133
|
+
if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == ']' || c == '}' || c == ':') { state = JSON_STATE_VALUE; s--; sz++; action = JSON_ACTION_END; }
|
|
134
|
+
else if (c < 32 || c > 126) { return -1; }
|
|
135
|
+
case JSON_STATE_STRING:
|
|
136
|
+
if (c < 32 || (c > 126 && c < 192)) { return -1; }
|
|
137
|
+
else if (c == '"') { action = JSON_ACTION_END; state = JSON_STATE_VALUE; }
|
|
138
|
+
else if (c == '\\') { state = JSON_STATE_ESCAPE; }
|
|
139
|
+
else if (c >= 192 && c < 224) { utf8_bytes = 1; state = JSON_STATE_UTF8; }
|
|
140
|
+
else if (c >= 224 && c < 240) { utf8_bytes = 2; state = JSON_STATE_UTF8; }
|
|
141
|
+
else if (c >= 240 && c < 247) { utf8_bytes = 3; state = JSON_STATE_UTF8; }
|
|
142
|
+
else if (c >= 128 && c < 192) { return -1; }
|
|
143
|
+
break;
|
|
144
|
+
case JSON_STATE_ESCAPE:
|
|
145
|
+
if (c == '"' || c == '\\' || c == '/' || c == 'b' || c == 'f' || c == 'n' || c == 'r' || c == 't' || c == 'u') { state = JSON_STATE_STRING; }
|
|
146
|
+
else { return -1; }
|
|
147
|
+
break;
|
|
148
|
+
case JSON_STATE_UTF8:
|
|
149
|
+
if (c < 128 || c > 191) { return -1; }
|
|
150
|
+
utf8_bytes--;
|
|
151
|
+
if (utf8_bytes == 0) { state = JSON_STATE_STRING; }
|
|
152
|
+
break;
|
|
153
|
+
default: return -1;
|
|
154
|
+
}
|
|
155
|
+
if (action == JSON_ACTION_END_STRUCT) { depth--; }
|
|
156
|
+
if (depth == 1) {
|
|
157
|
+
if (action == JSON_ACTION_START || action == JSON_ACTION_START_STRUCT) {
|
|
158
|
+
if (index == 0) { *value = s; }
|
|
159
|
+
else if (keysz > 0 && index == 1) { k = s; }
|
|
160
|
+
else { index--; }
|
|
161
|
+
} else if (action == JSON_ACTION_END || action == JSON_ACTION_END_STRUCT) {
|
|
162
|
+
if (*value != NULL && index == 0) { *valuesz = (size_t)(s + 1 - *value); return 0; }
|
|
163
|
+
else if (keysz > 0 && k != NULL) {
|
|
164
|
+
if (keysz == (size_t)(s - k - 1) && memcmp(key, k + 1, keysz) == 0) { index = 0; }
|
|
165
|
+
else { index = 2; }
|
|
166
|
+
k = NULL;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
if (action == JSON_ACTION_START_STRUCT) { depth++; }
|
|
171
|
+
}
|
|
172
|
+
return -1;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
inline std::string json_escape(std::string s) { return '"' + s + '"'; }
|
|
176
|
+
|
|
177
|
+
inline int json_unescape(const char *s, size_t n, char *out) {
|
|
178
|
+
int r = 0;
|
|
179
|
+
if (*s++ != '"') { return -1; }
|
|
180
|
+
while (n > 2) {
|
|
181
|
+
char c = *s;
|
|
182
|
+
if (c == '\\') { s++; n--; switch (*s) {
|
|
183
|
+
case 'b': c = '\b'; break;
|
|
184
|
+
case 'f': c = '\f'; break;
|
|
185
|
+
case 'n': c = '\n'; break;
|
|
186
|
+
case 'r': c = '\r'; break;
|
|
187
|
+
case 't': c = '\t'; break;
|
|
188
|
+
case '\\': c = '\\'; break;
|
|
189
|
+
case '/': c = '/'; break;
|
|
190
|
+
case '\"': c = '\"'; break;
|
|
191
|
+
default: return -1;
|
|
192
|
+
}}
|
|
193
|
+
if (out != NULL) { *out++ = c; }
|
|
194
|
+
s++; n--; r++;
|
|
195
|
+
}
|
|
196
|
+
if (*s != '"') { return -1; }
|
|
197
|
+
if (out != NULL) { *out = '\0'; }
|
|
198
|
+
return r;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
inline std::string json_parse(const std::string s, const std::string key, const int index) {
|
|
202
|
+
const char *value; size_t value_sz;
|
|
203
|
+
if (key == "") { json_parse_c(s.c_str(), s.length(), nullptr, index, &value, &value_sz); }
|
|
204
|
+
else { json_parse_c(s.c_str(), s.length(), key.c_str(), key.length(), &value, &value_sz); }
|
|
205
|
+
if (value != nullptr) {
|
|
206
|
+
if (value[0] != '"') { return std::string(value, value_sz); }
|
|
207
|
+
int n = json_unescape(value, value_sz, nullptr);
|
|
208
|
+
if (n > 0) { char *decoded = new char[n + 1]; json_unescape(value, value_sz, decoded); std::string result(decoded, n); delete[] decoded; return result; }
|
|
209
|
+
}
|
|
210
|
+
return "";
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
} // namespace webview
|
|
214
|
+
|
|
215
|
+
#if defined(WEBVIEW_GTK)
|
|
216
|
+
#include <JavaScriptCore/JavaScript.h>
|
|
217
|
+
#include <gtk/gtk.h>
|
|
218
|
+
#include <webkit2/webkit2.h>
|
|
219
|
+
namespace webview {
|
|
220
|
+
class gtk_webkit_engine {
|
|
221
|
+
public:
|
|
222
|
+
gtk_webkit_engine(bool debug, void *window) : m_window(static_cast<GtkWidget *>(window)) {
|
|
223
|
+
gtk_init_check(0, NULL);
|
|
224
|
+
m_window = static_cast<GtkWidget *>(window);
|
|
225
|
+
if (m_window == nullptr) { m_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); }
|
|
226
|
+
g_signal_connect(G_OBJECT(m_window), "destroy", G_CALLBACK(+[](GtkWidget *, gpointer arg) { static_cast<gtk_webkit_engine *>(arg)->terminate(); }), this);
|
|
227
|
+
m_webview = webkit_web_view_new();
|
|
228
|
+
WebKitUserContentManager *manager = webkit_web_view_get_user_content_manager(WEBKIT_WEB_VIEW(m_webview));
|
|
229
|
+
g_signal_connect(manager, "script-message-received::external", G_CALLBACK(+[](WebKitUserContentManager *, WebKitJavascriptResult *r, gpointer arg) {
|
|
230
|
+
auto *w = static_cast<gtk_webkit_engine *>(arg);
|
|
231
|
+
#if WEBKIT_MAJOR_VERSION >= 2 && WEBKIT_MINOR_VERSION >= 22
|
|
232
|
+
JSCValue *value = webkit_javascript_result_get_js_value(r);
|
|
233
|
+
char *s = jsc_value_to_string(value);
|
|
234
|
+
#else
|
|
235
|
+
JSGlobalContextRef ctx = webkit_javascript_result_get_global_context(r);
|
|
236
|
+
JSValueRef value = webkit_javascript_result_get_value(r);
|
|
237
|
+
JSStringRef js = JSValueToStringCopy(ctx, value, NULL);
|
|
238
|
+
size_t n = JSStringGetMaximumUTF8CStringSize(js);
|
|
239
|
+
char *s = g_new(char, n);
|
|
240
|
+
JSStringGetUTF8CString(js, s, n);
|
|
241
|
+
JSStringRelease(js);
|
|
242
|
+
#endif
|
|
243
|
+
w->on_message(s);
|
|
244
|
+
g_free(s);
|
|
245
|
+
}), this);
|
|
246
|
+
webkit_user_content_manager_register_script_message_handler(manager, "external");
|
|
247
|
+
init("window.external={invoke:function(s){window.webkit.messageHandlers.external.postMessage(s);}}");
|
|
248
|
+
gtk_container_add(GTK_CONTAINER(m_window), GTK_WIDGET(m_webview));
|
|
249
|
+
gtk_widget_grab_focus(GTK_WIDGET(m_webview));
|
|
250
|
+
if (debug) {
|
|
251
|
+
WebKitSettings *settings = webkit_web_view_get_settings(WEBKIT_WEB_VIEW(m_webview));
|
|
252
|
+
webkit_settings_set_enable_write_console_messages_to_stdout(settings, true);
|
|
253
|
+
webkit_settings_set_enable_developer_extras(settings, true);
|
|
254
|
+
}
|
|
255
|
+
gtk_widget_show_all(m_window);
|
|
256
|
+
}
|
|
257
|
+
void *window() { return (void *)m_window; }
|
|
258
|
+
void run() { gtk_main(); }
|
|
259
|
+
void terminate() { gtk_main_quit(); }
|
|
260
|
+
void dispatch(std::function<void()> f) { g_idle_add_full(G_PRIORITY_HIGH_IDLE, (GSourceFunc)([](void *f) -> int { (*static_cast<dispatch_fn_t *>(f))(); return G_SOURCE_REMOVE; }), new std::function<void()>(f), [](void *f) { delete static_cast<dispatch_fn_t *>(f); }); }
|
|
261
|
+
void set_title(const std::string title) { gtk_window_set_title(GTK_WINDOW(m_window), title.c_str()); }
|
|
262
|
+
void set_size(int width, int height, int hints) {
|
|
263
|
+
gtk_window_set_resizable(GTK_WINDOW(m_window), hints != WEBVIEW_HINT_FIXED);
|
|
264
|
+
if (hints == WEBVIEW_HINT_NONE) { gtk_window_resize(GTK_WINDOW(m_window), width, height); }
|
|
265
|
+
else if (hints == WEBVIEW_HINT_FIXED) { gtk_widget_set_size_request(m_window, width, height); }
|
|
266
|
+
else { GdkGeometry g; g.min_width = g.max_width = width; g.min_height = g.max_height = height; GdkWindowHints h = (hints == WEBVIEW_HINT_MIN ? GDK_HINT_MIN_SIZE : GDK_HINT_MAX_SIZE); gtk_window_set_geometry_hints(GTK_WINDOW(m_window), nullptr, &g, h); }
|
|
267
|
+
}
|
|
268
|
+
void navigate(const std::string url) { webkit_web_view_load_uri(WEBKIT_WEB_VIEW(m_webview), url.c_str()); }
|
|
269
|
+
void init(const std::string js) {
|
|
270
|
+
WebKitUserContentManager *manager = webkit_web_view_get_user_content_manager(WEBKIT_WEB_VIEW(m_webview));
|
|
271
|
+
webkit_user_content_manager_add_script(manager, webkit_user_script_new(js.c_str(), WEBKIT_USER_CONTENT_INJECT_TOP_FRAME, WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_START, NULL, NULL));
|
|
272
|
+
}
|
|
273
|
+
void eval(const std::string js) { webkit_web_view_run_javascript(WEBKIT_WEB_VIEW(m_webview), js.c_str(), NULL, NULL, NULL); }
|
|
274
|
+
private:
|
|
275
|
+
virtual void on_message(const std::string msg) = 0;
|
|
276
|
+
GtkWidget *m_window;
|
|
277
|
+
GtkWidget *m_webview;
|
|
278
|
+
};
|
|
279
|
+
using browser_engine = gtk_webkit_engine;
|
|
280
|
+
} // namespace webview
|
|
281
|
+
|
|
282
|
+
#elif defined(WEBVIEW_COCOA)
|
|
283
|
+
#define OBJC_OLD_DISPATCH_PROTOTYPES 1
|
|
284
|
+
#include <CoreGraphics/CoreGraphics.h>
|
|
285
|
+
#include <objc/objc-runtime.h>
|
|
286
|
+
#define NSBackingStoreBuffered 2
|
|
287
|
+
#define NSWindowStyleMaskResizable 8
|
|
288
|
+
#define NSWindowStyleMaskMiniaturizable 4
|
|
289
|
+
#define NSWindowStyleMaskTitled 1
|
|
290
|
+
#define NSWindowStyleMaskClosable 2
|
|
291
|
+
#define NSApplicationActivationPolicyRegular 0
|
|
292
|
+
#define WKUserScriptInjectionTimeAtDocumentStart 0
|
|
293
|
+
namespace webview {
|
|
294
|
+
id operator"" _cls(const char *s, std::size_t) { return (id)objc_getClass(s); }
|
|
295
|
+
SEL operator"" _sel(const char *s, std::size_t) { return sel_registerName(s); }
|
|
296
|
+
id operator"" _str(const char *s, std::size_t) { return objc_msgSend("NSString"_cls, "stringWithUTF8String:"_sel, s); }
|
|
297
|
+
class cocoa_wkwebview_engine {
|
|
298
|
+
public:
|
|
299
|
+
cocoa_wkwebview_engine(bool debug, void *window) {
|
|
300
|
+
id app = objc_msgSend("NSApplication"_cls, "sharedApplication"_sel);
|
|
301
|
+
objc_msgSend(app, "setActivationPolicy:"_sel, NSApplicationActivationPolicyRegular);
|
|
302
|
+
auto cls = objc_allocateClassPair((Class)"NSResponder"_cls, "AppDelegate", 0);
|
|
303
|
+
class_addProtocol(cls, objc_getProtocol("NSTouchBarProvider"));
|
|
304
|
+
class_addMethod(cls, "applicationShouldTerminateAfterLastWindowClosed:"_sel, (IMP)(+[](id, SEL, id) -> BOOL { return 1; }), "c@:@");
|
|
305
|
+
class_addMethod(cls, "userContentController:didReceiveScriptMessage:"_sel, (IMP)(+[](id self, SEL, id, id msg) {
|
|
306
|
+
auto w = (cocoa_wkwebview_engine *)objc_getAssociatedObject(self, "webview");
|
|
307
|
+
w->on_message((const char *)objc_msgSend(objc_msgSend(msg, "body"_sel), "UTF8String"_sel));
|
|
308
|
+
}), "v@:@@");
|
|
309
|
+
objc_registerClassPair(cls);
|
|
310
|
+
auto delegate = objc_msgSend((id)cls, "new"_sel);
|
|
311
|
+
objc_setAssociatedObject(delegate, "webview", (id)this, OBJC_ASSOCIATION_ASSIGN);
|
|
312
|
+
objc_msgSend(app, sel_registerName("setDelegate:"), delegate);
|
|
313
|
+
if (window == nullptr) {
|
|
314
|
+
m_window = objc_msgSend("NSWindow"_cls, "alloc"_sel);
|
|
315
|
+
m_window = objc_msgSend(m_window, "initWithContentRect:styleMask:backing:defer:"_sel, CGRectMake(0, 0, 0, 0), 0, NSBackingStoreBuffered, 0);
|
|
316
|
+
} else { m_window = (id)window; }
|
|
317
|
+
auto config = objc_msgSend("WKWebViewConfiguration"_cls, "new"_sel);
|
|
318
|
+
m_manager = objc_msgSend(config, "userContentController"_sel);
|
|
319
|
+
m_webview = objc_msgSend("WKWebView"_cls, "alloc"_sel);
|
|
320
|
+
if (debug) { objc_msgSend(objc_msgSend(config, "preferences"_sel), "setValue:forKey:"_sel, objc_msgSend("NSNumber"_cls, "numberWithBool:"_sel, 1), "developerExtrasEnabled"_str); }
|
|
321
|
+
objc_msgSend(m_webview, "initWithFrame:configuration:"_sel, CGRectMake(0, 0, 0, 0), config);
|
|
322
|
+
objc_msgSend(m_manager, "addScriptMessageHandler:name:"_sel, delegate, "external"_str);
|
|
323
|
+
init(R"script(window.external = { invoke: function(s) { window.webkit.messageHandlers.external.postMessage(s); }, };)script");
|
|
324
|
+
objc_msgSend(m_window, "setContentView:"_sel, m_webview);
|
|
325
|
+
objc_msgSend(m_window, "makeKeyAndOrderFront:"_sel, nullptr);
|
|
326
|
+
}
|
|
327
|
+
~cocoa_wkwebview_engine() { close(); }
|
|
328
|
+
void *window() { return (void *)m_window; }
|
|
329
|
+
void terminate() { close(); objc_msgSend("NSApp"_cls, "terminate:"_sel, nullptr); }
|
|
330
|
+
void run() { id app = objc_msgSend("NSApplication"_cls, "sharedApplication"_sel); dispatch([&]() { objc_msgSend(app, "activateIgnoringOtherApps:"_sel, 1); }); objc_msgSend(app, "run"_sel); }
|
|
331
|
+
void dispatch(std::function<void()> f) { dispatch_async_f(dispatch_get_main_queue(), new dispatch_fn_t(f), (dispatch_function_t)([](void *arg) { auto f = static_cast<dispatch_fn_t *>(arg); (*f)(); delete f; })); }
|
|
332
|
+
void set_title(const std::string title) { objc_msgSend(m_window, "setTitle:"_sel, objc_msgSend("NSString"_cls, "stringWithUTF8String:"_sel, title.c_str())); }
|
|
333
|
+
void set_size(int width, int height, int hints) {
|
|
334
|
+
auto style = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniurizable;
|
|
335
|
+
if (hints != WEBVIEW_HINT_FIXED) { style = style | NSWindowStyleMaskResizable; }
|
|
336
|
+
objc_msgSend(m_window, "setStyleMask:"_sel, style);
|
|
337
|
+
struct { CGFloat width; CGFloat height; } size;
|
|
338
|
+
if (hints == WEBVIEW_HINT_MIN) { size.width = width; size.height = height; objc_msgSend(m_window, "setContentMinSize:"_sel, size); }
|
|
339
|
+
else if (hints == WEBVIEW_HINT_MAX) { size.width = width; size.height = height; objc_msgSend(m_window, "setContentMaxSize:"_sel, size); }
|
|
340
|
+
else { objc_msgSend(m_window, "setFrame:display:animate:"_sel, CGRectMake(0, 0, width, height), 1, 0); }
|
|
341
|
+
}
|
|
342
|
+
void navigate(const std::string url) { auto nsurl = objc_msgSend("NSURL"_cls, "URLWithString:"_sel, objc_msgSend("NSString"_cls, "stringWithUTF8String:"_sel, url.c_str())); objc_msgSend(m_webview, "loadRequest:"_sel, objc_msgSend("NSURLRequest"_cls, "requestWithURL:"_sel, nsurl)); }
|
|
343
|
+
void init(const std::string js) { objc_msgSend(m_manager, "addUserScript:"_sel, objc_msgSend(objc_msgSend("WKUserScript"_cls, "alloc"_sel), "initWithSource:injectionTime:forMainFrameOnly:"_sel, objc_msgSend("NSString"_cls, "stringWithUTF8String:"_sel, js.c_str()), WKUserScriptInjectionTimeAtDocumentStart, 1)); }
|
|
344
|
+
void eval(const std::string js) { objc_msgSend(m_webview, "evaluateJavaScript:completionHandler:"_sel, objc_msgSend("NSString"_cls, "stringWithUTF8String:"_sel, js.c_str()), nullptr); }
|
|
345
|
+
private:
|
|
346
|
+
virtual void on_message(const std::string msg) = 0;
|
|
347
|
+
void close() { objc_msgSend(m_window, "close"_sel); }
|
|
348
|
+
id m_window;
|
|
349
|
+
id m_webview;
|
|
350
|
+
id m_manager;
|
|
351
|
+
};
|
|
352
|
+
using browser_engine = cocoa_wkwebview_engine;
|
|
353
|
+
} // namespace webview
|
|
354
|
+
|
|
355
355
|
#elif defined(WEBVIEW_EDGE)
|
|
356
356
|
#define WIN32_LEAN_AND_MEAN
|
|
357
357
|
#include <windows.h>
|
|
358
358
|
#pragma comment(lib, "user32.lib")
|
|
359
359
|
#include <objbase.h>
|
|
360
|
-
#include <winrt/Windows.Foundation.h>
|
|
361
|
-
#include <winrt/Windows.Web.UI.Interop.h>
|
|
362
|
-
#pragma comment(lib, "windowsapp")
|
|
363
360
|
#include "webview2.h"
|
|
364
361
|
#pragma comment(lib, "ole32.lib")
|
|
365
362
|
#pragma comment(lib, "oleaut32.lib")
|
|
@@ -374,178 +371,289 @@ public:
|
|
|
374
371
|
virtual void init(const std::string js) = 0;
|
|
375
372
|
virtual void resize(HWND) = 0;
|
|
376
373
|
};
|
|
374
|
+
|
|
375
|
+
// Old Edge HTML (Edge Legacy) fallback - requires C++/WinRT
|
|
376
|
+
#if defined(WEBVIEW_USE_EDGE_HTML)
|
|
377
|
+
#include <winrt/Windows.Foundation.h>
|
|
378
|
+
#include <winrt/Windows.Web.UI.Interop.h>
|
|
379
|
+
#pragma comment(lib, "windowsapp")
|
|
377
380
|
class edge_html : public browser {
|
|
378
|
-
public:
|
|
379
|
-
bool embed(HWND wnd, bool debug, msg_cb_t cb) override {
|
|
380
|
-
init_apartment(winrt::apartment_type::single_threaded);
|
|
381
|
-
auto process = WebViewControlProcess();
|
|
382
|
-
auto op = process.CreateWebViewControlAsync(reinterpret_cast<int64_t>(wnd), Rect());
|
|
383
|
-
if (op.Status() != AsyncStatus::Completed) { handle h(CreateEvent(nullptr, false, false, nullptr)); op.Completed([h = h.get()](auto, auto) { SetEvent(h); }); HANDLE hs[] = {h.get()}; DWORD i; CoWaitForMultipleHandles(COWAIT_DISPATCH_WINDOW_MESSAGES | COWAIT_DISPATCH_CALLS | COWAIT_INPUTAVAILABLE, INFINITE, 1, hs, &i); }
|
|
384
|
-
m_webview = op.GetResults();
|
|
385
|
-
m_webview.Settings().IsScriptNotifyAllowed(true);
|
|
386
|
-
m_webview.IsVisible(true);
|
|
387
|
-
m_webview.ScriptNotify([=](auto const &sender, auto const &args) { std::string s = winrt::to_string(args.Value()); cb(s.c_str()); });
|
|
388
|
-
m_webview.NavigationStarting([=](auto const &sender, auto const &args) { m_webview.AddInitializeScript(winrt::to_hstring(init_js)); });
|
|
389
|
-
init("window.external.invoke = s => window.external.notify(s)");
|
|
390
|
-
return true;
|
|
391
|
-
}
|
|
392
|
-
void navigate(const std::string url) override { std::string html = html_from_uri(url); if (html != "") { m_webview.NavigateToString(winrt::to_hstring(html)); } else { Uri uri(winrt::to_hstring(url)); m_webview.Navigate(uri); } }
|
|
393
|
-
void init(const std::string js) override { init_js = init_js + "(function(){" + js + "})();"; }
|
|
394
|
-
void eval(const std::string js) override { m_webview.InvokeScriptAsync(L"eval", single_threaded_vector<hstring>({winrt::to_hstring(js)})); }
|
|
395
|
-
void resize(HWND wnd) override { if (m_webview == nullptr) { return; } RECT r; GetClientRect(wnd, &r); Rect bounds(r.left, r.top, r.right - r.left, r.bottom - r.top); m_webview.Bounds(bounds); }
|
|
396
|
-
private:
|
|
381
|
+
public:
|
|
382
|
+
bool embed(HWND wnd, bool debug, msg_cb_t cb) override {
|
|
383
|
+
init_apartment(winrt::apartment_type::single_threaded);
|
|
384
|
+
auto process = WebViewControlProcess();
|
|
385
|
+
auto op = process.CreateWebViewControlAsync(reinterpret_cast<int64_t>(wnd), Rect());
|
|
386
|
+
if (op.Status() != AsyncStatus::Completed) { handle h(CreateEvent(nullptr, false, false, nullptr)); op.Completed([h = h.get()](auto, auto) { SetEvent(h); }); HANDLE hs[] = {h.get()}; DWORD i; CoWaitForMultipleHandles(COWAIT_DISPATCH_WINDOW_MESSAGES | COWAIT_DISPATCH_CALLS | COWAIT_INPUTAVAILABLE, INFINITE, 1, hs, &i); }
|
|
387
|
+
m_webview = op.GetResults();
|
|
388
|
+
m_webview.Settings().IsScriptNotifyAllowed(true);
|
|
389
|
+
m_webview.IsVisible(true);
|
|
390
|
+
m_webview.ScriptNotify([=](auto const &sender, auto const &args) { std::string s = winrt::to_string(args.Value()); cb(s.c_str()); });
|
|
391
|
+
m_webview.NavigationStarting([=](auto const &sender, auto const &args) { m_webview.AddInitializeScript(winrt::to_hstring(init_js)); });
|
|
392
|
+
init("window.external.invoke = s => window.external.notify(s)");
|
|
393
|
+
return true;
|
|
394
|
+
}
|
|
395
|
+
void navigate(const std::string url) override { std::string html = html_from_uri(url); if (html != "") { m_webview.NavigateToString(winrt::to_hstring(html)); } else { Uri uri(winrt::to_hstring(url)); m_webview.Navigate(uri); } }
|
|
396
|
+
void init(const std::string js) override { init_js = init_js + "(function(){" + js + "})();"; }
|
|
397
|
+
void eval(const std::string js) override { m_webview.InvokeScriptAsync(L"eval", single_threaded_vector<hstring>({winrt::to_hstring(js)})); }
|
|
398
|
+
void resize(HWND wnd) override { if (m_webview == nullptr) { return; } RECT r; GetClientRect(wnd, &r); Rect bounds(r.left, r.top, r.right - r.left, r.bottom - r.top); m_webview.Bounds(bounds); }
|
|
399
|
+
private:
|
|
397
400
|
WebViewControl m_webview = nullptr;
|
|
398
401
|
std::string init_js = "";
|
|
399
402
|
};
|
|
403
|
+
#endif // WEBVIEW_USE_EDGE_HTML
|
|
404
|
+
|
|
400
405
|
class edge_chromium : public browser {
|
|
401
406
|
public:
|
|
402
407
|
bool embed(HWND wnd, bool debug, msg_cb_t cb) override {
|
|
403
|
-
CoInitializeEx(nullptr,
|
|
408
|
+
CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
|
|
404
409
|
std::atomic_flag flag = ATOMIC_FLAG_INIT;
|
|
405
410
|
flag.test_and_set();
|
|
406
|
-
|
|
407
|
-
|
|
411
|
+
|
|
412
|
+
// Use modern WebView2 API
|
|
413
|
+
HRESULT res = CreateCoreWebView2EnvironmentWithOptions(nullptr, nullptr, nullptr,
|
|
414
|
+
new webview2_com_handler(wnd,
|
|
415
|
+
[&](ICoreWebView2Controller *controller, ICoreWebView2 *webview) {
|
|
416
|
+
m_controller = controller;
|
|
417
|
+
m_webview = webview;
|
|
418
|
+
flag.clear();
|
|
419
|
+
}));
|
|
420
|
+
|
|
421
|
+
if (res != S_OK) {
|
|
422
|
+
CoUninitialize();
|
|
423
|
+
return false;
|
|
424
|
+
}
|
|
425
|
+
|
|
408
426
|
MSG msg = {};
|
|
409
|
-
while (flag.test_and_set() && GetMessage(&msg, NULL, 0, 0)) {
|
|
410
|
-
|
|
411
|
-
|
|
427
|
+
while (flag.test_and_set() && GetMessage(&msg, NULL, 0, 0)) {
|
|
428
|
+
TranslateMessage(&msg);
|
|
429
|
+
DispatchMessage(&msg);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
if (m_webview) {
|
|
433
|
+
init("window.external={invoke:s=>window.chrome.webview.postMessage(s)}");
|
|
434
|
+
return true;
|
|
435
|
+
}
|
|
436
|
+
return false;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
void resize(HWND wnd) override {
|
|
440
|
+
if (m_controller == nullptr) { return; }
|
|
441
|
+
RECT bounds;
|
|
442
|
+
GetClientRect(wnd, &bounds);
|
|
443
|
+
m_controller->put_Bounds(bounds);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
void navigate(const std::string url) override {
|
|
447
|
+
if (!m_webview) return;
|
|
448
|
+
auto wurl = to_lpwstr(url);
|
|
449
|
+
m_webview->Navigate(wurl);
|
|
450
|
+
delete[] wurl;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
void init(const std::string js) override {
|
|
454
|
+
if (!m_webview) return;
|
|
455
|
+
LPCWSTR wjs = to_lpwstr(js);
|
|
456
|
+
m_webview->AddScriptToExecuteOnDocumentCreated(wjs, nullptr);
|
|
457
|
+
delete[] wjs;
|
|
412
458
|
}
|
|
413
|
-
|
|
414
|
-
void
|
|
415
|
-
|
|
416
|
-
|
|
459
|
+
|
|
460
|
+
void eval(const std::string js) override {
|
|
461
|
+
if (!m_webview) return;
|
|
462
|
+
LPCWSTR wjs = to_lpwstr(js);
|
|
463
|
+
m_webview->ExecuteScript(wjs, nullptr);
|
|
464
|
+
delete[] wjs;
|
|
465
|
+
}
|
|
466
|
+
|
|
417
467
|
private:
|
|
418
|
-
LPWSTR to_lpwstr(const std::string s) {
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
468
|
+
LPWSTR to_lpwstr(const std::string s) {
|
|
469
|
+
int n = MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, NULL, 0);
|
|
470
|
+
wchar_t *ws = new wchar_t[n];
|
|
471
|
+
MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, ws, n);
|
|
472
|
+
return ws;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
ICoreWebView2 *m_webview = nullptr;
|
|
476
|
+
ICoreWebView2Controller *m_controller = nullptr;
|
|
477
|
+
|
|
478
|
+
class webview2_com_handler : public ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler {
|
|
479
|
+
using webview2_com_handler_cb_t = std::function<void(ICoreWebView2Controller *, ICoreWebView2 *)>;
|
|
422
480
|
public:
|
|
423
481
|
webview2_com_handler(HWND hwnd, webview2_com_handler_cb_t cb) : m_window(hwnd), m_cb(cb) {}
|
|
424
|
-
|
|
425
|
-
ULONG STDMETHODCALLTYPE
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
HRESULT STDMETHODCALLTYPE
|
|
482
|
+
|
|
483
|
+
ULONG STDMETHODCALLTYPE AddRef() override { return 1; }
|
|
484
|
+
ULONG STDMETHODCALLTYPE Release() override { return 1; }
|
|
485
|
+
|
|
486
|
+
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID *ppv) override {
|
|
487
|
+
*ppv = nullptr;
|
|
488
|
+
if (riid == IID_IUnknown || riid == IID_ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler) {
|
|
489
|
+
*ppv = this;
|
|
490
|
+
return S_OK;
|
|
491
|
+
}
|
|
492
|
+
return E_NOINTERFACE;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
HRESULT STDMETHODCALLTYPE Invoke(HRESULT res, ICoreWebView2Environment *env) override {
|
|
496
|
+
if (env) {
|
|
497
|
+
env->CreateCoreWebView2Controller(m_window,
|
|
498
|
+
new create_controller_handler(m_cb));
|
|
499
|
+
}
|
|
500
|
+
return S_OK;
|
|
501
|
+
}
|
|
502
|
+
|
|
429
503
|
private:
|
|
430
504
|
HWND m_window;
|
|
431
505
|
webview2_com_handler_cb_t m_cb;
|
|
506
|
+
|
|
507
|
+
class create_controller_handler : public ICoreWebView2CreateCoreWebView2ControllerCompletedHandler {
|
|
508
|
+
public:
|
|
509
|
+
create_controller_handler(webview2_com_handler_cb_t cb) : m_cb(cb) {}
|
|
510
|
+
|
|
511
|
+
ULONG STDMETHODCALLTYPE AddRef() override { return 1; }
|
|
512
|
+
ULONG STDMETHODCALLTYPE Release() override { return 1; }
|
|
513
|
+
|
|
514
|
+
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID *ppv) override {
|
|
515
|
+
*ppv = nullptr;
|
|
516
|
+
if (riid == IID_IUnknown || riid == IID_ICoreWebView2CreateCoreWebView2ControllerCompletedHandler) {
|
|
517
|
+
*ppv = this;
|
|
518
|
+
return S_OK;
|
|
519
|
+
}
|
|
520
|
+
return E_NOINTERFACE;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
HRESULT STDMETHODCALLTYPE Invoke(HRESULT res, ICoreWebView2Controller *controller) override {
|
|
524
|
+
if (controller) {
|
|
525
|
+
ICoreWebView2 *webview = nullptr;
|
|
526
|
+
controller->get_CoreWebView2(&webview);
|
|
527
|
+
m_cb(controller, webview);
|
|
528
|
+
}
|
|
529
|
+
return S_OK;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
private:
|
|
533
|
+
webview2_com_handler_cb_t m_cb;
|
|
534
|
+
};
|
|
432
535
|
};
|
|
433
536
|
};
|
|
434
|
-
class win32_edge_engine {
|
|
435
|
-
public:
|
|
436
|
-
win32_edge_engine(bool debug, void *window) {
|
|
437
|
-
if (window == nullptr) {
|
|
438
|
-
WNDCLASSEX wc; ZeroMemory(&wc, sizeof(WNDCLASSEX));
|
|
439
|
-
wc.cbSize = sizeof(WNDCLASSEX); wc.hInstance = GetModuleHandle(nullptr); wc.lpszClassName = "webview";
|
|
440
|
-
wc.lpfnWndProc = (WNDPROC)(+[](HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) -> int {
|
|
441
|
-
auto w = (win32_edge_engine *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
442
|
-
switch (msg) {
|
|
443
|
-
case WM_SIZE: w->m_browser->resize(hwnd); break;
|
|
444
|
-
case WM_CLOSE: DestroyWindow(hwnd); break;
|
|
445
|
-
case WM_DESTROY: w->terminate(); break;
|
|
446
|
-
case WM_GETMINMAXINFO: { auto lpmmi = (LPMINMAXINFO)lp; if (w == nullptr) { return 0; }
|
|
447
|
-
if (w->m_maxsz.x > 0 && w->m_maxsz.y > 0) { lpmmi->ptMaxSize = w->m_maxsz; lpmmi->ptMaxTrackSize = w->m_maxsz; }
|
|
448
|
-
if (w->m_minsz.x > 0 && w->m_minsz.y > 0) { lpmmi->ptMinTrackSize = w->m_minsz; }
|
|
449
|
-
} break;
|
|
450
|
-
default: return DefWindowProc(hwnd, msg, wp, lp);
|
|
451
|
-
}
|
|
452
|
-
return 0;
|
|
453
|
-
});
|
|
454
|
-
RegisterClassEx(&wc);
|
|
455
|
-
m_window = CreateWindow("webview", "", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 640, 480, nullptr, nullptr, GetModuleHandle(nullptr), nullptr);
|
|
456
|
-
SetWindowLongPtr(m_window, GWLP_USERDATA, (LONG_PTR)this);
|
|
457
|
-
} else { m_window = *(static_cast<HWND *>(window)); }
|
|
537
|
+
class win32_edge_engine {
|
|
538
|
+
public:
|
|
539
|
+
win32_edge_engine(bool debug, void *window) {
|
|
540
|
+
if (window == nullptr) {
|
|
541
|
+
WNDCLASSEX wc; ZeroMemory(&wc, sizeof(WNDCLASSEX));
|
|
542
|
+
wc.cbSize = sizeof(WNDCLASSEX); wc.hInstance = GetModuleHandle(nullptr); wc.lpszClassName = "webview";
|
|
543
|
+
wc.lpfnWndProc = (WNDPROC)(+[](HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) -> int {
|
|
544
|
+
auto w = (win32_edge_engine *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
545
|
+
switch (msg) {
|
|
546
|
+
case WM_SIZE: w->m_browser->resize(hwnd); break;
|
|
547
|
+
case WM_CLOSE: DestroyWindow(hwnd); break;
|
|
548
|
+
case WM_DESTROY: w->terminate(); break;
|
|
549
|
+
case WM_GETMINMAXINFO: { auto lpmmi = (LPMINMAXINFO)lp; if (w == nullptr) { return 0; }
|
|
550
|
+
if (w->m_maxsz.x > 0 && w->m_maxsz.y > 0) { lpmmi->ptMaxSize = w->m_maxsz; lpmmi->ptMaxTrackSize = w->m_maxsz; }
|
|
551
|
+
if (w->m_minsz.x > 0 && w->m_minsz.y > 0) { lpmmi->ptMinTrackSize = w->m_minsz; }
|
|
552
|
+
} break;
|
|
553
|
+
default: return DefWindowProc(hwnd, msg, wp, lp);
|
|
554
|
+
}
|
|
555
|
+
return 0;
|
|
556
|
+
});
|
|
557
|
+
RegisterClassEx(&wc);
|
|
558
|
+
m_window = CreateWindow("webview", "", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 640, 480, nullptr, nullptr, GetModuleHandle(nullptr), nullptr);
|
|
559
|
+
SetWindowLongPtr(m_window, GWLP_USERDATA, (LONG_PTR)this);
|
|
560
|
+
} else { m_window = *(static_cast<HWND *>(window)); }
|
|
458
561
|
ShowWindow(m_window, SW_SHOW); UpdateWindow(m_window); SetFocus(m_window);
|
|
459
562
|
auto cb = std::bind(&win32_edge_engine::on_message, this, std::placeholders::_1);
|
|
460
|
-
if (!m_browser->embed(m_window, debug, cb)) {
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
if (msg.message == WM_APP) { auto f = (dispatch_fn_t *)(msg.lParam); (*f)(); delete f; }
|
|
465
|
-
else if (msg.message == WM_QUIT) { return; }
|
|
466
|
-
}}
|
|
467
|
-
void *window() { return (void *)m_window; }
|
|
468
|
-
void terminate() { PostQuitMessage(0); }
|
|
469
|
-
void dispatch(dispatch_fn_t f) { PostThreadMessage(m_main_thread, WM_APP, 0, (LPARAM) new dispatch_fn_t(f)); }
|
|
470
|
-
void set_title(const std::string title) { SetWindowText(m_window, title.c_str()); }
|
|
471
|
-
void set_size(int width, int height, int hints) {
|
|
472
|
-
auto style = GetWindowLong(m_window, GWL_STYLE);
|
|
473
|
-
if (hints == WEBVIEW_HINT_FIXED) { style &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX); }
|
|
474
|
-
else { style |= (WS_THICKFRAME | WS_MAXIMIZEBOX); }
|
|
475
|
-
SetWindowLong(m_window, GWL_STYLE, style);
|
|
476
|
-
if (hints == WEBVIEW_HINT_MAX) { m_maxsz.x = width; m_maxsz.y = height; }
|
|
477
|
-
else if (hints == WEBVIEW_HINT_MIN) { m_minsz.x = width; m_minsz.y = height; }
|
|
478
|
-
else { RECT r; r.left = r.top = 0; r.right = width; r.bottom = height; AdjustWindowRect(&r, WS_OVERLAPPEDWINDOW, 0); SetWindowPos(m_window, NULL, r.left, r.top, r.right - r.left, r.bottom - r.top, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE | SWP_FRAMECHANGED); m_browser->resize(m_window); }
|
|
479
|
-
}
|
|
480
|
-
void navigate(const std::string url) { m_browser->navigate(url); }
|
|
481
|
-
void eval(const std::string js) { m_browser->eval(js); }
|
|
482
|
-
void init(const std::string js) { m_browser->init(js); }
|
|
483
|
-
private:
|
|
484
|
-
virtual void on_message(const std::string msg) = 0;
|
|
485
|
-
HWND m_window;
|
|
486
|
-
POINT m_minsz;
|
|
487
|
-
POINT m_maxsz;
|
|
488
|
-
DWORD m_main_thread = GetCurrentThreadId();
|
|
489
|
-
std::unique_ptr<webview::browser> m_browser = std::make_unique<webview::edge_chromium>();
|
|
490
|
-
};
|
|
491
|
-
using browser_engine = win32_edge_engine;
|
|
492
|
-
} // namespace webview
|
|
493
|
-
#endif
|
|
494
|
-
|
|
495
|
-
namespace webview {
|
|
496
|
-
class webview : public browser_engine {
|
|
497
|
-
public:
|
|
498
|
-
webview(bool debug = false, void *wnd = nullptr) : browser_engine(debug, wnd) {}
|
|
499
|
-
void navigate(const std::string url) {
|
|
500
|
-
if (url == "") { browser_engine::navigate("data:text/html," + url_encode("<html><body>Hello</body></html>")); return; }
|
|
501
|
-
std::string html = html_from_uri(url);
|
|
502
|
-
if (html != "") { browser_engine::navigate("data:text/html," + url_encode(html)); }
|
|
503
|
-
else { browser_engine::navigate(url); }
|
|
504
|
-
}
|
|
505
|
-
using binding_t = std::function<void(std::string, std::string, void *)>;
|
|
506
|
-
using binding_ctx_t = std::pair<binding_t *, void *>;
|
|
507
|
-
using sync_binding_t = std::function<std::string(std::string)>;
|
|
508
|
-
using sync_binding_ctx_t = std::pair<webview *, sync_binding_t>;
|
|
509
|
-
void bind(const std::string name, sync_binding_t fn) {
|
|
510
|
-
bind(name, [](std::string seq, std::string req, void *arg) { auto pair = static_cast<sync_binding_ctx_t *>(arg); pair->first->resolve(seq, 0, pair->second(req)); }, new sync_binding_ctx_t(this, fn));
|
|
511
|
-
}
|
|
512
|
-
void bind(const std::string name, binding_t f, void *arg) {
|
|
513
|
-
auto js = "(function() { var name = '" + name + "'; var RPC = window._rpc = (window._rpc || {nextSeq: 1}); window[name] = function() { var seq = RPC.nextSeq++; var promise = new Promise(function(resolve, reject) { RPC[seq] = { resolve: resolve, reject: reject, }; }); window.external.invoke(JSON.stringify({ id: seq, method: name, params: Array.prototype.slice.call(arguments), })); return promise; } })())";
|
|
514
|
-
init(js);
|
|
515
|
-
bindings[name] = new binding_ctx_t(new binding_t(f), arg);
|
|
516
|
-
}
|
|
517
|
-
void resolve(const std::string seq, int status, const std::string result) {
|
|
518
|
-
dispatch([=]() {
|
|
519
|
-
if (status == 0) { eval("window._rpc[" + seq + "].resolve(" + result + "); window._rpc[" + seq + "] = undefined"); }
|
|
520
|
-
else { eval("window._rpc[" + seq + "].reject(" + result + "); window._rpc[" + seq + "] = undefined"); }
|
|
521
|
-
});
|
|
522
|
-
}
|
|
523
|
-
private:
|
|
524
|
-
void on_message(const std::string msg) {
|
|
525
|
-
auto seq = json_parse(msg, "id", 0);
|
|
526
|
-
auto name = json_parse(msg, "method", 0);
|
|
527
|
-
auto args = json_parse(msg, "params", 0);
|
|
528
|
-
if (bindings.find(name) == bindings.end()) { return; }
|
|
529
|
-
auto fn = bindings[name];
|
|
530
|
-
(*fn->first)(seq, args, fn->second);
|
|
531
|
-
}
|
|
532
|
-
std::map<std::string, binding_ctx_t *> bindings;
|
|
533
|
-
};
|
|
534
|
-
} // namespace webview
|
|
535
|
-
|
|
536
|
-
WEBVIEW_API webview_t webview_create(int debug, void *wnd) { return new webview::webview(debug, wnd); }
|
|
537
|
-
WEBVIEW_API void webview_destroy(webview_t w) { delete static_cast<webview::webview *>(w); }
|
|
538
|
-
WEBVIEW_API void webview_run(webview_t w) { static_cast<webview::webview *>(w)->run(); }
|
|
539
|
-
WEBVIEW_API void webview_terminate(webview_t w) { static_cast<webview::webview *>(w)->terminate(); }
|
|
540
|
-
WEBVIEW_API void webview_dispatch(webview_t w, void (*fn)(webview_t, void *), void *arg) { static_cast<webview::webview *>(w)->dispatch([=]() { fn(w, arg); }); }
|
|
541
|
-
WEBVIEW_API void *webview_get_window(webview_t w) { return static_cast<webview::webview *>(w)->window(); }
|
|
542
|
-
WEBVIEW_API void webview_set_title(webview_t w, const char *title) { static_cast<webview::webview *>(w)->set_title(title); }
|
|
543
|
-
WEBVIEW_API void webview_set_size(webview_t w, int width, int height, int hints) { static_cast<webview::webview *>(w)->set_size(width, height, hints); }
|
|
544
|
-
WEBVIEW_API void webview_navigate(webview_t w, const char *url) { static_cast<webview::webview *>(w)->navigate(url); }
|
|
545
|
-
WEBVIEW_API void webview_init(webview_t w, const char *js) { static_cast<webview::webview *>(w)->init(js); }
|
|
546
|
-
WEBVIEW_API void webview_eval(webview_t w, const char *js) { static_cast<webview::webview *>(w)->eval(js); }
|
|
547
|
-
WEBVIEW_API void webview_bind(webview_t w, const char *name, void (*fn)(const char *seq, const char *req, void *arg), void *arg) { static_cast<webview::webview *>(w)->bind(name, [=](std::string seq, std::string req, void *arg) { fn(seq.c_str(), req.c_str(), arg); }, arg); }
|
|
548
|
-
WEBVIEW_API void webview_return(webview_t w, const char *seq, int status, const char *result) { static_cast<webview::webview *>(w)->resolve(seq, status, result); }
|
|
549
|
-
|
|
550
|
-
#endif
|
|
563
|
+
if (!m_browser->embed(m_window, debug, cb)) {
|
|
564
|
+
#if defined(WEBVIEW_USE_EDGE_HTML)
|
|
565
|
+
m_browser = std::make_unique<webview::edge_html>();
|
|
566
|
+
m_browser->embed(m_window, debug, cb);
|
|
551
567
|
#endif
|
|
568
|
+
}
|
|
569
|
+
m_browser->resize(m_window);
|
|
570
|
+
}
|
|
571
|
+
void run() { MSG msg; BOOL res; while ((res = GetMessage(&msg, nullptr, 0, 0)) != -1) { if (msg.hwnd) { TranslateMessage(&msg); DispatchMessage(&msg); continue; }
|
|
572
|
+
if (msg.message == WM_APP) { auto f = (dispatch_fn_t *)(msg.lParam); (*f)(); delete f; }
|
|
573
|
+
else if (msg.message == WM_QUIT) { return; }
|
|
574
|
+
}}
|
|
575
|
+
void *window() { return (void *)m_window; }
|
|
576
|
+
void terminate() { PostQuitMessage(0); }
|
|
577
|
+
void dispatch(dispatch_fn_t f) { PostThreadMessage(m_main_thread, WM_APP, 0, (LPARAM) new dispatch_fn_t(f)); }
|
|
578
|
+
void set_title(const std::string title) { SetWindowText(m_window, title.c_str()); }
|
|
579
|
+
void set_size(int width, int height, int hints) {
|
|
580
|
+
auto style = GetWindowLong(m_window, GWL_STYLE);
|
|
581
|
+
if (hints == WEBVIEW_HINT_FIXED) { style &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX); }
|
|
582
|
+
else { style |= (WS_THICKFRAME | WS_MAXIMIZEBOX); }
|
|
583
|
+
SetWindowLong(m_window, GWL_STYLE, style);
|
|
584
|
+
if (hints == WEBVIEW_HINT_MAX) { m_maxsz.x = width; m_maxsz.y = height; }
|
|
585
|
+
else if (hints == WEBVIEW_HINT_MIN) { m_minsz.x = width; m_minsz.y = height; }
|
|
586
|
+
else { RECT r; r.left = r.top = 0; r.right = width; r.bottom = height; AdjustWindowRect(&r, WS_OVERLAPPEDWINDOW, 0); SetWindowPos(m_window, NULL, r.left, r.top, r.right - r.left, r.bottom - r.top, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE | SWP_FRAMECHANGED); m_browser->resize(m_window); }
|
|
587
|
+
}
|
|
588
|
+
void navigate(const std::string url) { m_browser->navigate(url); }
|
|
589
|
+
void eval(const std::string js) { m_browser->eval(js); }
|
|
590
|
+
void init(const std::string js) { m_browser->init(js); }
|
|
591
|
+
private:
|
|
592
|
+
virtual void on_message(const std::string msg) = 0;
|
|
593
|
+
HWND m_window;
|
|
594
|
+
POINT m_minsz;
|
|
595
|
+
POINT m_maxsz;
|
|
596
|
+
DWORD m_main_thread = GetCurrentThreadId();
|
|
597
|
+
std::unique_ptr<webview::browser> m_browser = std::make_unique<webview::edge_chromium>();
|
|
598
|
+
};
|
|
599
|
+
using browser_engine = win32_edge_engine;
|
|
600
|
+
} // namespace webview
|
|
601
|
+
#endif
|
|
602
|
+
|
|
603
|
+
namespace webview {
|
|
604
|
+
class webview : public browser_engine {
|
|
605
|
+
public:
|
|
606
|
+
webview(bool debug = false, void *wnd = nullptr) : browser_engine(debug, wnd) {}
|
|
607
|
+
void navigate(const std::string url) {
|
|
608
|
+
if (url == "") { browser_engine::navigate("data:text/html," + url_encode("<html><body>Hello</body></html>")); return; }
|
|
609
|
+
std::string html = html_from_uri(url);
|
|
610
|
+
if (html != "") { browser_engine::navigate("data:text/html," + url_encode(html)); }
|
|
611
|
+
else { browser_engine::navigate(url); }
|
|
612
|
+
}
|
|
613
|
+
using binding_t = std::function<void(std::string, std::string, void *)>;
|
|
614
|
+
using binding_ctx_t = std::pair<binding_t *, void *>;
|
|
615
|
+
using sync_binding_t = std::function<std::string(std::string)>;
|
|
616
|
+
using sync_binding_ctx_t = std::pair<webview *, sync_binding_t>;
|
|
617
|
+
void bind(const std::string name, sync_binding_t fn) {
|
|
618
|
+
bind(name, [](std::string seq, std::string req, void *arg) { auto pair = static_cast<sync_binding_ctx_t *>(arg); pair->first->resolve(seq, 0, pair->second(req)); }, new sync_binding_ctx_t(this, fn));
|
|
619
|
+
}
|
|
620
|
+
void bind(const std::string name, binding_t f, void *arg) {
|
|
621
|
+
auto js = "(function() { var name = '" + name + "'; var RPC = window._rpc = (window._rpc || {nextSeq: 1}); window[name] = function() { var seq = RPC.nextSeq++; var promise = new Promise(function(resolve, reject) { RPC[seq] = { resolve: resolve, reject: reject, }; }); window.external.invoke(JSON.stringify({ id: seq, method: name, params: Array.prototype.slice.call(arguments), })); return promise; } })())";
|
|
622
|
+
init(js);
|
|
623
|
+
bindings[name] = new binding_ctx_t(new binding_t(f), arg);
|
|
624
|
+
}
|
|
625
|
+
void resolve(const std::string seq, int status, const std::string result) {
|
|
626
|
+
dispatch([=]() {
|
|
627
|
+
if (status == 0) { eval("window._rpc[" + seq + "].resolve(" + result + "); window._rpc[" + seq + "] = undefined"); }
|
|
628
|
+
else { eval("window._rpc[" + seq + "].reject(" + result + "); window._rpc[" + seq + "] = undefined"); }
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
private:
|
|
632
|
+
void on_message(const std::string msg) {
|
|
633
|
+
auto seq = json_parse(msg, "id", 0);
|
|
634
|
+
auto name = json_parse(msg, "method", 0);
|
|
635
|
+
auto args = json_parse(msg, "params", 0);
|
|
636
|
+
if (bindings.find(name) == bindings.end()) { return; }
|
|
637
|
+
auto fn = bindings[name];
|
|
638
|
+
(*fn->first)(seq, args, fn->second);
|
|
639
|
+
}
|
|
640
|
+
std::map<std::string, binding_ctx_t *> bindings;
|
|
641
|
+
};
|
|
642
|
+
} // namespace webview
|
|
643
|
+
|
|
644
|
+
WEBVIEW_API webview_t webview_create(int debug, void *wnd) { return new webview::webview(debug, wnd); }
|
|
645
|
+
WEBVIEW_API void webview_destroy(webview_t w) { delete static_cast<webview::webview *>(w); }
|
|
646
|
+
WEBVIEW_API void webview_run(webview_t w) { static_cast<webview::webview *>(w)->run(); }
|
|
647
|
+
WEBVIEW_API void webview_terminate(webview_t w) { static_cast<webview::webview *>(w)->terminate(); }
|
|
648
|
+
WEBVIEW_API void webview_dispatch(webview_t w, void (*fn)(webview_t, void *), void *arg) { static_cast<webview::webview *>(w)->dispatch([=]() { fn(w, arg); }); }
|
|
649
|
+
WEBVIEW_API void *webview_get_window(webview_t w) { return static_cast<webview::webview *>(w)->window(); }
|
|
650
|
+
WEBVIEW_API void webview_set_title(webview_t w, const char *title) { static_cast<webview::webview *>(w)->set_title(title); }
|
|
651
|
+
WEBVIEW_API void webview_set_size(webview_t w, int width, int height, int hints) { static_cast<webview::webview *>(w)->set_size(width, height, hints); }
|
|
652
|
+
WEBVIEW_API void webview_navigate(webview_t w, const char *url) { static_cast<webview::webview *>(w)->navigate(url); }
|
|
653
|
+
WEBVIEW_API void webview_init(webview_t w, const char *js) { static_cast<webview::webview *>(w)->init(js); }
|
|
654
|
+
WEBVIEW_API void webview_eval(webview_t w, const char *js) { static_cast<webview::webview *>(w)->eval(js); }
|
|
655
|
+
WEBVIEW_API void webview_bind(webview_t w, const char *name, void (*fn)(const char *seq, const char *req, void *arg), void *arg) { static_cast<webview::webview *>(w)->bind(name, [=](std::string seq, std::string req, void *arg) { fn(seq.c_str(), req.c_str(), arg); }, arg); }
|
|
656
|
+
WEBVIEW_API void webview_return(webview_t w, const char *seq, int status, const char *result) { static_cast<webview::webview *>(w)->resolve(seq, status, result); }
|
|
657
|
+
|
|
658
|
+
#endif
|
|
659
|
+
#endif
|