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.
Files changed (54) hide show
  1. package/Core/CMakeLists.txt +190 -7
  2. package/Core/Features/App/app.cpp +129 -0
  3. package/Core/Features/App/app.ts +126 -0
  4. package/Core/Features/Browser/browser.cpp +181 -0
  5. package/Core/Features/Browser/browser.ts +182 -0
  6. package/Core/Features/Clipboard/clipboard.cpp +234 -0
  7. package/Core/Features/Clipboard/clipboard.ts +113 -0
  8. package/Core/Features/Display/display.cpp +209 -0
  9. package/Core/Features/Display/display.ts +104 -0
  10. package/Core/Features/Event/Events.ts +166 -0
  11. package/Core/Features/Event/events.cpp +200 -0
  12. package/Core/Features/Keyboard/keyboard.cpp +186 -0
  13. package/Core/Features/Keyboard/keyboard.ts +175 -0
  14. package/Core/Features/Menu/context-menu.css +293 -0
  15. package/Core/Features/Menu/menu.cpp +481 -0
  16. package/Core/Features/Menu/menu.ts +439 -0
  17. package/Core/Features/Tray/tray.cpp +310 -0
  18. package/Core/Features/Tray/tray.ts +68 -0
  19. package/Core/Features/WebGPU/webgpu.cpp +939 -0
  20. package/Core/Features/WebGPU/webgpu.ts +1013 -0
  21. package/Core/Features/WebView/webview.cpp +1052 -0
  22. package/Core/Features/WebView/webview.ts +510 -0
  23. package/Core/Features/Window/window.cpp +664 -0
  24. package/Core/Features/Window/window.ts +142 -0
  25. package/Core/Features/WindowManager/window_manager.cpp +341 -0
  26. package/Core/include/plusui/app.hpp +73 -0
  27. package/Core/include/plusui/browser.hpp +66 -0
  28. package/Core/include/plusui/clipboard.hpp +41 -0
  29. package/Core/include/plusui/events.hpp +58 -0
  30. package/Core/include/{keyboard.hpp → plusui/keyboard.hpp} +21 -44
  31. package/Core/include/plusui/menu.hpp +153 -0
  32. package/Core/include/plusui/tray.hpp +93 -0
  33. package/Core/include/plusui/webgpu.hpp +434 -0
  34. package/Core/include/plusui/webview.hpp +142 -0
  35. package/Core/include/plusui/window.hpp +111 -0
  36. package/Core/include/plusui/window_manager.hpp +57 -0
  37. package/Core/vendor/WebView2EnvironmentOptions.h +406 -0
  38. package/Core/vendor/stb_image.h +7988 -0
  39. package/Core/vendor/webview.h +618 -510
  40. package/Core/vendor/webview2.h +52079 -0
  41. package/README.md +19 -0
  42. package/package.json +12 -15
  43. package/Core/include/app.hpp +0 -121
  44. package/Core/include/menu.hpp +0 -79
  45. package/Core/include/tray.hpp +0 -81
  46. package/Core/include/window.hpp +0 -106
  47. package/Core/src/app.cpp +0 -311
  48. package/Core/src/display.cpp +0 -424
  49. package/Core/src/tray.cpp +0 -275
  50. package/Core/src/window.cpp +0 -528
  51. package/dist/index.d.ts +0 -205
  52. package/dist/index.js +0 -198
  53. package/src/index.ts +0 -574
  54. /package/Core/include/{display.hpp → plusui/display.hpp} +0 -0
@@ -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, 0);
408
+ CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
404
409
  std::atomic_flag flag = ATOMIC_FLAG_INIT;
405
410
  flag.test_and_set();
406
- HRESULT res = CreateWebView2EnvironmentWithDetails(nullptr, nullptr, nullptr, new webview2_com_handler(wnd, [&](IWebView2WebView *webview) { m_webview = webview; flag.clear(); }));
407
- if (res != S_OK) { CoUninitialize(); return false; }
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)) { TranslateMessage(&msg); DispatchMessage(&msg); }
410
- init("window.external={invoke:s=>window.chrome.webview.postMessage(s)}");
411
- return true;
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
- void resize(HWND wnd) override { if (m_webview == nullptr) { return; } RECT bounds; GetClientRect(wnd, &bounds); m_webview->put_Bounds(bounds); }
414
- void navigate(const std::string url) override { auto wurl = to_lpwstr(url); m_webview->Navigate(wurl); delete[] wurl; }
415
- void init(const std::string js) override { LPCWSTR wjs = to_lpwstr(js); m_webview->AddScriptToExecuteOnDocumentCreated(wjs, nullptr); delete[] wjs; }
416
- void eval(const std::string js) override { LPCWSTR wjs = to_lpwstr(js); m_webview->ExecuteScript(wjs, nullptr); delete[] wjs; }
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) { int n = MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, NULL, 0); wchar_t *ws = new wchar_t[n]; MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, ws, n); return ws; }
419
- IWebView2WebView *m_webview = nullptr;
420
- class webview2_com_handler : public IWebView2CreateWebView2EnvironmentCompletedHandler, public IWebView2CreateWebViewCompletedHandler {
421
- using webview2_com_handler_cb_t = std::function<void(IWebView2WebView *)>;
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
- ULONG STDMETHODCALLTYPE AddRef() { return 1; }
425
- ULONG STDMETHODCALLTYPE Release() { return 1; }
426
- HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID *ppv) { return S_OK; }
427
- HRESULT STDMETHODCALLTYPE Invoke(HRESULT res, IWebView2Environment *env) { env->CreateWebView(m_window, this); return S_OK; }
428
- HRESULT STDMETHODCALLTYPE Invoke( HRESULT res, IWebView2WebView *webview) { webview->AddRef(); m_cb(webview); return S_OK; }
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)) { m_browser = std::make_unique<webview::edge_html>(); m_browser->embed(m_window, debug, cb); }
461
- m_browser->resize(m_window);
462
- }
463
- void run() { MSG msg; BOOL res; while ((res = GetMessage(&msg, nullptr, 0, 0)) != -1) { if (msg.hwnd) { TranslateMessage(&msg); DispatchMessage(&msg); continue; }
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