plusui-native-core 0.1.104 → 0.1.106

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 (49) hide show
  1. package/Core/.claude/settings.local.json +7 -0
  2. package/Core/CMakeLists.txt +1 -1
  3. package/Core/Features/API/Connect_API.ts +20 -52
  4. package/Core/Features/API/app-api.ts +28 -28
  5. package/Core/Features/API/browser-api.ts +38 -38
  6. package/Core/Features/API/clipboard-api.ts +21 -21
  7. package/Core/Features/API/display-api.ts +33 -33
  8. package/Core/Features/API/keyboard-api.ts +33 -33
  9. package/Core/Features/API/menu-api.ts +39 -39
  10. package/Core/Features/API/router-api.ts +23 -23
  11. package/Core/Features/API/tray-api.ts +22 -22
  12. package/Core/Features/API/webgpu-api.ts +55 -55
  13. package/Core/Features/App/app.cpp +128 -102
  14. package/Core/Features/Browser/browser.cpp +227 -227
  15. package/Core/Features/Browser/browser.ts +161 -161
  16. package/Core/Features/Clipboard/clipboard.cpp +235 -235
  17. package/Core/Features/Display/display.cpp +212 -212
  18. package/Core/Features/FileDrop/filedrop.cpp +448 -324
  19. package/Core/Features/FileDrop/filedrop.css +421 -421
  20. package/Core/Features/FileDrop/filedrop.ts +5 -9
  21. package/Core/Features/Keyboard/keyboard_linux.cpp +4 -0
  22. package/Core/Features/Router/router.cpp +62 -62
  23. package/Core/Features/Router/router.ts +113 -113
  24. package/Core/Features/Tray/tray.cpp +328 -324
  25. package/Core/Features/WebGPU/webgpu.cpp +948 -948
  26. package/Core/Features/Window/webview.cpp +1026 -1014
  27. package/Core/Features/Window/webview.ts +516 -516
  28. package/Core/Features/Window/window.cpp +2265 -1988
  29. package/Core/include/plusui/api.hpp +237 -237
  30. package/Core/include/plusui/app.hpp +33 -33
  31. package/Core/include/plusui/browser.hpp +67 -67
  32. package/Core/include/plusui/clipboard.hpp +41 -41
  33. package/Core/include/plusui/connect.hpp +340 -340
  34. package/Core/include/plusui/connection.hpp +3 -3
  35. package/Core/include/plusui/display.hpp +90 -90
  36. package/Core/include/plusui/filedrop.hpp +92 -77
  37. package/Core/include/plusui/keyboard.hpp +112 -112
  38. package/Core/include/plusui/menu.hpp +153 -153
  39. package/Core/include/plusui/plusui +18 -18
  40. package/Core/include/plusui/router.hpp +42 -42
  41. package/Core/include/plusui/tray.hpp +94 -94
  42. package/Core/include/plusui/webgpu.hpp +434 -434
  43. package/Core/include/plusui/window.hpp +180 -177
  44. package/Core/scripts/generate-umbrella-header.mjs +77 -77
  45. package/Core/vendor/WebView2EnvironmentOptions.h +406 -406
  46. package/Core/vendor/webview.h +487 -487
  47. package/Core/vendor/webview2.h +52079 -52079
  48. package/README.md +19 -19
  49. package/package.json +1 -1
@@ -1,357 +1,357 @@
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>
@@ -378,25 +378,25 @@ public:
378
378
  #include <winrt/Windows.Web.UI.Interop.h>
379
379
  #pragma comment(lib, "windowsapp")
380
380
  class edge_html : public browser {
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:
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:
400
400
  WebViewControl m_webview = nullptr;
401
401
  std::string init_js = "";
402
402
  };
@@ -534,30 +534,30 @@ private:
534
534
  };
535
535
  };
536
536
  };
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)); }
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)); }
561
561
  ShowWindow(m_window, SW_SHOW); UpdateWindow(m_window); SetFocus(m_window);
562
562
  auto cb = std::bind(&win32_edge_engine::on_message, this, std::placeholders::_1);
563
563
  if (!m_browser->embed(m_window, debug, cb)) {
@@ -567,93 +567,93 @@ public:
567
567
  #endif
568
568
  }
569
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
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