47#if defined(WEBVIEW_SHARED) || defined(WEBVIEW_BUILD_SHARED)
48#if defined(_WIN32) || defined(__CYGWIN__)
49#if defined(WEBVIEW_BUILD_SHARED)
50#define WEBVIEW_API __declspec(dllexport)
52#define WEBVIEW_API __declspec(dllimport)
55#define WEBVIEW_API __attribute__((visibility("default")))
57#elif !defined(WEBVIEW_STATIC) && defined(__cplusplus)
58#define WEBVIEW_API inline
60#define WEBVIEW_API extern
67#ifndef WEBVIEW_VERSION_MAJOR
69#define WEBVIEW_VERSION_MAJOR 0
72#ifndef WEBVIEW_VERSION_MINOR
74#define WEBVIEW_VERSION_MINOR 12
77#ifndef WEBVIEW_VERSION_PATCH
79#define WEBVIEW_VERSION_PATCH 0
82#ifndef WEBVIEW_VERSION_PRE_RELEASE
84#define WEBVIEW_VERSION_PRE_RELEASE ""
87#ifndef WEBVIEW_VERSION_BUILD_METADATA
89#define WEBVIEW_VERSION_BUILD_METADATA ""
98#define WEBVIEW_STRINGIFY(x) #x
101#define WEBVIEW_EXPAND_AND_STRINGIFY(x) WEBVIEW_STRINGIFY(x)
109#define WEBVIEW_VERSION_NUMBER \
110 WEBVIEW_EXPAND_AND_STRINGIFY(WEBVIEW_VERSION_MAJOR) \
111 "." WEBVIEW_EXPAND_AND_STRINGIFY( \
112 WEBVIEW_VERSION_MINOR) "." WEBVIEW_EXPAND_AND_STRINGIFY(WEBVIEW_VERSION_PATCH)
206#define WEBVIEW_SUCCEEDED(error) ((int)(error) >= 0)
209#define WEBVIEW_FAILED(error) ((int)(error) < 0)
385 void (*fn)(
const char *
id,
386 const char *req,
void *arg),
411 int status,
const char *result);
423#ifndef WEBVIEW_HEADER
425#if !defined(WEBVIEW_GTK) && !defined(WEBVIEW_COCOA) && !defined(WEBVIEW_EDGE)
426#if defined(__APPLE__)
428#elif defined(__unix__)
433#error "please, specify webview backend"
437#ifndef WEBVIEW_DEPRECATED
438#if __cplusplus >= 201402L
439#define WEBVIEW_DEPRECATED(reason) [[deprecated(reason)]]
440#elif defined(_MSC_VER)
441#define WEBVIEW_DEPRECATED(reason) __declspec(deprecated(reason))
443#define WEBVIEW_DEPRECATED(reason) __attribute__((deprecated(reason)))
447#ifndef WEBVIEW_DEPRECATED_PRIVATE
448#define WEBVIEW_DEPRECATED_PRIVATE \
449 WEBVIEW_DEPRECATED("Private API should not be used")
464#include <type_traits>
471#ifndef WIN32_LEAN_AND_MEAN
472#define WIN32_LEAN_AND_MEAN
482class bad_access :
public std::exception {};
484template <
typename T>
class optional {
487 optional() =
default;
489 optional(
const T &other) noexcept : m_has_data{
true} {
490 new (&m_data) T{other};
493 optional(T &&other) noexcept : m_has_data{
true} {
494 new (&m_data) T{std::move(other)};
497 optional(
const optional<T> &other)
noexcept { *
this = other; }
499 optional &operator=(
const optional<T> &other)
noexcept {
500 if (
this == &other) {
503 m_has_data = other.has_value();
506 new (&m_data) T{*
reinterpret_cast<const T *
>(&other.m_data)};
511 optional(optional<T> &&other)
noexcept { *
this = std::move(other); }
513 optional &operator=(optional<T> &&other)
noexcept {
514 if (
this == &other) {
517 m_has_data = other.has_value();
520 new (&m_data) T{std::move(*
reinterpret_cast<T *
>(&other.m_data))};
528 reinterpret_cast<T *
>(&m_data)->~T();
532 const T &get()
const {
537 return *
reinterpret_cast<const T *
>(&m_data);
545 return *
reinterpret_cast<T *
>(&m_data);
548 bool has_value()
const {
return m_has_data; }
552 typename std::aligned_storage<
sizeof(T),
alignof(T)>::type m_data;
556template <>
class optional<void> {};
558template <
typename Value,
typename Error,
typename Exception>
561 using value_type = Value;
562 using error_type = Error;
563 using exception_type = Exception;
565 basic_result() : basic_result(value_type{}) {}
567 basic_result(
const value_type &value) : m_value{value} {}
568 basic_result(value_type &&value) : m_value{std::forward<value_type>(value)} {}
570 basic_result(
const error_type &error) : m_error{error} {}
571 basic_result(error_type &&error) : m_error{std::forward<error_type>(error)} {}
573 bool ok()
const {
return has_value() && !has_error(); }
574 bool has_value()
const {
return m_value.has_value(); }
575 bool has_error()
const {
return m_error.has_value(); }
579 throw exception_type{error()};
583 const value_type &value()
const {
587 return m_value.get();
590 const error_type &error()
const {
594 return m_error.get();
598 optional<value_type> m_value;
599 optional<error_type> m_error;
602template <
typename Error,
typename Exception>
603class basic_result<void, Error, Exception> {
605 using value_type = void;
606 using error_type = Error;
607 using exception_type = Exception;
609 basic_result() =
default;
611 basic_result(error_type &&error) : m_error{std::forward<error_type>(error)} {}
613 bool ok()
const {
return !has_error(); }
615 bool has_error()
const {
return m_error.has_value(); }
619 throw exception_type{error()};
623 const error_type &error()
const {
627 return m_error.get();
631 optional<error_type> m_error;
636using dispatch_fn_t = std::function<void()>;
640 error_info(
webview_error_t code,
const std::string &message = {})
noexcept
641 : m_code{code}, m_message{message} {}
642 error_info() =
default;
645 const std::string &message()
const {
return m_message; }
649 std::string m_message;
652class exception :
public std::exception {
655 std::exception_ptr cause) noexcept
656 : exception{error_info{code, message}, cause} {}
659 : exception{error_info{code, message}} {}
661 exception(
const error_info &error, std::exception_ptr cause) noexcept
666 exception(
const error_info &error) noexcept : m_error{error} {}
668 exception() =
default;
670 const error_info &error()
const {
return m_error; }
671 std::exception_ptr cause()
const {
return m_cause; }
673 const char *what() const noexcept
override {
674 return m_error.message().c_str();
679 std::exception_ptr m_cause;
683using result = detail::basic_result<T, error_info, exception>;
685using noresult = detail::basic_result<void, error_info, exception>;
690constexpr const webview_version_info_t library_version_info{
698inline std::wstring widen_string(
const std::string &input) {
700 return std::wstring();
703 DWORD flags = MB_ERR_INVALID_CHARS;
704 auto input_c = input.c_str();
705 auto input_length =
static_cast<int>(input.size());
706 auto required_length =
707 MultiByteToWideChar(cp, flags, input_c, input_length,
nullptr, 0);
708 if (required_length > 0) {
709 std::wstring output(
static_cast<std::size_t
>(required_length), L
'\0');
710 if (MultiByteToWideChar(cp, flags, input_c, input_length, &output[0],
711 required_length) > 0) {
716 return std::wstring();
720inline std::string narrow_string(
const std::wstring &input) {
722 enum TYPE :
unsigned int {
724 err_invalid_chars = 0x00000080U
728 return std::string();
731 DWORD flags = wc_flags::err_invalid_chars;
732 auto input_c = input.c_str();
733 auto input_length =
static_cast<int>(input.size());
734 auto required_length = WideCharToMultiByte(cp, flags, input_c, input_length,
735 nullptr, 0,
nullptr,
nullptr);
736 if (required_length > 0) {
737 std::string output(
static_cast<std::size_t
>(required_length),
'\0');
738 if (WideCharToMultiByte(cp, flags, input_c, input_length, &output[0],
739 required_length,
nullptr,
nullptr) > 0) {
744 return std::string();
748inline int json_parse_c(
const char *s,
size_t sz,
const char *key,
size_t keysz,
749 const char **value,
size_t *valuesz) {
756 } state = JSON_STATE_VALUE;
757 const char *k =
nullptr;
765 if (key ==
nullptr) {
766 index =
static_cast<decltype(index)
>(keysz);
773 for (; sz > 0; s++, sz--) {
778 JSON_ACTION_START_STRUCT,
779 JSON_ACTION_END_STRUCT
780 } action = JSON_ACTION_NONE;
781 auto c =
static_cast<unsigned char>(*s);
783 case JSON_STATE_VALUE:
784 if (c ==
' ' || c ==
'\t' || c ==
'\n' || c ==
'\r' || c ==
',' ||
787 }
else if (c ==
'"') {
788 action = JSON_ACTION_START;
789 state = JSON_STATE_STRING;
790 }
else if (c ==
'{' || c ==
'[') {
791 action = JSON_ACTION_START_STRUCT;
792 }
else if (c ==
'}' || c ==
']') {
793 action = JSON_ACTION_END_STRUCT;
794 }
else if (c ==
't' || c ==
'f' || c ==
'n' || c ==
'-' ||
795 (c >=
'0' && c <=
'9')) {
796 action = JSON_ACTION_START;
797 state = JSON_STATE_LITERAL;
802 case JSON_STATE_LITERAL:
803 if (c ==
' ' || c ==
'\t' || c ==
'\n' || c ==
'\r' || c ==
',' ||
804 c ==
']' || c ==
'}' || c ==
':') {
805 state = JSON_STATE_VALUE;
808 action = JSON_ACTION_END;
809 }
else if (c < 32 || c > 126) {
812 case JSON_STATE_STRING:
813 if (c < 32 || (c > 126 && c < 192)) {
815 }
else if (c ==
'"') {
816 action = JSON_ACTION_END;
817 state = JSON_STATE_VALUE;
818 }
else if (c ==
'\\') {
819 state = JSON_STATE_ESCAPE;
820 }
else if (c >= 192 && c < 224) {
822 state = JSON_STATE_UTF8;
823 }
else if (c >= 224 && c < 240) {
825 state = JSON_STATE_UTF8;
826 }
else if (c >= 240 && c < 247) {
828 state = JSON_STATE_UTF8;
829 }
else if (c >= 128 && c < 192) {
833 case JSON_STATE_ESCAPE:
834 if (c ==
'"' || c ==
'\\' || c ==
'/' || c ==
'b' || c ==
'f' ||
835 c ==
'n' || c ==
'r' || c ==
't' || c ==
'u') {
836 state = JSON_STATE_STRING;
841 case JSON_STATE_UTF8:
842 if (c < 128 || c > 191) {
846 if (utf8_bytes == 0) {
847 state = JSON_STATE_STRING;
854 if (action == JSON_ACTION_END_STRUCT) {
859 if (action == JSON_ACTION_START || action == JSON_ACTION_START_STRUCT) {
862 }
else if (keysz > 0 && index == 1) {
867 }
else if (action == JSON_ACTION_END ||
868 action == JSON_ACTION_END_STRUCT) {
869 if (*value !=
nullptr && index == 0) {
870 *valuesz =
static_cast<size_t>(s + 1 - *value);
872 }
else if (keysz > 0 && k !=
nullptr) {
873 if (keysz ==
static_cast<size_t>(s - k - 1) &&
874 memcmp(key, k + 1, keysz) == 0) {
884 if (action == JSON_ACTION_START_STRUCT) {
891constexpr bool is_json_special_char(
char c) {
892 return c ==
'"' || c ==
'\\' || c ==
'\b' || c ==
'\f' || c ==
'\n' ||
893 c ==
'\r' || c ==
'\t';
896constexpr bool is_ascii_control_char(
char c) {
return c >= 0 && c <= 0x1f; }
898inline std::string json_escape(
const std::string &s,
bool add_quotes =
true) {
901 size_t required_length = add_quotes ? 2 : 0;
903 if (is_json_special_char(c)) {
905 required_length += 2;
908 if (is_ascii_control_char(c)) {
910 required_length += 6;
917 result.reserve(required_length);
923 if (is_json_special_char(c)) {
924 static constexpr char special_escape_table[256] =
925 "\0\0\0\0\0\0\0\0btn\0fr\0\0"
926 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
927 "\0\0\"\0\0\0\0\0\0\0\0\0\0\0\0\0"
928 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
929 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
930 "\0\0\0\0\0\0\0\0\0\0\0\0\\";
933 result += special_escape_table[
static_cast<unsigned char>(c)];
936 if (is_ascii_control_char(c)) {
938 static constexpr char hex_alphabet[]{
"0123456789abcdef"};
939 auto uc =
static_cast<unsigned char>(c);
940 auto h = (uc >> 4) & 0x0f;
944 result += hex_alphabet[h];
945 result += hex_alphabet[l];
955 assert(required_length == result.size());
959inline int json_unescape(
const char *s,
size_t n,
char *out) {
998 if (out !=
nullptr) {
1008 if (out !=
nullptr) {
1014inline std::string json_parse(
const std::string &s,
const std::string &key,
1019 json_parse_c(s.c_str(), s.length(),
nullptr, index, &value, &value_sz);
1021 json_parse_c(s.c_str(), s.length(), key.c_str(), key.length(), &value,
1024 if (value !=
nullptr) {
1025 if (value[0] !=
'"') {
1026 return {value, value_sz};
1028 int n = json_unescape(value, value_sz,
nullptr);
1030 char *decoded =
new char[n + 1];
1031 json_unescape(value, value_sz, decoded);
1032 std::string result(decoded, n);
1041template <
typename T>
class library_symbol {
1045 constexpr explicit library_symbol(
const char *name) : m_name(name) {}
1046 constexpr const char *get_name()
const {
return m_name; }
1054class native_library {
1056 native_library() =
default;
1058 explicit native_library(
const std::string &name)
1059 : m_handle{load_library(name)} {}
1062 explicit native_library(
const std::wstring &name)
1063 : m_handle{load_library(name)} {}
1069 FreeLibrary(m_handle);
1077 native_library(
const native_library &other) =
delete;
1078 native_library &operator=(
const native_library &other) =
delete;
1079 native_library(native_library &&other)
noexcept { *
this = std::move(other); }
1081 native_library &operator=(native_library &&other)
noexcept {
1082 if (
this == &other) {
1085 m_handle = other.m_handle;
1086 other.m_handle =
nullptr;
1091 operator bool()
const {
return is_loaded(); }
1094 template <
typename Symbol>
1095 typename Symbol::type get(
const Symbol &symbol)
const {
1100#pragma GCC diagnostic push
1101#pragma GCC diagnostic ignored "-Wcast-function-type"
1103 return reinterpret_cast<typename Symbol::type
>(
1104 GetProcAddress(m_handle, symbol.get_name()));
1106#pragma GCC diagnostic pop
1109 return reinterpret_cast<typename Symbol::type
>(
1110 dlsym(m_handle, symbol.get_name()));
1118 bool is_loaded()
const {
return !!m_handle; }
1120 void detach() { m_handle =
nullptr; }
1123 static inline bool is_loaded(
const std::string &name) {
1125 auto handle = GetModuleHandleW(widen_string(name).c_str());
1127 auto handle = dlopen(name.c_str(), RTLD_NOW | RTLD_NOLOAD);
1137 using mod_handle_t = HMODULE;
1139 using mod_handle_t =
void *;
1142 static inline mod_handle_t load_library(
const std::string &name) {
1144 return load_library(widen_string(name));
1146 return dlopen(name.c_str(), RTLD_NOW);
1151 static inline mod_handle_t load_library(
const std::wstring &name) {
1152 return LoadLibraryW(name.c_str());
1156 mod_handle_t m_handle{};
1159template <
typename WorkFn,
typename ResultFn>
1160webview_error_t api_filter(WorkFn &&do_work, ResultFn &&put_result)
noexcept {
1162 auto result = do_work();
1164 put_result(result.value());
1167 return result.error().code();
1168 }
catch (
const exception &e) {
1169 return e.error().code();
1175template <
typename WorkFn>
1178 auto result = do_work();
1182 return result.error().code();
1183 }
catch (
const exception &e) {
1184 return e.error().code();
1194 user_script(
const std::string &code, std::unique_ptr<impl> &&impl_)
1195 : m_code{code}, m_impl{std::move(impl_)} {}
1197 user_script(
const user_script &other) =
delete;
1198 user_script &operator=(
const user_script &other) =
delete;
1199 user_script(user_script &&other)
noexcept { *
this = std::move(other); }
1201 user_script &operator=(user_script &&other)
noexcept {
1202 if (
this == &other) {
1205 m_code = std::move(other.m_code);
1206 m_impl = std::move(other.m_impl);
1210 const std::string &get_code()
const {
return m_code; }
1212 impl &get_impl() {
return *m_impl; }
1214 const impl &get_impl()
const {
return *m_impl; }
1218 std::unique_ptr<impl> m_impl;
1223 virtual ~engine_base() =
default;
1225 noresult navigate(
const std::string &url) {
1227 return navigate_impl(
"about:blank");
1229 return navigate_impl(url);
1232 using binding_t = std::function<void(std::string, std::string,
void *)>;
1233 class binding_ctx_t {
1235 binding_ctx_t(binding_t callback,
void *arg)
1236 : m_callback(callback), m_arg(arg) {}
1237 void call(std::string
id, std::string args)
const {
1239 m_callback(
id, args, m_arg);
1245 binding_t m_callback;
1250 using sync_binding_t = std::function<std::string(std::string)>;
1253 noresult bind(
const std::string &name, sync_binding_t fn) {
1254 auto wrapper = [
this, fn](
const std::string &id,
const std::string &req,
1255 void * ) { resolve(
id, 0, fn(req)); };
1256 return bind(name, wrapper,
nullptr);
1260 noresult bind(
const std::string &name, binding_t fn,
void *arg) {
1262 if (bindings.count(name) > 0) {
1265 bindings.emplace(name, binding_ctx_t(fn, arg));
1266 replace_bind_script();
1269 eval(
"if (window.__webview__) {\n\
1270window.__webview__.onBind(" +
1271 json_escape(name) +
")\n\
1276 noresult unbind(
const std::string &name) {
1277 auto found = bindings.find(name);
1278 if (found == bindings.end()) {
1281 bindings.erase(found);
1282 replace_bind_script();
1285 eval(
"if (window.__webview__) {\n\
1286window.__webview__.onUnbind(" +
1287 json_escape(name) +
")\n\
1292 noresult resolve(
const std::string &
id,
int status,
1293 const std::string &result) {
1295 return dispatch(std::bind(
1296 [
id, status,
this](std::string escaped_result) {
1297 std::string js =
"window.__webview__.onReply(" + json_escape(
id) +
1298 ", " + std::to_string(status) +
", " +
1299 escaped_result +
")";
1302 result.empty() ?
"undefined" : json_escape(result)));
1305 result<void *> window() {
return window_impl(); }
1306 result<void *> widget() {
return widget_impl(); }
1307 result<void *> browser_controller() {
return browser_controller_impl(); }
1308 noresult run() {
return run_impl(); }
1309 noresult terminate() {
return terminate_impl(); }
1310 noresult dispatch(std::function<
void()> f) {
return dispatch_impl(f); }
1311 noresult set_title(
const std::string &title) {
return set_title_impl(title); }
1314 return set_size_impl(width, height, hints);
1317 noresult set_html(
const std::string &html) {
return set_html_impl(html); }
1319 noresult init(
const std::string &js) {
1320 add_user_script(js);
1324 noresult eval(
const std::string &js) {
return eval_impl(js); }
1327 virtual noresult navigate_impl(
const std::string &url) = 0;
1328 virtual result<void *> window_impl() = 0;
1329 virtual result<void *> widget_impl() = 0;
1330 virtual result<void *> browser_controller_impl() = 0;
1331 virtual noresult run_impl() = 0;
1332 virtual noresult terminate_impl() = 0;
1333 virtual noresult dispatch_impl(std::function<
void()> f) = 0;
1334 virtual noresult set_title_impl(
const std::string &title) = 0;
1335 virtual noresult set_size_impl(
int width,
int height,
1337 virtual noresult set_html_impl(
const std::string &html) = 0;
1338 virtual noresult eval_impl(
const std::string &js) = 0;
1340 virtual user_script *add_user_script(
const std::string &js) {
1341 return std::addressof(*m_user_scripts.emplace(m_user_scripts.end(),
1342 add_user_script_impl(js)));
1345 virtual user_script add_user_script_impl(
const std::string &js) = 0;
1348 remove_all_user_scripts_impl(
const std::list<user_script> &scripts) = 0;
1350 virtual bool are_user_scripts_equal_impl(
const user_script &first,
1351 const user_script &second) = 0;
1353 virtual user_script *replace_user_script(
const user_script &old_script,
1354 const std::string &new_script_code) {
1355 remove_all_user_scripts_impl(m_user_scripts);
1356 user_script *old_script_ptr{};
1357 for (
auto &script : m_user_scripts) {
1358 auto is_old_script = are_user_scripts_equal_impl(script, old_script);
1359 script = add_user_script_impl(is_old_script ? new_script_code
1360 : script.get_code());
1361 if (is_old_script) {
1362 old_script_ptr = std::addressof(script);
1365 return old_script_ptr;
1368 void replace_bind_script() {
1369 if (m_bind_script) {
1370 m_bind_script = replace_user_script(*m_bind_script, create_bind_script());
1372 m_bind_script = add_user_script(create_bind_script());
1376 void add_init_script(
const std::string &post_fn) {
1377 add_user_script(create_init_script(post_fn));
1380 std::string create_init_script(
const std::string &post_fn) {
1381 auto js = std::string{} +
"(function() {\n\
1383 function generateId() {\n\
1384 var crypto = window.crypto || window.msCrypto;\n\
1385 var bytes = new Uint8Array(16);\n\
1386 crypto.getRandomValues(bytes);\n\
1387 return Array.prototype.slice.call(bytes).map(function(n) {\n\
1388 return n.toString(16).padStart(2, '0');\n\
1391 var Webview = (function() {\n\
1392 var _promises = {};\n\
1393 function Webview_() {}\n\
1394 Webview_.prototype.post = function(message) {\n\
1396 post_fn +
")(message);\n\
1398 Webview_.prototype.call = function(method) {\n\
1399 var _id = generateId();\n\
1400 var _params = Array.prototype.slice.call(arguments, 1);\n\
1401 var promise = new Promise(function(resolve, reject) {\n\
1402 _promises[_id] = { resolve, reject };\n\
1404 this.post(JSON.stringify({\n\
1411 Webview_.prototype.onReply = function(id, status, result) {\n\
1412 var promise = _promises[id];\n\
1413 if (result !== undefined) {\n\
1415 result = JSON.parse(result);\n\
1417 promise.reject(new Error(\"Failed to parse binding result as JSON\"));\n\
1421 if (status === 0) {\n\
1422 promise.resolve(result);\n\
1424 promise.reject(result);\n\
1427 Webview_.prototype.onBind = function(name) {\n\
1428 if (Object.hasOwn(window, name)) {\n\
1429 throw new Error('Property \"' + name + '\" already exists');\n\
1431 window[name] = (function() {\n\
1432 var params = [name].concat(Array.prototype.slice.call(arguments));\n\
1433 return Webview_.prototype.call.apply(this, params);\n\
1436 Webview_.prototype.onUnbind = function(name) {\n\
1437 if (!Object.hasOwn(window, name)) {\n\
1438 throw new Error('Property \"' + name + '\" does not exist');\n\
1440 delete window[name];\n\
1444 window.__webview__ = new Webview();\n\
1449 std::string create_bind_script() {
1450 std::string js_names =
"[";
1452 for (
const auto &binding : bindings) {
1458 js_names += json_escape(binding.first);
1462 auto js = std::string{} +
"(function() {\n\
1466 methods.forEach(function(name) {\n\
1467 window.__webview__.onBind(name);\n\
1473 virtual void on_message(
const std::string &msg) {
1474 auto id = json_parse(msg,
"id", 0);
1475 auto name = json_parse(msg,
"method", 0);
1476 auto args = json_parse(msg,
"params", 0);
1477 auto found = bindings.find(name);
1478 if (found == bindings.end()) {
1481 const auto &context = found->second;
1482 dispatch([=] { context.call(
id, args); });
1485 virtual void on_window_created() { inc_window_count(); }
1487 virtual void on_window_destroyed(
bool skip_termination =
false) {
1488 if (dec_window_count() <= 0) {
1489 if (!skip_termination) {
1496 static std::atomic_uint &window_ref_count() {
1497 static std::atomic_uint ref_count{0};
1501 static unsigned int inc_window_count() {
return ++window_ref_count(); }
1503 static unsigned int dec_window_count() {
1504 auto &count = window_ref_count();
1511 std::map<std::string, binding_ctx_t> bindings;
1512 user_script *m_bind_script{};
1513 std::list<user_script> m_user_scripts;
1518WEBVIEW_DEPRECATED_PRIVATE
1519inline int json_parse_c(
const char *s,
size_t sz,
const char *key,
size_t keysz,
1520 const char **value,
size_t *valuesz) {
1521 return detail::json_parse_c(s, sz, key, keysz, value, valuesz);
1524WEBVIEW_DEPRECATED_PRIVATE
1525inline std::string json_escape(
const std::string &s) {
1526 return detail::json_escape(s);
1529WEBVIEW_DEPRECATED_PRIVATE
1530inline int json_unescape(
const char *s,
size_t n,
char *out) {
1531 return detail::json_unescape(s, n, out);
1534WEBVIEW_DEPRECATED_PRIVATE
1535inline std::string json_parse(
const std::string &s,
const std::string &key,
1537 return detail::json_parse(s, key, index);
1542#if defined(WEBVIEW_GTK)
1559#if GTK_MAJOR_VERSION >= 4
1562#include <webkit/webkit.h>
1564#ifdef GDK_WINDOWING_X11
1565#include <gdk/x11/gdkx.h>
1568#elif GTK_MAJOR_VERSION >= 3
1570#include <JavaScriptCore/JavaScript.h>
1571#include <webkit2/webkit2.h>
1573#ifdef GDK_WINDOWING_X11
1574#include <gdk/gdkx.h>
1580#include <sys/stat.h>
1589namespace webkit_dmabuf {
1592static inline std::string get_env(
const std::string &name) {
1593 auto *value = std::getenv(name.c_str());
1601static inline void set_env(
const std::string &name,
const std::string &value) {
1602 ::setenv(name.c_str(), value.c_str(), 1);
1607static inline bool is_using_nvidia_driver() {
1608 struct ::stat buffer {};
1609 if (::stat(
"/sys/module/nvidia", &buffer) != 0) {
1612 return S_ISDIR(buffer.st_mode);
1616static inline bool is_wayland_display() {
1617 if (!get_env(
"WAYLAND_DISPLAY").empty()) {
1620 if (get_env(
"XDG_SESSION_TYPE") ==
"wayland") {
1623 if (get_env(
"DESKTOP_SESSION").find(
"wayland") != std::string::npos) {
1631static inline bool is_gdk_x11_backend() {
1632#ifdef GDK_WINDOWING_X11
1633 auto *gdk_display = gdk_display_get_default();
1634 return GDK_IS_X11_DISPLAY(gdk_display);
1648static inline bool is_webkit_dmabuf_bugged() {
1649 auto wk_major = webkit_get_major_version();
1650 auto wk_minor = webkit_get_minor_version();
1652 auto is_affected_wk_version = wk_major == 2 && wk_minor >= 42;
1653 if (!is_affected_wk_version) {
1656 if (!get_env(
"WEBKIT_DISABLE_DMABUF_RENDERER").empty()) {
1659 if (is_wayland_display()) {
1662 if (!is_gdk_x11_backend()) {
1665 if (!is_using_nvidia_driver()) {
1673static inline void apply_webkit_dmabuf_workaround() {
1674 if (!is_webkit_dmabuf_bugged()) {
1677 set_env(
"WEBKIT_DISABLE_DMABUF_RENDERER",
"1");
1681class user_script::impl {
1683 impl(WebKitUserScript *script) : m_script{script} {
1684 webkit_user_script_ref(script);
1687 ~impl() { webkit_user_script_unref(m_script); }
1689 impl(
const impl &) =
delete;
1690 impl &operator=(
const impl &) =
delete;
1691 impl(impl &&) =
delete;
1692 impl &operator=(impl &&) =
delete;
1694 WebKitUserScript *get_native()
const {
return m_script; }
1697 WebKitUserScript *m_script{};
1705 static gboolean init_check() {
1706#if GTK_MAJOR_VERSION >= 4
1707 return gtk_init_check();
1709 return gtk_init_check(
nullptr,
nullptr);
1713 static GtkWidget *window_new() {
1714#if GTK_MAJOR_VERSION >= 4
1715 return gtk_window_new();
1717 return gtk_window_new(GTK_WINDOW_TOPLEVEL);
1721 static void window_set_child(GtkWindow *window, GtkWidget *widget) {
1722#if GTK_MAJOR_VERSION >= 4
1723 gtk_window_set_child(window, widget);
1725 gtk_container_add(GTK_CONTAINER(window), widget);
1729 static void window_remove_child(GtkWindow *window, GtkWidget *widget) {
1730#if GTK_MAJOR_VERSION >= 4
1731 if (gtk_window_get_child(window) == widget) {
1732 gtk_window_set_child(window,
nullptr);
1735 gtk_container_remove(GTK_CONTAINER(window), widget);
1739 static void widget_set_visible(GtkWidget *widget,
bool visible) {
1740#if GTK_MAJOR_VERSION >= 4
1741 gtk_widget_set_visible(widget, visible ? TRUE : FALSE);
1744 gtk_widget_show(widget);
1746 gtk_widget_hide(widget);
1751 static void window_set_size(GtkWindow *window,
int width,
int height) {
1752#if GTK_MAJOR_VERSION >= 4
1753 gtk_window_set_default_size(window, width, height);
1755 gtk_window_resize(window, width, height);
1759 static void window_set_max_size(GtkWindow *window,
int width,
int height) {
1761#if GTK_MAJOR_VERSION < 4
1763 g.max_width = width;
1764 g.max_height = height;
1765 GdkWindowHints h = GDK_HINT_MAX_SIZE;
1766 gtk_window_set_geometry_hints(GTK_WINDOW(window),
nullptr, &g, h);
1779class webkitgtk_compat {
1781#if GTK_MAJOR_VERSION >= 4
1782 using wk_handler_js_value_t = JSCValue;
1784 using wk_handler_js_value_t = WebKitJavascriptResult;
1787 using on_script_message_received_t =
1788 std::function<void(WebKitUserContentManager *,
const std::string &)>;
1790 connect_script_message_received(WebKitUserContentManager *manager,
1791 const std::string &handler_name,
1792 on_script_message_received_t handler) {
1793 std::string signal_name =
"script-message-received::";
1794 signal_name += handler_name;
1796 auto callback = +[](WebKitUserContentManager *manager,
1797 wk_handler_js_value_t *r, gpointer arg) {
1798 auto *handler =
static_cast<on_script_message_received_t *
>(arg);
1799 (*handler)(manager, get_string_from_js_result(r));
1802 auto deleter = +[](gpointer data, GClosure *) {
1803 delete static_cast<on_script_message_received_t *
>(data);
1806 g_signal_connect_data(manager, signal_name.c_str(), G_CALLBACK(callback),
1807 new on_script_message_received_t{handler}, deleter,
1808 static_cast<GConnectFlags
>(0) );
1811 static std::string get_string_from_js_result(JSCValue *r) {
1812 char *cs = jsc_value_to_string(r);
1818#if GTK_MAJOR_VERSION < 4
1819 static std::string get_string_from_js_result(WebKitJavascriptResult *r) {
1820#if (WEBKIT_MAJOR_VERSION == 2 && WEBKIT_MINOR_VERSION >= 22) || \
1821 WEBKIT_MAJOR_VERSION > 2
1822 JSCValue *value = webkit_javascript_result_get_js_value(r);
1823 return get_string_from_js_result(value);
1825 JSGlobalContextRef ctx = webkit_javascript_result_get_global_context(r);
1826 JSValueRef value = webkit_javascript_result_get_value(r);
1827 JSStringRef js = JSValueToStringCopy(ctx, value,
nullptr);
1828 size_t n = JSStringGetMaximumUTF8CStringSize(js);
1829 char *cs = g_new(
char, n);
1830 JSStringGetUTF8CString(js, cs, n);
1831 JSStringRelease(js);
1839 static void user_content_manager_register_script_message_handler(
1840 WebKitUserContentManager *manager,
const gchar *name) {
1841#if GTK_MAJOR_VERSION >= 4
1842 webkit_user_content_manager_register_script_message_handler(manager, name,
1845 webkit_user_content_manager_register_script_message_handler(manager, name);
1850class gtk_webkit_engine :
public engine_base {
1852 gtk_webkit_engine(
bool debug,
void *window)
1853 : m_owns_window{!window}, m_window(static_cast<GtkWidget *>(window)) {
1854 if (m_owns_window) {
1855 if (!gtk_compat::init_check()) {
1858 m_window = gtk_compat::window_new();
1859 on_window_created();
1860 auto on_window_destroy = +[](GtkWidget *, gpointer arg) {
1861 auto *w =
static_cast<gtk_webkit_engine *
>(arg);
1862 w->m_window =
nullptr;
1863 w->on_window_destroyed();
1865 g_signal_connect(G_OBJECT(m_window),
"destroy",
1866 G_CALLBACK(on_window_destroy),
this);
1868 webkit_dmabuf::apply_webkit_dmabuf_workaround();
1870 m_webview = webkit_web_view_new();
1871 g_object_ref_sink(m_webview);
1872 WebKitUserContentManager *manager = m_user_content_manager =
1873 webkit_web_view_get_user_content_manager(WEBKIT_WEB_VIEW(m_webview));
1874 webkitgtk_compat::connect_script_message_received(
1875 manager,
"__webview__",
1876 [
this](WebKitUserContentManager *,
const std::string &r) {
1879 webkitgtk_compat::user_content_manager_register_script_message_handler(
1880 manager,
"__webview__");
1881 add_init_script(
"function(message) {\n\
1882 return window.webkit.messageHandlers.__webview__.postMessage(message);\n\
1885 gtk_compat::window_set_child(GTK_WINDOW(m_window), GTK_WIDGET(m_webview));
1886 gtk_compat::widget_set_visible(GTK_WIDGET(m_webview),
true);
1888 WebKitSettings *settings =
1889 webkit_web_view_get_settings(WEBKIT_WEB_VIEW(m_webview));
1890 webkit_settings_set_javascript_can_access_clipboard(settings,
true);
1892 webkit_settings_set_enable_write_console_messages_to_stdout(settings,
1894 webkit_settings_set_enable_developer_extras(settings,
true);
1897 if (m_owns_window) {
1898 gtk_widget_grab_focus(GTK_WIDGET(m_webview));
1899 gtk_compat::widget_set_visible(GTK_WIDGET(m_window),
true);
1903 gtk_webkit_engine(
const gtk_webkit_engine &) =
delete;
1904 gtk_webkit_engine &operator=(
const gtk_webkit_engine &) =
delete;
1905 gtk_webkit_engine(gtk_webkit_engine &&) =
delete;
1906 gtk_webkit_engine &operator=(gtk_webkit_engine &&) =
delete;
1908 virtual ~gtk_webkit_engine() {
1910 if (m_owns_window) {
1912 g_signal_handlers_disconnect_by_data(GTK_WINDOW(m_window),
this);
1913 gtk_window_close(GTK_WINDOW(m_window));
1914 on_window_destroyed(
true);
1916 gtk_compat::window_remove_child(GTK_WINDOW(m_window),
1917 GTK_WIDGET(m_webview));
1921 g_object_unref(m_webview);
1923 if (m_owns_window) {
1925 deplete_run_loop_event_queue();
1930 result<void *> window_impl()
override {
1937 result<void *> widget_impl()
override {
1944 result<void *> browser_controller_impl()
override {
1951 noresult run_impl()
override {
1952 m_stop_run_loop =
false;
1953 while (!m_stop_run_loop) {
1954 g_main_context_iteration(
nullptr, TRUE);
1959 noresult terminate_impl()
override {
1960 return dispatch_impl([&] { m_stop_run_loop =
true; });
1963 noresult dispatch_impl(std::function<
void()> f)
override {
1964 g_idle_add_full(G_PRIORITY_HIGH_IDLE, (GSourceFunc)([](
void *fn) ->
int {
1965 (*
static_cast<dispatch_fn_t *
>(fn))();
1966 return G_SOURCE_REMOVE;
1968 new std::function<void()>(f),
1969 [](
void *fn) {
delete static_cast<dispatch_fn_t *
>(fn); });
1973 noresult set_title_impl(
const std::string &title)
override {
1974 gtk_window_set_title(GTK_WINDOW(m_window), title.c_str());
1978 noresult set_size_impl(
int width,
int height,
webview_hint_t hints)
override {
1981 gtk_compat::window_set_size(GTK_WINDOW(m_window), width, height);
1983 gtk_widget_set_size_request(m_window, width, height);
1985 gtk_compat::window_set_max_size(GTK_WINDOW(m_window), width, height);
1990 noresult navigate_impl(
const std::string &url)
override {
1991 webkit_web_view_load_uri(WEBKIT_WEB_VIEW(m_webview), url.c_str());
1995 noresult set_html_impl(
const std::string &html)
override {
1996 webkit_web_view_load_html(WEBKIT_WEB_VIEW(m_webview), html.c_str(),
2001 noresult eval_impl(
const std::string &js)
override {
2003 if (!webkit_web_view_get_uri(WEBKIT_WEB_VIEW(m_webview))) {
2006#if (WEBKIT_MAJOR_VERSION == 2 && WEBKIT_MINOR_VERSION >= 40) || \
2007 WEBKIT_MAJOR_VERSION > 2
2008 webkit_web_view_evaluate_javascript(WEBKIT_WEB_VIEW(m_webview), js.c_str(),
2009 static_cast<gssize
>(js.size()),
nullptr,
2010 nullptr,
nullptr,
nullptr,
nullptr);
2012 webkit_web_view_run_javascript(WEBKIT_WEB_VIEW(m_webview), js.c_str(),
2013 nullptr,
nullptr,
nullptr);
2018 user_script add_user_script_impl(
const std::string &js)
override {
2019 auto *wk_script = webkit_user_script_new(
2020 js.c_str(), WEBKIT_USER_CONTENT_INJECT_TOP_FRAME,
2021 WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_START,
nullptr,
nullptr);
2022 webkit_user_content_manager_add_script(m_user_content_manager, wk_script);
2023 user_script script{js, std::unique_ptr<user_script::impl>{
2024 new user_script::impl{wk_script}}};
2025 webkit_user_script_unref(wk_script);
2029 void remove_all_user_scripts_impl(
2030 const std::list<user_script> & )
override {
2031 webkit_user_content_manager_remove_all_scripts(m_user_content_manager);
2034 bool are_user_scripts_equal_impl(
const user_script &first,
2035 const user_script &second)
override {
2036 auto *wk_first = first.get_impl().get_native();
2037 auto *wk_second = second.get_impl().get_native();
2038 return wk_first == wk_second;
2042#if GTK_MAJOR_VERSION >= 4
2043 static char *get_string_from_js_result(JSCValue *r) {
2044 return jsc_value_to_string(r);
2047 static char *get_string_from_js_result(WebKitJavascriptResult *r) {
2049#if (WEBKIT_MAJOR_VERSION == 2 && WEBKIT_MINOR_VERSION >= 22) || \
2050 WEBKIT_MAJOR_VERSION > 2
2051 JSCValue *value = webkit_javascript_result_get_js_value(r);
2052 s = jsc_value_to_string(value);
2054 JSGlobalContextRef ctx = webkit_javascript_result_get_global_context(r);
2055 JSValueRef value = webkit_javascript_result_get_value(r);
2056 JSStringRef js = JSValueToStringCopy(ctx, value,
nullptr);
2057 size_t n = JSStringGetMaximumUTF8CStringSize(js);
2059 JSStringGetUTF8CString(js, s, n);
2060 JSStringRelease(js);
2067 void deplete_run_loop_event_queue() {
2069 dispatch([&] { done =
true; });
2071 g_main_context_iteration(
nullptr, TRUE);
2075 bool m_owns_window{};
2076 GtkWidget *m_window{};
2077 GtkWidget *m_webview{};
2078 WebKitUserContentManager *m_user_content_manager{};
2079 bool m_stop_run_loop{};
2084using browser_engine = detail::gtk_webkit_engine;
2088#elif defined(WEBVIEW_COCOA)
2100#include <CoreGraphics/CoreGraphics.h>
2101#include <objc/NSObjCRuntime.h>
2102#include <objc/objc-runtime.h>
2113template <
typename Result,
typename Callable,
typename... Args>
2114Result invoke(Callable callable, Args... args)
noexcept {
2115 return reinterpret_cast<Result (*)(Args...)
>(callable)(args...);
2119template <
typename Result,
typename... Args>
2120Result msg_send(Args... args)
noexcept {
2121 return invoke<Result>(objc_msgSend, args...);
2125class autoreleasepool {
2128 : m_pool(msg_send<id>(objc_getClass(
"NSAutoreleasePool"),
2129 sel_registerName(
"new"))) {}
2131 ~autoreleasepool() {
2133 msg_send<void>(m_pool, sel_registerName(
"drain"));
2137 autoreleasepool(
const autoreleasepool &) =
delete;
2138 autoreleasepool &operator=(
const autoreleasepool &) =
delete;
2139 autoreleasepool(autoreleasepool &&) =
delete;
2140 autoreleasepool &operator=(autoreleasepool &&) =
delete;
2146inline id autoreleased(
id object) {
2147 msg_send<void>(
object, sel_registerName(
"autorelease"));
2153enum NSBackingStoreType : NSUInteger { NSBackingStoreBuffered = 2 };
2155enum NSWindowStyleMask : NSUInteger {
2156 NSWindowStyleMaskTitled = 1,
2157 NSWindowStyleMaskClosable = 2,
2158 NSWindowStyleMaskMiniaturizable = 4,
2159 NSWindowStyleMaskResizable = 8
2162enum NSApplicationActivationPolicy : NSInteger {
2163 NSApplicationActivationPolicyRegular = 0
2166enum WKUserScriptInjectionTime : NSInteger {
2167 WKUserScriptInjectionTimeAtDocumentStart = 0
2170enum NSModalResponse : NSInteger { NSModalResponseOK = 1 };
2173inline id operator"" _cls(
const char *s, std::size_t) {
2174 return (
id)objc_getClass(s);
2176inline SEL operator"" _sel(
const char *s, std::size_t) {
2177 return sel_registerName(s);
2179inline id operator"" _str(
const char *s, std::size_t) {
2180 return objc::msg_send<id>(
"NSString"_cls,
"stringWithUTF8String:"_sel, s);
2183class user_script::impl {
2185 impl(
id script) : m_script{script} {
2186 objc::msg_send<void>(script,
"retain"_sel);
2189 ~impl() { objc::msg_send<void>(m_script,
"release"_sel); }
2191 impl(
const impl &) =
delete;
2192 impl &operator=(
const impl &) =
delete;
2193 impl(impl &&) =
delete;
2194 impl &operator=(impl &&) =
delete;
2196 id get_native()
const {
return m_script; }
2202class cocoa_wkwebview_engine :
public engine_base {
2204 cocoa_wkwebview_engine(
bool debug,
void *window)
2206 m_window{static_cast<id>(window)},
2207 m_owns_window{!window} {
2208 auto app = get_shared_application();
2210 if (!m_owns_window) {
2214 auto delegate = objc::msg_send<id>(app,
"delegate"_sel);
2218 m_app_delegate = create_app_delegate();
2219 objc_setAssociatedObject(m_app_delegate,
"webview", (
id)
this,
2220 OBJC_ASSOCIATION_ASSIGN);
2221 objc::msg_send<void>(app,
"setDelegate:"_sel, m_app_delegate);
2231 if (get_and_set_is_first_instance()) {
2232 objc::msg_send<void>(app,
"run"_sel);
2240 cocoa_wkwebview_engine(
const cocoa_wkwebview_engine &) =
delete;
2241 cocoa_wkwebview_engine &operator=(
const cocoa_wkwebview_engine &) =
delete;
2242 cocoa_wkwebview_engine(cocoa_wkwebview_engine &&) =
delete;
2243 cocoa_wkwebview_engine &operator=(cocoa_wkwebview_engine &&) =
delete;
2245 virtual ~cocoa_wkwebview_engine() {
2246 objc::autoreleasepool arp;
2249 if (
auto ui_delegate =
2250 objc::msg_send<id>(m_webview,
"UIDelegate"_sel)) {
2251 objc::msg_send<void>(m_webview,
"setUIDelegate:"_sel,
nullptr);
2252 objc::msg_send<void>(ui_delegate,
"release"_sel);
2254 if (m_webview == objc::msg_send<id>(m_window,
"contentView"_sel)) {
2255 objc::msg_send<void>(m_window,
"setContentView:"_sel,
nullptr);
2257 objc::msg_send<void>(m_webview,
"release"_sel);
2258 m_webview =
nullptr;
2260 if (m_owns_window) {
2263 objc::msg_send<void>(m_window,
"setDelegate:"_sel,
nullptr);
2264 objc::msg_send<void>(m_window,
"close"_sel);
2265 on_window_destroyed(
true);
2269 if (m_window_delegate) {
2270 objc::msg_send<void>(m_window_delegate,
"release"_sel);
2271 m_window_delegate =
nullptr;
2273 if (m_app_delegate) {
2274 auto app = get_shared_application();
2275 objc::msg_send<void>(app,
"setDelegate:"_sel,
nullptr);
2277 objc::msg_send<void>(m_app_delegate,
"release"_sel);
2278 m_app_delegate =
nullptr;
2280 if (m_owns_window) {
2282 deplete_run_loop_event_queue();
2289 result<void *> window_impl()
override {
2296 result<void *> widget_impl()
override {
2303 result<void *> browser_controller_impl()
override {
2310 noresult terminate_impl()
override {
2315 noresult run_impl()
override {
2316 auto app = get_shared_application();
2317 objc::msg_send<void>(app,
"run"_sel);
2321 noresult dispatch_impl(std::function<
void()> f)
override {
2322 dispatch_async_f(dispatch_get_main_queue(),
new dispatch_fn_t(f),
2323 (dispatch_function_t)([](
void *arg) {
2324 auto f =
static_cast<dispatch_fn_t *
>(arg);
2331 noresult set_title_impl(
const std::string &title)
override {
2332 objc::autoreleasepool arp;
2334 objc::msg_send<void>(m_window,
"setTitle:"_sel,
2335 objc::msg_send<id>(
"NSString"_cls,
2336 "stringWithUTF8String:"_sel,
2341 noresult set_size_impl(
int width,
int height,
webview_hint_t hints)
override {
2342 objc::autoreleasepool arp;
2344 auto style =
static_cast<NSWindowStyleMask
>(
2345 NSWindowStyleMaskTitled | NSWindowStyleMaskClosable |
2346 NSWindowStyleMaskMiniaturizable);
2349 static_cast<NSWindowStyleMask
>(style | NSWindowStyleMaskResizable);
2351 objc::msg_send<void>(m_window,
"setStyleMask:"_sel, style);
2354 objc::msg_send<void>(m_window,
"setContentMinSize:"_sel,
2355 CGSizeMake(width, height));
2357 objc::msg_send<void>(m_window,
"setContentMaxSize:"_sel,
2358 CGSizeMake(width, height));
2360 objc::msg_send<void>(m_window,
"setFrame:display:animate:"_sel,
2361 CGRectMake(0, 0, width, height), YES, NO);
2363 objc::msg_send<void>(m_window,
"center"_sel);
2367 noresult navigate_impl(
const std::string &url)
override {
2368 objc::autoreleasepool arp;
2370 auto nsurl = objc::msg_send<id>(
2371 "NSURL"_cls,
"URLWithString:"_sel,
2372 objc::msg_send<id>(
"NSString"_cls,
"stringWithUTF8String:"_sel,
2375 objc::msg_send<void>(
2376 m_webview,
"loadRequest:"_sel,
2377 objc::msg_send<id>(
"NSURLRequest"_cls,
"requestWithURL:"_sel, nsurl));
2381 noresult set_html_impl(
const std::string &html)
override {
2382 objc::autoreleasepool arp;
2383 objc::msg_send<void>(m_webview,
"loadHTMLString:baseURL:"_sel,
2384 objc::msg_send<id>(
"NSString"_cls,
2385 "stringWithUTF8String:"_sel,
2390 noresult eval_impl(
const std::string &js)
override {
2391 objc::autoreleasepool arp;
2393 auto nsurl = objc::msg_send<id>(m_webview,
"URL"_sel);
2397 objc::msg_send<void>(m_webview,
"evaluateJavaScript:completionHandler:"_sel,
2398 objc::msg_send<id>(
"NSString"_cls,
2399 "stringWithUTF8String:"_sel,
2405 user_script add_user_script_impl(
const std::string &js)
override {
2406 objc::autoreleasepool arp;
2407 auto wk_script = objc::msg_send<id>(
2408 objc::msg_send<id>(
"WKUserScript"_cls,
"alloc"_sel),
2409 "initWithSource:injectionTime:forMainFrameOnly:"_sel,
2410 objc::msg_send<id>(
"NSString"_cls,
"stringWithUTF8String:"_sel,
2412 WKUserScriptInjectionTimeAtDocumentStart, YES);
2414 objc::msg_send<void>(m_manager,
"addUserScript:"_sel, wk_script);
2415 user_script script{js, std::unique_ptr<user_script::impl>{
2416 new user_script::impl{wk_script}}};
2417 objc::msg_send<void>(wk_script,
"release"_sel);
2421 void remove_all_user_scripts_impl(
2422 const std::list<user_script> & )
override {
2423 objc::autoreleasepool arp;
2425 objc::msg_send<id>(m_manager,
"removeAllUserScripts"_sel);
2428 bool are_user_scripts_equal_impl(
const user_script &first,
2429 const user_script &second)
override {
2430 auto *wk_first = first.get_impl().get_native();
2431 auto *wk_second = second.get_impl().get_native();
2432 return wk_first == wk_second;
2436 id create_app_delegate() {
2437 objc::autoreleasepool arp;
2438 constexpr auto class_name =
"WebviewAppDelegate";
2440 auto cls = objc_lookUpClass(class_name);
2445 cls = objc_allocateClassPair((Class)
"NSResponder"_cls, class_name, 0);
2446 class_addProtocol(cls, objc_getProtocol(
"NSTouchBarProvider"));
2447 class_addMethod(cls,
2448 "applicationShouldTerminateAfterLastWindowClosed:"_sel,
2449 (IMP)(+[](
id,
SEL,
id) -> BOOL {
return NO; }),
"c@:@");
2450 class_addMethod(cls,
"applicationDidFinishLaunching:"_sel,
2451 (IMP)(+[](
id self,
SEL,
id notification) {
2453 objc::msg_send<id>(notification,
"object"_sel);
2454 auto w = get_associated_webview(self);
2455 w->on_application_did_finish_launching(self, app);
2458 objc_registerClassPair(cls);
2460 return objc::msg_send<id>((
id)cls,
"new"_sel);
2462 id create_script_message_handler() {
2463 objc::autoreleasepool arp;
2464 constexpr auto class_name =
"WebviewWKScriptMessageHandler";
2466 auto cls = objc_lookUpClass(class_name);
2468 cls = objc_allocateClassPair((Class)
"NSResponder"_cls, class_name, 0);
2469 class_addProtocol(cls, objc_getProtocol(
"WKScriptMessageHandler"));
2471 cls,
"userContentController:didReceiveScriptMessage:"_sel,
2472 (IMP)(+[](
id self,
SEL,
id,
id msg) {
2473 auto w = get_associated_webview(self);
2474 w->on_message(objc::msg_send<const char *>(
2475 objc::msg_send<id>(msg,
"body"_sel),
"UTF8String"_sel));
2478 objc_registerClassPair(cls);
2480 auto instance = objc::msg_send<id>((
id)cls,
"new"_sel);
2481 objc_setAssociatedObject(instance,
"webview", (
id)
this,
2482 OBJC_ASSOCIATION_ASSIGN);
2485 static id create_webkit_ui_delegate() {
2486 objc::autoreleasepool arp;
2487 constexpr auto class_name =
"WebviewWKUIDelegate";
2489 auto cls = objc_lookUpClass(class_name);
2491 cls = objc_allocateClassPair((Class)
"NSObject"_cls, class_name, 0);
2492 class_addProtocol(cls, objc_getProtocol(
"WKUIDelegate"));
2495 "webView:runOpenPanelWithParameters:initiatedByFrame:completionHandler:"_sel,
2496 (IMP)(+[](
id,
SEL,
id,
id parameters,
id,
id completion_handler) {
2497 auto allows_multiple_selection =
2498 objc::msg_send<BOOL>(parameters,
"allowsMultipleSelection"_sel);
2499 auto allows_directories =
2500 objc::msg_send<BOOL>(parameters,
"allowsDirectories"_sel);
2503 auto panel = objc::msg_send<id>(
"NSOpenPanel"_cls,
"openPanel"_sel);
2504 objc::msg_send<void>(panel,
"setCanChooseFiles:"_sel, YES);
2505 objc::msg_send<void>(panel,
"setCanChooseDirectories:"_sel,
2506 allows_directories);
2507 objc::msg_send<void>(panel,
"setAllowsMultipleSelection:"_sel,
2508 allows_multiple_selection);
2509 auto modal_response =
2510 objc::msg_send<NSModalResponse>(panel,
"runModal"_sel);
2515 id urls = modal_response == NSModalResponseOK
2516 ? objc::msg_send<id>(panel,
"URLs"_sel)
2520 auto sig = objc::msg_send<id>(
2521 "NSMethodSignature"_cls,
"signatureWithObjCTypes:"_sel,
"v@?@");
2522 auto invocation = objc::msg_send<id>(
2523 "NSInvocation"_cls,
"invocationWithMethodSignature:"_sel, sig);
2524 objc::msg_send<void>(invocation,
"setTarget:"_sel,
2525 completion_handler);
2526 objc::msg_send<void>(invocation,
"setArgument:atIndex:"_sel, &urls,
2528 objc::msg_send<void>(invocation,
"invoke"_sel);
2531 objc_registerClassPair(cls);
2533 return objc::msg_send<id>((
id)cls,
"new"_sel);
2535 static id create_window_delegate() {
2536 objc::autoreleasepool arp;
2537 constexpr auto class_name =
"WebviewNSWindowDelegate";
2539 auto cls = objc_lookUpClass(class_name);
2541 cls = objc_allocateClassPair((Class)
"NSObject"_cls, class_name, 0);
2542 class_addProtocol(cls, objc_getProtocol(
"NSWindowDelegate"));
2543 class_addMethod(cls,
"windowWillClose:"_sel,
2544 (IMP)(+[](
id self,
SEL,
id notification) {
2546 objc::msg_send<id>(notification,
"object"_sel);
2547 auto w = get_associated_webview(self);
2548 w->on_window_will_close(self, window);
2551 objc_registerClassPair(cls);
2553 return objc::msg_send<id>((
id)cls,
"new"_sel);
2555 static id get_shared_application() {
2556 return objc::msg_send<id>(
"NSApplication"_cls,
"sharedApplication"_sel);
2558 static cocoa_wkwebview_engine *get_associated_webview(
id object) {
2560 (cocoa_wkwebview_engine *)objc_getAssociatedObject(
object,
"webview");
2564 static id get_main_bundle() noexcept {
2565 return objc::msg_send<id>(
"NSBundle"_cls,
"mainBundle"_sel);
2567 static bool is_app_bundled() noexcept {
2568 auto bundle = get_main_bundle();
2572 auto bundle_path = objc::msg_send<id>(bundle,
"bundlePath"_sel);
2574 objc::msg_send<BOOL>(bundle_path,
"hasSuffix:"_sel,
".app"_str);
2577 void on_application_did_finish_launching(
id ,
id app) {
2579 if (m_owns_window) {
2593 if (!is_app_bundled()) {
2596 objc::msg_send<void>(app,
"setActivationPolicy:"_sel,
2597 NSApplicationActivationPolicyRegular);
2600 objc::msg_send<void>(app,
"activateIgnoringOtherApps:"_sel, YES);
2605 void on_window_will_close(
id ,
id ) {
2607 m_webview =
nullptr;
2609 dispatch([
this] { on_window_destroyed(); });
2611 void set_up_window() {
2612 objc::autoreleasepool arp;
2615 if (m_owns_window) {
2616 m_window = objc::msg_send<id>(
"NSWindow"_cls,
"alloc"_sel);
2617 auto style = NSWindowStyleMaskTitled;
2618 m_window = objc::msg_send<id>(
2619 m_window,
"initWithContentRect:styleMask:backing:defer:"_sel,
2620 CGRectMake(0, 0, 0, 0), style, NSBackingStoreBuffered, NO);
2622 m_window_delegate = create_window_delegate();
2623 objc_setAssociatedObject(m_window_delegate,
"webview", (
id)
this,
2624 OBJC_ASSOCIATION_ASSIGN);
2625 objc::msg_send<void>(m_window,
"setDelegate:"_sel, m_window_delegate);
2627 on_window_created();
2632 objc::msg_send<void>(m_window,
"setContentView:"_sel, m_webview);
2634 if (m_owns_window) {
2635 objc::msg_send<void>(m_window,
"makeKeyAndOrderFront:"_sel,
nullptr);
2638 void set_up_web_view() {
2639 objc::autoreleasepool arp;
2641 auto config = objc::autoreleased(
2642 objc::msg_send<id>(
"WKWebViewConfiguration"_cls,
"new"_sel));
2644 m_manager = objc::msg_send<id>(config,
"userContentController"_sel);
2645 m_webview = objc::msg_send<id>(
"WKWebView"_cls,
"alloc"_sel);
2647 auto preferences = objc::msg_send<id>(config,
"preferences"_sel);
2649 objc::msg_send<id>(
"NSNumber"_cls,
"numberWithBool:"_sel, YES);
2654 objc::msg_send<id>(preferences,
"setValue:forKey:"_sel, yes_value,
2655 "developerExtrasEnabled"_str);
2660 objc::msg_send<id>(preferences,
"setValue:forKey:"_sel, yes_value,
2661 "fullScreenEnabled"_str);
2665 objc::msg_send<id>(preferences,
"setValue:forKey:"_sel, yes_value,
2666 "javaScriptCanAccessClipboard"_str);
2670 objc::msg_send<id>(preferences,
"setValue:forKey:"_sel, yes_value,
2671 "DOMPasteAllowed"_str);
2673 auto ui_delegate = create_webkit_ui_delegate();
2674 objc::msg_send<void>(m_webview,
"initWithFrame:configuration:"_sel,
2675 CGRectMake(0, 0, 0, 0), config);
2676 objc_setAssociatedObject(ui_delegate,
"webview", (
id)
this,
2677 OBJC_ASSOCIATION_ASSIGN);
2678 objc::msg_send<void>(m_webview,
"setUIDelegate:"_sel, ui_delegate);
2687#if defined(__has_builtin)
2688#if __has_builtin(__builtin_available)
2689 if (__builtin_available(macOS 13.3, iOS 16.4, tvOS 16.4, *)) {
2690 objc::msg_send<void>(
2691 m_webview,
"setInspectable:"_sel,
2692 objc::msg_send<id>(
"NSNumber"_cls,
"numberWithBool:"_sel, YES));
2695#error __builtin_available not supported by compiler
2698#error __has_builtin not supported by compiler
2702 auto script_message_handler =
2703 objc::autoreleased(create_script_message_handler());
2704 objc::msg_send<void>(m_manager,
"addScriptMessageHandler:name:"_sel,
2705 script_message_handler,
"__webview__"_str);
2707 add_init_script(
"function(message) {\n\
2708 return window.webkit.messageHandlers.__webview__.postMessage(message);\n\
2711 void stop_run_loop() {
2712 objc::autoreleasepool arp;
2713 auto app = get_shared_application();
2715 objc::msg_send<void>(app,
"stop:"_sel,
nullptr);
2720 auto event = objc::msg_send<id>(
2722 "otherEventWithType:location:modifierFlags:timestamp:windowNumber:context:subtype:data1:data2:"_sel,
2723 type, CGPointMake(0, 0), 0, 0, 0,
nullptr, 0, 0, 0);
2724 objc::msg_send<void>(app,
"postEvent:atStart:"_sel, event, YES);
2726 static bool get_and_set_is_first_instance() noexcept {
2727 static std::atomic_bool first{
true};
2736 void deplete_run_loop_event_queue() {
2737 objc::autoreleasepool arp;
2738 auto app = get_shared_application();
2740 dispatch([&] { done =
true; });
2741 auto mask = NSUIntegerMax;
2743 auto mode = objc::msg_send<id>(
"NSString"_cls,
"stringWithUTF8String:"_sel,
2744 "kCFRunLoopDefaultMode");
2746 objc::autoreleasepool arp2;
2747 auto event = objc::msg_send<id>(
2748 app,
"nextEventMatchingMask:untilDate:inMode:dequeue:"_sel, mask,
2749 nullptr, mode, YES);
2751 objc::msg_send<void>(app,
"sendEvent:"_sel, event);
2757 id m_app_delegate{};
2758 id m_window_delegate{};
2762 bool m_owns_window{};
2767using browser_engine = detail::cocoa_wkwebview_engine;
2771#elif defined(WEBVIEW_EDGE)
2782#define WIN32_LEAN_AND_MEAN
2788#include "WebView2.h"
2791#pragma comment(lib, "advapi32.lib")
2792#pragma comment(lib, "ole32.lib")
2793#pragma comment(lib, "shell32.lib")
2794#pragma comment(lib, "shlwapi.lib")
2795#pragma comment(lib, "user32.lib")
2796#pragma comment(lib, "version.lib")
2802using msg_cb_t = std::function<void(
const std::string)>;
2806template <
typename T>
2807std::array<unsigned int, 4>
2808parse_version(
const std::basic_string<T> &version)
noexcept {
2809 auto parse_component = [](
auto sb,
auto se) ->
unsigned int {
2811 auto n = std::stol(std::basic_string<T>(sb, se));
2812 return n < 0 ? 0 : n;
2813 }
catch (std::exception &) {
2817 auto end = version.end();
2818 auto sb = version.begin();
2820 unsigned int ci = 0;
2821 std::array<unsigned int, 4> components{};
2822 while (sb != end && se != end && ci < components.size()) {
2823 if (*se ==
static_cast<T
>(
'.')) {
2824 components[ci++] = parse_component(sb, se);
2830 if (sb < se && ci < components.size()) {
2831 components[ci] = parse_component(sb, se);
2836template <
typename T, std::
size_t Length>
2837auto parse_version(
const T (&version)[Length])
noexcept {
2838 return parse_version(std::basic_string<T>(version, Length));
2842get_file_version_string(
const std::wstring &file_path)
noexcept {
2844 DWORD info_buffer_length =
2845 GetFileVersionInfoSizeW(file_path.c_str(), &dummy_handle);
2846 if (info_buffer_length == 0) {
2847 return std::wstring();
2849 std::vector<char> info_buffer;
2850 info_buffer.reserve(info_buffer_length);
2851 if (!GetFileVersionInfoW(file_path.c_str(), 0, info_buffer_length,
2852 info_buffer.data())) {
2853 return std::wstring();
2855 auto sub_block = L
"\\StringFileInfo\\040904B0\\ProductVersion";
2856 LPWSTR version =
nullptr;
2857 unsigned int version_length = 0;
2858 if (!VerQueryValueW(info_buffer.data(), sub_block,
2859 reinterpret_cast<LPVOID *
>(&version), &version_length)) {
2860 return std::wstring();
2862 if (!version || version_length == 0) {
2863 return std::wstring();
2865 return std::wstring(version, version_length);
2875class com_init_wrapper {
2877 com_init_wrapper() =
default;
2879 com_init_wrapper(DWORD dwCoInit) {
2884 switch (CoInitializeEx(
nullptr, dwCoInit)) {
2887 m_initialized =
true;
2889 case RPC_E_CHANGED_MODE:
2892 "CoInitializeEx already called with a different concurrency model"};
2895 "Unexpected result from CoInitializeEx"};
2899 ~com_init_wrapper() {
2900 if (m_initialized) {
2902 m_initialized =
false;
2906 com_init_wrapper(
const com_init_wrapper &other) =
delete;
2907 com_init_wrapper &operator=(
const com_init_wrapper &other) =
delete;
2908 com_init_wrapper(com_init_wrapper &&other) { *
this = std::move(other); }
2910 com_init_wrapper &operator=(com_init_wrapper &&other) {
2911 if (
this == &other) {
2914 m_initialized = std::exchange(other.m_initialized,
false);
2919 bool m_initialized =
false;
2922namespace ntdll_symbols {
2923using RtlGetVersion_t =
2924 unsigned int (WINAPI *)(RTL_OSVERSIONINFOW *);
2926constexpr auto RtlGetVersion = library_symbol<RtlGetVersion_t>(
"RtlGetVersion");
2929namespace user32_symbols {
2930using DPI_AWARENESS_CONTEXT = HANDLE;
2931using SetProcessDpiAwarenessContext_t = BOOL(WINAPI *)(DPI_AWARENESS_CONTEXT);
2932using SetProcessDPIAware_t = BOOL(WINAPI *)();
2933using GetDpiForWindow_t = UINT(WINAPI *)(HWND);
2934using EnableNonClientDpiScaling_t = BOOL(WINAPI *)(HWND);
2935using AdjustWindowRectExForDpi_t = BOOL(WINAPI *)(LPRECT, DWORD, BOOL, DWORD,
2937using GetWindowDpiAwarenessContext_t = DPI_AWARENESS_CONTEXT(WINAPI *)(HWND);
2938using AreDpiAwarenessContextsEqual_t = BOOL(WINAPI *)(DPI_AWARENESS_CONTEXT,
2939 DPI_AWARENESS_CONTEXT);
2944enum class dpi_awareness : intptr_t {
2945 per_monitor_v2_aware = -4,
2946 per_monitor_aware = -3
2949constexpr auto SetProcessDpiAwarenessContext =
2950 library_symbol<SetProcessDpiAwarenessContext_t>(
2951 "SetProcessDpiAwarenessContext");
2952constexpr auto SetProcessDPIAware =
2953 library_symbol<SetProcessDPIAware_t>(
"SetProcessDPIAware");
2954constexpr auto GetDpiForWindow =
2955 library_symbol<GetDpiForWindow_t>(
"GetDpiForWindow");
2956constexpr auto EnableNonClientDpiScaling =
2957 library_symbol<EnableNonClientDpiScaling_t>(
"EnableNonClientDpiScaling");
2958constexpr auto AdjustWindowRectExForDpi =
2959 library_symbol<AdjustWindowRectExForDpi_t>(
"AdjustWindowRectExForDpi");
2960constexpr auto GetWindowDpiAwarenessContext =
2961 library_symbol<GetWindowDpiAwarenessContext_t>(
2962 "GetWindowDpiAwarenessContext");
2963constexpr auto AreDpiAwarenessContextsEqual =
2964 library_symbol<AreDpiAwarenessContextsEqual_t>(
2965 "AreDpiAwarenessContextsEqual");
2968namespace dwmapi_symbols {
2972 DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_V10_0_19041 = 19,
2975 DWMWA_USE_IMMERSIVE_DARK_MODE = 20
2976} DWMWINDOWATTRIBUTE;
2977using DwmSetWindowAttribute_t = HRESULT(WINAPI *)(HWND, DWORD, LPCVOID, DWORD);
2979constexpr auto DwmSetWindowAttribute =
2980 library_symbol<DwmSetWindowAttribute_t>(
"DwmSetWindowAttribute");
2983namespace shcore_symbols {
2984typedef enum { PROCESS_PER_MONITOR_DPI_AWARE = 2 } PROCESS_DPI_AWARENESS;
2985using SetProcessDpiAwareness_t = HRESULT(WINAPI *)(PROCESS_DPI_AWARENESS);
2987constexpr auto SetProcessDpiAwareness =
2988 library_symbol<SetProcessDpiAwareness_t>(
"SetProcessDpiAwareness");
2993 explicit reg_key(HKEY root_key,
const wchar_t *sub_key, DWORD options,
2994 REGSAM sam_desired) {
2997 RegOpenKeyExW(root_key, sub_key, options, sam_desired, &handle);
2998 if (status == ERROR_SUCCESS) {
3003 explicit reg_key(HKEY root_key,
const std::wstring &sub_key, DWORD options,
3005 : reg_key(root_key, sub_key.c_str(), options, sam_desired) {}
3007 virtual ~reg_key() {
3009 RegCloseKey(m_handle);
3014 reg_key(
const reg_key &other) =
delete;
3015 reg_key &operator=(
const reg_key &other) =
delete;
3016 reg_key(reg_key &&other) =
delete;
3017 reg_key &operator=(reg_key &&other) =
delete;
3019 bool is_open()
const {
return !!m_handle; }
3020 bool get_handle()
const {
return m_handle; }
3022 template <
typename Container>
3023 void query_bytes(
const wchar_t *name, Container &result)
const {
3024 DWORD buf_length = 0;
3026 auto status = RegQueryValueExW(m_handle, name,
nullptr,
nullptr,
nullptr,
3028 if (status != ERROR_SUCCESS && status != ERROR_MORE_DATA) {
3033 result.resize(buf_length /
sizeof(
typename Container::value_type));
3034 auto *buf =
reinterpret_cast<LPBYTE
>(&result[0]);
3036 RegQueryValueExW(m_handle, name,
nullptr,
nullptr, buf, &buf_length);
3037 if (status != ERROR_SUCCESS) {
3043 std::wstring query_string(
const wchar_t *name)
const {
3044 std::wstring result;
3045 query_bytes(name, result);
3047 for (std::size_t length = result.size(); length > 0; --length) {
3048 if (result[length - 1] != 0) {
3049 result.resize(length);
3056 unsigned int query_uint(
const wchar_t *name,
3057 unsigned int default_value)
const {
3058 std::vector<char> data;
3059 query_bytes(name, data);
3060 if (data.size() <
sizeof(DWORD)) {
3061 return default_value;
3063 return static_cast<unsigned int>(*
reinterpret_cast<DWORD *
>(data.data()));
3067 HKEY m_handle =
nullptr;
3074inline int compare_os_version(
unsigned int major,
unsigned int minor,
3075 unsigned int build) {
3079 auto ntdll = native_library(L
"ntdll.dll");
3080 if (
auto fn = ntdll.get(ntdll_symbols::RtlGetVersion)) {
3081 RTL_OSVERSIONINFOW vi{};
3082 vi.dwOSVersionInfoSize =
sizeof(vi);
3086 if (vi.dwMajorVersion == major) {
3087 if (vi.dwMinorVersion == minor) {
3088 return static_cast<int>(vi.dwBuildNumber) -
static_cast<int>(build);
3090 return static_cast<int>(vi.dwMinorVersion) -
static_cast<int>(minor);
3092 return static_cast<int>(vi.dwMajorVersion) -
static_cast<int>(major);
3097inline bool is_per_monitor_v2_awareness_available() {
3099 return compare_os_version(10, 0, 15063) >= 0;
3102inline bool enable_dpi_awareness() {
3103 auto user32 = native_library(L
"user32.dll");
3104 if (
auto fn = user32.get(user32_symbols::SetProcessDpiAwarenessContext)) {
3105 auto dpi_awareness =
3106 reinterpret_cast<user32_symbols::DPI_AWARENESS_CONTEXT
>(
3107 is_per_monitor_v2_awareness_available()
3108 ? user32_symbols::dpi_awareness::per_monitor_v2_aware
3109 : user32_symbols::dpi_awareness::per_monitor_aware);
3110 if (fn(dpi_awareness)) {
3113 return GetLastError() == ERROR_ACCESS_DENIED;
3115 if (
auto shcore = native_library(L
"shcore.dll")) {
3116 if (
auto fn = shcore.get(shcore_symbols::SetProcessDpiAwareness)) {
3117 auto result = fn(shcore_symbols::PROCESS_PER_MONITOR_DPI_AWARE);
3118 return result == S_OK || result == E_ACCESSDENIED;
3121 if (
auto fn = user32.get(user32_symbols::SetProcessDPIAware)) {
3127inline bool enable_non_client_dpi_scaling_if_needed(HWND window) {
3128 auto user32 = native_library(L
"user32.dll");
3129 auto get_ctx_fn = user32.get(user32_symbols::GetWindowDpiAwarenessContext);
3133 auto awareness = get_ctx_fn(window);
3137 auto ctx_equal_fn = user32.get(user32_symbols::AreDpiAwarenessContextsEqual);
3138 if (!ctx_equal_fn) {
3142 auto per_monitor =
reinterpret_cast<user32_symbols::DPI_AWARENESS_CONTEXT
>(
3143 user32_symbols::dpi_awareness::per_monitor_aware);
3144 if (!ctx_equal_fn(awareness, per_monitor)) {
3147 auto enable_fn = user32.get(user32_symbols::EnableNonClientDpiScaling);
3151 return !!enable_fn(window);
3154constexpr int get_default_window_dpi() {
3155 constexpr const int default_dpi = 96;
3159inline int get_window_dpi(HWND window) {
3160 auto user32 = native_library(L
"user32.dll");
3161 if (
auto fn = user32.get(user32_symbols::GetDpiForWindow)) {
3162 auto dpi =
static_cast<int>(fn(window));
3165 return get_default_window_dpi();
3168constexpr int scale_value_for_dpi(
int value,
int from_dpi,
int to_dpi) {
3169 return (value * to_dpi) / from_dpi;
3172constexpr SIZE scale_size(
int width,
int height,
int from_dpi,
int to_dpi) {
3173 auto scaled_width = scale_value_for_dpi(width, from_dpi, to_dpi);
3174 auto scaled_height = scale_value_for_dpi(height, from_dpi, to_dpi);
3175 return {scaled_width, scaled_height};
3178inline SIZE make_window_frame_size(HWND window,
int width,
int height,
3180 auto style = GetWindowLong(window, GWL_STYLE);
3181 RECT r{0, 0, width, height};
3182 auto user32 = native_library(L
"user32.dll");
3183 if (
auto fn = user32.get(user32_symbols::AdjustWindowRectExForDpi)) {
3184 fn(&r, style, FALSE, 0,
static_cast<UINT
>(dpi));
3186 AdjustWindowRect(&r, style, 0);
3188 auto frame_width = r.right - r.left;
3189 auto frame_height = r.bottom - r.top;
3190 return {frame_width, frame_height};
3193inline bool is_dark_theme_enabled() {
3194 constexpr auto *sub_key =
3195 L
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize";
3196 reg_key key(HKEY_CURRENT_USER, sub_key, 0, KEY_READ);
3197 if (!key.is_open()) {
3201 return key.query_uint(L
"AppsUseLightTheme", 1) == 0;
3204inline void apply_window_theme(HWND window) {
3205 auto dark_theme_enabled = is_dark_theme_enabled();
3209 BOOL use_dark_mode{dark_theme_enabled ? TRUE : FALSE};
3210 static native_library dwmapi{L
"dwmapi.dll"};
3211 if (
auto fn = dwmapi.get(dwmapi_symbols::DwmSetWindowAttribute)) {
3213 if (fn(window, dwmapi_symbols::DWMWA_USE_IMMERSIVE_DARK_MODE,
3214 &use_dark_mode,
sizeof(use_dark_mode)) != S_OK) {
3216 dwmapi_symbols::DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_V10_0_19041,
3217 &use_dark_mode,
sizeof(use_dark_mode));
3223#ifndef WEBVIEW_MSWEBVIEW2_BUILTIN_IMPL
3224#define WEBVIEW_MSWEBVIEW2_BUILTIN_IMPL 1
3229#ifndef WEBVIEW_MSWEBVIEW2_EXPLICIT_LINK
3230#define WEBVIEW_MSWEBVIEW2_EXPLICIT_LINK WEBVIEW_MSWEBVIEW2_BUILTIN_IMPL
3235#if WEBVIEW_MSWEBVIEW2_BUILTIN_IMPL == 1 && \
3236 WEBVIEW_MSWEBVIEW2_EXPLICIT_LINK != 1
3237#undef WEBVIEW_MSWEBVIEW2_EXPLICIT_LINK
3238#error Please set WEBVIEW_MSWEBVIEW2_EXPLICIT_LINK=1.
3241#if WEBVIEW_MSWEBVIEW2_BUILTIN_IMPL == 1
3244template <
typename T>
3246get_last_native_path_component(
const std::basic_string<T> &path) {
3247 auto pos = path.find_last_of(
static_cast<T
>(
'\\'));
3248 if (pos != std::basic_string<T>::npos) {
3249 return path.substr(pos + 1);
3251 return std::basic_string<T>();
3255template <
typename T>
struct cast_info_t {
3260namespace mswebview2 {
3262 IID_ICoreWebView2CreateCoreWebView2ControllerCompletedHandler{
3266 {0x81, 0x27, 0xC9, 0xF5, 0xBD, 0xE7, 0xF6, 0x8C}};
3268 IID_ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler{
3272 {0xB6, 0xB5, 0x12, 0x4F, 0xEE, 0x6C, 0xC1, 0x4D}};
3273static constexpr IID IID_ICoreWebView2PermissionRequestedEventHandler{
3277 {0x91, 0xD7, 0xD0, 0x97, 0xFB, 0xEC, 0x6B, 0xFD}};
3278static constexpr IID IID_ICoreWebView2WebMessageReceivedEventHandler{
3282 {0x8E, 0x07, 0x89, 0x8E, 0xA0, 0x1E, 0xCB, 0xD2}};
3284 IID_ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandler{
3288 {0xBC, 0x6F, 0x8E, 0x78, 0x95, 0xFC, 0xEA, 0x17}};
3290#if WEBVIEW_MSWEBVIEW2_BUILTIN_IMPL == 1
3291enum class webview2_runtime_type { installed = 0, embedded = 1 };
3293namespace webview2_symbols {
3294using CreateWebViewEnvironmentWithOptionsInternal_t =
3295 HRESULT(STDMETHODCALLTYPE *)(
3296 bool, webview2_runtime_type, PCWSTR, IUnknown *,
3297 ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler *);
3298using DllCanUnloadNow_t = HRESULT(STDMETHODCALLTYPE *)();
3300static constexpr auto CreateWebViewEnvironmentWithOptionsInternal =
3301 library_symbol<CreateWebViewEnvironmentWithOptionsInternal_t>(
3302 "CreateWebViewEnvironmentWithOptionsInternal");
3303static constexpr auto DllCanUnloadNow =
3304 library_symbol<DllCanUnloadNow_t>(
"DllCanUnloadNow");
3308#if WEBVIEW_MSWEBVIEW2_EXPLICIT_LINK == 1
3309namespace webview2_symbols {
3310using CreateCoreWebView2EnvironmentWithOptions_t = HRESULT(STDMETHODCALLTYPE *)(
3311 PCWSTR, PCWSTR, ICoreWebView2EnvironmentOptions *,
3312 ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler *);
3313using GetAvailableCoreWebView2BrowserVersionString_t =
3314 HRESULT(STDMETHODCALLTYPE *)(PCWSTR, LPWSTR *);
3316static constexpr auto CreateCoreWebView2EnvironmentWithOptions =
3317 library_symbol<CreateCoreWebView2EnvironmentWithOptions_t>(
3318 "CreateCoreWebView2EnvironmentWithOptions");
3319static constexpr auto GetAvailableCoreWebView2BrowserVersionString =
3320 library_symbol<GetAvailableCoreWebView2BrowserVersionString_t>(
3321 "GetAvailableCoreWebView2BrowserVersionString");
3327 HRESULT create_environment_with_options(
3328 PCWSTR browser_dir, PCWSTR user_data_dir,
3329 ICoreWebView2EnvironmentOptions *env_options,
3330 ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler
3331 *created_handler)
const {
3332#if WEBVIEW_MSWEBVIEW2_EXPLICIT_LINK == 1
3333 if (m_lib.is_loaded()) {
3334 if (
auto fn = m_lib.get(
3335 webview2_symbols::CreateCoreWebView2EnvironmentWithOptions)) {
3336 return fn(browser_dir, user_data_dir, env_options, created_handler);
3339#if WEBVIEW_MSWEBVIEW2_BUILTIN_IMPL == 1
3340 return create_environment_with_options_impl(browser_dir, user_data_dir,
3341 env_options, created_handler);
3346 return ::CreateCoreWebView2EnvironmentWithOptions(
3347 browser_dir, user_data_dir, env_options, created_handler);
3352 get_available_browser_version_string(PCWSTR browser_dir,
3353 LPWSTR *version)
const {
3354#if WEBVIEW_MSWEBVIEW2_EXPLICIT_LINK == 1
3355 if (m_lib.is_loaded()) {
3356 if (
auto fn = m_lib.get(
3357 webview2_symbols::GetAvailableCoreWebView2BrowserVersionString)) {
3358 return fn(browser_dir, version);
3361#if WEBVIEW_MSWEBVIEW2_BUILTIN_IMPL == 1
3362 return get_available_browser_version_string_impl(browser_dir, version);
3367 return ::GetAvailableCoreWebView2BrowserVersionString(browser_dir, version);
3372#if WEBVIEW_MSWEBVIEW2_BUILTIN_IMPL == 1
3373 struct client_info_t {
3375 std::wstring dll_path;
3376 std::wstring version;
3377 webview2_runtime_type runtime_type;
3380 HRESULT create_environment_with_options_impl(
3381 PCWSTR browser_dir, PCWSTR user_data_dir,
3382 ICoreWebView2EnvironmentOptions *env_options,
3383 ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler
3384 *created_handler)
const {
3385 auto found_client = find_available_client(browser_dir);
3386 if (!found_client.found) {
3389 auto client_dll = native_library(found_client.dll_path.c_str());
3390 if (
auto fn = client_dll.get(
3391 webview2_symbols::CreateWebViewEnvironmentWithOptionsInternal)) {
3392 return fn(
true, found_client.runtime_type, user_data_dir, env_options,
3395 if (
auto fn = client_dll.get(webview2_symbols::DllCanUnloadNow)) {
3397 client_dll.detach();
3400 return ERROR_SUCCESS;
3404 get_available_browser_version_string_impl(PCWSTR browser_dir,
3405 LPWSTR *version)
const {
3409 auto found_client = find_available_client(browser_dir);
3410 if (!found_client.found) {
3413 auto info_length_bytes =
3414 found_client.version.size() *
sizeof(found_client.version[0]);
3415 auto info =
static_cast<LPWSTR
>(CoTaskMemAlloc(info_length_bytes));
3419 CopyMemory(info, found_client.version.c_str(), info_length_bytes);
3424 client_info_t find_available_client(PCWSTR browser_dir)
const {
3426 return find_embedded_client(api_version, browser_dir);
3429 find_installed_client(api_version,
true, default_release_channel_guid);
3430 if (!found_client.found) {
3431 found_client = find_installed_client(api_version,
false,
3432 default_release_channel_guid);
3434 return found_client;
3437 std::wstring make_client_dll_path(
const std::wstring &dir)
const {
3438 auto dll_path = dir;
3439 if (!dll_path.empty()) {
3440 auto last_char = dir[dir.size() - 1];
3441 if (last_char != L
'\\' && last_char != L
'/') {
3445 dll_path += L
"EBWebView\\";
3446#if defined(_M_X64) || defined(__x86_64__)
3448#elif defined(_M_IX86) || defined(__i386__)
3450#elif defined(_M_ARM64) || defined(__aarch64__)
3451 dll_path += L
"arm64";
3453#error WebView2 integration for this platform is not yet supported.
3455 dll_path += L
"\\EmbeddedBrowserWebView.dll";
3460 find_installed_client(
unsigned int min_api_version,
bool system,
3461 const std::wstring &release_channel)
const {
3462 std::wstring sub_key = client_state_reg_sub_key;
3463 sub_key += release_channel;
3464 auto root_key = system ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
3465 reg_key key(root_key, sub_key, 0, KEY_READ | KEY_WOW64_32KEY);
3466 if (!key.is_open()) {
3469 auto ebwebview_value = key.query_string(L
"EBWebView");
3471 auto client_version_string =
3472 get_last_native_path_component(ebwebview_value);
3473 auto client_version = parse_version(client_version_string);
3474 if (client_version[2] < min_api_version) {
3479 auto client_dll_path = make_client_dll_path(ebwebview_value);
3480 return {
true, client_dll_path, client_version_string,
3481 webview2_runtime_type::installed};
3484 client_info_t find_embedded_client(
unsigned int min_api_version,
3485 const std::wstring &dir)
const {
3486 auto client_dll_path = make_client_dll_path(dir);
3488 auto client_version_string = get_file_version_string(client_dll_path);
3489 auto client_version = parse_version(client_version_string);
3490 if (client_version[2] < min_api_version) {
3495 return {
true, client_dll_path, client_version_string,
3496 webview2_runtime_type::embedded};
3504 static constexpr unsigned int api_version = 1150;
3506 static constexpr auto client_state_reg_sub_key =
3507 L
"SOFTWARE\\Microsoft\\EdgeUpdate\\ClientState\\";
3510 static constexpr auto stable_release_guid =
3511 L
"{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}";
3513 static constexpr auto default_release_channel_guid = stable_release_guid;
3516#if WEBVIEW_MSWEBVIEW2_EXPLICIT_LINK == 1
3517 native_library m_lib{L
"WebView2Loader.dll"};
3521namespace cast_info {
3522static constexpr auto controller_completed =
3523 cast_info_t<ICoreWebView2CreateCoreWebView2ControllerCompletedHandler>{
3524 IID_ICoreWebView2CreateCoreWebView2ControllerCompletedHandler};
3526static constexpr auto environment_completed =
3527 cast_info_t<ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler>{
3528 IID_ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler};
3530static constexpr auto message_received =
3531 cast_info_t<ICoreWebView2WebMessageReceivedEventHandler>{
3532 IID_ICoreWebView2WebMessageReceivedEventHandler};
3534static constexpr auto permission_requested =
3535 cast_info_t<ICoreWebView2PermissionRequestedEventHandler>{
3536 IID_ICoreWebView2PermissionRequestedEventHandler};
3538static constexpr auto add_script_to_execute_on_document_created_completed =
3540 ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandler>{
3541 IID_ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandler};
3549template <
typename From,
typename To>
3550To *cast_if_equal_iid(From *from, REFIID riid,
const cast_info_t<To> &info,
3551 LPVOID *ppv =
nullptr) noexcept {
3553 if (IsEqualIID(riid, info.iid)) {
3554 ptr =
static_cast<To *
>(from);
3563class webview2_com_handler
3564 :
public ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler,
3565 public ICoreWebView2CreateCoreWebView2ControllerCompletedHandler,
3566 public ICoreWebView2WebMessageReceivedEventHandler,
3567 public ICoreWebView2PermissionRequestedEventHandler {
3568 using webview2_com_handler_cb_t =
3569 std::function<void(ICoreWebView2Controller *, ICoreWebView2 *webview)>;
3572 webview2_com_handler(HWND hwnd, msg_cb_t msgCb, webview2_com_handler_cb_t cb)
3573 : m_window(hwnd), m_msgCb(msgCb), m_cb(cb) {}
3575 virtual ~webview2_com_handler() =
default;
3576 webview2_com_handler(
const webview2_com_handler &other) =
delete;
3577 webview2_com_handler &operator=(
const webview2_com_handler &other) =
delete;
3578 webview2_com_handler(webview2_com_handler &&other) =
delete;
3579 webview2_com_handler &operator=(webview2_com_handler &&other) =
delete;
3581 ULONG STDMETHODCALLTYPE AddRef() {
return ++m_ref_count; }
3582 ULONG STDMETHODCALLTYPE Release() {
3583 if (m_ref_count > 1) {
3584 return --m_ref_count;
3589 HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID *ppv) {
3590 using namespace mswebview2::cast_info;
3606 if (cast_if_equal_iid(
this, riid, controller_completed, ppv) ||
3607 cast_if_equal_iid(
this, riid, environment_completed, ppv) ||
3608 cast_if_equal_iid(
this, riid, message_received, ppv) ||
3609 cast_if_equal_iid(
this, riid, permission_requested, ppv)) {
3613 return E_NOINTERFACE;
3615 HRESULT STDMETHODCALLTYPE Invoke(HRESULT res, ICoreWebView2Environment *env) {
3616 if (SUCCEEDED(res)) {
3617 res = env->CreateCoreWebView2Controller(m_window,
this);
3618 if (SUCCEEDED(res)) {
3622 try_create_environment();
3625 HRESULT STDMETHODCALLTYPE Invoke(HRESULT res,
3626 ICoreWebView2Controller *controller) {
3632 case HRESULT_FROM_WIN32(ERROR_INVALID_STATE):
3636 try_create_environment();
3640 ICoreWebView2 *webview;
3641 ::EventRegistrationToken token;
3642 controller->get_CoreWebView2(&webview);
3643 webview->add_WebMessageReceived(
this, &token);
3644 webview->add_PermissionRequested(
this, &token);
3646 m_cb(controller, webview);
3649 HRESULT STDMETHODCALLTYPE
3650 Invoke(ICoreWebView2 * ,
3651 ICoreWebView2WebMessageReceivedEventArgs *args) {
3653 auto res = args->TryGetWebMessageAsString(&message);
3654 if (SUCCEEDED(res)) {
3655 m_msgCb(narrow_string(message));
3658 CoTaskMemFree(message);
3661 HRESULT STDMETHODCALLTYPE
3662 Invoke(ICoreWebView2 * ,
3663 ICoreWebView2PermissionRequestedEventArgs *args) {
3664 COREWEBVIEW2_PERMISSION_KIND kind;
3665 args->get_PermissionKind(&kind);
3666 if (kind == COREWEBVIEW2_PERMISSION_KIND_CLIPBOARD_READ) {
3667 args->put_State(COREWEBVIEW2_PERMISSION_STATE_ALLOW);
3674 void set_attempt_handler(std::function<HRESULT()> attempt_handler)
noexcept {
3675 m_attempt_handler = attempt_handler;
3681 void try_create_environment() noexcept {
3686 if (m_attempts < m_max_attempts) {
3688 auto res = m_attempt_handler();
3689 if (SUCCEEDED(res)) {
3694 if (res == HRESULT_FROM_WIN32(ERROR_INVALID_STATE)) {
3697 try_create_environment();
3701 m_cb(
nullptr,
nullptr);
3707 webview2_com_handler_cb_t m_cb;
3708 std::atomic<ULONG> m_ref_count{1};
3709 std::function<HRESULT()> m_attempt_handler;
3710 unsigned int m_max_attempts = 5;
3711 unsigned int m_attempts = 0;
3714class webview2_user_script_added_handler
3715 :
public ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandler {
3717 using callback_fn = std::function<void(HRESULT errorCode, LPCWSTR
id)>;
3719 webview2_user_script_added_handler(callback_fn cb) : m_cb{cb} {}
3721 virtual ~webview2_user_script_added_handler() =
default;
3722 webview2_user_script_added_handler(
3723 const webview2_user_script_added_handler &other) =
delete;
3724 webview2_user_script_added_handler &
3725 operator=(
const webview2_user_script_added_handler &other) =
delete;
3726 webview2_user_script_added_handler(
3727 webview2_user_script_added_handler &&other) =
delete;
3728 webview2_user_script_added_handler &
3729 operator=(webview2_user_script_added_handler &&other) =
delete;
3731 ULONG STDMETHODCALLTYPE AddRef() {
return ++m_ref_count; }
3732 ULONG STDMETHODCALLTYPE Release() {
3733 if (m_ref_count > 1) {
3734 return --m_ref_count;
3740 HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID *ppv) {
3741 using namespace mswebview2::cast_info;
3747 if (cast_if_equal_iid(
this, riid,
3748 add_script_to_execute_on_document_created_completed,
3753 return E_NOINTERFACE;
3756 HRESULT STDMETHODCALLTYPE Invoke(HRESULT res, LPCWSTR
id) {
3763 std::atomic<ULONG> m_ref_count{1};
3766class user_script::impl {
3768 impl(
const std::wstring &
id,
const std::wstring &code)
3769 : m_id{id}, m_code{code} {}
3771 impl(
const impl &) =
delete;
3772 impl &operator=(
const impl &) =
delete;
3773 impl(impl &&) =
delete;
3774 impl &operator=(impl &&) =
delete;
3776 const std::wstring &get_id()
const {
return m_id; }
3777 const std::wstring &get_code()
const {
return m_code; }
3781 std::wstring m_code;
3784class win32_edge_engine :
public engine_base {
3786 win32_edge_engine(
bool debug,
void *window) : m_owns_window{!window} {
3787 if (!is_webview2_available()) {
3789 "WebView2 is unavailable"};
3792 HINSTANCE hInstance = GetModuleHandle(
nullptr);
3794 if (m_owns_window) {
3795 m_com_init = {COINIT_APARTMENTTHREADED};
3796 enable_dpi_awareness();
3798 HICON icon = (HICON)LoadImage(
3799 hInstance, IDI_APPLICATION, IMAGE_ICON, GetSystemMetrics(SM_CXICON),
3800 GetSystemMetrics(SM_CYICON), LR_DEFAULTCOLOR);
3804 ZeroMemory(&wc,
sizeof(WNDCLASSEX));
3805 wc.cbSize =
sizeof(WNDCLASSEX);
3806 wc.hInstance = hInstance;
3807 wc.lpszClassName = L
"webview";
3809 wc.lpfnWndProc = (WNDPROC)(+[](HWND hwnd, UINT msg, WPARAM wp,
3810 LPARAM lp) -> LRESULT {
3811 win32_edge_engine *w{};
3813 if (msg == WM_NCCREATE) {
3814 auto *lpcs{
reinterpret_cast<LPCREATESTRUCT
>(lp)};
3815 w =
static_cast<win32_edge_engine *
>(lpcs->lpCreateParams);
3817 SetWindowLongPtrW(hwnd, GWLP_USERDATA,
reinterpret_cast<LONG_PTR
>(w));
3818 enable_non_client_dpi_scaling_if_needed(hwnd);
3819 apply_window_theme(hwnd);
3821 w =
reinterpret_cast<win32_edge_engine *
>(
3822 GetWindowLongPtrW(hwnd, GWLP_USERDATA));
3826 return DefWindowProcW(hwnd, msg, wp, lp);
3834 DestroyWindow(hwnd);
3837 w->m_window =
nullptr;
3838 SetWindowLongPtrW(hwnd, GWLP_USERDATA, 0);
3839 w->on_window_destroyed();
3841 case WM_GETMINMAXINFO: {
3842 auto lpmmi = (LPMINMAXINFO)lp;
3843 if (w->m_maxsz.x > 0 && w->m_maxsz.y > 0) {
3844 lpmmi->ptMaxSize = w->m_maxsz;
3845 lpmmi->ptMaxTrackSize = w->m_maxsz;
3847 if (w->m_minsz.x > 0 && w->m_minsz.y > 0) {
3848 lpmmi->ptMinTrackSize = w->m_minsz;
3852 auto dpi =
static_cast<int>(wp);
3853 auto *size{
reinterpret_cast<SIZE *
>(lp)};
3854 *size = w->get_scaled_size(w->m_dpi, dpi);
3861 auto dpi =
static_cast<int>(HIWORD(wp));
3862 w->on_dpi_changed(dpi);
3865 case WM_SETTINGCHANGE: {
3866 auto *area =
reinterpret_cast<const wchar_t *
>(lp);
3868 w->on_system_setting_change(area);
3873 if (LOWORD(wp) != WA_INACTIVE) {
3878 return DefWindowProcW(hwnd, msg, wp, lp);
3882 RegisterClassExW(&wc);
3884 CreateWindowW(L
"webview", L
"", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,
3885 CW_USEDEFAULT, 0, 0,
nullptr,
nullptr, hInstance,
this);
3889 on_window_created();
3891 m_dpi = get_window_dpi(m_window);
3892 constexpr const int initial_width = 640;
3893 constexpr const int initial_height = 480;
3896 m_window = IsWindow(
static_cast<HWND
>(window))
3897 ?
static_cast<HWND
>(window)
3898 : *(static_cast<HWND *>(window));
3899 m_dpi = get_window_dpi(m_window);
3903 WNDCLASSEXW widget_wc{};
3904 widget_wc.cbSize =
sizeof(WNDCLASSEX);
3905 widget_wc.hInstance = hInstance;
3906 widget_wc.lpszClassName = L
"webview_widget";
3907 widget_wc.lpfnWndProc = (WNDPROC)(+[](HWND hwnd, UINT msg, WPARAM wp,
3908 LPARAM lp) -> LRESULT {
3909 win32_edge_engine *w{};
3911 if (msg == WM_NCCREATE) {
3912 auto *lpcs{
reinterpret_cast<LPCREATESTRUCT
>(lp)};
3913 w =
static_cast<win32_edge_engine *
>(lpcs->lpCreateParams);
3915 SetWindowLongPtrW(hwnd, GWLP_USERDATA,
reinterpret_cast<LONG_PTR
>(w));
3917 w =
reinterpret_cast<win32_edge_engine *
>(
3918 GetWindowLongPtrW(hwnd, GWLP_USERDATA));
3922 return DefWindowProcW(hwnd, msg, wp, lp);
3927 w->resize_webview();
3930 w->m_widget =
nullptr;
3931 SetWindowLongPtrW(hwnd, GWLP_USERDATA, 0);
3934 return DefWindowProcW(hwnd, msg, wp, lp);
3938 RegisterClassExW(&widget_wc);
3939 CreateWindowExW(WS_EX_CONTROLPARENT, L
"webview_widget",
nullptr, WS_CHILD,
3940 0, 0, 0, 0, m_window,
nullptr, hInstance,
this);
3946 WNDCLASSEXW message_wc{};
3947 message_wc.cbSize =
sizeof(WNDCLASSEX);
3948 message_wc.hInstance = hInstance;
3949 message_wc.lpszClassName = L
"webview_message";
3950 message_wc.lpfnWndProc = (WNDPROC)(+[](HWND hwnd, UINT msg, WPARAM wp,
3951 LPARAM lp) -> LRESULT {
3952 win32_edge_engine *w{};
3954 if (msg == WM_NCCREATE) {
3955 auto *lpcs{
reinterpret_cast<LPCREATESTRUCT
>(lp)};
3956 w =
static_cast<win32_edge_engine *
>(lpcs->lpCreateParams);
3957 w->m_message_window = hwnd;
3958 SetWindowLongPtrW(hwnd, GWLP_USERDATA,
reinterpret_cast<LONG_PTR
>(w));
3960 w =
reinterpret_cast<win32_edge_engine *
>(
3961 GetWindowLongPtrW(hwnd, GWLP_USERDATA));
3965 return DefWindowProcW(hwnd, msg, wp, lp);
3970 if (
auto f = (dispatch_fn_t *)(lp)) {
3976 w->m_message_window =
nullptr;
3977 SetWindowLongPtrW(hwnd, GWLP_USERDATA, 0);
3980 return DefWindowProcW(hwnd, msg, wp, lp);
3984 RegisterClassExW(&message_wc);
3985 CreateWindowExW(0, L
"webview_message",
nullptr, 0, 0, 0, 0, 0, HWND_MESSAGE,
3986 nullptr, hInstance,
this);
3987 if (!m_message_window) {
3991 if (m_owns_window) {
3992 ShowWindow(m_window, SW_SHOW);
3993 UpdateWindow(m_window);
3998 std::bind(&win32_edge_engine::on_message,
this, std::placeholders::_1);
4000 embed(m_widget, debug, cb).ensure_ok();
4003 virtual ~win32_edge_engine() {
4004 if (m_com_handler) {
4005 m_com_handler->Release();
4006 m_com_handler =
nullptr;
4009 m_webview->Release();
4010 m_webview =
nullptr;
4013 m_controller->Release();
4014 m_controller =
nullptr;
4018 auto wndproc =
reinterpret_cast<LONG_PTR
>(
4019 +[](HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) -> LRESULT {
4020 return DefWindowProcW(hwnd, msg, wp, lp);
4023 SetWindowLongPtrW(m_widget, GWLP_WNDPROC, wndproc);
4025 if (m_window && m_owns_window) {
4026 SetWindowLongPtrW(m_window, GWLP_WNDPROC, wndproc);
4029 DestroyWindow(m_widget);
4033 if (m_owns_window) {
4034 DestroyWindow(m_window);
4035 on_window_destroyed(
true);
4039 if (m_owns_window) {
4042 deplete_run_loop_event_queue();
4045 if (m_message_window) {
4046 SetWindowLongPtrW(m_message_window, GWLP_WNDPROC, wndproc);
4047 DestroyWindow(m_message_window);
4048 m_message_window =
nullptr;
4052 win32_edge_engine(
const win32_edge_engine &other) =
delete;
4053 win32_edge_engine &operator=(
const win32_edge_engine &other) =
delete;
4054 win32_edge_engine(win32_edge_engine &&other) =
delete;
4055 win32_edge_engine &operator=(win32_edge_engine &&other) =
delete;
4058 noresult run_impl()
override {
4060 while (GetMessageW(&msg,
nullptr, 0, 0) > 0) {
4061 TranslateMessage(&msg);
4062 DispatchMessageW(&msg);
4066 result<void *> window_impl()
override {
4072 result<void *> widget_impl()
override {
4078 result<void *> browser_controller_impl()
override {
4080 return m_controller;
4084 noresult terminate_impl()
override {
4088 noresult dispatch_impl(dispatch_fn_t f)
override {
4089 PostMessageW(m_message_window, WM_APP, 0, (LPARAM)
new dispatch_fn_t(f));
4093 noresult set_title_impl(
const std::string &title)
override {
4094 SetWindowTextW(m_window, widen_string(title).c_str());
4098 noresult set_size_impl(
int width,
int height,
webview_hint_t hints)
override {
4099 auto style = GetWindowLong(m_window, GWL_STYLE);
4101 style &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
4103 style |= (WS_THICKFRAME | WS_MAXIMIZEBOX);
4105 SetWindowLong(m_window, GWL_STYLE, style);
4114 auto dpi = get_window_dpi(m_window);
4117 scale_size(width, height, get_default_window_dpi(), dpi);
4119 make_window_frame_size(m_window, scaled_size.cx, scaled_size.cy, dpi);
4120 SetWindowPos(m_window,
nullptr, 0, 0, frame_size.cx, frame_size.cy,
4121 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE |
4127 noresult navigate_impl(
const std::string &url)
override {
4128 auto wurl = widen_string(url);
4129 m_webview->Navigate(wurl.c_str());
4133 noresult eval_impl(
const std::string &js)
override {
4136 auto wjs = widen_string(js);
4137 m_webview->ExecuteScript(wjs.c_str(),
nullptr);
4141 noresult set_html_impl(
const std::string &html)
override {
4142 m_webview->NavigateToString(widen_string(html).c_str());
4146 user_script add_user_script_impl(
const std::string &js)
override {
4147 auto wjs = widen_string(js);
4148 std::wstring script_id;
4150 webview2_user_script_added_handler handler{[&](HRESULT res, LPCWSTR id) {
4151 if (SUCCEEDED(res)) {
4157 m_webview->AddScriptToExecuteOnDocumentCreated(wjs.c_str(), &handler);
4158 if (SUCCEEDED(res)) {
4161 deplete_run_loop_event_queue();
4166 return user_script{js, std::unique_ptr<user_script::impl>{
4167 new user_script::impl{script_id, wjs}}};
4171 remove_all_user_scripts_impl(
const std::list<user_script> &scripts)
override {
4172 for (
const auto &script : scripts) {
4173 const auto &
id = script.get_impl().get_id();
4174 m_webview->RemoveScriptToExecuteOnDocumentCreated(
id.c_str());
4178 bool are_user_scripts_equal_impl(
const user_script &first,
4179 const user_script &second)
override {
4180 const auto &first_id = first.get_impl().get_id();
4181 const auto &second_id = second.get_impl().get_id();
4182 return first_id == second_id;
4186 noresult embed(HWND wnd,
bool debug, msg_cb_t cb) {
4187 std::atomic_flag flag = ATOMIC_FLAG_INIT;
4188 flag.test_and_set();
4190 wchar_t currentExePath[MAX_PATH];
4191 GetModuleFileNameW(
nullptr, currentExePath, MAX_PATH);
4192 wchar_t *currentExeName = PathFindFileNameW(currentExePath);
4194 wchar_t dataPath[MAX_PATH];
4196 SHGetFolderPathW(
nullptr, CSIDL_APPDATA,
nullptr, 0, dataPath))) {
4199 wchar_t userDataFolder[MAX_PATH];
4200 PathCombineW(userDataFolder, dataPath, currentExeName);
4202 m_com_handler =
new webview2_com_handler(
4204 [&](ICoreWebView2Controller *controller, ICoreWebView2 *webview) {
4205 if (!controller || !webview) {
4209 controller->AddRef();
4211 m_controller = controller;
4212 m_webview = webview;
4216 m_com_handler->set_attempt_handler([&] {
4217 return m_webview2_loader.create_environment_with_options(
4218 nullptr, userDataFolder,
nullptr, m_com_handler);
4220 m_com_handler->try_create_environment();
4223 bool got_quit_msg =
false;
4225 while (flag.test_and_set() && GetMessageW(&msg,
nullptr, 0, 0) >= 0) {
4226 if (msg.message == WM_QUIT) {
4227 got_quit_msg =
true;
4230 TranslateMessage(&msg);
4231 DispatchMessageW(&msg);
4236 if (!m_controller || !m_webview) {
4239 ICoreWebView2Settings *settings =
nullptr;
4240 auto res = m_webview->get_Settings(&settings);
4244 res = settings->put_AreDevToolsEnabled(debug ? TRUE : FALSE);
4247 "put_AreDevToolsEnabled failed"};
4249 res = settings->put_IsStatusBarEnabled(FALSE);
4252 "put_IsStatusBarEnabled failed"};
4254 add_init_script(
"function(message) {\n\
4255 return window.chrome.webview.postMessage(message);\n\
4258 m_controller->put_IsVisible(TRUE);
4259 ShowWindow(m_widget, SW_SHOW);
4260 UpdateWindow(m_widget);
4261 if (m_owns_window) {
4267 void resize_widget() {
4270 if (GetClientRect(GetParent(m_widget), &r)) {
4271 MoveWindow(m_widget, r.left, r.top, r.right - r.left, r.bottom - r.top,
4277 void resize_webview() {
4278 if (m_widget && m_controller) {
4280 if (GetClientRect(m_widget, &bounds)) {
4281 m_controller->put_Bounds(bounds);
4286 void focus_webview() {
4288 m_controller->MoveFocus(COREWEBVIEW2_MOVE_FOCUS_REASON_PROGRAMMATIC);
4292 bool is_webview2_available() const noexcept {
4293 LPWSTR version_info =
nullptr;
4294 auto res = m_webview2_loader.get_available_browser_version_string(
4295 nullptr, &version_info);
4298 auto ok = SUCCEEDED(res) && version_info;
4300 CoTaskMemFree(version_info);
4305 void on_dpi_changed(
int dpi) {
4306 auto scaled_size = get_scaled_size(m_dpi, dpi);
4308 make_window_frame_size(m_window, scaled_size.cx, scaled_size.cy, dpi);
4309 SetWindowPos(m_window,
nullptr, 0, 0, frame_size.cx, frame_size.cy,
4310 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE | SWP_FRAMECHANGED);
4314 SIZE get_size()
const {
4316 GetClientRect(m_window, &bounds);
4317 auto width = bounds.right - bounds.left;
4318 auto height = bounds.bottom - bounds.top;
4319 return {width, height};
4322 SIZE get_scaled_size(
int from_dpi,
int to_dpi)
const {
4323 auto size = get_size();
4324 return scale_size(size.cx, size.cy, from_dpi, to_dpi);
4327 void on_system_setting_change(
const wchar_t *area) {
4329 if (lstrcmpW(area, L
"ImmersiveColorSet") == 0) {
4330 apply_window_theme(m_window);
4335 void deplete_run_loop_event_queue() {
4337 dispatch([&] { done =
true; });
4340 if (GetMessageW(&msg,
nullptr, 0, 0) > 0) {
4341 TranslateMessage(&msg);
4342 DispatchMessageW(&msg);
4350 com_init_wrapper m_com_init;
4351 HWND m_window =
nullptr;
4352 HWND m_widget =
nullptr;
4353 HWND m_message_window =
nullptr;
4354 POINT m_minsz = POINT{0, 0};
4355 POINT m_maxsz = POINT{0, 0};
4356 DWORD m_main_thread = GetCurrentThreadId();
4357 ICoreWebView2 *m_webview =
nullptr;
4358 ICoreWebView2Controller *m_controller =
nullptr;
4359 webview2_com_handler *m_com_handler =
nullptr;
4360 mswebview2::loader m_webview2_loader;
4362 bool m_owns_window{};
4367using browser_engine = detail::win32_edge_engine;
4374using webview = browser_engine;
4378inline webview *cast_to_webview(
void *w) {
4381 "Cannot cast null pointer to webview instance"};
4383 return static_cast<webview *
>(w);
4390 using namespace webview::detail;
4391 webview::webview *w{};
4392 auto err = api_filter(
4393 [=]() -> webview::result<webview::webview *> {
4394 return new webview::webview{
static_cast<bool>(debug), wnd};
4396 [&](webview::webview *w_) { w = w_; });
4404 using namespace webview::detail;
4405 return api_filter([=]() -> webview::noresult {
4406 delete cast_to_webview(w);
4412 using namespace webview::detail;
4413 return api_filter([=] {
return cast_to_webview(w)->run(); });
4417 using namespace webview::detail;
4418 return api_filter([=] {
return cast_to_webview(w)->terminate(); });
4424 using namespace webview::detail;
4429 [=] {
return cast_to_webview(w)->dispatch([=]() { fn(w, arg); }); });
4433 using namespace webview::detail;
4434 void *window =
nullptr;
4435 auto err = api_filter([=] {
return cast_to_webview(w)->window(); },
4436 [&](
void *value) { window = value; });
4445 using namespace webview::detail;
4447 auto err = api_filter(
4448 [=]() -> webview::result<void *> {
4449 auto *w_ = cast_to_webview(w);
4452 return w_->window();
4454 return w_->widget();
4456 return w_->browser_controller();
4461 [&](
void *handle_) { handle = handle_; });
4469 using namespace webview::detail;
4473 return api_filter([=] {
return cast_to_webview(w)->set_title(title); });
4478 using namespace webview::detail;
4480 [=] {
return cast_to_webview(w)->set_size(width, height, hints); });
4484 using namespace webview::detail;
4488 return api_filter([=] {
return cast_to_webview(w)->navigate(url); });
4492 using namespace webview::detail;
4496 return api_filter([=] {
return cast_to_webview(w)->set_html(html); });
4500 using namespace webview::detail;
4504 return api_filter([=] {
return cast_to_webview(w)->init(js); });
4508 using namespace webview::detail;
4512 return api_filter([=] {
return cast_to_webview(w)->eval(js); });
4516 void (*fn)(
const char *
id,
4517 const char *req,
void *arg),
4519 using namespace webview::detail;
4523 return api_filter([=] {
4524 return cast_to_webview(w)->bind(
4526 [=](
const std::string &seq,
const std::string &req,
void *arg_) {
4527 fn(seq.c_str(), req.c_str(), arg_);
4534 using namespace webview::detail;
4538 return api_filter([=] {
return cast_to_webview(w)->unbind(name); });
4542 int status,
const char *result) {
4543 using namespace webview::detail;
4544 if (!
id || !result) {
4548 [=] {
return cast_to_webview(w)->resolve(
id, status, result); });
4552 return &webview::detail::library_version_info;
Holds the library's version information.
Definition webview.h:127
char build_metadata[48]
SemVer 2.0.0 build metadata prefixed with "+", otherwise an empty string.
Definition webview.h:136
webview_version_t version
The elements of the version number.
Definition webview.h:129
char version_number[32]
SemVer 2.0.0 version number in MAJOR.MINOR.PATCH format.
Definition webview.h:131
char pre_release[48]
Definition webview.h:134
Holds the elements of a MAJOR.MINOR.PATCH version number.
Definition webview.h:117
unsigned int major
Major version.
Definition webview.h:119
unsigned int minor
Minor version.
Definition webview.h:121
unsigned int patch
Patch version.
Definition webview.h:123
void * webview_t
Pointer to a webview instance.
Definition webview.h:140
WEBVIEW_API webview_error_t webview_bind(webview_t w, const char *name, void(*fn)(const char *id, const char *req, void *arg), void *arg)
WEBVIEW_API const webview_version_info_t * webview_version(void)
WEBVIEW_API webview_error_t webview_run(webview_t w)
#define WEBVIEW_VERSION_BUILD_METADATA
SemVer 2.0.0 build metadata prefixed with "+".
Definition webview.h:89
WEBVIEW_API webview_error_t webview_terminate(webview_t w)
WEBVIEW_API webview_error_t webview_unbind(webview_t w, const char *name)
#define WEBVIEW_API
Definition webview.h:60
WEBVIEW_API void * webview_get_window(webview_t w)
webview_native_handle_kind_t
Native handle kind. The actual type depends on the backend.
Definition webview.h:143
@ WEBVIEW_NATIVE_HANDLE_KIND_BROWSER_CONTROLLER
Definition webview.h:153
@ WEBVIEW_NATIVE_HANDLE_KIND_UI_WIDGET
Definition webview.h:149
@ WEBVIEW_NATIVE_HANDLE_KIND_UI_WINDOW
Definition webview.h:146
WEBVIEW_API void * webview_get_native_handle(webview_t w, webview_native_handle_kind_t kind)
WEBVIEW_API webview_error_t webview_set_title(webview_t w, const char *title)
#define WEBVIEW_VERSION_MINOR
The current library minor version.
Definition webview.h:74
#define WEBVIEW_VERSION_MAJOR
The current library major version.
Definition webview.h:69
WEBVIEW_API webview_error_t webview_destroy(webview_t w)
WEBVIEW_API webview_error_t webview_navigate(webview_t w, const char *url)
WEBVIEW_API webview_error_t webview_dispatch(webview_t w, void(*fn)(webview_t w, void *arg), void *arg)
WEBVIEW_API webview_t webview_create(int debug, void *window)
webview_hint_t
Window size hints.
Definition webview.h:157
@ WEBVIEW_HINT_NONE
Width and height are default size.
Definition webview.h:159
@ WEBVIEW_HINT_MIN
Width and height are minimum bounds.
Definition webview.h:161
@ WEBVIEW_HINT_FIXED
Window size can not be changed by a user.
Definition webview.h:165
@ WEBVIEW_HINT_MAX
Width and height are maximum bounds.
Definition webview.h:163
#define WEBVIEW_VERSION_PRE_RELEASE
SemVer 2.0.0 pre-release labels prefixed with "-".
Definition webview.h:84
WEBVIEW_API webview_error_t webview_eval(webview_t w, const char *js)
webview_error_t
Error codes returned to callers of the API.
Definition webview.h:184
@ WEBVIEW_ERROR_INVALID_ARGUMENT
One or more invalid arguments have been specified e.g. in a function call.
Definition webview.h:192
@ WEBVIEW_ERROR_DUPLICATE
Signifies that something already exists.
Definition webview.h:199
@ WEBVIEW_ERROR_NOT_FOUND
Signifies that something does not exist.
Definition webview.h:201
@ WEBVIEW_ERROR_MISSING_DEPENDENCY
Missing dependency.
Definition webview.h:186
@ WEBVIEW_ERROR_INVALID_STATE
Invalid state detected.
Definition webview.h:190
@ WEBVIEW_ERROR_CANCELED
Operation canceled.
Definition webview.h:188
@ WEBVIEW_ERROR_UNSPECIFIED
An unspecified error occurred. A more specific error code may be needed.
Definition webview.h:194
@ WEBVIEW_ERROR_OK
Definition webview.h:197
WEBVIEW_API webview_error_t webview_set_size(webview_t w, int width, int height, webview_hint_t hints)
WEBVIEW_API webview_error_t webview_return(webview_t w, const char *id, int status, const char *result)
#define WEBVIEW_VERSION_PATCH
The current library patch version.
Definition webview.h:79
WEBVIEW_API webview_error_t webview_init(webview_t w, const char *js)
#define WEBVIEW_VERSION_NUMBER
SemVer 2.0.0 version number in MAJOR.MINOR.PATCH format.
Definition webview.h:109
WEBVIEW_API webview_error_t webview_set_html(webview_t w, const char *html)