29 #define WEBVIEW_API extern
32 #ifndef WEBVIEW_VERSION_MAJOR
34 #define WEBVIEW_VERSION_MAJOR 0
37 #ifndef WEBVIEW_VERSION_MINOR
39 #define WEBVIEW_VERSION_MINOR 10
42 #ifndef WEBVIEW_VERSION_PATCH
44 #define WEBVIEW_VERSION_PATCH 0
47 #ifndef WEBVIEW_VERSION_PRE_RELEASE
49 #define WEBVIEW_VERSION_PRE_RELEASE ""
52 #ifndef WEBVIEW_VERSION_BUILD_METADATA
54 #define WEBVIEW_VERSION_BUILD_METADATA ""
58 #define WEBVIEW_STRINGIFY(x) #x
61 #define WEBVIEW_EXPAND_AND_STRINGIFY(x) WEBVIEW_STRINGIFY(x)
64 #define WEBVIEW_VERSION_NUMBER \
65 WEBVIEW_EXPAND_AND_STRINGIFY(WEBVIEW_VERSION_MAJOR) \
66 "." WEBVIEW_EXPAND_AND_STRINGIFY(WEBVIEW_VERSION_MINOR) "." WEBVIEW_EXPAND_AND_STRINGIFY( \
67 WEBVIEW_VERSION_PATCH)
86 char version_number[32];
91 char build_metadata[48];
99 typedef void* webview_t;
109 WEBVIEW_API webview_t webview_create(
int debug,
void* window);
112 WEBVIEW_API
void webview_destroy(webview_t w);
116 WEBVIEW_API
void webview_run(webview_t w);
120 WEBVIEW_API
void webview_terminate(webview_t w);
124 WEBVIEW_API
void webview_dispatch(webview_t w,
void (*fn)(webview_t w,
void* arg),
void* arg);
129 WEBVIEW_API
void* webview_get_window(webview_t w);
132 WEBVIEW_API
void webview_set_title(webview_t w,
const char* title);
135 #define WEBVIEW_HINT_NONE 0
136 #define WEBVIEW_HINT_MIN 1
137 #define WEBVIEW_HINT_MAX 2
138 #define WEBVIEW_HINT_FIXED 3
140 WEBVIEW_API
void webview_set_size(webview_t w,
int width,
int height,
int hints);
147 WEBVIEW_API
void webview_navigate(webview_t w,
const char* url);
151 WEBVIEW_API
void webview_set_html(webview_t w,
const char* html);
156 WEBVIEW_API
void webview_init(webview_t w,
const char* js);
161 WEBVIEW_API
void webview_eval(webview_t w,
const char* js);
168 WEBVIEW_API
void webview_bind(webview_t w,
const char* name,
169 void (*fn)(
const char* seq,
const char* req,
void* arg),
173 WEBVIEW_API
void webview_unbind(webview_t w,
const char* name);
179 WEBVIEW_API
void webview_return(webview_t w,
const char* seq,
int status,
const char* result);
188 #ifndef WEBVIEW_HEADER
190 #if !defined(WEBVIEW_GTK) && !defined(WEBVIEW_COCOA) && !defined(WEBVIEW_EDGE)
191 #if defined(__APPLE__)
192 #define WEBVIEW_COCOA
193 #elif defined(__unix__)
195 #elif defined(_WIN32)
198 #error "please, specify webview backend"
202 #ifndef WEBVIEW_DEPRECATED
203 #if __cplusplus >= 201402L
204 #define WEBVIEW_DEPRECATED(reason) [[deprecated(reason)]]
205 #elif defined(_MSC_VER)
206 #define WEBVIEW_DEPRECATED(reason) __declspec(deprecated(reason))
208 #define WEBVIEW_DEPRECATED(reason) __attribute__((deprecated(reason)))
212 #ifndef WEBVIEW_DEPRECATED_PRIVATE
213 #define WEBVIEW_DEPRECATED_PRIVATE WEBVIEW_DEPRECATED("Private API should not be used")
218 #include <functional>
231 using dispatch_fn_t = std::function<void()>;
238 { WEBVIEW_VERSION_MAJOR, WEBVIEW_VERSION_MINOR, WEBVIEW_VERSION_PATCH },
239 WEBVIEW_VERSION_NUMBER,
240 WEBVIEW_VERSION_PRE_RELEASE,
241 WEBVIEW_VERSION_BUILD_METADATA
244 inline int json_parse_c(
const char* s,
size_t sz,
const char* key,
size_t keysz,
245 const char** value,
size_t* valuesz)
254 } state = JSON_STATE_VALUE;
255 const char* k =
nullptr;
265 index =
static_cast<decltype(index)
>(keysz);
273 for (; sz > 0; s++, sz--)
280 JSON_ACTION_START_STRUCT,
281 JSON_ACTION_END_STRUCT
282 } action = JSON_ACTION_NONE;
283 auto c =
static_cast<unsigned char>(*s);
286 case JSON_STATE_VALUE:
287 if (c ==
' ' || c ==
'\t' || c ==
'\n' || c ==
'\r' || c ==
',' || c ==
':')
293 action = JSON_ACTION_START;
294 state = JSON_STATE_STRING;
296 else if (c ==
'{' || c ==
'[')
298 action = JSON_ACTION_START_STRUCT;
300 else if (c ==
'}' || c ==
']')
302 action = JSON_ACTION_END_STRUCT;
304 else if (c ==
't' || c ==
'f' || c ==
'n' || c ==
'-' ||
305 (c >=
'0' && c <=
'9'))
307 action = JSON_ACTION_START;
308 state = JSON_STATE_LITERAL;
315 case JSON_STATE_LITERAL:
316 if (c ==
' ' || c ==
'\t' || c ==
'\n' || c ==
'\r' || c ==
',' ||
317 c ==
']' || c ==
'}' || c ==
':')
319 state = JSON_STATE_VALUE;
322 action = JSON_ACTION_END;
324 else if (c < 32 || c > 126)
328 case JSON_STATE_STRING:
329 if (c < 32 || (c > 126 && c < 192))
335 action = JSON_ACTION_END;
336 state = JSON_STATE_VALUE;
340 state = JSON_STATE_ESCAPE;
342 else if (c >= 192 && c < 224)
345 state = JSON_STATE_UTF8;
347 else if (c >= 224 && c < 240)
350 state = JSON_STATE_UTF8;
352 else if (c >= 240 && c < 247)
355 state = JSON_STATE_UTF8;
357 else if (c >= 128 && c < 192)
362 case JSON_STATE_ESCAPE:
363 if (c ==
'"' || c ==
'\\' || c ==
'/' || c ==
'b' || c ==
'f' || c ==
'n' ||
364 c ==
'r' || c ==
't' || c ==
'u')
366 state = JSON_STATE_STRING;
373 case JSON_STATE_UTF8:
374 if (c < 128 || c > 191)
381 state = JSON_STATE_STRING;
388 if (action == JSON_ACTION_END_STRUCT)
395 if (action == JSON_ACTION_START || action == JSON_ACTION_START_STRUCT)
401 else if (keysz > 0 && index == 1)
410 else if (action == JSON_ACTION_END || action == JSON_ACTION_END_STRUCT)
412 if (*value !=
nullptr && index == 0)
414 *valuesz = (size_t)(s + 1 - *value);
417 else if (keysz > 0 && k !=
nullptr)
419 if (keysz == (
size_t)(s - k - 1) && memcmp(key, k + 1, keysz) == 0)
432 if (action == JSON_ACTION_START_STRUCT)
440 inline std::string json_escape(
const std::string& s)
443 return '"' + s +
'"';
446 inline int json_unescape(
const char* s,
size_t n,
char* out)
509 inline std::string json_parse(
const std::string& s,
const std::string& key,
const int index)
511 const char* value =
nullptr;
515 json_parse_c(s.c_str(), s.length(),
nullptr, index, &value, &value_sz);
519 json_parse_c(s.c_str(), s.length(), key.c_str(), key.length(), &value, &value_sz);
521 if (value !=
nullptr)
525 return { value, value_sz };
527 int n = json_unescape(value, value_sz,
nullptr);
530 char* decoded =
new char[1ull + n];
531 json_unescape(value, value_sz, decoded);
532 std::string result(decoded, n);
542 WEBVIEW_DEPRECATED_PRIVATE
543 inline int json_parse_c(
const char* s,
size_t sz,
const char* key,
size_t keysz,
544 const char** value,
size_t* valuesz)
546 return detail::json_parse_c(s, sz, key, keysz, value, valuesz);
549 WEBVIEW_DEPRECATED_PRIVATE
550 inline std::string json_escape(
const std::string& s)
552 return detail::json_escape(s);
555 WEBVIEW_DEPRECATED_PRIVATE
556 inline int json_unescape(
const char* s,
size_t n,
char* out)
558 return detail::json_unescape(s, n, out);
561 WEBVIEW_DEPRECATED_PRIVATE
562 inline std::string json_parse(
const std::string& s,
const std::string& key,
const int index)
564 return detail::json_parse(s, key, index);
569 #if defined(WEBVIEW_GTK)
580 #include <JavaScriptCore/JavaScript.h>
582 #include <webkit2/webkit2.h>
589 class gtk_webkit_engine
592 gtk_webkit_engine(
bool debug,
void* window) : m_window(static_cast<GtkWidget*>(window))
594 if (gtk_init_check(
nullptr,
nullptr) == FALSE)
598 m_window =
static_cast<GtkWidget*
>(window);
599 if (m_window ==
nullptr)
601 m_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
603 g_signal_connect(G_OBJECT(m_window),
"destroy",
604 G_CALLBACK(+[](GtkWidget*, gpointer arg)
605 {
static_cast<gtk_webkit_engine*
>(arg)->terminate(); }),
608 m_webview = webkit_web_view_new();
609 WebKitUserContentManager* manager =
610 webkit_web_view_get_user_content_manager(WEBKIT_WEB_VIEW(m_webview));
611 g_signal_connect(manager,
"script-message-received::external",
612 G_CALLBACK(+[](WebKitUserContentManager*,
613 WebKitJavascriptResult* r, gpointer arg)
615 auto* w =
static_cast<gtk_webkit_engine*
>(arg);
616 char* s = get_string_from_js_result(r);
621 webkit_user_content_manager_register_script_message_handler(manager,
"external");
622 init(
"window.external={invoke:function(s){window.webkit.messageHandlers."
623 "external.postMessage(s);}}");
625 gtk_container_add(GTK_CONTAINER(m_window), GTK_WIDGET(m_webview));
626 gtk_widget_grab_focus(GTK_WIDGET(m_webview));
628 WebKitSettings* settings = webkit_web_view_get_settings(WEBKIT_WEB_VIEW(m_webview));
629 webkit_settings_set_javascript_can_access_clipboard(settings,
true);
632 webkit_settings_set_enable_write_console_messages_to_stdout(settings,
true);
633 webkit_settings_set_enable_developer_extras(settings,
true);
636 gtk_widget_show_all(m_window);
638 virtual ~gtk_webkit_engine() =
default;
641 return (
void*)m_window;
651 void dispatch(std::function<
void()> f)
653 g_idle_add_full(G_PRIORITY_HIGH_IDLE, (GSourceFunc)([](
void* f) ->
int {
654 (*
static_cast<dispatch_fn_t*
>(f))();
655 return G_SOURCE_REMOVE;
657 new std::function<
void()>(f),
658 [](
void* f) {
delete static_cast<dispatch_fn_t*
>(f); });
661 void set_title(
const std::string& title)
663 gtk_window_set_title(GTK_WINDOW(m_window), title.c_str());
666 void set_size(
int width,
int height,
int hints)
668 gtk_window_set_resizable(GTK_WINDOW(m_window), hints != WEBVIEW_HINT_FIXED);
669 if (hints == WEBVIEW_HINT_NONE)
671 gtk_window_resize(GTK_WINDOW(m_window), width, height);
673 else if (hints == WEBVIEW_HINT_FIXED)
675 gtk_widget_set_size_request(m_window, width, height);
680 g.min_width = g.max_width = width;
681 g.min_height = g.max_height = height;
683 (hints == WEBVIEW_HINT_MIN ? GDK_HINT_MIN_SIZE : GDK_HINT_MAX_SIZE);
685 gtk_window_set_geometry_hints(GTK_WINDOW(m_window),
nullptr, &g, h);
689 void navigate(
const std::string& url)
691 webkit_web_view_load_uri(WEBKIT_WEB_VIEW(m_webview), url.c_str());
694 void add_navigate_listener(std::function<
void(
const std::string&,
void*)> callback,
697 g_signal_connect(WEBKIT_WEB_VIEW(m_webview),
"load-changed",
698 G_CALLBACK(on_load_changed),
this);
699 navigateCallbackArg = arg;
700 navigateCallback = std::move(callback);
703 void add_scheme_handler(
const std::string& scheme,
704 std::function<
void(
const std::string&,
void*)> callback,
707 auto view = WEBKIT_WEB_VIEW(m_webview);
708 auto context = webkit_web_view_get_context(view);
710 scheme_handlers.insert({ scheme, { .arg = arg, .fkt = callback } });
711 webkit_web_context_register_uri_scheme(context, scheme.c_str(), scheme_handler,
712 static_cast<gpointer
>(
this),
nullptr);
715 void set_html(
const std::string& html)
717 webkit_web_view_load_html(WEBKIT_WEB_VIEW(m_webview), html.c_str(),
nullptr);
720 void init(
const std::string& js)
722 WebKitUserContentManager* manager =
723 webkit_web_view_get_user_content_manager(WEBKIT_WEB_VIEW(m_webview));
724 webkit_user_content_manager_add_script(
725 manager, webkit_user_script_new(
726 js.c_str(), WEBKIT_USER_CONTENT_INJECT_TOP_FRAME,
727 WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_START,
nullptr,
nullptr));
730 void eval(
const std::string& js)
732 webkit_web_view_run_javascript(WEBKIT_WEB_VIEW(m_webview), js.c_str(),
nullptr,
737 virtual void on_message(
const std::string& msg) = 0;
742 std::function<void(
const std::string&,
void*)> fkt;
745 std::map<std::string, handler_t> scheme_handlers;
747 void scheme_handler_call(
const std::string& scheme,
const std::string& url)
749 auto handler = scheme_handlers.find(scheme);
750 if (handler != scheme_handlers.end())
752 const auto& arg = handler->second;
753 arg.fkt(url, arg.arg);
757 static void scheme_handler(WebKitURISchemeRequest* request, gpointer user_data)
759 auto _this =
static_cast<gtk_webkit_engine*
>(user_data);
761 auto scheme = webkit_uri_scheme_request_get_scheme(request);
762 auto uri = webkit_uri_scheme_request_get_uri(request);
763 _this->scheme_handler_call(scheme, uri);
766 static char* get_string_from_js_result(WebKitJavascriptResult* r)
769 #if WEBKIT_MAJOR_VERSION >= 2 && WEBKIT_MINOR_VERSION >= 22
770 JSCValue* value = webkit_javascript_result_get_js_value(r);
771 s = jsc_value_to_string(value);
773 JSGlobalContextRef ctx = webkit_javascript_result_get_global_context(r);
774 JSValueRef value = webkit_javascript_result_get_value(r);
775 JSStringRef js = JSValueToStringCopy(ctx, value,
nullptr);
776 size_t n = JSStringGetMaximumUTF8CStringSize(js);
778 JSStringGetUTF8CString(js, s, n);
785 GtkWidget* m_webview;
787 void* navigateCallbackArg =
nullptr;
788 std::function<void(
const std::string&,
void*)> navigateCallback =
nullptr;
790 static void on_load_changed(WebKitWebView* web_view, WebKitLoadEvent load_event,
793 if (load_event == WEBKIT_LOAD_FINISHED)
795 auto inst =
static_cast<gtk_webkit_engine*
>(arg);
796 inst->navigateCallback(webkit_web_view_get_uri(web_view),
797 inst->navigateCallbackArg);
804 using browser_engine = detail::gtk_webkit_engine;
808 #elif defined(WEBVIEW_COCOA)
820 #include <CoreGraphics/CoreGraphics.h>
821 #include <objc/NSObjCRuntime.h>
822 #include <objc/objc-runtime.h>
836 template <
typename Result,
typename Callable,
typename... Args>
837 Result invoke(Callable callable, Args... args) noexcept
839 return reinterpret_cast<Result (*)(Args...)
>(callable)(args...);
843 template <
typename Result,
typename... Args> Result msg_send(Args... args) noexcept
845 return invoke<Result>(objc_msgSend, args...);
850 enum NSBackingStoreType : NSUInteger
852 NSBackingStoreBuffered = 2
855 enum NSWindowStyleMask : NSUInteger
857 NSWindowStyleMaskTitled = 1,
858 NSWindowStyleMaskClosable = 2,
859 NSWindowStyleMaskMiniaturizable = 4,
860 NSWindowStyleMaskResizable = 8
863 enum NSApplicationActivationPolicy : NSInteger
865 NSApplicationActivationPolicyRegular = 0
868 enum WKUserScriptInjectionTime : NSInteger
870 WKUserScriptInjectionTimeAtDocumentStart = 0
873 enum NSModalResponse : NSInteger
875 NSModalResponseOK = 1
879 inline id operator"" _cls(
const char* s, std::size_t)
881 return (
id)objc_getClass(s);
883 inline SEL operator"" _sel(
const char* s, std::size_t)
885 return sel_registerName(s);
887 inline id operator"" _str(
const char* s, std::size_t)
889 return objc::msg_send<id>(
"NSString"_cls,
"stringWithUTF8String:"_sel, s);
892 class cocoa_wkwebview_engine
895 cocoa_wkwebview_engine(
bool debug,
void* window)
896 : m_debug{ debug }, m_parent_window{ window }
898 auto app = get_shared_application();
899 auto delegate = create_app_delegate();
900 objc_setAssociatedObject(delegate,
"webview", (
id)
this, OBJC_ASSOCIATION_ASSIGN);
901 objc::msg_send<void>(app,
"setDelegate:"_sel, delegate);
906 on_application_did_finish_launching(delegate, app);
915 objc::msg_send<void>(app,
"run"_sel);
918 virtual ~cocoa_wkwebview_engine() =
default;
921 return (
void*)m_window;
925 auto app = get_shared_application();
926 objc::msg_send<void>(app,
"terminate:"_sel,
nullptr);
930 auto app = get_shared_application();
931 objc::msg_send<void>(app,
"run"_sel);
933 void dispatch(std::function<
void()> f)
935 dispatch_async_f(dispatch_get_main_queue(),
new dispatch_fn_t(f),
936 (dispatch_function_t)([](
void* arg) {
937 auto f =
static_cast<dispatch_fn_t*
>(arg);
942 void set_title(
const std::string& title)
944 objc::msg_send<void>(
945 m_window,
"setTitle:"_sel,
946 objc::msg_send<id>(
"NSString"_cls,
"stringWithUTF8String:"_sel, title.c_str()));
948 void set_size(
int width,
int height,
int hints)
950 auto style =
static_cast<NSWindowStyleMask
>(NSWindowStyleMaskTitled |
951 NSWindowStyleMaskClosable |
952 NSWindowStyleMaskMiniaturizable);
953 if (hints != WEBVIEW_HINT_FIXED)
955 style =
static_cast<NSWindowStyleMask
>(style | NSWindowStyleMaskResizable);
957 objc::msg_send<void>(m_window,
"setStyleMask:"_sel, style);
959 if (hints == WEBVIEW_HINT_MIN)
961 objc::msg_send<void>(m_window,
"setContentMinSize:"_sel,
962 CGSizeMake(width, height));
964 else if (hints == WEBVIEW_HINT_MAX)
966 objc::msg_send<void>(m_window,
"setContentMaxSize:"_sel,
967 CGSizeMake(width, height));
971 objc::msg_send<void>(m_window,
"setFrame:display:animate:"_sel,
972 CGRectMake(0, 0, width, height), YES, NO);
974 objc::msg_send<void>(m_window,
"center"_sel);
976 void navigate(
const std::string& url)
978 auto nsurl = objc::msg_send<id>(
979 "NSURL"_cls,
"URLWithString:"_sel,
980 objc::msg_send<id>(
"NSString"_cls,
"stringWithUTF8String:"_sel, url.c_str()));
982 objc::msg_send<void>(
983 m_webview,
"loadRequest:"_sel,
984 objc::msg_send<id>(
"NSURLRequest"_cls,
"requestWithURL:"_sel, nsurl));
987 void add_navigate_listener(std::function<
void(
const std::string&,
void*)> callback,
990 m_navigateCallback = callback;
991 m_navigateCallbackArg = arg;
994 void add_scheme_handler(
const std::string& scheme,
995 std::function<
void(
const std::string&,
void*)> callback,
1001 void set_html(
const std::string& html)
1003 objc::msg_send<void>(
1004 m_webview,
"loadHTMLString:baseURL:"_sel,
1005 objc::msg_send<id>(
"NSString"_cls,
"stringWithUTF8String:"_sel, html.c_str()),
1008 void init(
const std::string& js)
1014 objc::msg_send<void>(
1015 m_manager,
"addUserScript:"_sel,
1017 objc::msg_send<id>(
"WKUserScript"_cls,
"alloc"_sel),
1018 "initWithSource:injectionTime:forMainFrameOnly:"_sel,
1019 objc::msg_send<id>(
"NSString"_cls,
"stringWithUTF8String:"_sel, js.c_str()),
1020 WKUserScriptInjectionTimeAtDocumentStart, YES));
1022 void eval(
const std::string& js)
1024 objc::msg_send<void>(
1025 m_webview,
"evaluateJavaScript:completionHandler:"_sel,
1026 objc::msg_send<id>(
"NSString"_cls,
"stringWithUTF8String:"_sel, js.c_str()),
1031 virtual void on_message(
const std::string& msg) = 0;
1032 id create_app_delegate()
1038 objc_allocateClassPair((Class)
"NSResponder"_cls,
"WebviewAppDelegate", 0);
1039 class_addProtocol(cls, objc_getProtocol(
"NSTouchBarProvider"));
1040 class_addMethod(cls,
"applicationShouldTerminateAfterLastWindowClosed:"_sel,
1041 (IMP)(+[](
id,
SEL,
id) -> BOOL {
return 1; }),
"c@:@");
1046 if (!m_parent_window)
1048 class_addMethod(cls,
"applicationDidFinishLaunching:"_sel,
1049 (IMP)(+[](
id self,
SEL,
id notification)
1052 objc::msg_send<id>(notification,
"object"_sel);
1053 auto w = get_associated_webview(
self);
1054 w->on_application_did_finish_launching(
self, app);
1058 objc_registerClassPair(cls);
1059 return objc::msg_send<id>((
id)cls,
"new"_sel);
1061 id create_script_message_handler()
1063 auto cls = objc_allocateClassPair((Class)
"NSResponder"_cls,
1064 "WebkitScriptMessageHandler", 0);
1065 class_addProtocol(cls, objc_getProtocol(
"WKScriptMessageHandler"));
1067 cls,
"userContentController:didReceiveScriptMessage:"_sel,
1068 (IMP)(+[](
id self,
SEL,
id,
id msg)
1070 auto w = get_associated_webview(
self);
1071 w->on_message(objc::msg_send<const char*>(
1072 objc::msg_send<id>(msg,
"body"_sel),
"UTF8String"_sel));
1075 objc_registerClassPair(cls);
1076 auto instance = objc::msg_send<id>((
id)cls,
"new"_sel);
1077 objc_setAssociatedObject(instance,
"webview", (
id)
this, OBJC_ASSOCIATION_ASSIGN);
1080 static id create_webkit_ui_delegate()
1082 auto cls = objc_allocateClassPair((Class)
"NSObject"_cls,
"WebkitUIDelegate", 0);
1083 class_addProtocol(cls, objc_getProtocol(
"WKUIDelegate"));
1086 "webView:runOpenPanelWithParameters:initiatedByFrame:completionHandler:"_sel,
1087 (IMP)(+[](
id,
SEL,
id,
id parameters,
id,
id completion_handler)
1089 auto allows_multiple_selection =
1090 objc::msg_send<BOOL>(parameters,
"allowsMultipleSelection"_sel);
1091 auto allows_directories =
1092 objc::msg_send<BOOL>(parameters,
"allowsDirectories"_sel);
1095 auto panel = objc::msg_send<id>(
"NSOpenPanel"_cls,
"openPanel"_sel);
1096 objc::msg_send<void>(panel,
"setCanChooseFiles:"_sel, YES);
1097 objc::msg_send<void>(panel,
"setCanChooseDirectories:"_sel,
1098 allows_directories);
1099 objc::msg_send<void>(panel,
"setAllowsMultipleSelection:"_sel,
1100 allows_multiple_selection);
1101 auto modal_response =
1102 objc::msg_send<NSModalResponse>(panel,
"runModal"_sel);
1107 id urls = modal_response == NSModalResponseOK
1108 ? objc::msg_send<id>(panel,
"URLs"_sel)
1112 auto sig = objc::msg_send<id>(
"NSMethodSignature"_cls,
1113 "signatureWithObjCTypes:"_sel,
"v@?@");
1114 auto invocation = objc::msg_send<id>(
1115 "NSInvocation"_cls,
"invocationWithMethodSignature:"_sel, sig);
1116 objc::msg_send<void>(invocation,
"setTarget:"_sel,
1117 completion_handler);
1118 objc::msg_send<void>(invocation,
"setArgument:atIndex:"_sel, &urls,
1120 objc::msg_send<void>(invocation,
"invoke"_sel);
1123 objc_registerClassPair(cls);
1124 return objc::msg_send<id>((
id)cls,
"new"_sel);
1126 id create_webkit_navigation_delegate()
1129 objc_allocateClassPair((Class)
"NSObject"_cls,
"WebkitNavigationDelegate", 0);
1130 class_addProtocol(cls, objc_getProtocol(
"WKNavigationDelegate"));
1131 class_addMethod(cls,
"webView:didFinishNavigation:"_sel,
1132 (IMP)(+[](
id delegate,
SEL sel,
id webview,
id navigation)
1134 auto w = get_associated_webview(delegate);
1135 auto url = objc::msg_send<id>(webview,
"URL"_sel);
1136 auto nstr = objc::msg_send<id>(url,
"absoluteString"_sel);
1137 auto str = objc::msg_send<char*>(nstr,
"UTF8String"_sel);
1138 w->m_navigateCallback(str, w->m_navigateCallbackArg);
1141 objc_registerClassPair(cls);
1142 auto instance = objc::msg_send<id>((
id)cls,
"new"_sel);
1143 objc_setAssociatedObject(instance,
"webview", (
id)
this, OBJC_ASSOCIATION_ASSIGN);
1146 static id get_shared_application()
1148 return objc::msg_send<id>(
"NSApplication"_cls,
"sharedApplication"_sel);
1150 static cocoa_wkwebview_engine* get_associated_webview(
id object)
1152 auto w = (cocoa_wkwebview_engine*)objc_getAssociatedObject(
object,
"webview");
1156 static id get_main_bundle() noexcept
1158 return objc::msg_send<id>(
"NSBundle"_cls,
"mainBundle"_sel);
1160 static bool is_app_bundled() noexcept
1162 auto bundle = get_main_bundle();
1167 auto bundle_path = objc::msg_send<id>(bundle,
"bundlePath"_sel);
1168 auto bundled = objc::msg_send<BOOL>(bundle_path,
"hasSuffix:"_sel,
".app"_str);
1171 void on_application_did_finish_launching(
id ,
id app)
1174 if (!m_parent_window)
1178 objc::msg_send<void>(app,
"stop:"_sel,
nullptr);
1189 if (!is_app_bundled())
1193 objc::msg_send<void>(app,
"setActivationPolicy:"_sel,
1194 NSApplicationActivationPolicyRegular);
1197 objc::msg_send<void>(app,
"activateIgnoringOtherApps:"_sel, YES);
1201 if (!m_parent_window)
1203 m_window = objc::msg_send<id>(
"NSWindow"_cls,
"alloc"_sel);
1204 auto style = NSWindowStyleMaskTitled;
1205 m_window = objc::msg_send<id>(
1206 m_window,
"initWithContentRect:styleMask:backing:defer:"_sel,
1207 CGRectMake(0, 0, 0, 0), style, NSBackingStoreBuffered, NO);
1211 m_window = (id)m_parent_window;
1215 auto config = objc::msg_send<id>(
"WKWebViewConfiguration"_cls,
"new"_sel);
1216 m_manager = objc::msg_send<id>(config,
"userContentController"_sel);
1217 m_webview = objc::msg_send<id>(
"WKWebView"_cls,
"alloc"_sel);
1224 objc::msg_send<id>(config,
"preferences"_sel),
"setValue:forKey:"_sel,
1225 objc::msg_send<id>(
"NSNumber"_cls,
"numberWithBool:"_sel, YES),
1226 "developerExtrasEnabled"_str);
1231 objc::msg_send<id>(objc::msg_send<id>(config,
"preferences"_sel),
1232 "setValue:forKey:"_sel,
1233 objc::msg_send<id>(
"NSNumber"_cls,
"numberWithBool:"_sel, YES),
1234 "fullScreenEnabled"_str);
1238 objc::msg_send<id>(objc::msg_send<id>(config,
"preferences"_sel),
1239 "setValue:forKey:"_sel,
1240 objc::msg_send<id>(
"NSNumber"_cls,
"numberWithBool:"_sel, YES),
1241 "javaScriptCanAccessClipboard"_str);
1245 objc::msg_send<id>(objc::msg_send<id>(config,
"preferences"_sel),
1246 "setValue:forKey:"_sel,
1247 objc::msg_send<id>(
"NSNumber"_cls,
"numberWithBool:"_sel, YES),
1248 "DOMPasteAllowed"_str);
1250 auto ui_delegate = create_webkit_ui_delegate();
1251 objc::msg_send<void>(m_webview,
"initWithFrame:configuration:"_sel,
1252 CGRectMake(0, 0, 0, 0), config);
1253 objc::msg_send<void>(m_webview,
"setUIDelegate:"_sel, ui_delegate);
1255 auto navigation_delegate = create_webkit_navigation_delegate();
1256 objc::msg_send<void>(m_webview,
"setNavigationDelegate:"_sel, navigation_delegate);
1257 auto script_message_handler = create_script_message_handler();
1258 objc::msg_send<void>(m_manager,
"addScriptMessageHandler:name:"_sel,
1259 script_message_handler,
"external"_str);
1263 invoke: function(s) {
1264 window.webkit.messageHandlers.external.postMessage(s);
1268 objc::msg_send<void>(m_window, "setContentView:"_sel, m_webview);
1269 objc::msg_send<void>(m_window,
"makeKeyAndOrderFront:"_sel,
nullptr);
1272 void* m_parent_window;
1276 void* m_navigateCallbackArg =
nullptr;
1277 std::function<void(
const std::string&,
void*)> m_navigateCallback = 0;
1282 using browser_engine = detail::cocoa_wkwebview_engine;
1286 #elif defined(WEBVIEW_EDGE)
1297 #define WIN32_LEAN_AND_MEAN
1299 #include <shlwapi.h>
1301 #include <windows.h>
1303 #include "WebView2.h"
1306 #pragma comment(lib, "advapi32.lib")
1307 #pragma comment(lib, "ole32.lib")
1308 #pragma comment(lib, "shell32.lib")
1309 #pragma comment(lib, "shlwapi.lib")
1310 #pragma comment(lib, "user32.lib")
1311 #pragma comment(lib, "version.lib")
1319 using msg_cb_t = std::function<void(
const std::string)>;
1322 inline std::wstring widen_string(
const std::string& input)
1326 return std::wstring();
1329 DWORD flags = MB_ERR_INVALID_CHARS;
1330 auto input_c = input.c_str();
1331 auto input_length =
static_cast<int>(input.size());
1332 auto required_length =
1333 MultiByteToWideChar(cp, flags, input_c, input_length,
nullptr, 0);
1334 if (required_length > 0)
1336 std::wstring output(
static_cast<std::size_t
>(required_length), L
'\0');
1337 if (MultiByteToWideChar(cp, flags, input_c, input_length, &output[0],
1338 required_length) > 0)
1344 return std::wstring();
1348 inline std::string narrow_string(
const std::wstring& input)
1352 return std::string();
1355 DWORD flags = WC_ERR_INVALID_CHARS;
1356 auto input_c = input.c_str();
1357 auto input_length =
static_cast<int>(input.size());
1358 auto required_length =
1359 WideCharToMultiByte(cp, flags, input_c, input_length,
nullptr, 0,
nullptr,
nullptr);
1360 if (required_length > 0)
1362 std::string output(
static_cast<std::size_t
>(required_length),
'\0');
1363 if (WideCharToMultiByte(cp, flags, input_c, input_length, &output[0],
1364 required_length,
nullptr,
nullptr) > 0)
1370 return std::string();
1375 template <
typename T>
1376 std::array<unsigned int, 4> parse_version(
const std::basic_string<T>& version) noexcept
1378 auto parse_component = [](
auto sb,
auto se) ->
unsigned int
1382 auto n = std::stol(std::basic_string<T>(sb, se));
1383 return n < 0 ? 0 : n;
1385 catch (std::exception&)
1390 auto end = version.end();
1391 auto sb = version.begin();
1393 unsigned int ci = 0;
1394 std::array<unsigned int, 4> components{};
1395 while (sb != end && se != end && ci < components.size())
1397 if (*se ==
static_cast<T
>(
'.'))
1399 components[ci++] = parse_component(sb, se);
1405 if (sb < se && ci < components.size())
1407 components[ci] = parse_component(sb, se);
1412 template <
typename T, std::
size_t Length>
1413 auto parse_version(
const T (&version)[Length]) noexcept
1415 return parse_version(std::basic_string<T>(version, Length));
1418 std::wstring get_file_version_string(
const std::wstring& file_path) noexcept
1421 DWORD info_buffer_length = GetFileVersionInfoSizeW(file_path.c_str(), &dummy_handle);
1422 if (info_buffer_length == 0)
1424 return std::wstring();
1426 std::vector<char> info_buffer;
1427 info_buffer.reserve(info_buffer_length);
1428 if (!GetFileVersionInfoW(file_path.c_str(), 0, info_buffer_length, info_buffer.data()))
1430 return std::wstring();
1432 auto sub_block = L
"\\StringFileInfo\\040904B0\\ProductVersion";
1433 LPWSTR version =
nullptr;
1434 unsigned int version_length = 0;
1435 if (!VerQueryValueW(info_buffer.data(), sub_block,
reinterpret_cast<LPVOID*
>(&version),
1438 return std::wstring();
1440 if (!version || version_length == 0)
1442 return std::wstring();
1444 return std::wstring(version, version_length);
1449 class com_init_wrapper
1452 com_init_wrapper(DWORD dwCoInit)
1458 switch (CoInitializeEx(
nullptr, dwCoInit))
1462 m_initialized =
true;
1472 m_initialized =
false;
1476 com_init_wrapper(
const com_init_wrapper& other) =
delete;
1477 com_init_wrapper& operator=(
const com_init_wrapper& other) =
delete;
1478 com_init_wrapper(com_init_wrapper&& other) =
delete;
1479 com_init_wrapper& operator=(com_init_wrapper&& other) =
delete;
1481 bool is_initialized()
const
1483 return m_initialized;
1487 bool m_initialized =
false;
1491 template <
typename T>
class library_symbol
1496 constexpr
explicit library_symbol(
const char* name) : m_name(name)
1499 constexpr
const char* get_name()
const
1510 class native_library
1513 explicit native_library(
const wchar_t* name) : m_handle(LoadLibraryW(name))
1521 FreeLibrary(m_handle);
1526 native_library(
const native_library& other) =
delete;
1527 native_library& operator=(
const native_library& other) =
delete;
1528 native_library(native_library&& other) =
default;
1529 native_library& operator=(native_library&& other) =
default;
1532 operator bool()
const
1538 template <
typename Symbol>
typename Symbol::type get(
const Symbol& symbol)
const
1542 return reinterpret_cast<typename Symbol::type
>(
1543 GetProcAddress(m_handle, symbol.get_name()));
1549 bool is_loaded()
const
1560 HMODULE m_handle =
nullptr;
1563 struct user32_symbols
1565 using DPI_AWARENESS_CONTEXT = HANDLE;
1566 using SetProcessDpiAwarenessContext_t = BOOL(WINAPI*)(DPI_AWARENESS_CONTEXT);
1567 using SetProcessDPIAware_t = BOOL(WINAPI*)();
1569 static constexpr
auto SetProcessDpiAwarenessContext =
1570 library_symbol<SetProcessDpiAwarenessContext_t>(
"SetProcessDpiAwarenessContext");
1571 static constexpr
auto SetProcessDPIAware =
1572 library_symbol<SetProcessDPIAware_t>(
"SetProcessDPIAware");
1575 struct shcore_symbols
1579 PROCESS_PER_MONITOR_DPI_AWARE = 2
1580 } PROCESS_DPI_AWARENESS;
1581 using SetProcessDpiAwareness_t = HRESULT(WINAPI*)(PROCESS_DPI_AWARENESS);
1583 static constexpr
auto SetProcessDpiAwareness =
1584 library_symbol<SetProcessDpiAwareness_t>(
"SetProcessDpiAwareness");
1590 explicit reg_key(HKEY root_key,
const wchar_t* sub_key, DWORD options,
1594 auto status = RegOpenKeyExW(root_key, sub_key, options, sam_desired, &handle);
1595 if (status == ERROR_SUCCESS)
1601 explicit reg_key(HKEY root_key,
const std::wstring& sub_key, DWORD options,
1603 : reg_key(root_key, sub_key.c_str(), options, sam_desired)
1611 RegCloseKey(m_handle);
1616 reg_key(
const reg_key& other) =
delete;
1617 reg_key& operator=(
const reg_key& other) =
delete;
1618 reg_key(reg_key&& other) =
delete;
1619 reg_key& operator=(reg_key&& other) =
delete;
1621 bool is_open()
const
1625 bool get_handle()
const
1630 std::wstring query_string(
const wchar_t* name)
const
1632 DWORD buf_length = 0;
1635 RegQueryValueExW(m_handle, name,
nullptr,
nullptr,
nullptr, &buf_length);
1636 if (status != ERROR_SUCCESS && status != ERROR_MORE_DATA)
1638 return std::wstring();
1641 std::wstring result(buf_length /
sizeof(
wchar_t), 0);
1642 auto buf =
reinterpret_cast<LPBYTE
>(&result[0]);
1643 status = RegQueryValueExW(m_handle, name,
nullptr,
nullptr, buf, &buf_length);
1644 if (status != ERROR_SUCCESS)
1646 return std::wstring();
1649 for (std::size_t length = result.size(); length > 0; --length)
1651 if (result[length - 1] != 0)
1653 result.resize(length);
1661 HKEY m_handle =
nullptr;
1664 inline bool enable_dpi_awareness()
1666 auto user32 = native_library(L
"user32.dll");
1667 if (
auto fn = user32.get(user32_symbols::SetProcessDpiAwarenessContext))
1669 if (fn(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE))
1673 return GetLastError() == ERROR_ACCESS_DENIED;
1675 if (
auto shcore = native_library(L
"shcore.dll"))
1677 if (
auto fn = shcore.get(shcore_symbols::SetProcessDpiAwareness))
1679 auto result = fn(shcore_symbols::PROCESS_PER_MONITOR_DPI_AWARE);
1680 return result == S_OK || result == E_ACCESSDENIED;
1683 if (
auto fn = user32.get(user32_symbols::SetProcessDPIAware))
1691 #ifndef WEBVIEW_MSWEBVIEW2_BUILTIN_IMPL
1692 #define WEBVIEW_MSWEBVIEW2_BUILTIN_IMPL 1
1697 #ifndef WEBVIEW_MSWEBVIEW2_EXPLICIT_LINK
1698 #define WEBVIEW_MSWEBVIEW2_EXPLICIT_LINK WEBVIEW_MSWEBVIEW2_BUILTIN_IMPL
1703 #if WEBVIEW_MSWEBVIEW2_BUILTIN_IMPL == 1 && WEBVIEW_MSWEBVIEW2_EXPLICIT_LINK != 1
1704 #undef WEBVIEW_MSWEBVIEW2_EXPLICIT_LINK
1705 #error Please set WEBVIEW_MSWEBVIEW2_EXPLICIT_LINK=1.
1708 #if WEBVIEW_MSWEBVIEW2_BUILTIN_IMPL == 1
1711 template <
typename T>
1712 std::basic_string<T> get_last_native_path_component(
const std::basic_string<T>& path)
1714 if (
auto pos = path.find_last_of(
static_cast<T
>(
'\\'));
1715 pos != std::basic_string<T>::npos)
1717 return path.substr(pos + 1);
1719 return std::basic_string<T>();
1723 template <
typename T>
struct cast_info_t
1729 namespace mswebview2
1731 static constexpr IID IID_ICoreWebView2CreateCoreWebView2ControllerCompletedHandler{
1732 0x6C4819F3, 0xC9B7, 0x4260, 0x81, 0x27, 0xC9, 0xF5, 0xBD, 0xE7, 0xF6, 0x8C
1734 static constexpr IID IID_ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler{
1735 0x4E8A3389, 0xC9D8, 0x4BD2, 0xB6, 0xB5, 0x12, 0x4F, 0xEE, 0x6C, 0xC1, 0x4D
1737 static constexpr IID IID_ICoreWebView2PermissionRequestedEventHandler{
1738 0x15E1C6A3, 0xC72A, 0x4DF3, 0x91, 0xD7, 0xD0, 0x97, 0xFB, 0xEC, 0x6B, 0xFD
1740 static constexpr IID IID_ICoreWebView2WebMessageReceivedEventHandler{
1741 0x57213F19, 0x00E6, 0x49FA, 0x8E, 0x07, 0x89, 0x8E, 0xA0, 0x1E, 0xCB, 0xD2
1744 #if WEBVIEW_MSWEBVIEW2_BUILTIN_IMPL == 1
1745 enum class webview2_runtime_type
1751 namespace webview2_symbols
1753 using CreateWebViewEnvironmentWithOptionsInternal_t = HRESULT(STDMETHODCALLTYPE*)(
1754 bool, webview2_runtime_type, PCWSTR, IUnknown*,
1755 ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler*);
1756 using DllCanUnloadNow_t = HRESULT(STDMETHODCALLTYPE*)();
1758 static constexpr
auto CreateWebViewEnvironmentWithOptionsInternal =
1759 library_symbol<CreateWebViewEnvironmentWithOptionsInternal_t>(
1760 "CreateWebViewEnvironmentWithOptionsInternal");
1761 static constexpr
auto DllCanUnloadNow =
1762 library_symbol<DllCanUnloadNow_t>(
"DllCanUnloadNow");
1766 #if WEBVIEW_MSWEBVIEW2_EXPLICIT_LINK == 1
1767 namespace webview2_symbols
1769 using CreateCoreWebView2EnvironmentWithOptions_t = HRESULT(STDMETHODCALLTYPE*)(
1770 PCWSTR, PCWSTR, ICoreWebView2EnvironmentOptions*,
1771 ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler*);
1772 using GetAvailableCoreWebView2BrowserVersionString_t =
1773 HRESULT(STDMETHODCALLTYPE*)(PCWSTR, LPWSTR*);
1775 static constexpr
auto CreateCoreWebView2EnvironmentWithOptions =
1776 library_symbol<CreateCoreWebView2EnvironmentWithOptions_t>(
1777 "CreateCoreWebView2EnvironmentWithOptions");
1778 static constexpr
auto GetAvailableCoreWebView2BrowserVersionString =
1779 library_symbol<GetAvailableCoreWebView2BrowserVersionString_t>(
1780 "GetAvailableCoreWebView2BrowserVersionString");
1787 HRESULT create_environment_with_options(
1788 PCWSTR browser_dir, PCWSTR user_data_dir,
1789 ICoreWebView2EnvironmentOptions* env_options,
1790 ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler* created_handler)
1793 #if WEBVIEW_MSWEBVIEW2_EXPLICIT_LINK == 1
1794 if (m_lib.is_loaded())
1796 if (
auto fn = m_lib.get(
1797 webview2_symbols::CreateCoreWebView2EnvironmentWithOptions))
1799 return fn(browser_dir, user_data_dir, env_options, created_handler);
1802 #if WEBVIEW_MSWEBVIEW2_BUILTIN_IMPL == 1
1803 return create_environment_with_options_impl(browser_dir, user_data_dir,
1804 env_options, created_handler);
1809 return ::CreateCoreWebView2EnvironmentWithOptions(browser_dir, user_data_dir,
1810 env_options, created_handler);
1815 get_available_browser_version_string(PCWSTR browser_dir, LPWSTR* version)
const
1817 #if WEBVIEW_MSWEBVIEW2_EXPLICIT_LINK == 1
1818 if (m_lib.is_loaded())
1820 if (
auto fn = m_lib.get(
1821 webview2_symbols::GetAvailableCoreWebView2BrowserVersionString))
1823 return fn(browser_dir, version);
1826 #if WEBVIEW_MSWEBVIEW2_BUILTIN_IMPL == 1
1827 return get_available_browser_version_string_impl(browser_dir, version);
1832 return ::GetAvailableCoreWebView2BrowserVersionString(browser_dir, version);
1837 #if WEBVIEW_MSWEBVIEW2_BUILTIN_IMPL == 1
1838 struct client_info_t
1841 std::wstring dll_path;
1842 std::wstring version;
1843 webview2_runtime_type runtime_type;
1846 HRESULT create_environment_with_options_impl(
1847 PCWSTR browser_dir, PCWSTR user_data_dir,
1848 ICoreWebView2EnvironmentOptions* env_options,
1849 ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler* created_handler)
1852 auto found_client = find_available_client(browser_dir);
1853 if (!found_client.found)
1857 auto client_dll = native_library(found_client.dll_path.c_str());
1858 if (
auto fn = client_dll.get(
1859 webview2_symbols::CreateWebViewEnvironmentWithOptionsInternal))
1861 return fn(
true, found_client.runtime_type, user_data_dir, env_options,
1864 if (
auto fn = client_dll.get(webview2_symbols::DllCanUnloadNow))
1868 client_dll.detach();
1871 return ERROR_SUCCESS;
1875 get_available_browser_version_string_impl(PCWSTR browser_dir, LPWSTR* version)
const
1881 auto found_client = find_available_client(browser_dir);
1882 if (!found_client.found)
1886 auto info_length_bytes =
1887 found_client.version.size() *
sizeof(found_client.version[0]);
1888 auto info =
static_cast<LPWSTR
>(CoTaskMemAlloc(info_length_bytes));
1893 CopyMemory(info, found_client.version.c_str(), info_length_bytes);
1898 client_info_t find_available_client(PCWSTR browser_dir)
const
1902 return find_embedded_client(api_version, browser_dir);
1905 find_installed_client(api_version,
true, default_release_channel_guid);
1906 if (!found_client.found)
1909 find_installed_client(api_version,
false, default_release_channel_guid);
1911 return found_client;
1914 std::wstring make_client_dll_path(
const std::wstring& dir)
const
1916 auto dll_path = dir;
1917 if (!dll_path.empty())
1919 auto last_char = dir[dir.size() - 1];
1920 if (last_char != L
'\\' && last_char != L
'/')
1925 dll_path += L
"EBWebView\\";
1926 #if defined(_M_X64) || defined(__x86_64__)
1928 #elif defined(_M_IX86) || defined(__i386__)
1930 #elif defined(_M_ARM64) || defined(__aarch64__)
1931 dll_path += L
"arm64";
1933 #error WebView2 integration for this platform is not yet supported.
1935 dll_path += L
"\\EmbeddedBrowserWebView.dll";
1939 client_info_t find_installed_client(
unsigned int min_api_version,
bool system,
1940 const std::wstring& release_channel)
const
1942 std::wstring sub_key = client_state_reg_sub_key;
1943 sub_key += release_channel;
1944 auto root_key = system ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
1945 reg_key key(root_key, sub_key, 0, KEY_READ | KEY_WOW64_32KEY);
1950 auto ebwebview_value = key.query_string(L
"EBWebView");
1952 auto client_version_string = get_last_native_path_component(ebwebview_value);
1953 auto client_version = parse_version(client_version_string);
1954 if (client_version[2] < min_api_version)
1960 auto client_dll_path = make_client_dll_path(ebwebview_value);
1961 return {
true, client_dll_path, client_version_string,
1962 webview2_runtime_type::installed };
1965 client_info_t find_embedded_client(
unsigned int min_api_version,
1966 const std::wstring& dir)
const
1968 auto client_dll_path = make_client_dll_path(dir);
1970 auto client_version_string = get_file_version_string(client_dll_path);
1971 auto client_version = parse_version(client_version_string);
1972 if (client_version[2] < min_api_version)
1978 return {
true, client_dll_path, client_version_string,
1979 webview2_runtime_type::embedded };
1987 static constexpr
unsigned int api_version = 1150;
1989 static constexpr
auto client_state_reg_sub_key =
1990 L
"SOFTWARE\\Microsoft\\EdgeUpdate\\ClientState\\";
1993 static constexpr
auto stable_release_guid =
1994 L
"{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}";
1996 static constexpr
auto default_release_channel_guid = stable_release_guid;
1999 #if WEBVIEW_MSWEBVIEW2_EXPLICIT_LINK == 1
2000 native_library m_lib{ L
"WebView2Loader.dll" };
2006 static constexpr
auto controller_completed =
2007 cast_info_t<ICoreWebView2CreateCoreWebView2ControllerCompletedHandler>{
2008 IID_ICoreWebView2CreateCoreWebView2ControllerCompletedHandler
2011 static constexpr
auto environment_completed =
2012 cast_info_t<ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler>{
2013 IID_ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler
2016 static constexpr
auto message_received =
2017 cast_info_t<ICoreWebView2WebMessageReceivedEventHandler>{
2018 IID_ICoreWebView2WebMessageReceivedEventHandler
2021 static constexpr
auto permission_requested =
2022 cast_info_t<ICoreWebView2PermissionRequestedEventHandler>{
2023 IID_ICoreWebView2PermissionRequestedEventHandler
2028 class webview2_com_handler
2029 :
public ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler,
2030 public ICoreWebView2CreateCoreWebView2ControllerCompletedHandler,
2031 public ICoreWebView2WebMessageReceivedEventHandler,
2032 public ICoreWebView2PermissionRequestedEventHandler,
2033 public ICoreWebView2NavigationCompletedEventHandler
2035 using webview2_com_handler_cb_t =
2036 std::function<void(ICoreWebView2Controller*, ICoreWebView2* webview)>;
2039 webview2_com_handler(HWND hwnd, msg_cb_t msgCb, webview2_com_handler_cb_t cb)
2040 : m_window(hwnd), m_msgCb(msgCb), m_cb(cb)
2044 virtual ~webview2_com_handler() =
default;
2045 webview2_com_handler(
const webview2_com_handler& other) =
delete;
2046 webview2_com_handler& operator=(
const webview2_com_handler& other) =
delete;
2047 webview2_com_handler(webview2_com_handler&& other) =
delete;
2048 webview2_com_handler& operator=(webview2_com_handler&& other) =
delete;
2050 ULONG STDMETHODCALLTYPE AddRef()
2052 return ++m_ref_count;
2054 ULONG STDMETHODCALLTYPE Release()
2056 if (m_ref_count > 1)
2058 return --m_ref_count;
2063 HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID* ppv)
2065 using namespace mswebview2::cast_info;
2082 if (cast_if_equal_iid(riid, controller_completed, ppv) ||
2083 cast_if_equal_iid(riid, environment_completed, ppv) ||
2084 cast_if_equal_iid(riid, message_received, ppv) ||
2085 cast_if_equal_iid(riid, permission_requested, ppv))
2090 return E_NOINTERFACE;
2092 HRESULT STDMETHODCALLTYPE Invoke(HRESULT res, ICoreWebView2Environment* env)
2096 res = env->CreateCoreWebView2Controller(m_window,
this);
2102 try_create_environment();
2105 HRESULT STDMETHODCALLTYPE Invoke(HRESULT res, ICoreWebView2Controller* controller)
2114 case HRESULT_FROM_WIN32(ERROR_INVALID_STATE):
2118 try_create_environment();
2122 ICoreWebView2* webview;
2123 ::EventRegistrationToken token;
2124 controller->get_CoreWebView2(&webview);
2125 webview->add_WebMessageReceived(
this, &token);
2126 webview->add_PermissionRequested(
this, &token);
2127 webview->add_NavigationCompleted(
this, &token);
2129 m_cb(controller, webview);
2132 HRESULT STDMETHODCALLTYPE Invoke(ICoreWebView2* sender,
2133 ICoreWebView2WebMessageReceivedEventArgs* args)
2136 args->TryGetWebMessageAsString(&message);
2137 m_msgCb(narrow_string(message));
2138 sender->PostWebMessageAsString(message);
2140 CoTaskMemFree(message);
2143 HRESULT STDMETHODCALLTYPE Invoke(ICoreWebView2* sender,
2144 ICoreWebView2PermissionRequestedEventArgs* args)
2146 COREWEBVIEW2_PERMISSION_KIND kind;
2147 args->get_PermissionKind(&kind);
2148 if (kind == COREWEBVIEW2_PERMISSION_KIND_CLIPBOARD_READ)
2150 args->put_State(COREWEBVIEW2_PERMISSION_STATE_ALLOW);
2154 HRESULT STDMETHODCALLTYPE Invoke(ICoreWebView2* sender,
2155 ICoreWebView2NavigationCompletedEventArgs* args)
2157 PWSTR uri =
nullptr;
2158 auto hr = sender->get_Source(&uri);
2161 auto curi = std::wstring_convert<std::codecvt_utf8<wchar_t> >().to_bytes(uri);
2162 if (navigateCallback)
2163 navigateCallback(curi, navigateCallbackArg);
2173 template <
typename T>
2174 T* cast_if_equal_iid(REFIID riid,
const cast_info_t<T>& info,
2175 LPVOID* ppv =
nullptr) noexcept
2178 if (IsEqualIID(riid, info.iid))
2180 ptr =
static_cast<T*
>(
this);
2192 void set_attempt_handler(std::function<HRESULT()> attempt_handler) noexcept
2194 m_attempt_handler = attempt_handler;
2200 void try_create_environment() noexcept
2207 if (m_attempts < m_max_attempts)
2210 auto res = m_attempt_handler();
2217 if (res == HRESULT_FROM_WIN32(ERROR_INVALID_STATE))
2221 try_create_environment();
2225 m_cb(
nullptr,
nullptr);
2228 void STDMETHODCALLTYPE add_navigate_listener(
2229 std::function<
void(
const std::string&,
void*)> callback,
void* arg)
2231 navigateCallback = std::move(callback);
2232 navigateCallbackArg = arg;
2238 webview2_com_handler_cb_t m_cb;
2239 std::atomic<ULONG> m_ref_count{ 1 };
2240 std::function<HRESULT()> m_attempt_handler;
2241 unsigned int m_max_attempts = 5;
2242 unsigned int m_attempts = 0;
2243 void* navigateCallbackArg =
nullptr;
2244 std::function<void(
const std::string&,
void*)> navigateCallback = 0;
2247 class win32_edge_engine
2250 win32_edge_engine(
bool debug,
void* window)
2252 if (!is_webview2_available())
2256 if (!m_com_init.is_initialized())
2260 enable_dpi_awareness();
2261 if (window ==
nullptr)
2263 HINSTANCE hInstance = GetModuleHandle(
nullptr);
2264 HICON icon = (HICON)LoadImage(hInstance, IDI_APPLICATION, IMAGE_ICON,
2265 GetSystemMetrics(SM_CXICON),
2266 GetSystemMetrics(SM_CYICON), LR_DEFAULTCOLOR);
2269 ZeroMemory(&wc,
sizeof(WNDCLASSEX));
2270 wc.cbSize =
sizeof(WNDCLASSEX);
2271 wc.hInstance = hInstance;
2272 wc.lpszClassName = L
"webview";
2275 (WNDPROC)(+[](HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) -> LRESULT
2278 (win32_edge_engine*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
2285 DestroyWindow(hwnd);
2290 case WM_GETMINMAXINFO:
2292 auto lpmmi = (LPMINMAXINFO)lp;
2297 if (w->m_maxsz.x > 0 && w->m_maxsz.y > 0)
2299 lpmmi->ptMaxSize = w->m_maxsz;
2300 lpmmi->ptMaxTrackSize = w->m_maxsz;
2302 if (w->m_minsz.x > 0 && w->m_minsz.y > 0)
2304 lpmmi->ptMinTrackSize = w->m_minsz;
2309 return DefWindowProcW(hwnd, msg, wp, lp);
2313 RegisterClassExW(&wc);
2314 m_window = CreateWindowW(L
"webview", L
"", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,
2315 CW_USEDEFAULT, 640, 480,
nullptr,
nullptr, hInstance,
2317 if (m_window ==
nullptr)
2321 SetWindowLongPtr(m_window, GWLP_USERDATA, (LONG_PTR)
this);
2325 m_window = *(
static_cast<HWND*
>(window));
2328 ShowWindow(m_window, SW_SHOW);
2329 UpdateWindow(m_window);
2332 auto cb = std::bind(&win32_edge_engine::on_message,
this, std::placeholders::_1);
2334 embed(m_window, debug, cb);
2336 m_controller->MoveFocus(COREWEBVIEW2_MOVE_FOCUS_REASON_PROGRAMMATIC);
2339 virtual ~win32_edge_engine()
2343 m_com_handler->Release();
2344 m_com_handler =
nullptr;
2348 m_webview->Release();
2349 m_webview =
nullptr;
2353 m_controller->Release();
2354 m_controller =
nullptr;
2358 win32_edge_engine(
const win32_edge_engine& other) =
delete;
2359 win32_edge_engine& operator=(
const win32_edge_engine& other) =
delete;
2360 win32_edge_engine(win32_edge_engine&& other) =
delete;
2361 win32_edge_engine& operator=(win32_edge_engine&& other) =
delete;
2367 while ((res = GetMessage(&msg,
nullptr, 0, 0)) != -1)
2371 TranslateMessage(&msg);
2372 DispatchMessage(&msg);
2375 if (msg.message == WM_APP)
2377 auto f = (dispatch_fn_t*)(msg.lParam);
2381 else if (msg.message == WM_QUIT)
2389 return (
void*)m_window;
2395 void dispatch(dispatch_fn_t f)
2397 PostThreadMessage(m_main_thread, WM_APP, 0, (LPARAM)
new dispatch_fn_t(f));
2400 void set_title(
const std::string& title)
2402 SetWindowTextW(m_window, widen_string(title).c_str());
2405 void set_size(
int width,
int height,
int hints)
2407 auto style = GetWindowLong(m_window, GWL_STYLE);
2408 if (hints == WEBVIEW_HINT_FIXED)
2410 style &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
2414 style |= (WS_THICKFRAME | WS_MAXIMIZEBOX);
2416 SetWindowLong(m_window, GWL_STYLE, style);
2418 if (hints == WEBVIEW_HINT_MAX)
2423 else if (hints == WEBVIEW_HINT_MIN)
2434 AdjustWindowRect(&r, WS_OVERLAPPEDWINDOW, 0);
2435 SetWindowPos(m_window,
nullptr, r.left, r.top, r.right - r.left,
2437 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE | SWP_FRAMECHANGED);
2442 void navigate(
const std::string& url)
2444 auto wurl = widen_string(url);
2445 m_webview->Navigate(wurl.c_str());
2448 void init(
const std::string& js)
2450 auto wjs = widen_string(js);
2451 m_webview->AddScriptToExecuteOnDocumentCreated(wjs.c_str(),
nullptr);
2454 void eval(
const std::string& js)
2456 auto wjs = widen_string(js);
2457 m_webview->ExecuteScript(wjs.c_str(),
nullptr);
2460 void add_navigate_listener(std::function<
void(
const std::string&,
void*)> callback,
2463 m_com_handler->add_navigate_listener(callback, arg);
2466 void add_scheme_handler(
const std::string& scheme,
2467 std::function<
void(
const std::string&,
void*)> callback,
2473 void set_html(
const std::string& html)
2475 m_webview->NavigateToString(widen_string(html).c_str());
2479 bool embed(HWND wnd,
bool debug, msg_cb_t cb)
2481 std::atomic_flag flag = ATOMIC_FLAG_INIT;
2482 flag.test_and_set();
2484 wchar_t currentExePath[MAX_PATH];
2485 GetModuleFileNameW(
nullptr, currentExePath, MAX_PATH);
2486 wchar_t* currentExeName = PathFindFileNameW(currentExePath);
2488 wchar_t dataPath[MAX_PATH];
2489 if (!SUCCEEDED(SHGetFolderPathW(
nullptr, CSIDL_APPDATA,
nullptr, 0, dataPath)))
2493 wchar_t userDataFolder[MAX_PATH];
2494 PathCombineW(userDataFolder, dataPath, currentExeName);
2496 m_com_handler =
new webview2_com_handler(
2498 [&](ICoreWebView2Controller* controller, ICoreWebView2* webview)
2500 if (!controller || !webview)
2505 controller->AddRef();
2507 m_controller = controller;
2508 m_webview = webview;
2512 m_com_handler->set_attempt_handler(
2515 return m_webview2_loader.create_environment_with_options(
2516 nullptr, userDataFolder,
nullptr, m_com_handler);
2518 m_com_handler->try_create_environment();
2521 while (flag.test_and_set() && GetMessage(&msg,
nullptr, 0, 0))
2523 TranslateMessage(&msg);
2524 DispatchMessage(&msg);
2526 if (!m_controller || !m_webview)
2530 ICoreWebView2Settings* settings =
nullptr;
2531 auto res = m_webview->get_Settings(&settings);
2536 res = settings->put_AreDevToolsEnabled(debug ? TRUE : FALSE);
2541 init(
"window.external={invoke:s=>window.chrome.webview.postMessage(s)}");
2545 void resize(HWND wnd)
2547 if (m_controller ==
nullptr)
2552 GetClientRect(wnd, &bounds);
2553 m_controller->put_Bounds(bounds);
2556 bool is_webview2_available() const noexcept
2558 LPWSTR version_info =
nullptr;
2560 m_webview2_loader.get_available_browser_version_string(
nullptr, &version_info);
2563 auto ok = SUCCEEDED(res) && version_info;
2566 CoTaskMemFree(version_info);
2571 virtual void on_message(
const std::string& msg) = 0;
2577 com_init_wrapper m_com_init{ COINIT_APARTMENTTHREADED };
2578 HWND m_window =
nullptr;
2579 POINT m_minsz = POINT{ 0, 0 };
2580 POINT m_maxsz = POINT{ 0, 0 };
2581 DWORD m_main_thread = GetCurrentThreadId();
2582 ICoreWebView2* m_webview =
nullptr;
2583 ICoreWebView2Controller* m_controller =
nullptr;
2584 webview2_com_handler* m_com_handler =
nullptr;
2585 mswebview2::loader m_webview2_loader;
2590 using browser_engine = detail::win32_edge_engine;
2599 class webview :
public browser_engine
2602 webview(
bool debug =
false,
void* wnd =
nullptr) : browser_engine(debug, wnd)
2606 void navigate(
const std::string& url)
2610 browser_engine::navigate(
"about:blank");
2613 browser_engine::navigate(url);
2616 using binding_t = std::function<void(std::string, std::string,
void*)>;
2620 binding_ctx_t(binding_t callback,
void* arg) : callback(callback), arg(arg)
2629 using sync_binding_t = std::function<std::string(std::string)>;
2632 void bind(
const std::string& name, sync_binding_t fn)
2634 auto wrapper = [
this, fn](
const std::string& seq,
const std::string& req,
void* )
2635 { resolve(seq, 0, fn(req)); };
2636 bind(name, wrapper,
nullptr);
2640 void bind(
const std::string& name, binding_t fn,
void* arg)
2642 if (bindings.count(name) > 0)
2646 bindings.emplace(name, binding_ctx_t(fn, arg));
2647 auto js =
"(function() { var name = '" + name +
"';" + R
""(
2648 var RPC = window._rpc = (window._rpc || {nextSeq: 1});
2649 window[name] = function() {
2650 var seq = RPC.nextSeq++;
2651 var promise = new Promise(function(resolve, reject) {
2657 window.external.invoke(JSON.stringify({
2660 params: Array.prototype.slice.call(arguments),
2669 void unbind(
const std::string& name)
2671 auto found = bindings.find(name);
2672 if (found != bindings.end())
2674 auto js =
"delete window['" + name +
"'];";
2677 bindings.erase(found);
2681 void resolve(
const std::string& seq,
int status,
const std::string& result)
2684 [seq, status, result,
this]()
2688 eval(
"window._rpc[" + seq +
"].resolve(" + result +
2689 "); delete window._rpc[" + seq +
"]");
2693 eval(
"window._rpc[" + seq +
"].reject(" + result +
2694 "); delete window._rpc[" + seq +
"]");
2700 void on_message(
const std::string& msg)
override
2702 auto seq = detail::json_parse(msg,
"id", 0);
2703 auto name = detail::json_parse(msg,
"method", 0);
2704 auto args = detail::json_parse(msg,
"params", 0);
2705 auto found = bindings.find(name);
2706 if (found == bindings.end())
2710 const auto& context = found->second;
2711 context.callback(seq, args, context.arg);
2714 std::map<std::string, binding_ctx_t> bindings;
2718 WEBVIEW_API webview_t webview_create(
int debug,
void* wnd)
2720 auto w =
new webview::webview(debug, wnd);
2729 WEBVIEW_API
void webview_destroy(webview_t w)
2731 delete static_cast<webview::webview*
>(w);
2734 WEBVIEW_API
void webview_run(webview_t w)
2736 static_cast<webview::webview*
>(w)->run();
2739 WEBVIEW_API
void webview_terminate(webview_t w)
2741 static_cast<webview::webview*
>(w)->terminate();
2744 WEBVIEW_API
void webview_dispatch(webview_t w,
void (*fn)(webview_t,
void*),
void* arg)
2746 static_cast<webview::webview*
>(w)->dispatch([=]() { fn(w, arg); });
2749 WEBVIEW_API
void* webview_get_window(webview_t w)
2751 return static_cast<webview::webview*
>(w)->window();
2754 WEBVIEW_API
void webview_set_title(webview_t w,
const char* title)
2756 static_cast<webview::webview*
>(w)->set_title(title);
2759 WEBVIEW_API
void webview_set_size(webview_t w,
int width,
int height,
int hints)
2761 static_cast<webview::webview*
>(w)->set_size(width, height, hints);
2764 WEBVIEW_API
void webview_navigate(webview_t w,
const char* url)
2766 static_cast<webview::webview*
>(w)->navigate(url);
2769 WEBVIEW_API
void webview_set_html(webview_t w,
const char* html)
2771 static_cast<webview::webview*
>(w)->set_html(html);
2774 WEBVIEW_API
void webview_init(webview_t w,
const char* js)
2776 static_cast<webview::webview*
>(w)->init(js);
2779 WEBVIEW_API
void webview_eval(webview_t w,
const char* js)
2781 static_cast<webview::webview*
>(w)->eval(js);
2784 WEBVIEW_API
void webview_bind(webview_t w,
const char* name,
2785 void (*fn)(
const char* seq,
const char* req,
void* arg),
void* arg)
2787 static_cast<webview::webview*
>(w)->bind(
2789 [=](
const std::string& seq,
const std::string& req,
void* arg)
2790 { fn(seq.c_str(), req.c_str(), arg); },
2794 WEBVIEW_API
void webview_unbind(webview_t w,
const char* name)
2796 static_cast<webview::webview*
>(w)->unbind(name);
2799 WEBVIEW_API
void webview_return(webview_t w,
const char* seq,
int status,
const char* result)
2801 static_cast<webview::webview*
>(w)->resolve(seq, status, result);
2806 return &webview::detail::library_version_info;