├── .gitignore ├── LICENSE ├── README.md ├── project.janet ├── test └── test.janet ├── webview.c └── webview.h /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 Calvin Rose 2 | Copyright (c) 2017 Serge Zaitsev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 8 | of the Software, and to permit persons to whom the Software is furnished to do 9 | so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # webview 2 | 3 | [Janet](https://janet-lang.org) bindings to the [webview](https://github.com/zserge/webview) project. 4 | Use an embedded browser view to build UI with minimal dependencies. 5 | 6 | ## Building 7 | 8 | To build, one must have janet installed on their system. 9 | 10 | So far, these bindings have only been tested on linux, but should work on macos and 11 | windows with some debugging. 12 | 13 | Use the build.janet script to build the file `webview.so` on macos and linux, and `webview.dll` 14 | on windows. 15 | 16 | ``` 17 | jpm build 18 | ``` 19 | 20 | ## Testing 21 | 22 | Run a sample application that opens Hacker News with 23 | 24 | ``` 25 | jpm test 26 | ``` 27 | 28 | ## API 29 | 30 | The API pretty closely matches the C API for webview. See the C api for more information. 31 | 32 | ### `(webview/init & options)` 33 | 34 | Create a new webview handle. The options are: 35 | - :title - string 36 | - :width - integer 37 | - :height - integer 38 | - :url - string 39 | - :debug - boolean 40 | - :resizable - boolean 41 | 42 | ### `(webview/loop handle [,blocking])` 43 | 44 | Process on event in a loop. Typical usage looks like 45 | 46 | ``` 47 | (while (webview/loop my-handle) nil) 48 | ``` 49 | 50 | ### `(webview/set-title handle title)` 51 | 52 | Set the title of the webview. 53 | 54 | ### `(webview/exit handle)` 55 | 56 | Closes the window. 57 | 58 | ### `(webview/terminate handle)` 59 | 60 | Exists the webview loop. 61 | 62 | ### `(webview/debug str)` 63 | 64 | Prints a string to the appropriate debug console. 65 | 66 | ### `(webview/eval handle javascript)` 67 | 68 | Evaluate some JavaScript inside the webview browser. 69 | -------------------------------------------------------------------------------- /project.janet: -------------------------------------------------------------------------------- 1 | (declare-project 2 | :name "webview" 3 | :author "Calvin Rose" 4 | :license "MIT" 5 | :url "https://github.com/janet-lang/webview" 6 | :repo "git+https://github.com/janet-lang/webview.git") 7 | 8 | (defn shell 9 | "Helper for using pkg-config" 10 | [cmd] 11 | (def p (os/spawn (string/split " " cmd) :p {:in :pipe :out :pipe})) 12 | (def output (:read (p :out) :all)) 13 | (string/replace "\n" "" output)) 14 | 15 | (def webview-def (case (os/which) 16 | :windows "WEBVIEW_WINAPI" 17 | :macos "WEBVIEW_COCOA" 18 | "WEBVIEW_GTK")) 19 | 20 | (defn parts 21 | "Split on whitespace." 22 | [str] 23 | (peg/match 24 | '{:ws (set " \t\n\r\0") 25 | :non-ws '(some (if-not :ws 1)) 26 | :main (any (* (any :ws) :non-ws))} 27 | str)) 28 | 29 | (def more-cflags (case (os/which) 30 | # :windows "-lole32 -lcomctl32 -loleaut32 -luuid -mwindows" # flags are for mingw 31 | :windows [] 32 | :macos [] 33 | (parts 34 | (shell `pkg-config --cflags gtk+-3.0 webkit2gtk-4.0`)))) 35 | 36 | (def more-lflags (case (os/which) 37 | # :windows "-lole32 -lcomctl32 -loleaut32 -luuid -mwindows" # flags are for mingw 38 | :windows [] 39 | :macos ["-framework" "WebKit"] 40 | (parts 41 | (shell `pkg-config --libs gtk+-3.0 webkit2gtk-4.0`)))) 42 | 43 | (declare-native 44 | :name "webview" 45 | :cflags [;default-cflags ;more-cflags] 46 | :lflags [;default-lflags ;more-lflags] 47 | :defines {webview-def true} 48 | :source @["webview.c"]) 49 | -------------------------------------------------------------------------------- /test/test.janet: -------------------------------------------------------------------------------- 1 | # A simple test application 2 | 3 | (import ../build/webview :as webview) 4 | 5 | (def wv (webview/init 6 | :title "Hacker News" 7 | :url "https://news.ycombinator.com" 8 | :width 1000 9 | :height 800 10 | :resizable true 11 | :debug false)) 12 | 13 | # Main loop 14 | (while (webview/loop wv) nil) 15 | 16 | (webview/terminate wv) 17 | -------------------------------------------------------------------------------- /webview.c: -------------------------------------------------------------------------------- 1 | #define WEBVIEW_IMPLEMENTATION 2 | #include "webview.h" 3 | #include "janet.h" 4 | 5 | #if defined(JANET_WINDOWS) 6 | #pragma comment( lib, "gdi32" ) 7 | #pragma comment( lib, "advapi32" ) 8 | #endif 9 | 10 | JanetAbstractType WV = { 11 | "webview/view", 12 | NULL, 13 | NULL, 14 | NULL, 15 | NULL, 16 | NULL, 17 | NULL, 18 | NULL 19 | }; 20 | 21 | /* Get an integer from the option table */ 22 | static int opt_int(JanetTable *opts, const char *key, int default_value) { 23 | Janet keyword = janet_ckeywordv(key); 24 | Janet check = janet_table_get(opts, keyword); 25 | if (janet_checkint(check)) 26 | return janet_unwrap_integer(check); 27 | if (janet_checktype(check, JANET_NIL)) 28 | return default_value; 29 | janet_panicf("expected integer for option :%s, got %v", key, check); 30 | return 0; 31 | } 32 | 33 | /* Get an integer from the option table */ 34 | static const char * opt_string(JanetTable *opts, const char *key, const char *default_value) { 35 | Janet keyword = janet_ckeywordv(key); 36 | Janet check = janet_table_get(opts, keyword); 37 | if (janet_checktype(check, JANET_STRING)) 38 | return (const char *) janet_unwrap_string(check); 39 | if (janet_checktype(check, JANET_NIL)) 40 | return default_value; 41 | janet_panicf("expected string for option :%s, got %v", key, check); 42 | return ""; 43 | } 44 | 45 | /* Check boolean option */ 46 | static int opt_bool(JanetTable *opts, const char *key, int default_value) { 47 | Janet keyword = janet_ckeywordv(key); 48 | Janet check = janet_table_get(opts, keyword); 49 | if (janet_checktypes(check, JANET_TFLAG_BOOLEAN)) 50 | return janet_unwrap_boolean(check); 51 | if (janet_checktype(check, JANET_NIL)) 52 | return default_value; 53 | janet_panicf("expected string for option :%s, got %v", key, check); 54 | return 0; 55 | } 56 | 57 | static Janet cfun_init(int32_t argc, Janet *argv) { 58 | if (argc & 1) janet_panic("expected even number of arguments"); 59 | JanetTable *opts = janet_table(argc / 2); 60 | for (int32_t i = 0; i < argc; i += 2) 61 | janet_table_put(opts, argv[i], argv[i + 1]); 62 | 63 | const char *title = opt_string(opts, "title", "Janet Webview Application"); 64 | const char *url = opt_string(opts, "url", "http://localhost"); 65 | int width = opt_int(opts, "width", 800); 66 | int height = opt_int(opts, "height", 600); 67 | int resizable = opt_bool(opts, "resizable", 1); 68 | int debug = opt_bool(opts, "debug", 1); 69 | 70 | struct webview *wv = janet_abstract(&WV, sizeof(struct webview)); 71 | wv->title = title; 72 | wv->url = url; 73 | wv->width = width; 74 | wv->height = height; 75 | wv->debug = debug; 76 | wv->resizable = resizable; 77 | webview_init(wv); 78 | return janet_wrap_abstract(wv); 79 | } 80 | 81 | static Janet cfun_loop(int32_t argc, Janet *argv) { 82 | janet_arity(argc, 1, 2); 83 | int blocking = 1; 84 | struct webview *wv = janet_getabstract(argv, 0, &WV); 85 | if (argc == 2) { 86 | blocking = janet_getboolean(argv, 1); 87 | } 88 | return janet_wrap_boolean(webview_loop(wv, blocking) == 0); 89 | } 90 | 91 | static Janet cfun_exit(int32_t argc, Janet *argv) { 92 | janet_fixarity(argc, 1); 93 | struct webview *wv = janet_getabstract(argv, 0, &WV); 94 | webview_exit(wv); 95 | return argv[0]; 96 | } 97 | 98 | static Janet cfun_set_title(int32_t argc, Janet *argv) { 99 | janet_fixarity(argc, 2); 100 | struct webview *wv = janet_getabstract(argv, 0, &WV); 101 | const char *str = (const char *) janet_getstring(argv, 1); 102 | webview_set_title(wv, str); 103 | return argv[0]; 104 | } 105 | 106 | static Janet cfun_terminate(int32_t argc, Janet *argv) { 107 | janet_fixarity(argc, 1); 108 | struct webview *wv = janet_getabstract(argv, 0, &WV); 109 | webview_terminate(wv); 110 | return argv[0]; 111 | } 112 | 113 | static Janet cfun_debug(int32_t argc, Janet *argv) { 114 | janet_fixarity(argc, 1); 115 | const char *str = (const char *) janet_getstring(argv, 0); 116 | webview_debug("%s", str); 117 | return argv[0]; 118 | } 119 | 120 | static Janet cfun_eval(int32_t argc, Janet *argv) { 121 | janet_fixarity(argc, 2); 122 | struct webview *wv = janet_getabstract(argv, 0, &WV); 123 | const char *str = (const char *) janet_getstring(argv, 1); 124 | webview_eval(wv, str); 125 | return argv[0]; 126 | } 127 | 128 | /****************/ 129 | /* Module Entry */ 130 | /****************/ 131 | 132 | static const JanetReg cfuns[] = { 133 | { 134 | "init", cfun_init, 135 | "(webview/init & options)\n\n" 136 | "Initialize a new webview object. Returns a handle for webview which can be further manipulated." 137 | }, 138 | { 139 | "loop", cfun_loop, 140 | "(webview/loop handle [,blocking])\n\n" 141 | "Get the next webview event. Returns handle." 142 | }, 143 | { 144 | "exit", cfun_exit, 145 | "(webview/exit handle)\n\n" 146 | "Close a webview handle. This means closing the webview window." 147 | }, 148 | { 149 | "set-title", cfun_set_title, 150 | "(webview/set-title handle title)\n\n" 151 | "Sets the title of the webview window. Returns the handle." 152 | }, 153 | { 154 | "terminate", cfun_terminate, 155 | "(webview/terminate handle)\n\n" 156 | "Exits the main loop. Returns the handle." 157 | }, 158 | { 159 | "debug", cfun_debug, 160 | "(webview/debug str)\n\n" 161 | "Prints a string to the console or debug output for the embedded browser. Returns str." 162 | }, 163 | { 164 | "eval", cfun_eval, 165 | "(webview/eval handle javascript)\n\n" 166 | "Evaluate javascript inside the browser. Returns handle." 167 | }, 168 | {NULL, NULL, NULL} 169 | }; 170 | 171 | JANET_MODULE_ENTRY(JanetTable *env) { 172 | janet_cfuns(env, "webview", cfuns); 173 | } 174 | -------------------------------------------------------------------------------- /webview.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2017 Serge Zaitsev 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | #ifndef WEBVIEW_H 25 | #define WEBVIEW_H 26 | 27 | #ifdef __cplusplus 28 | extern "C" { 29 | #endif 30 | 31 | #ifdef WEBVIEW_STATIC 32 | #define WEBVIEW_API static 33 | #else 34 | #define WEBVIEW_API extern 35 | #endif 36 | 37 | #include 38 | #include 39 | #include 40 | 41 | #if defined(WEBVIEW_GTK) 42 | #include 43 | #include 44 | #include 45 | 46 | struct webview_priv { 47 | GtkWidget *window; 48 | GtkWidget *scroller; 49 | GtkWidget *webview; 50 | GtkWidget *inspector_window; 51 | GAsyncQueue *queue; 52 | int ready; 53 | int js_busy; 54 | int should_exit; 55 | }; 56 | #elif defined(WEBVIEW_WINAPI) 57 | #define CINTERFACE 58 | #include 59 | 60 | #include 61 | #include 62 | #include 63 | #include 64 | #include 65 | 66 | #include 67 | 68 | struct webview_priv { 69 | HWND hwnd; 70 | IOleObject **browser; 71 | BOOL is_fullscreen; 72 | DWORD saved_style; 73 | DWORD saved_ex_style; 74 | RECT saved_rect; 75 | }; 76 | #elif defined(WEBVIEW_COCOA) 77 | #include 78 | #include 79 | #include 80 | 81 | struct webview_priv { 82 | id pool; 83 | id window; 84 | id webview; 85 | id windowDelegate; 86 | int should_exit; 87 | }; 88 | #else 89 | #error "Define one of: WEBVIEW_GTK, WEBVIEW_COCOA or WEBVIEW_WINAPI" 90 | #endif 91 | 92 | struct webview; 93 | 94 | typedef void (*webview_external_invoke_cb_t)(struct webview *w, 95 | const char *arg); 96 | 97 | struct webview { 98 | const char *url; 99 | const char *title; 100 | int width; 101 | int height; 102 | int resizable; 103 | int debug; 104 | webview_external_invoke_cb_t external_invoke_cb; 105 | struct webview_priv priv; 106 | void *userdata; 107 | }; 108 | 109 | enum webview_dialog_type { 110 | WEBVIEW_DIALOG_TYPE_OPEN = 0, 111 | WEBVIEW_DIALOG_TYPE_SAVE = 1, 112 | WEBVIEW_DIALOG_TYPE_ALERT = 2 113 | }; 114 | 115 | #define WEBVIEW_DIALOG_FLAG_FILE (0 << 0) 116 | #define WEBVIEW_DIALOG_FLAG_DIRECTORY (1 << 0) 117 | 118 | #define WEBVIEW_DIALOG_FLAG_INFO (1 << 1) 119 | #define WEBVIEW_DIALOG_FLAG_WARNING (2 << 1) 120 | #define WEBVIEW_DIALOG_FLAG_ERROR (3 << 1) 121 | #define WEBVIEW_DIALOG_FLAG_ALERT_MASK (3 << 1) 122 | 123 | typedef void (*webview_dispatch_fn)(struct webview *w, void *arg); 124 | 125 | struct webview_dispatch_arg { 126 | webview_dispatch_fn fn; 127 | struct webview *w; 128 | void *arg; 129 | }; 130 | 131 | #define DEFAULT_URL \ 132 | "data:text/" \ 133 | "html,%3C%21DOCTYPE%20html%3E%0A%3Chtml%20lang=%22en%22%3E%0A%3Chead%3E%" \ 134 | "3Cmeta%20charset=%22utf-8%22%3E%3Cmeta%20http-equiv=%22X-UA-Compatible%22%" \ 135 | "20content=%22IE=edge%22%3E%3C%2Fhead%3E%0A%3Cbody%3E%3Cdiv%20id=%22app%22%" \ 136 | "3E%3C%2Fdiv%3E%3Cscript%20type=%22text%2Fjavascript%22%3E%3C%2Fscript%3E%" \ 137 | "3C%2Fbody%3E%0A%3C%2Fhtml%3E" 138 | 139 | #define CSS_INJECT_FUNCTION \ 140 | "(function(e){var " \ 141 | "t=document.createElement('style'),d=document.head||document." \ 142 | "getElementsByTagName('head')[0];t.setAttribute('type','text/" \ 143 | "css'),t.styleSheet?t.styleSheet.cssText=e:t.appendChild(document." \ 144 | "createTextNode(e)),d.appendChild(t)})" 145 | 146 | static const char *webview_check_url(const char *url) { 147 | if (url == NULL || strlen(url) == 0) { 148 | return DEFAULT_URL; 149 | } 150 | return url; 151 | } 152 | 153 | WEBVIEW_API int webview(const char *title, const char *url, int width, 154 | int height, int resizable); 155 | 156 | WEBVIEW_API int webview_init(struct webview *w); 157 | WEBVIEW_API int webview_loop(struct webview *w, int blocking); 158 | WEBVIEW_API int webview_eval(struct webview *w, const char *js); 159 | WEBVIEW_API int webview_inject_css(struct webview *w, const char *css); 160 | WEBVIEW_API void webview_set_title(struct webview *w, const char *title); 161 | WEBVIEW_API void webview_set_fullscreen(struct webview *w, int fullscreen); 162 | WEBVIEW_API void webview_set_color(struct webview *w, uint8_t r, uint8_t g, 163 | uint8_t b, uint8_t a); 164 | WEBVIEW_API void webview_dialog(struct webview *w, 165 | enum webview_dialog_type dlgtype, int flags, 166 | const char *title, const char *arg, 167 | char *result, size_t resultsz); 168 | WEBVIEW_API void webview_dispatch(struct webview *w, webview_dispatch_fn fn, 169 | void *arg); 170 | WEBVIEW_API void webview_terminate(struct webview *w); 171 | WEBVIEW_API void webview_exit(struct webview *w); 172 | WEBVIEW_API void webview_debug(const char *format, ...); 173 | WEBVIEW_API void webview_print_log(const char *s); 174 | 175 | #ifdef WEBVIEW_IMPLEMENTATION 176 | #undef WEBVIEW_IMPLEMENTATION 177 | 178 | WEBVIEW_API int webview(const char *title, const char *url, int width, 179 | int height, int resizable) { 180 | struct webview webview; 181 | memset(&webview, 0, sizeof(webview)); 182 | webview.title = title; 183 | webview.url = url; 184 | webview.width = width; 185 | webview.height = height; 186 | webview.resizable = resizable; 187 | int r = webview_init(&webview); 188 | if (r != 0) { 189 | return r; 190 | } 191 | while (webview_loop(&webview, 1) == 0) { 192 | } 193 | webview_exit(&webview); 194 | return 0; 195 | } 196 | 197 | WEBVIEW_API void webview_debug(const char *format, ...) { 198 | char buf[4096]; 199 | va_list ap; 200 | va_start(ap, format); 201 | vsnprintf(buf, sizeof(buf), format, ap); 202 | webview_print_log(buf); 203 | va_end(ap); 204 | } 205 | 206 | static int webview_js_encode(const char *s, char *esc, size_t n) { 207 | int r = 1; /* At least one byte for trailing zero */ 208 | for (; *s; s++) { 209 | const unsigned char c = *s; 210 | if (c >= 0x20 && c < 0x80 && strchr("<>\\'\"", c) == NULL) { 211 | if (n > 0) { 212 | *esc++ = c; 213 | n--; 214 | } 215 | r++; 216 | } else { 217 | if (n > 0) { 218 | snprintf(esc, n, "\\x%02x", (int)c); 219 | esc += 4; 220 | n -= 4; 221 | } 222 | r += 4; 223 | } 224 | } 225 | return r; 226 | } 227 | 228 | WEBVIEW_API int webview_inject_css(struct webview *w, const char *css) { 229 | int n = webview_js_encode(css, NULL, 0); 230 | char *esc = (char *)calloc(1, sizeof(CSS_INJECT_FUNCTION) + n + 4); 231 | if (esc == NULL) { 232 | return -1; 233 | } 234 | char *js = (char *)calloc(1, n); 235 | webview_js_encode(css, js, n); 236 | snprintf(esc, sizeof(CSS_INJECT_FUNCTION) + n + 4, "%s(\"%s\")", 237 | CSS_INJECT_FUNCTION, js); 238 | int r = webview_eval(w, esc); 239 | free(js); 240 | free(esc); 241 | return r; 242 | } 243 | 244 | #if defined(WEBVIEW_GTK) 245 | static void external_message_received_cb(WebKitUserContentManager *m, 246 | WebKitJavascriptResult *r, 247 | gpointer arg) { 248 | (void)m; 249 | struct webview *w = (struct webview *)arg; 250 | if (w->external_invoke_cb == NULL) { 251 | return; 252 | } 253 | JSGlobalContextRef context = webkit_javascript_result_get_global_context(r); 254 | JSValueRef value = webkit_javascript_result_get_value(r); 255 | JSStringRef js = JSValueToStringCopy(context, value, NULL); 256 | size_t n = JSStringGetMaximumUTF8CStringSize(js); 257 | char *s = g_new(char, n); 258 | JSStringGetUTF8CString(js, s, n); 259 | w->external_invoke_cb(w, s); 260 | JSStringRelease(js); 261 | g_free(s); 262 | } 263 | 264 | static void webview_load_changed_cb(WebKitWebView *webview, 265 | WebKitLoadEvent event, gpointer arg) { 266 | (void)webview; 267 | struct webview *w = (struct webview *)arg; 268 | if (event == WEBKIT_LOAD_FINISHED) { 269 | w->priv.ready = 1; 270 | } 271 | } 272 | 273 | static void webview_destroy_cb(GtkWidget *widget, gpointer arg) { 274 | (void)widget; 275 | struct webview *w = (struct webview *)arg; 276 | webview_terminate(w); 277 | } 278 | 279 | static gboolean webview_context_menu_cb(WebKitWebView *webview, 280 | GtkWidget *default_menu, 281 | WebKitHitTestResult *hit_test_result, 282 | gboolean triggered_with_keyboard, 283 | gpointer userdata) { 284 | (void)webview; 285 | (void)default_menu; 286 | (void)hit_test_result; 287 | (void)triggered_with_keyboard; 288 | (void)userdata; 289 | return TRUE; 290 | } 291 | 292 | WEBVIEW_API int webview_init(struct webview *w) { 293 | if (gtk_init_check(0, NULL) == FALSE) { 294 | return -1; 295 | } 296 | 297 | w->priv.ready = 0; 298 | w->priv.should_exit = 0; 299 | w->priv.queue = g_async_queue_new(); 300 | w->priv.window = gtk_window_new(GTK_WINDOW_TOPLEVEL); 301 | gtk_window_set_title(GTK_WINDOW(w->priv.window), w->title); 302 | 303 | if (w->resizable) { 304 | gtk_window_set_default_size(GTK_WINDOW(w->priv.window), w->width, 305 | w->height); 306 | } else { 307 | gtk_widget_set_size_request(w->priv.window, w->width, w->height); 308 | } 309 | gtk_window_set_resizable(GTK_WINDOW(w->priv.window), !!w->resizable); 310 | gtk_window_set_position(GTK_WINDOW(w->priv.window), GTK_WIN_POS_CENTER); 311 | 312 | w->priv.scroller = gtk_scrolled_window_new(NULL, NULL); 313 | gtk_container_add(GTK_CONTAINER(w->priv.window), w->priv.scroller); 314 | 315 | WebKitUserContentManager *m = webkit_user_content_manager_new(); 316 | webkit_user_content_manager_register_script_message_handler(m, "external"); 317 | g_signal_connect(m, "script-message-received::external", 318 | G_CALLBACK(external_message_received_cb), w); 319 | 320 | w->priv.webview = webkit_web_view_new_with_user_content_manager(m); 321 | webkit_web_view_load_uri(WEBKIT_WEB_VIEW(w->priv.webview), 322 | webview_check_url(w->url)); 323 | g_signal_connect(G_OBJECT(w->priv.webview), "load-changed", 324 | G_CALLBACK(webview_load_changed_cb), w); 325 | gtk_container_add(GTK_CONTAINER(w->priv.scroller), w->priv.webview); 326 | 327 | if (w->debug) { 328 | WebKitSettings *settings = 329 | webkit_web_view_get_settings(WEBKIT_WEB_VIEW(w->priv.webview)); 330 | webkit_settings_set_enable_write_console_messages_to_stdout(settings, true); 331 | webkit_settings_set_enable_developer_extras(settings, true); 332 | } else { 333 | g_signal_connect(G_OBJECT(w->priv.webview), "context-menu", 334 | G_CALLBACK(webview_context_menu_cb), w); 335 | } 336 | 337 | gtk_widget_show_all(w->priv.window); 338 | 339 | webkit_web_view_run_javascript( 340 | WEBKIT_WEB_VIEW(w->priv.webview), 341 | "window.external={invoke:function(x){" 342 | "window.webkit.messageHandlers.external.postMessage(x);}}", 343 | NULL, NULL, NULL); 344 | 345 | g_signal_connect(G_OBJECT(w->priv.window), "destroy", 346 | G_CALLBACK(webview_destroy_cb), w); 347 | return 0; 348 | } 349 | 350 | WEBVIEW_API int webview_loop(struct webview *w, int blocking) { 351 | gtk_main_iteration_do(blocking); 352 | return w->priv.should_exit; 353 | } 354 | 355 | WEBVIEW_API void webview_set_title(struct webview *w, const char *title) { 356 | gtk_window_set_title(GTK_WINDOW(w->priv.window), title); 357 | } 358 | 359 | WEBVIEW_API void webview_set_fullscreen(struct webview *w, int fullscreen) { 360 | if (fullscreen) { 361 | gtk_window_fullscreen(GTK_WINDOW(w->priv.window)); 362 | } else { 363 | gtk_window_unfullscreen(GTK_WINDOW(w->priv.window)); 364 | } 365 | } 366 | 367 | WEBVIEW_API void webview_set_color(struct webview *w, uint8_t r, uint8_t g, 368 | uint8_t b, uint8_t a) { 369 | GdkRGBA color = {r / 255.0, g / 255.0, b / 255.0, a / 255.0}; 370 | webkit_web_view_set_background_color(WEBKIT_WEB_VIEW(w->priv.webview), 371 | &color); 372 | } 373 | 374 | WEBVIEW_API void webview_dialog(struct webview *w, 375 | enum webview_dialog_type dlgtype, int flags, 376 | const char *title, const char *arg, 377 | char *result, size_t resultsz) { 378 | GtkWidget *dlg; 379 | if (result != NULL) { 380 | result[0] = '\0'; 381 | } 382 | if (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN || 383 | dlgtype == WEBVIEW_DIALOG_TYPE_SAVE) { 384 | dlg = gtk_file_chooser_dialog_new( 385 | title, GTK_WINDOW(w->priv.window), 386 | (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN 387 | ? (flags & WEBVIEW_DIALOG_FLAG_DIRECTORY 388 | ? GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER 389 | : GTK_FILE_CHOOSER_ACTION_OPEN) 390 | : GTK_FILE_CHOOSER_ACTION_SAVE), 391 | "_Cancel", GTK_RESPONSE_CANCEL, 392 | (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN ? "_Open" : "_Save"), 393 | GTK_RESPONSE_ACCEPT, NULL); 394 | gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(dlg), FALSE); 395 | gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dlg), FALSE); 396 | gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(dlg), TRUE); 397 | gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dlg), TRUE); 398 | gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER(dlg), TRUE); 399 | gint response = gtk_dialog_run(GTK_DIALOG(dlg)); 400 | if (response == GTK_RESPONSE_ACCEPT) { 401 | gchar *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dlg)); 402 | g_strlcpy(result, filename, resultsz); 403 | g_free(filename); 404 | } 405 | gtk_widget_destroy(dlg); 406 | } else if (dlgtype == WEBVIEW_DIALOG_TYPE_ALERT) { 407 | GtkMessageType type = GTK_MESSAGE_OTHER; 408 | switch (flags & WEBVIEW_DIALOG_FLAG_ALERT_MASK) { 409 | case WEBVIEW_DIALOG_FLAG_INFO: 410 | type = GTK_MESSAGE_INFO; 411 | break; 412 | case WEBVIEW_DIALOG_FLAG_WARNING: 413 | type = GTK_MESSAGE_WARNING; 414 | break; 415 | case WEBVIEW_DIALOG_FLAG_ERROR: 416 | type = GTK_MESSAGE_ERROR; 417 | break; 418 | } 419 | dlg = gtk_message_dialog_new(GTK_WINDOW(w->priv.window), GTK_DIALOG_MODAL, 420 | type, GTK_BUTTONS_OK, "%s", title); 421 | gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dlg), "%s", 422 | arg); 423 | gtk_dialog_run(GTK_DIALOG(dlg)); 424 | gtk_widget_destroy(dlg); 425 | } 426 | } 427 | 428 | static void webview_eval_finished(GObject *object, GAsyncResult *result, 429 | gpointer userdata) { 430 | (void)object; 431 | (void)result; 432 | struct webview *w = (struct webview *)userdata; 433 | w->priv.js_busy = 0; 434 | } 435 | 436 | WEBVIEW_API int webview_eval(struct webview *w, const char *js) { 437 | while (w->priv.ready == 0) { 438 | g_main_context_iteration(NULL, TRUE); 439 | } 440 | w->priv.js_busy = 1; 441 | webkit_web_view_run_javascript(WEBKIT_WEB_VIEW(w->priv.webview), js, NULL, 442 | webview_eval_finished, w); 443 | while (w->priv.js_busy) { 444 | g_main_context_iteration(NULL, TRUE); 445 | } 446 | return 0; 447 | } 448 | 449 | static gboolean webview_dispatch_wrapper(gpointer userdata) { 450 | struct webview *w = (struct webview *)userdata; 451 | for (;;) { 452 | struct webview_dispatch_arg *arg = 453 | (struct webview_dispatch_arg *)g_async_queue_try_pop(w->priv.queue); 454 | if (arg == NULL) { 455 | break; 456 | } 457 | (arg->fn)(w, arg->arg); 458 | g_free(arg); 459 | } 460 | return FALSE; 461 | } 462 | 463 | WEBVIEW_API void webview_dispatch(struct webview *w, webview_dispatch_fn fn, 464 | void *arg) { 465 | struct webview_dispatch_arg *context = 466 | (struct webview_dispatch_arg *)g_new(struct webview_dispatch_arg, 1); 467 | context->w = w; 468 | context->arg = arg; 469 | context->fn = fn; 470 | g_async_queue_lock(w->priv.queue); 471 | g_async_queue_push_unlocked(w->priv.queue, context); 472 | if (g_async_queue_length_unlocked(w->priv.queue) == 1) { 473 | gdk_threads_add_idle(webview_dispatch_wrapper, w); 474 | } 475 | g_async_queue_unlock(w->priv.queue); 476 | } 477 | 478 | WEBVIEW_API void webview_terminate(struct webview *w) { 479 | w->priv.should_exit = 1; 480 | } 481 | 482 | WEBVIEW_API void webview_exit(struct webview *w) { (void)w; } 483 | WEBVIEW_API void webview_print_log(const char *s) { 484 | fprintf(stderr, "%s\n", s); 485 | } 486 | 487 | #endif /* WEBVIEW_GTK */ 488 | 489 | #if defined(WEBVIEW_WINAPI) 490 | 491 | #pragma comment(lib, "user32.lib") 492 | #pragma comment(lib, "ole32.lib") 493 | #pragma comment(lib, "oleaut32.lib") 494 | 495 | #define WM_WEBVIEW_DISPATCH (WM_APP + 1) 496 | 497 | typedef struct { 498 | IOleInPlaceFrame frame; 499 | HWND window; 500 | } _IOleInPlaceFrameEx; 501 | 502 | typedef struct { 503 | IOleInPlaceSite inplace; 504 | _IOleInPlaceFrameEx frame; 505 | } _IOleInPlaceSiteEx; 506 | 507 | typedef struct { 508 | IDocHostUIHandler ui; 509 | } _IDocHostUIHandlerEx; 510 | 511 | typedef struct { 512 | IInternetSecurityManager mgr; 513 | } _IInternetSecurityManagerEx; 514 | 515 | typedef struct { 516 | IServiceProvider provider; 517 | _IInternetSecurityManagerEx mgr; 518 | } _IServiceProviderEx; 519 | 520 | typedef struct { 521 | IOleClientSite client; 522 | _IOleInPlaceSiteEx inplace; 523 | _IDocHostUIHandlerEx ui; 524 | IDispatch external; 525 | _IServiceProviderEx provider; 526 | } _IOleClientSiteEx; 527 | 528 | #ifdef __cplusplus 529 | #define iid_ref(x) &(x) 530 | #define iid_unref(x) *(x) 531 | #else 532 | #define iid_ref(x) (x) 533 | #define iid_unref(x) (x) 534 | #endif 535 | 536 | static inline WCHAR *webview_to_utf16(const char *s) { 537 | DWORD size = MultiByteToWideChar(CP_UTF8, 0, s, -1, 0, 0); 538 | WCHAR *ws = (WCHAR *)GlobalAlloc(GMEM_FIXED, sizeof(WCHAR) * size); 539 | if (ws == NULL) { 540 | return NULL; 541 | } 542 | MultiByteToWideChar(CP_UTF8, 0, s, -1, ws, size); 543 | return ws; 544 | } 545 | 546 | static inline char *webview_from_utf16(WCHAR *ws) { 547 | int n = WideCharToMultiByte(CP_UTF8, 0, ws, -1, NULL, 0, NULL, NULL); 548 | char *s = (char *)GlobalAlloc(GMEM_FIXED, n); 549 | if (s == NULL) { 550 | return NULL; 551 | } 552 | WideCharToMultiByte(CP_UTF8, 0, ws, -1, s, n, NULL, NULL); 553 | return s; 554 | } 555 | 556 | static int iid_eq(REFIID a, const IID *b) { 557 | return memcmp((const void *)iid_ref(a), (const void *)b, sizeof(GUID)) == 0; 558 | } 559 | 560 | static HRESULT STDMETHODCALLTYPE JS_QueryInterface(IDispatch FAR *This, 561 | REFIID riid, 562 | LPVOID FAR *ppvObj) { 563 | if (iid_eq(riid, &IID_IDispatch)) { 564 | *ppvObj = This; 565 | return S_OK; 566 | } 567 | *ppvObj = 0; 568 | return E_NOINTERFACE; 569 | } 570 | static ULONG STDMETHODCALLTYPE JS_AddRef(IDispatch FAR *This) { return 1; } 571 | static ULONG STDMETHODCALLTYPE JS_Release(IDispatch FAR *This) { return 1; } 572 | static HRESULT STDMETHODCALLTYPE JS_GetTypeInfoCount(IDispatch FAR *This, 573 | UINT *pctinfo) { 574 | return S_OK; 575 | } 576 | static HRESULT STDMETHODCALLTYPE JS_GetTypeInfo(IDispatch FAR *This, 577 | UINT iTInfo, LCID lcid, 578 | ITypeInfo **ppTInfo) { 579 | return S_OK; 580 | } 581 | #define WEBVIEW_JS_INVOKE_ID 0x1000 582 | static HRESULT STDMETHODCALLTYPE JS_GetIDsOfNames(IDispatch FAR *This, 583 | REFIID riid, 584 | LPOLESTR *rgszNames, 585 | UINT cNames, LCID lcid, 586 | DISPID *rgDispId) { 587 | if (cNames != 1) { 588 | return S_FALSE; 589 | } 590 | if (wcscmp(rgszNames[0], L"invoke") == 0) { 591 | rgDispId[0] = WEBVIEW_JS_INVOKE_ID; 592 | return S_OK; 593 | } 594 | return S_FALSE; 595 | } 596 | 597 | static HRESULT STDMETHODCALLTYPE 598 | JS_Invoke(IDispatch FAR *This, DISPID dispIdMember, REFIID riid, LCID lcid, 599 | WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, 600 | EXCEPINFO *pExcepInfo, UINT *puArgErr) { 601 | size_t offset = (size_t) & ((_IOleClientSiteEx *)NULL)->external; 602 | _IOleClientSiteEx *ex = (_IOleClientSiteEx *)((char *)(This)-offset); 603 | struct webview *w = (struct webview *)GetWindowLongPtr( 604 | ex->inplace.frame.window, GWLP_USERDATA); 605 | if (pDispParams->cArgs == 1 && pDispParams->rgvarg[0].vt == VT_BSTR) { 606 | BSTR bstr = pDispParams->rgvarg[0].bstrVal; 607 | char *s = webview_from_utf16(bstr); 608 | if (s != NULL) { 609 | if (dispIdMember == WEBVIEW_JS_INVOKE_ID) { 610 | if (w->external_invoke_cb != NULL) { 611 | w->external_invoke_cb(w, s); 612 | } 613 | } else { 614 | return S_FALSE; 615 | } 616 | GlobalFree(s); 617 | } 618 | } 619 | return S_OK; 620 | } 621 | 622 | static IDispatchVtbl ExternalDispatchTable = { 623 | JS_QueryInterface, JS_AddRef, JS_Release, JS_GetTypeInfoCount, 624 | JS_GetTypeInfo, JS_GetIDsOfNames, JS_Invoke}; 625 | 626 | static ULONG STDMETHODCALLTYPE Site_AddRef(IOleClientSite FAR *This) { 627 | return 1; 628 | } 629 | static ULONG STDMETHODCALLTYPE Site_Release(IOleClientSite FAR *This) { 630 | return 1; 631 | } 632 | static HRESULT STDMETHODCALLTYPE Site_SaveObject(IOleClientSite FAR *This) { 633 | return E_NOTIMPL; 634 | } 635 | static HRESULT STDMETHODCALLTYPE Site_GetMoniker(IOleClientSite FAR *This, 636 | DWORD dwAssign, 637 | DWORD dwWhichMoniker, 638 | IMoniker **ppmk) { 639 | return E_NOTIMPL; 640 | } 641 | static HRESULT STDMETHODCALLTYPE 642 | Site_GetContainer(IOleClientSite FAR *This, LPOLECONTAINER FAR *ppContainer) { 643 | *ppContainer = 0; 644 | return E_NOINTERFACE; 645 | } 646 | static HRESULT STDMETHODCALLTYPE Site_ShowObject(IOleClientSite FAR *This) { 647 | return NOERROR; 648 | } 649 | static HRESULT STDMETHODCALLTYPE Site_OnShowWindow(IOleClientSite FAR *This, 650 | BOOL fShow) { 651 | return E_NOTIMPL; 652 | } 653 | static HRESULT STDMETHODCALLTYPE 654 | Site_RequestNewObjectLayout(IOleClientSite FAR *This) { 655 | return E_NOTIMPL; 656 | } 657 | static HRESULT STDMETHODCALLTYPE Site_QueryInterface(IOleClientSite FAR *This, 658 | REFIID riid, 659 | void **ppvObject) { 660 | if (iid_eq(riid, &IID_IUnknown) || iid_eq(riid, &IID_IOleClientSite)) { 661 | *ppvObject = &((_IOleClientSiteEx *)This)->client; 662 | } else if (iid_eq(riid, &IID_IOleInPlaceSite)) { 663 | *ppvObject = &((_IOleClientSiteEx *)This)->inplace; 664 | } else if (iid_eq(riid, &IID_IDocHostUIHandler)) { 665 | *ppvObject = &((_IOleClientSiteEx *)This)->ui; 666 | } else if (iid_eq(riid, &IID_IServiceProvider)) { 667 | *ppvObject = &((_IOleClientSiteEx *)This)->provider; 668 | } else { 669 | *ppvObject = 0; 670 | return (E_NOINTERFACE); 671 | } 672 | return S_OK; 673 | } 674 | static HRESULT STDMETHODCALLTYPE InPlace_QueryInterface( 675 | IOleInPlaceSite FAR *This, REFIID riid, LPVOID FAR *ppvObj) { 676 | return (Site_QueryInterface( 677 | (IOleClientSite *)((char *)This - sizeof(IOleClientSite)), riid, ppvObj)); 678 | } 679 | static ULONG STDMETHODCALLTYPE InPlace_AddRef(IOleInPlaceSite FAR *This) { 680 | return 1; 681 | } 682 | static ULONG STDMETHODCALLTYPE InPlace_Release(IOleInPlaceSite FAR *This) { 683 | return 1; 684 | } 685 | static HRESULT STDMETHODCALLTYPE InPlace_GetWindow(IOleInPlaceSite FAR *This, 686 | HWND FAR *lphwnd) { 687 | *lphwnd = ((_IOleInPlaceSiteEx FAR *)This)->frame.window; 688 | return S_OK; 689 | } 690 | static HRESULT STDMETHODCALLTYPE 691 | InPlace_ContextSensitiveHelp(IOleInPlaceSite FAR *This, BOOL fEnterMode) { 692 | return E_NOTIMPL; 693 | } 694 | static HRESULT STDMETHODCALLTYPE 695 | InPlace_CanInPlaceActivate(IOleInPlaceSite FAR *This) { 696 | return S_OK; 697 | } 698 | static HRESULT STDMETHODCALLTYPE 699 | InPlace_OnInPlaceActivate(IOleInPlaceSite FAR *This) { 700 | return S_OK; 701 | } 702 | static HRESULT STDMETHODCALLTYPE 703 | InPlace_OnUIActivate(IOleInPlaceSite FAR *This) { 704 | return S_OK; 705 | } 706 | static HRESULT STDMETHODCALLTYPE InPlace_GetWindowContext( 707 | IOleInPlaceSite FAR *This, LPOLEINPLACEFRAME FAR *lplpFrame, 708 | LPOLEINPLACEUIWINDOW FAR *lplpDoc, LPRECT lprcPosRect, LPRECT lprcClipRect, 709 | LPOLEINPLACEFRAMEINFO lpFrameInfo) { 710 | *lplpFrame = (LPOLEINPLACEFRAME) & ((_IOleInPlaceSiteEx *)This)->frame; 711 | *lplpDoc = 0; 712 | lpFrameInfo->fMDIApp = FALSE; 713 | lpFrameInfo->hwndFrame = ((_IOleInPlaceFrameEx *)*lplpFrame)->window; 714 | lpFrameInfo->haccel = 0; 715 | lpFrameInfo->cAccelEntries = 0; 716 | return S_OK; 717 | } 718 | static HRESULT STDMETHODCALLTYPE InPlace_Scroll(IOleInPlaceSite FAR *This, 719 | SIZE scrollExtent) { 720 | return E_NOTIMPL; 721 | } 722 | static HRESULT STDMETHODCALLTYPE 723 | InPlace_OnUIDeactivate(IOleInPlaceSite FAR *This, BOOL fUndoable) { 724 | return S_OK; 725 | } 726 | static HRESULT STDMETHODCALLTYPE 727 | InPlace_OnInPlaceDeactivate(IOleInPlaceSite FAR *This) { 728 | return S_OK; 729 | } 730 | static HRESULT STDMETHODCALLTYPE 731 | InPlace_DiscardUndoState(IOleInPlaceSite FAR *This) { 732 | return E_NOTIMPL; 733 | } 734 | static HRESULT STDMETHODCALLTYPE 735 | InPlace_DeactivateAndUndo(IOleInPlaceSite FAR *This) { 736 | return E_NOTIMPL; 737 | } 738 | static HRESULT STDMETHODCALLTYPE 739 | InPlace_OnPosRectChange(IOleInPlaceSite FAR *This, LPCRECT lprcPosRect) { 740 | IOleObject *browserObject; 741 | IOleInPlaceObject *inplace; 742 | browserObject = *((IOleObject **)((char *)This - sizeof(IOleObject *) - 743 | sizeof(IOleClientSite))); 744 | if (!browserObject->lpVtbl->QueryInterface(browserObject, 745 | iid_unref(&IID_IOleInPlaceObject), 746 | (void **)&inplace)) { 747 | inplace->lpVtbl->SetObjectRects(inplace, lprcPosRect, lprcPosRect); 748 | inplace->lpVtbl->Release(inplace); 749 | } 750 | return S_OK; 751 | } 752 | static HRESULT STDMETHODCALLTYPE Frame_QueryInterface( 753 | IOleInPlaceFrame FAR *This, REFIID riid, LPVOID FAR *ppvObj) { 754 | return E_NOTIMPL; 755 | } 756 | static ULONG STDMETHODCALLTYPE Frame_AddRef(IOleInPlaceFrame FAR *This) { 757 | return 1; 758 | } 759 | static ULONG STDMETHODCALLTYPE Frame_Release(IOleInPlaceFrame FAR *This) { 760 | return 1; 761 | } 762 | static HRESULT STDMETHODCALLTYPE Frame_GetWindow(IOleInPlaceFrame FAR *This, 763 | HWND FAR *lphwnd) { 764 | *lphwnd = ((_IOleInPlaceFrameEx *)This)->window; 765 | return S_OK; 766 | } 767 | static HRESULT STDMETHODCALLTYPE 768 | Frame_ContextSensitiveHelp(IOleInPlaceFrame FAR *This, BOOL fEnterMode) { 769 | return E_NOTIMPL; 770 | } 771 | static HRESULT STDMETHODCALLTYPE Frame_GetBorder(IOleInPlaceFrame FAR *This, 772 | LPRECT lprectBorder) { 773 | return E_NOTIMPL; 774 | } 775 | static HRESULT STDMETHODCALLTYPE Frame_RequestBorderSpace( 776 | IOleInPlaceFrame FAR *This, LPCBORDERWIDTHS pborderwidths) { 777 | return E_NOTIMPL; 778 | } 779 | static HRESULT STDMETHODCALLTYPE Frame_SetBorderSpace( 780 | IOleInPlaceFrame FAR *This, LPCBORDERWIDTHS pborderwidths) { 781 | return E_NOTIMPL; 782 | } 783 | static HRESULT STDMETHODCALLTYPE Frame_SetActiveObject( 784 | IOleInPlaceFrame FAR *This, IOleInPlaceActiveObject *pActiveObject, 785 | LPCOLESTR pszObjName) { 786 | return S_OK; 787 | } 788 | static HRESULT STDMETHODCALLTYPE 789 | Frame_InsertMenus(IOleInPlaceFrame FAR *This, HMENU hmenuShared, 790 | LPOLEMENUGROUPWIDTHS lpMenuWidths) { 791 | return E_NOTIMPL; 792 | } 793 | static HRESULT STDMETHODCALLTYPE Frame_SetMenu(IOleInPlaceFrame FAR *This, 794 | HMENU hmenuShared, 795 | HOLEMENU holemenu, 796 | HWND hwndActiveObject) { 797 | return S_OK; 798 | } 799 | static HRESULT STDMETHODCALLTYPE Frame_RemoveMenus(IOleInPlaceFrame FAR *This, 800 | HMENU hmenuShared) { 801 | return E_NOTIMPL; 802 | } 803 | static HRESULT STDMETHODCALLTYPE Frame_SetStatusText(IOleInPlaceFrame FAR *This, 804 | LPCOLESTR pszStatusText) { 805 | return S_OK; 806 | } 807 | static HRESULT STDMETHODCALLTYPE 808 | Frame_EnableModeless(IOleInPlaceFrame FAR *This, BOOL fEnable) { 809 | return S_OK; 810 | } 811 | static HRESULT STDMETHODCALLTYPE 812 | Frame_TranslateAccelerator(IOleInPlaceFrame FAR *This, LPMSG lpmsg, WORD wID) { 813 | return E_NOTIMPL; 814 | } 815 | static HRESULT STDMETHODCALLTYPE UI_QueryInterface(IDocHostUIHandler FAR *This, 816 | REFIID riid, 817 | LPVOID FAR *ppvObj) { 818 | return (Site_QueryInterface((IOleClientSite *)((char *)This - 819 | sizeof(IOleClientSite) - 820 | sizeof(_IOleInPlaceSiteEx)), 821 | riid, ppvObj)); 822 | } 823 | static ULONG STDMETHODCALLTYPE UI_AddRef(IDocHostUIHandler FAR *This) { 824 | return 1; 825 | } 826 | static ULONG STDMETHODCALLTYPE UI_Release(IDocHostUIHandler FAR *This) { 827 | return 1; 828 | } 829 | static HRESULT STDMETHODCALLTYPE UI_ShowContextMenu( 830 | IDocHostUIHandler FAR *This, DWORD dwID, POINT __RPC_FAR *ppt, 831 | IUnknown __RPC_FAR *pcmdtReserved, IDispatch __RPC_FAR *pdispReserved) { 832 | return S_OK; 833 | } 834 | static HRESULT STDMETHODCALLTYPE 835 | UI_GetHostInfo(IDocHostUIHandler FAR *This, DOCHOSTUIINFO __RPC_FAR *pInfo) { 836 | pInfo->cbSize = sizeof(DOCHOSTUIINFO); 837 | pInfo->dwFlags = DOCHOSTUIFLAG_NO3DBORDER; 838 | pInfo->dwDoubleClick = DOCHOSTUIDBLCLK_DEFAULT; 839 | return S_OK; 840 | } 841 | static HRESULT STDMETHODCALLTYPE UI_ShowUI( 842 | IDocHostUIHandler FAR *This, DWORD dwID, 843 | IOleInPlaceActiveObject __RPC_FAR *pActiveObject, 844 | IOleCommandTarget __RPC_FAR *pCommandTarget, 845 | IOleInPlaceFrame __RPC_FAR *pFrame, IOleInPlaceUIWindow __RPC_FAR *pDoc) { 846 | return S_OK; 847 | } 848 | static HRESULT STDMETHODCALLTYPE UI_HideUI(IDocHostUIHandler FAR *This) { 849 | return S_OK; 850 | } 851 | static HRESULT STDMETHODCALLTYPE UI_UpdateUI(IDocHostUIHandler FAR *This) { 852 | return S_OK; 853 | } 854 | static HRESULT STDMETHODCALLTYPE UI_EnableModeless(IDocHostUIHandler FAR *This, 855 | BOOL fEnable) { 856 | return S_OK; 857 | } 858 | static HRESULT STDMETHODCALLTYPE 859 | UI_OnDocWindowActivate(IDocHostUIHandler FAR *This, BOOL fActivate) { 860 | return S_OK; 861 | } 862 | static HRESULT STDMETHODCALLTYPE 863 | UI_OnFrameWindowActivate(IDocHostUIHandler FAR *This, BOOL fActivate) { 864 | return S_OK; 865 | } 866 | static HRESULT STDMETHODCALLTYPE 867 | UI_ResizeBorder(IDocHostUIHandler FAR *This, LPCRECT prcBorder, 868 | IOleInPlaceUIWindow __RPC_FAR *pUIWindow, BOOL fRameWindow) { 869 | return S_OK; 870 | } 871 | static HRESULT STDMETHODCALLTYPE 872 | UI_TranslateAccelerator(IDocHostUIHandler FAR *This, LPMSG lpMsg, 873 | const GUID __RPC_FAR *pguidCmdGroup, DWORD nCmdID) { 874 | return S_FALSE; 875 | } 876 | static HRESULT STDMETHODCALLTYPE UI_GetOptionKeyPath( 877 | IDocHostUIHandler FAR *This, LPOLESTR __RPC_FAR *pchKey, DWORD dw) { 878 | return S_FALSE; 879 | } 880 | static HRESULT STDMETHODCALLTYPE UI_GetDropTarget( 881 | IDocHostUIHandler FAR *This, IDropTarget __RPC_FAR *pDropTarget, 882 | IDropTarget __RPC_FAR *__RPC_FAR *ppDropTarget) { 883 | return S_FALSE; 884 | } 885 | static HRESULT STDMETHODCALLTYPE UI_GetExternal( 886 | IDocHostUIHandler FAR *This, IDispatch __RPC_FAR *__RPC_FAR *ppDispatch) { 887 | *ppDispatch = (IDispatch *)(This + 1); 888 | return S_OK; 889 | } 890 | static HRESULT STDMETHODCALLTYPE UI_TranslateUrl( 891 | IDocHostUIHandler FAR *This, DWORD dwTranslate, OLECHAR __RPC_FAR *pchURLIn, 892 | OLECHAR __RPC_FAR *__RPC_FAR *ppchURLOut) { 893 | *ppchURLOut = 0; 894 | return S_FALSE; 895 | } 896 | static HRESULT STDMETHODCALLTYPE 897 | UI_FilterDataObject(IDocHostUIHandler FAR *This, IDataObject __RPC_FAR *pDO, 898 | IDataObject __RPC_FAR *__RPC_FAR *ppDORet) { 899 | *ppDORet = 0; 900 | return S_FALSE; 901 | } 902 | 903 | static const TCHAR *classname = "WebView"; 904 | static const SAFEARRAYBOUND ArrayBound = {1, 0}; 905 | 906 | static IOleClientSiteVtbl MyIOleClientSiteTable = { 907 | Site_QueryInterface, Site_AddRef, Site_Release, 908 | Site_SaveObject, Site_GetMoniker, Site_GetContainer, 909 | Site_ShowObject, Site_OnShowWindow, Site_RequestNewObjectLayout}; 910 | static IOleInPlaceSiteVtbl MyIOleInPlaceSiteTable = { 911 | InPlace_QueryInterface, 912 | InPlace_AddRef, 913 | InPlace_Release, 914 | InPlace_GetWindow, 915 | InPlace_ContextSensitiveHelp, 916 | InPlace_CanInPlaceActivate, 917 | InPlace_OnInPlaceActivate, 918 | InPlace_OnUIActivate, 919 | InPlace_GetWindowContext, 920 | InPlace_Scroll, 921 | InPlace_OnUIDeactivate, 922 | InPlace_OnInPlaceDeactivate, 923 | InPlace_DiscardUndoState, 924 | InPlace_DeactivateAndUndo, 925 | InPlace_OnPosRectChange}; 926 | 927 | static IOleInPlaceFrameVtbl MyIOleInPlaceFrameTable = { 928 | Frame_QueryInterface, 929 | Frame_AddRef, 930 | Frame_Release, 931 | Frame_GetWindow, 932 | Frame_ContextSensitiveHelp, 933 | Frame_GetBorder, 934 | Frame_RequestBorderSpace, 935 | Frame_SetBorderSpace, 936 | Frame_SetActiveObject, 937 | Frame_InsertMenus, 938 | Frame_SetMenu, 939 | Frame_RemoveMenus, 940 | Frame_SetStatusText, 941 | Frame_EnableModeless, 942 | Frame_TranslateAccelerator}; 943 | 944 | static IDocHostUIHandlerVtbl MyIDocHostUIHandlerTable = { 945 | UI_QueryInterface, 946 | UI_AddRef, 947 | UI_Release, 948 | UI_ShowContextMenu, 949 | UI_GetHostInfo, 950 | UI_ShowUI, 951 | UI_HideUI, 952 | UI_UpdateUI, 953 | UI_EnableModeless, 954 | UI_OnDocWindowActivate, 955 | UI_OnFrameWindowActivate, 956 | UI_ResizeBorder, 957 | UI_TranslateAccelerator, 958 | UI_GetOptionKeyPath, 959 | UI_GetDropTarget, 960 | UI_GetExternal, 961 | UI_TranslateUrl, 962 | UI_FilterDataObject}; 963 | 964 | 965 | 966 | static HRESULT STDMETHODCALLTYPE IS_QueryInterface(IInternetSecurityManager FAR *This, REFIID riid, void **ppvObject) { 967 | return E_NOTIMPL; 968 | } 969 | static ULONG STDMETHODCALLTYPE IS_AddRef(IInternetSecurityManager FAR *This) { return 1; } 970 | static ULONG STDMETHODCALLTYPE IS_Release(IInternetSecurityManager FAR *This) { return 1; } 971 | static HRESULT STDMETHODCALLTYPE IS_SetSecuritySite(IInternetSecurityManager FAR *This, IInternetSecurityMgrSite *pSited) { 972 | return INET_E_DEFAULT_ACTION; 973 | } 974 | static HRESULT STDMETHODCALLTYPE IS_GetSecuritySite(IInternetSecurityManager FAR *This, IInternetSecurityMgrSite **ppSite) { 975 | return INET_E_DEFAULT_ACTION; 976 | } 977 | static HRESULT STDMETHODCALLTYPE IS_MapUrlToZone(IInternetSecurityManager FAR *This, LPCWSTR pwszUrl, DWORD *pdwZone, DWORD dwFlags) { 978 | *pdwZone = URLZONE_LOCAL_MACHINE; 979 | return S_OK; 980 | } 981 | static HRESULT STDMETHODCALLTYPE IS_GetSecurityId(IInternetSecurityManager FAR *This, LPCWSTR pwszUrl, BYTE *pbSecurityId, DWORD *pcbSecurityId, DWORD_PTR dwReserved) { 982 | return INET_E_DEFAULT_ACTION; 983 | } 984 | static HRESULT STDMETHODCALLTYPE IS_ProcessUrlAction(IInternetSecurityManager FAR *This, LPCWSTR pwszUrl, DWORD dwAction, BYTE *pPolicy, DWORD cbPolicy, BYTE *pContext, DWORD cbContext, DWORD dwFlags, DWORD dwReserved) { 985 | return INET_E_DEFAULT_ACTION; 986 | } 987 | static HRESULT STDMETHODCALLTYPE IS_QueryCustomPolicy(IInternetSecurityManager FAR *This, LPCWSTR pwszUrl, REFGUID guidKey, BYTE **ppPolicy, DWORD *pcbPolicy, BYTE *pContext, DWORD cbContext, DWORD dwReserved) { 988 | return INET_E_DEFAULT_ACTION; 989 | } 990 | static HRESULT STDMETHODCALLTYPE IS_SetZoneMapping(IInternetSecurityManager FAR *This, DWORD dwZone, LPCWSTR lpszPattern, DWORD dwFlags) { 991 | return INET_E_DEFAULT_ACTION; 992 | } 993 | static HRESULT STDMETHODCALLTYPE IS_GetZoneMappings(IInternetSecurityManager FAR *This, DWORD dwZone, IEnumString **ppenumString, DWORD dwFlags) { 994 | return INET_E_DEFAULT_ACTION; 995 | } 996 | static IInternetSecurityManagerVtbl MyInternetSecurityManagerTable = {IS_QueryInterface, IS_AddRef, IS_Release, IS_SetSecuritySite, IS_GetSecuritySite, IS_MapUrlToZone, IS_GetSecurityId, IS_ProcessUrlAction, IS_QueryCustomPolicy, IS_SetZoneMapping, IS_GetZoneMappings}; 997 | 998 | static HRESULT STDMETHODCALLTYPE SP_QueryInterface(IServiceProvider FAR *This, REFIID riid, void **ppvObject) { 999 | return (Site_QueryInterface( 1000 | (IOleClientSite *)((char *)This - sizeof(IOleClientSite) - sizeof(_IOleInPlaceSiteEx) - sizeof(_IDocHostUIHandlerEx) - sizeof(IDispatch)), riid, ppvObject)); 1001 | } 1002 | static ULONG STDMETHODCALLTYPE SP_AddRef(IServiceProvider FAR *This) { return 1; } 1003 | static ULONG STDMETHODCALLTYPE SP_Release(IServiceProvider FAR *This) { return 1; } 1004 | static HRESULT STDMETHODCALLTYPE SP_QueryService(IServiceProvider FAR *This, REFGUID siid, REFIID riid, void **ppvObject) { 1005 | if (iid_eq(siid, &IID_IInternetSecurityManager) && iid_eq(riid, &IID_IInternetSecurityManager)) { 1006 | *ppvObject = &((_IServiceProviderEx *)This)->mgr; 1007 | } else { 1008 | *ppvObject = 0; 1009 | return (E_NOINTERFACE); 1010 | } 1011 | return S_OK; 1012 | } 1013 | static IServiceProviderVtbl MyServiceProviderTable = {SP_QueryInterface, SP_AddRef, SP_Release, SP_QueryService}; 1014 | 1015 | static void UnEmbedBrowserObject(struct webview *w) { 1016 | if (w->priv.browser != NULL) { 1017 | (*w->priv.browser)->lpVtbl->Close(*w->priv.browser, OLECLOSE_NOSAVE); 1018 | (*w->priv.browser)->lpVtbl->Release(*w->priv.browser); 1019 | GlobalFree(w->priv.browser); 1020 | w->priv.browser = NULL; 1021 | } 1022 | } 1023 | 1024 | static int EmbedBrowserObject(struct webview *w) { 1025 | RECT rect; 1026 | IWebBrowser2 *webBrowser2 = NULL; 1027 | LPCLASSFACTORY pClassFactory = NULL; 1028 | _IOleClientSiteEx *_iOleClientSiteEx = NULL; 1029 | IOleObject **browser = (IOleObject **)GlobalAlloc( 1030 | GMEM_FIXED, sizeof(IOleObject *) + sizeof(_IOleClientSiteEx)); 1031 | if (browser == NULL) { 1032 | goto error; 1033 | } 1034 | w->priv.browser = browser; 1035 | 1036 | _iOleClientSiteEx = (_IOleClientSiteEx *)(browser + 1); 1037 | _iOleClientSiteEx->client.lpVtbl = &MyIOleClientSiteTable; 1038 | _iOleClientSiteEx->inplace.inplace.lpVtbl = &MyIOleInPlaceSiteTable; 1039 | _iOleClientSiteEx->inplace.frame.frame.lpVtbl = &MyIOleInPlaceFrameTable; 1040 | _iOleClientSiteEx->inplace.frame.window = w->priv.hwnd; 1041 | _iOleClientSiteEx->ui.ui.lpVtbl = &MyIDocHostUIHandlerTable; 1042 | _iOleClientSiteEx->external.lpVtbl = &ExternalDispatchTable; 1043 | _iOleClientSiteEx->provider.provider.lpVtbl = &MyServiceProviderTable; 1044 | _iOleClientSiteEx->provider.mgr.mgr.lpVtbl = &MyInternetSecurityManagerTable; 1045 | 1046 | if (CoGetClassObject(iid_unref(&CLSID_WebBrowser), 1047 | CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER, NULL, 1048 | iid_unref(&IID_IClassFactory), 1049 | (void **)&pClassFactory) != S_OK) { 1050 | goto error; 1051 | } 1052 | 1053 | if (pClassFactory == NULL) { 1054 | goto error; 1055 | } 1056 | 1057 | if (pClassFactory->lpVtbl->CreateInstance(pClassFactory, 0, 1058 | iid_unref(&IID_IOleObject), 1059 | (void **)browser) != S_OK) { 1060 | goto error; 1061 | } 1062 | pClassFactory->lpVtbl->Release(pClassFactory); 1063 | if ((*browser)->lpVtbl->SetClientSite( 1064 | *browser, (IOleClientSite *)_iOleClientSiteEx) != S_OK) { 1065 | goto error; 1066 | } 1067 | (*browser)->lpVtbl->SetHostNames(*browser, L"My Host Name", 0); 1068 | 1069 | if (OleSetContainedObject((struct IUnknown *)(*browser), TRUE) != S_OK) { 1070 | goto error; 1071 | } 1072 | GetClientRect(w->priv.hwnd, &rect); 1073 | if ((*browser)->lpVtbl->DoVerb((*browser), OLEIVERB_SHOW, NULL, 1074 | (IOleClientSite *)_iOleClientSiteEx, -1, 1075 | w->priv.hwnd, &rect) != S_OK) { 1076 | goto error; 1077 | } 1078 | if ((*browser)->lpVtbl->QueryInterface((*browser), 1079 | iid_unref(&IID_IWebBrowser2), 1080 | (void **)&webBrowser2) != S_OK) { 1081 | goto error; 1082 | } 1083 | 1084 | webBrowser2->lpVtbl->put_Left(webBrowser2, 0); 1085 | webBrowser2->lpVtbl->put_Top(webBrowser2, 0); 1086 | webBrowser2->lpVtbl->put_Width(webBrowser2, rect.right); 1087 | webBrowser2->lpVtbl->put_Height(webBrowser2, rect.bottom); 1088 | webBrowser2->lpVtbl->Release(webBrowser2); 1089 | 1090 | return 0; 1091 | error: 1092 | UnEmbedBrowserObject(w); 1093 | if (pClassFactory != NULL) { 1094 | pClassFactory->lpVtbl->Release(pClassFactory); 1095 | } 1096 | if (browser != NULL) { 1097 | GlobalFree(browser); 1098 | } 1099 | return -1; 1100 | } 1101 | 1102 | #define WEBVIEW_DATA_URL_PREFIX "data:text/html," 1103 | static int DisplayHTMLPage(struct webview *w) { 1104 | IWebBrowser2 *webBrowser2; 1105 | VARIANT myURL; 1106 | LPDISPATCH lpDispatch; 1107 | IHTMLDocument2 *htmlDoc2; 1108 | BSTR bstr; 1109 | IOleObject *browserObject; 1110 | SAFEARRAY *sfArray; 1111 | VARIANT *pVar; 1112 | browserObject = *w->priv.browser; 1113 | int isDataURL = 0; 1114 | const char *webview_url = webview_check_url(w->url); 1115 | if (!browserObject->lpVtbl->QueryInterface( 1116 | browserObject, iid_unref(&IID_IWebBrowser2), (void **)&webBrowser2)) { 1117 | LPCSTR webPageName; 1118 | isDataURL = (strncmp(webview_url, WEBVIEW_DATA_URL_PREFIX, 1119 | strlen(WEBVIEW_DATA_URL_PREFIX)) == 0); 1120 | if (isDataURL) { 1121 | webPageName = "about:blank"; 1122 | } else { 1123 | webPageName = (LPCSTR)webview_url; 1124 | } 1125 | VariantInit(&myURL); 1126 | myURL.vt = VT_BSTR; 1127 | #ifndef UNICODE 1128 | { 1129 | wchar_t *buffer = webview_to_utf16(webPageName); 1130 | if (buffer == NULL) { 1131 | goto badalloc; 1132 | } 1133 | myURL.bstrVal = SysAllocString(buffer); 1134 | GlobalFree(buffer); 1135 | } 1136 | #else 1137 | myURL.bstrVal = SysAllocString(webPageName); 1138 | #endif 1139 | if (!myURL.bstrVal) { 1140 | badalloc: 1141 | webBrowser2->lpVtbl->Release(webBrowser2); 1142 | return (-6); 1143 | } 1144 | webBrowser2->lpVtbl->Navigate2(webBrowser2, &myURL, 0, 0, 0, 0); 1145 | VariantClear(&myURL); 1146 | if (!isDataURL) { 1147 | return 0; 1148 | } 1149 | 1150 | char *url = (char *)calloc(1, strlen(webview_url) + 1); 1151 | char *q = url; 1152 | for (const char *p = webview_url + strlen(WEBVIEW_DATA_URL_PREFIX); *q = *p; 1153 | p++, q++) { 1154 | if (*q == '%' && *(p + 1) && *(p + 2)) { 1155 | sscanf(p + 1, "%02x", q); 1156 | p = p + 2; 1157 | } 1158 | } 1159 | 1160 | if (webBrowser2->lpVtbl->get_Document(webBrowser2, &lpDispatch) == S_OK) { 1161 | if (lpDispatch->lpVtbl->QueryInterface(lpDispatch, 1162 | iid_unref(&IID_IHTMLDocument2), 1163 | (void **)&htmlDoc2) == S_OK) { 1164 | if ((sfArray = SafeArrayCreate(VT_VARIANT, 1, 1165 | (SAFEARRAYBOUND *)&ArrayBound))) { 1166 | if (!SafeArrayAccessData(sfArray, (void **)&pVar)) { 1167 | pVar->vt = VT_BSTR; 1168 | #ifndef UNICODE 1169 | { 1170 | wchar_t *buffer = webview_to_utf16(url); 1171 | if (buffer == NULL) { 1172 | goto release; 1173 | } 1174 | bstr = SysAllocString(buffer); 1175 | GlobalFree(buffer); 1176 | } 1177 | #else 1178 | bstr = SysAllocString(string); 1179 | #endif 1180 | if ((pVar->bstrVal = bstr)) { 1181 | htmlDoc2->lpVtbl->write(htmlDoc2, sfArray); 1182 | htmlDoc2->lpVtbl->close(htmlDoc2); 1183 | } 1184 | } 1185 | SafeArrayDestroy(sfArray); 1186 | } 1187 | release: 1188 | free(url); 1189 | htmlDoc2->lpVtbl->Release(htmlDoc2); 1190 | } 1191 | lpDispatch->lpVtbl->Release(lpDispatch); 1192 | } 1193 | webBrowser2->lpVtbl->Release(webBrowser2); 1194 | return (0); 1195 | } 1196 | return (-5); 1197 | } 1198 | 1199 | static LRESULT CALLBACK wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, 1200 | LPARAM lParam) { 1201 | struct webview *w = (struct webview *)GetWindowLongPtr(hwnd, GWLP_USERDATA); 1202 | switch (uMsg) { 1203 | case WM_CREATE: 1204 | w = (struct webview *)((CREATESTRUCT *)lParam)->lpCreateParams; 1205 | w->priv.hwnd = hwnd; 1206 | return EmbedBrowserObject(w); 1207 | case WM_DESTROY: 1208 | UnEmbedBrowserObject(w); 1209 | PostQuitMessage(0); 1210 | return TRUE; 1211 | case WM_SIZE: { 1212 | IWebBrowser2 *webBrowser2; 1213 | IOleObject *browser = *w->priv.browser; 1214 | if (browser->lpVtbl->QueryInterface(browser, iid_unref(&IID_IWebBrowser2), 1215 | (void **)&webBrowser2) == S_OK) { 1216 | RECT rect; 1217 | GetClientRect(hwnd, &rect); 1218 | webBrowser2->lpVtbl->put_Width(webBrowser2, rect.right); 1219 | webBrowser2->lpVtbl->put_Height(webBrowser2, rect.bottom); 1220 | } 1221 | return TRUE; 1222 | } 1223 | case WM_WEBVIEW_DISPATCH: { 1224 | webview_dispatch_fn f = (webview_dispatch_fn)wParam; 1225 | void *arg = (void *)lParam; 1226 | (*f)(w, arg); 1227 | return TRUE; 1228 | } 1229 | } 1230 | return DefWindowProc(hwnd, uMsg, wParam, lParam); 1231 | } 1232 | 1233 | #define WEBVIEW_KEY_FEATURE_BROWSER_EMULATION \ 1234 | "Software\\Microsoft\\Internet " \ 1235 | "Explorer\\Main\\FeatureControl\\FEATURE_BROWSER_EMULATION" 1236 | 1237 | static int webview_fix_ie_compat_mode() { 1238 | HKEY hKey; 1239 | DWORD ie_version = 11000; 1240 | TCHAR appname[MAX_PATH + 1]; 1241 | TCHAR *p; 1242 | if (GetModuleFileName(NULL, appname, MAX_PATH + 1) == 0) { 1243 | return -1; 1244 | } 1245 | for (p = &appname[strlen(appname) - 1]; p != appname && *p != '\\'; p--) { 1246 | } 1247 | p++; 1248 | if (RegCreateKey(HKEY_CURRENT_USER, WEBVIEW_KEY_FEATURE_BROWSER_EMULATION, 1249 | &hKey) != ERROR_SUCCESS) { 1250 | return -1; 1251 | } 1252 | if (RegSetValueEx(hKey, p, 0, REG_DWORD, (BYTE *)&ie_version, 1253 | sizeof(ie_version)) != ERROR_SUCCESS) { 1254 | RegCloseKey(hKey); 1255 | return -1; 1256 | } 1257 | RegCloseKey(hKey); 1258 | return 0; 1259 | } 1260 | 1261 | WEBVIEW_API int webview_init(struct webview *w) { 1262 | WNDCLASSEX wc; 1263 | HINSTANCE hInstance; 1264 | DWORD style; 1265 | RECT clientRect; 1266 | RECT rect; 1267 | 1268 | if (webview_fix_ie_compat_mode() < 0) { 1269 | return -1; 1270 | } 1271 | 1272 | hInstance = GetModuleHandle(NULL); 1273 | if (hInstance == NULL) { 1274 | return -1; 1275 | } 1276 | if (OleInitialize(NULL) != S_OK) { 1277 | return -1; 1278 | } 1279 | ZeroMemory(&wc, sizeof(WNDCLASSEX)); 1280 | wc.cbSize = sizeof(WNDCLASSEX); 1281 | wc.hInstance = hInstance; 1282 | wc.lpfnWndProc = wndproc; 1283 | wc.lpszClassName = classname; 1284 | RegisterClassEx(&wc); 1285 | 1286 | style = WS_OVERLAPPEDWINDOW; 1287 | if (!w->resizable) { 1288 | style = WS_OVERLAPPED | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU; 1289 | } 1290 | 1291 | rect.left = 0; 1292 | rect.top = 0; 1293 | rect.right = w->width; 1294 | rect.bottom = w->height; 1295 | AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, 0); 1296 | 1297 | GetClientRect(GetDesktopWindow(), &clientRect); 1298 | int left = (clientRect.right / 2) - ((rect.right - rect.left) / 2); 1299 | int top = (clientRect.bottom / 2) - ((rect.bottom - rect.top) / 2); 1300 | rect.right = rect.right - rect.left + left; 1301 | rect.left = left; 1302 | rect.bottom = rect.bottom - rect.top + top; 1303 | rect.top = top; 1304 | 1305 | w->priv.hwnd = 1306 | CreateWindowEx(0, classname, w->title, style, rect.left, rect.top, 1307 | rect.right - rect.left, rect.bottom - rect.top, 1308 | HWND_DESKTOP, NULL, hInstance, (void *)w); 1309 | if (w->priv.hwnd == 0) { 1310 | OleUninitialize(); 1311 | return -1; 1312 | } 1313 | 1314 | SetWindowLongPtr(w->priv.hwnd, GWLP_USERDATA, (LONG_PTR)w); 1315 | 1316 | DisplayHTMLPage(w); 1317 | 1318 | SetWindowText(w->priv.hwnd, w->title); 1319 | ShowWindow(w->priv.hwnd, SW_SHOWDEFAULT); 1320 | UpdateWindow(w->priv.hwnd); 1321 | SetFocus(w->priv.hwnd); 1322 | 1323 | return 0; 1324 | } 1325 | 1326 | WEBVIEW_API int webview_loop(struct webview *w, int blocking) { 1327 | MSG msg; 1328 | if (blocking) { 1329 | GetMessage(&msg, 0, 0, 0); 1330 | } else { 1331 | PeekMessage(&msg, 0, 0, 0, PM_REMOVE); 1332 | } 1333 | switch (msg.message) { 1334 | case WM_QUIT: 1335 | return -1; 1336 | case WM_COMMAND: 1337 | case WM_KEYDOWN: 1338 | case WM_KEYUP: { 1339 | HRESULT r = S_OK; 1340 | IWebBrowser2 *webBrowser2; 1341 | IOleObject *browser = *w->priv.browser; 1342 | if (browser->lpVtbl->QueryInterface(browser, iid_unref(&IID_IWebBrowser2), 1343 | (void **)&webBrowser2) == S_OK) { 1344 | IOleInPlaceActiveObject *pIOIPAO; 1345 | if (browser->lpVtbl->QueryInterface( 1346 | browser, iid_unref(&IID_IOleInPlaceActiveObject), 1347 | (void **)&pIOIPAO) == S_OK) { 1348 | r = pIOIPAO->lpVtbl->TranslateAccelerator(pIOIPAO, &msg); 1349 | pIOIPAO->lpVtbl->Release(pIOIPAO); 1350 | } 1351 | webBrowser2->lpVtbl->Release(webBrowser2); 1352 | } 1353 | if (r != S_FALSE) { 1354 | break; 1355 | } 1356 | } 1357 | default: 1358 | TranslateMessage(&msg); 1359 | DispatchMessage(&msg); 1360 | } 1361 | return 0; 1362 | } 1363 | 1364 | WEBVIEW_API int webview_eval(struct webview *w, const char *js) { 1365 | IWebBrowser2 *webBrowser2; 1366 | IHTMLDocument2 *htmlDoc2; 1367 | IDispatch *docDispatch; 1368 | IDispatch *scriptDispatch; 1369 | if ((*w->priv.browser) 1370 | ->lpVtbl->QueryInterface((*w->priv.browser), 1371 | iid_unref(&IID_IWebBrowser2), 1372 | (void **)&webBrowser2) != S_OK) { 1373 | return -1; 1374 | } 1375 | 1376 | if (webBrowser2->lpVtbl->get_Document(webBrowser2, &docDispatch) != S_OK) { 1377 | return -1; 1378 | } 1379 | if (docDispatch->lpVtbl->QueryInterface(docDispatch, 1380 | iid_unref(&IID_IHTMLDocument2), 1381 | (void **)&htmlDoc2) != S_OK) { 1382 | return -1; 1383 | } 1384 | if (htmlDoc2->lpVtbl->get_Script(htmlDoc2, &scriptDispatch) != S_OK) { 1385 | return -1; 1386 | } 1387 | DISPID dispid; 1388 | BSTR evalStr = SysAllocString(L"eval"); 1389 | if (scriptDispatch->lpVtbl->GetIDsOfNames( 1390 | scriptDispatch, iid_unref(&IID_NULL), &evalStr, 1, 1391 | LOCALE_SYSTEM_DEFAULT, &dispid) != S_OK) { 1392 | SysFreeString(evalStr); 1393 | return -1; 1394 | } 1395 | SysFreeString(evalStr); 1396 | 1397 | DISPPARAMS params; 1398 | VARIANT arg; 1399 | VARIANT result; 1400 | EXCEPINFO excepInfo; 1401 | UINT nArgErr = (UINT)-1; 1402 | params.cArgs = 1; 1403 | params.cNamedArgs = 0; 1404 | params.rgvarg = &arg; 1405 | arg.vt = VT_BSTR; 1406 | static const char *prologue = "(function(){"; 1407 | static const char *epilogue = ";})();"; 1408 | int n = strlen(prologue) + strlen(epilogue) + strlen(js) + 1; 1409 | char *eval = (char *)malloc(n); 1410 | snprintf(eval, n, "%s%s%s", prologue, js, epilogue); 1411 | wchar_t *buf = webview_to_utf16(eval); 1412 | if (buf == NULL) { 1413 | return -1; 1414 | } 1415 | arg.bstrVal = SysAllocString(buf); 1416 | if (scriptDispatch->lpVtbl->Invoke( 1417 | scriptDispatch, dispid, iid_unref(&IID_NULL), 0, DISPATCH_METHOD, 1418 | ¶ms, &result, &excepInfo, &nArgErr) != S_OK) { 1419 | return -1; 1420 | } 1421 | SysFreeString(arg.bstrVal); 1422 | free(eval); 1423 | scriptDispatch->lpVtbl->Release(scriptDispatch); 1424 | htmlDoc2->lpVtbl->Release(htmlDoc2); 1425 | docDispatch->lpVtbl->Release(docDispatch); 1426 | return 0; 1427 | } 1428 | 1429 | WEBVIEW_API void webview_dispatch(struct webview *w, webview_dispatch_fn fn, 1430 | void *arg) { 1431 | PostMessageW(w->priv.hwnd, WM_WEBVIEW_DISPATCH, (WPARAM)fn, (LPARAM)arg); 1432 | } 1433 | 1434 | WEBVIEW_API void webview_set_title(struct webview *w, const char *title) { 1435 | SetWindowText(w->priv.hwnd, title); 1436 | } 1437 | 1438 | WEBVIEW_API void webview_set_fullscreen(struct webview *w, int fullscreen) { 1439 | if (w->priv.is_fullscreen == !!fullscreen) { 1440 | return; 1441 | } 1442 | if (w->priv.is_fullscreen == 0) { 1443 | w->priv.saved_style = GetWindowLong(w->priv.hwnd, GWL_STYLE); 1444 | w->priv.saved_ex_style = GetWindowLong(w->priv.hwnd, GWL_EXSTYLE); 1445 | GetWindowRect(w->priv.hwnd, &w->priv.saved_rect); 1446 | } 1447 | w->priv.is_fullscreen = !!fullscreen; 1448 | if (fullscreen) { 1449 | MONITORINFO monitor_info; 1450 | SetWindowLong(w->priv.hwnd, GWL_STYLE, 1451 | w->priv.saved_style & ~(WS_CAPTION | WS_THICKFRAME)); 1452 | SetWindowLong(w->priv.hwnd, GWL_EXSTYLE, 1453 | w->priv.saved_ex_style & 1454 | ~(WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE | 1455 | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE)); 1456 | monitor_info.cbSize = sizeof(monitor_info); 1457 | GetMonitorInfo(MonitorFromWindow(w->priv.hwnd, MONITOR_DEFAULTTONEAREST), 1458 | &monitor_info); 1459 | RECT r; 1460 | r.left = monitor_info.rcMonitor.left; 1461 | r.top = monitor_info.rcMonitor.top; 1462 | r.right = monitor_info.rcMonitor.right; 1463 | r.bottom = monitor_info.rcMonitor.bottom; 1464 | SetWindowPos(w->priv.hwnd, NULL, r.left, r.top, r.right - r.left, 1465 | r.bottom - r.top, 1466 | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED); 1467 | } else { 1468 | SetWindowLong(w->priv.hwnd, GWL_STYLE, w->priv.saved_style); 1469 | SetWindowLong(w->priv.hwnd, GWL_EXSTYLE, w->priv.saved_ex_style); 1470 | SetWindowPos(w->priv.hwnd, NULL, w->priv.saved_rect.left, 1471 | w->priv.saved_rect.top, 1472 | w->priv.saved_rect.right - w->priv.saved_rect.left, 1473 | w->priv.saved_rect.bottom - w->priv.saved_rect.top, 1474 | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED); 1475 | } 1476 | } 1477 | 1478 | WEBVIEW_API void webview_set_color(struct webview *w, uint8_t r, uint8_t g, 1479 | uint8_t b, uint8_t a) { 1480 | HBRUSH brush = CreateSolidBrush(RGB(r, g, b)); 1481 | SetClassLongPtr(w->priv.hwnd, GCLP_HBRBACKGROUND, (LONG_PTR)brush); 1482 | } 1483 | 1484 | /* These are missing parts from MinGW */ 1485 | #ifndef __IFileDialog_INTERFACE_DEFINED__ 1486 | #define __IFileDialog_INTERFACE_DEFINED__ 1487 | enum _FILEOPENDIALOGOPTIONS { 1488 | FOS_OVERWRITEPROMPT = 0x2, 1489 | FOS_STRICTFILETYPES = 0x4, 1490 | FOS_NOCHANGEDIR = 0x8, 1491 | FOS_PICKFOLDERS = 0x20, 1492 | FOS_FORCEFILESYSTEM = 0x40, 1493 | FOS_ALLNONSTORAGEITEMS = 0x80, 1494 | FOS_NOVALIDATE = 0x100, 1495 | FOS_ALLOWMULTISELECT = 0x200, 1496 | FOS_PATHMUSTEXIST = 0x800, 1497 | FOS_FILEMUSTEXIST = 0x1000, 1498 | FOS_CREATEPROMPT = 0x2000, 1499 | FOS_SHAREAWARE = 0x4000, 1500 | FOS_NOREADONLYRETURN = 0x8000, 1501 | FOS_NOTESTFILECREATE = 0x10000, 1502 | FOS_HIDEMRUPLACES = 0x20000, 1503 | FOS_HIDEPINNEDPLACES = 0x40000, 1504 | FOS_NODEREFERENCELINKS = 0x100000, 1505 | FOS_DONTADDTORECENT = 0x2000000, 1506 | FOS_FORCESHOWHIDDEN = 0x10000000, 1507 | FOS_DEFAULTNOMINIMODE = 0x20000000, 1508 | FOS_FORCEPREVIEWPANEON = 0x40000000 1509 | }; 1510 | typedef DWORD FILEOPENDIALOGOPTIONS; 1511 | typedef enum FDAP { FDAP_BOTTOM = 0, FDAP_TOP = 1 } FDAP; 1512 | DEFINE_GUID(IID_IFileDialog, 0x42f85136, 0xdb7e, 0x439c, 0x85, 0xf1, 0xe4, 0x07, 1513 | 0x5d, 0x13, 0x5f, 0xc8); 1514 | typedef struct IFileDialogVtbl { 1515 | BEGIN_INTERFACE 1516 | HRESULT(STDMETHODCALLTYPE *QueryInterface) 1517 | (IFileDialog *This, REFIID riid, void **ppvObject); 1518 | ULONG(STDMETHODCALLTYPE *AddRef)(IFileDialog *This); 1519 | ULONG(STDMETHODCALLTYPE *Release)(IFileDialog *This); 1520 | HRESULT(STDMETHODCALLTYPE *Show)(IFileDialog *This, HWND hwndOwner); 1521 | HRESULT(STDMETHODCALLTYPE *SetFileTypes) 1522 | (IFileDialog *This, UINT cFileTypes, const COMDLG_FILTERSPEC *rgFilterSpec); 1523 | HRESULT(STDMETHODCALLTYPE *SetFileTypeIndex) 1524 | (IFileDialog *This, UINT iFileType); 1525 | HRESULT(STDMETHODCALLTYPE *GetFileTypeIndex) 1526 | (IFileDialog *This, UINT *piFileType); 1527 | HRESULT(STDMETHODCALLTYPE *Advise) 1528 | (IFileDialog *This, IFileDialogEvents *pfde, DWORD *pdwCookie); 1529 | HRESULT(STDMETHODCALLTYPE *Unadvise)(IFileDialog *This, DWORD dwCookie); 1530 | HRESULT(STDMETHODCALLTYPE *SetOptions) 1531 | (IFileDialog *This, FILEOPENDIALOGOPTIONS fos); 1532 | HRESULT(STDMETHODCALLTYPE *GetOptions) 1533 | (IFileDialog *This, FILEOPENDIALOGOPTIONS *pfos); 1534 | HRESULT(STDMETHODCALLTYPE *SetDefaultFolder) 1535 | (IFileDialog *This, IShellItem *psi); 1536 | HRESULT(STDMETHODCALLTYPE *SetFolder)(IFileDialog *This, IShellItem *psi); 1537 | HRESULT(STDMETHODCALLTYPE *GetFolder)(IFileDialog *This, IShellItem **ppsi); 1538 | HRESULT(STDMETHODCALLTYPE *GetCurrentSelection) 1539 | (IFileDialog *This, IShellItem **ppsi); 1540 | HRESULT(STDMETHODCALLTYPE *SetFileName)(IFileDialog *This, LPCWSTR pszName); 1541 | HRESULT(STDMETHODCALLTYPE *GetFileName)(IFileDialog *This, LPWSTR *pszName); 1542 | HRESULT(STDMETHODCALLTYPE *SetTitle)(IFileDialog *This, LPCWSTR pszTitle); 1543 | HRESULT(STDMETHODCALLTYPE *SetOkButtonLabel) 1544 | (IFileDialog *This, LPCWSTR pszText); 1545 | HRESULT(STDMETHODCALLTYPE *SetFileNameLabel) 1546 | (IFileDialog *This, LPCWSTR pszLabel); 1547 | HRESULT(STDMETHODCALLTYPE *GetResult)(IFileDialog *This, IShellItem **ppsi); 1548 | HRESULT(STDMETHODCALLTYPE *AddPlace) 1549 | (IFileDialog *This, IShellItem *psi, FDAP fdap); 1550 | HRESULT(STDMETHODCALLTYPE *SetDefaultExtension) 1551 | (IFileDialog *This, LPCWSTR pszDefaultExtension); 1552 | HRESULT(STDMETHODCALLTYPE *Close)(IFileDialog *This, HRESULT hr); 1553 | HRESULT(STDMETHODCALLTYPE *SetClientGuid)(IFileDialog *This, REFGUID guid); 1554 | HRESULT(STDMETHODCALLTYPE *ClearClientData)(IFileDialog *This); 1555 | HRESULT(STDMETHODCALLTYPE *SetFilter) 1556 | (IFileDialog *This, IShellItemFilter *pFilter); 1557 | END_INTERFACE 1558 | } IFileDialogVtbl; 1559 | interface IFileDialog { 1560 | CONST_VTBL IFileDialogVtbl *lpVtbl; 1561 | }; 1562 | DEFINE_GUID(IID_IFileOpenDialog, 0xd57c7288, 0xd4ad, 0x4768, 0xbe, 0x02, 0x9d, 1563 | 0x96, 0x95, 0x32, 0xd9, 0x60); 1564 | DEFINE_GUID(IID_IFileSaveDialog, 0x84bccd23, 0x5fde, 0x4cdb, 0xae, 0xa4, 0xaf, 1565 | 0x64, 0xb8, 0x3d, 0x78, 0xab); 1566 | #endif 1567 | 1568 | WEBVIEW_API void webview_dialog(struct webview *w, 1569 | enum webview_dialog_type dlgtype, int flags, 1570 | const char *title, const char *arg, 1571 | char *result, size_t resultsz) { 1572 | if (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN || 1573 | dlgtype == WEBVIEW_DIALOG_TYPE_SAVE) { 1574 | IFileDialog *dlg = NULL; 1575 | IShellItem *res = NULL; 1576 | WCHAR *ws = NULL; 1577 | char *s = NULL; 1578 | FILEOPENDIALOGOPTIONS opts, add_opts; 1579 | if (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN) { 1580 | if (CoCreateInstance( 1581 | iid_unref(&CLSID_FileOpenDialog), NULL, CLSCTX_INPROC_SERVER, 1582 | iid_unref(&IID_IFileOpenDialog), (void **)&dlg) != S_OK) { 1583 | goto error_dlg; 1584 | } 1585 | if (flags & WEBVIEW_DIALOG_FLAG_DIRECTORY) { 1586 | add_opts |= FOS_PICKFOLDERS; 1587 | } 1588 | add_opts |= FOS_NOCHANGEDIR | FOS_ALLNONSTORAGEITEMS | FOS_NOVALIDATE | 1589 | FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST | FOS_SHAREAWARE | 1590 | FOS_NOTESTFILECREATE | FOS_NODEREFERENCELINKS | 1591 | FOS_FORCESHOWHIDDEN | FOS_DEFAULTNOMINIMODE; 1592 | } else { 1593 | if (CoCreateInstance( 1594 | iid_unref(&CLSID_FileSaveDialog), NULL, CLSCTX_INPROC_SERVER, 1595 | iid_unref(&IID_IFileSaveDialog), (void **)&dlg) != S_OK) { 1596 | goto error_dlg; 1597 | } 1598 | add_opts |= FOS_OVERWRITEPROMPT | FOS_NOCHANGEDIR | 1599 | FOS_ALLNONSTORAGEITEMS | FOS_NOVALIDATE | FOS_SHAREAWARE | 1600 | FOS_NOTESTFILECREATE | FOS_NODEREFERENCELINKS | 1601 | FOS_FORCESHOWHIDDEN | FOS_DEFAULTNOMINIMODE; 1602 | } 1603 | if (dlg->lpVtbl->GetOptions(dlg, &opts) != S_OK) { 1604 | goto error_dlg; 1605 | } 1606 | opts &= ~FOS_NOREADONLYRETURN; 1607 | opts |= add_opts; 1608 | if (dlg->lpVtbl->SetOptions(dlg, opts) != S_OK) { 1609 | goto error_dlg; 1610 | } 1611 | if (dlg->lpVtbl->Show(dlg, w->priv.hwnd) != S_OK) { 1612 | goto error_dlg; 1613 | } 1614 | if (dlg->lpVtbl->GetResult(dlg, &res) != S_OK) { 1615 | goto error_dlg; 1616 | } 1617 | if (res->lpVtbl->GetDisplayName(res, SIGDN_FILESYSPATH, &ws) != S_OK) { 1618 | goto error_result; 1619 | } 1620 | s = webview_from_utf16(ws); 1621 | strncpy(result, s, resultsz); 1622 | result[resultsz - 1] = '\0'; 1623 | CoTaskMemFree(ws); 1624 | error_result: 1625 | res->lpVtbl->Release(res); 1626 | error_dlg: 1627 | dlg->lpVtbl->Release(dlg); 1628 | return; 1629 | } else if (dlgtype == WEBVIEW_DIALOG_TYPE_ALERT) { 1630 | #if 0 1631 | /* MinGW often doesn't contain TaskDialog, we'll use MessageBox for now */ 1632 | WCHAR *wtitle = webview_to_utf16(title); 1633 | WCHAR *warg = webview_to_utf16(arg); 1634 | TaskDialog(w->priv.hwnd, NULL, NULL, wtitle, warg, 0, NULL, NULL); 1635 | GlobalFree(warg); 1636 | GlobalFree(wtitle); 1637 | #else 1638 | UINT type = MB_OK; 1639 | switch (flags & WEBVIEW_DIALOG_FLAG_ALERT_MASK) { 1640 | case WEBVIEW_DIALOG_FLAG_INFO: 1641 | type |= MB_ICONINFORMATION; 1642 | break; 1643 | case WEBVIEW_DIALOG_FLAG_WARNING: 1644 | type |= MB_ICONWARNING; 1645 | break; 1646 | case WEBVIEW_DIALOG_FLAG_ERROR: 1647 | type |= MB_ICONERROR; 1648 | break; 1649 | } 1650 | MessageBox(w->priv.hwnd, arg, title, type); 1651 | #endif 1652 | } 1653 | } 1654 | 1655 | WEBVIEW_API void webview_terminate(struct webview *w) { PostQuitMessage(0); } 1656 | WEBVIEW_API void webview_exit(struct webview *w) { OleUninitialize(); } 1657 | WEBVIEW_API void webview_print_log(const char *s) { OutputDebugString(s); } 1658 | 1659 | #endif /* WEBVIEW_WINAPI */ 1660 | 1661 | #if defined(WEBVIEW_COCOA) 1662 | #define NSAlertStyleWarning 0 1663 | #define NSAlertStyleCritical 2 1664 | #define NSWindowStyleMaskResizable 8 1665 | #define NSWindowStyleMaskMiniaturizable 4 1666 | #define NSWindowStyleMaskTitled 1 1667 | #define NSWindowStyleMaskClosable 2 1668 | #define NSWindowStyleMaskFullScreen (1 << 14) 1669 | #define NSViewWidthSizable 2 1670 | #define NSViewHeightSizable 16 1671 | #define NSBackingStoreBuffered 2 1672 | #define NSEventMaskAny ULONG_MAX 1673 | #define NSEventModifierFlagCommand (1 << 20) 1674 | #define NSEventModifierFlagOption (1 << 19) 1675 | #define NSAlertStyleInformational 1 1676 | #define NSAlertFirstButtonReturn 1000 1677 | #define WKNavigationActionPolicyDownload 2 1678 | #define NSModalResponseOK 1 1679 | #define WKNavigationActionPolicyDownload 2 1680 | #define WKNavigationResponsePolicyAllow 1 1681 | #define WKUserScriptInjectionTimeAtDocumentStart 0 1682 | #define NSApplicationActivationPolicyRegular 0 1683 | 1684 | static id get_nsstring(const char *c_str) { 1685 | return objc_msgSend((id)objc_getClass("NSString"), 1686 | sel_registerName("stringWithUTF8String:"), c_str); 1687 | } 1688 | 1689 | static id create_menu_item(id title, const char *action, const char *key) { 1690 | id item = 1691 | objc_msgSend((id)objc_getClass("NSMenuItem"), sel_registerName("alloc")); 1692 | objc_msgSend(item, sel_registerName("initWithTitle:action:keyEquivalent:"), 1693 | title, sel_registerName(action), get_nsstring(key)); 1694 | objc_msgSend(item, sel_registerName("autorelease")); 1695 | 1696 | return item; 1697 | } 1698 | 1699 | static void webview_window_will_close(id self, SEL cmd, id notification) { 1700 | struct webview *w = 1701 | (struct webview *)objc_getAssociatedObject(self, "webview"); 1702 | webview_terminate(w); 1703 | } 1704 | 1705 | static void webview_external_invoke(id self, SEL cmd, id contentController, 1706 | id message) { 1707 | struct webview *w = 1708 | (struct webview *)objc_getAssociatedObject(contentController, "webview"); 1709 | if (w == NULL || w->external_invoke_cb == NULL) { 1710 | return; 1711 | } 1712 | 1713 | w->external_invoke_cb(w, (const char *)objc_msgSend( 1714 | objc_msgSend(message, sel_registerName("body")), 1715 | sel_registerName("UTF8String"))); 1716 | } 1717 | 1718 | static void run_open_panel(id self, SEL cmd, id webView, id parameters, 1719 | id frame, void (^completionHandler)(id)) { 1720 | 1721 | id openPanel = objc_msgSend((id)objc_getClass("NSOpenPanel"), 1722 | sel_registerName("openPanel")); 1723 | 1724 | objc_msgSend( 1725 | openPanel, sel_registerName("setAllowsMultipleSelection:"), 1726 | objc_msgSend(parameters, sel_registerName("allowsMultipleSelection"))); 1727 | 1728 | objc_msgSend(openPanel, sel_registerName("setCanChooseFiles:"), 1); 1729 | objc_msgSend( 1730 | openPanel, sel_registerName("beginWithCompletionHandler:"), ^(id result) { 1731 | if (result == (id)NSModalResponseOK) { 1732 | completionHandler(objc_msgSend(openPanel, sel_registerName("URLs"))); 1733 | } else { 1734 | completionHandler(nil); 1735 | } 1736 | }); 1737 | } 1738 | 1739 | static void run_save_panel(id self, SEL cmd, id download, id filename, 1740 | void (^completionHandler)(int allowOverwrite, 1741 | id destination)) { 1742 | id savePanel = objc_msgSend((id)objc_getClass("NSSavePanel"), 1743 | sel_registerName("savePanel")); 1744 | objc_msgSend(savePanel, sel_registerName("setCanCreateDirectories:"), 1); 1745 | objc_msgSend(savePanel, sel_registerName("setNameFieldStringValue:"), 1746 | filename); 1747 | objc_msgSend(savePanel, sel_registerName("beginWithCompletionHandler:"), 1748 | ^(id result) { 1749 | if (result == (id)NSModalResponseOK) { 1750 | id url = objc_msgSend(savePanel, sel_registerName("URL")); 1751 | id path = objc_msgSend(url, sel_registerName("path")); 1752 | completionHandler(1, path); 1753 | } else { 1754 | completionHandler(NO, nil); 1755 | } 1756 | }); 1757 | } 1758 | 1759 | static void run_confirmation_panel(id self, SEL cmd, id webView, id message, 1760 | id frame, void (^completionHandler)(bool)) { 1761 | 1762 | id alert = 1763 | objc_msgSend((id)objc_getClass("NSAlert"), sel_registerName("new")); 1764 | objc_msgSend(alert, sel_registerName("setIcon:"), 1765 | objc_msgSend((id)objc_getClass("NSImage"), 1766 | sel_registerName("imageNamed:"), 1767 | get_nsstring("NSCaution"))); 1768 | objc_msgSend(alert, sel_registerName("setShowsHelp:"), 0); 1769 | objc_msgSend(alert, sel_registerName("setInformativeText:"), message); 1770 | objc_msgSend(alert, sel_registerName("addButtonWithTitle:"), 1771 | get_nsstring("OK")); 1772 | objc_msgSend(alert, sel_registerName("addButtonWithTitle:"), 1773 | get_nsstring("Cancel")); 1774 | if (objc_msgSend(alert, sel_registerName("runModal")) == 1775 | (id)NSAlertFirstButtonReturn) { 1776 | completionHandler(true); 1777 | } else { 1778 | completionHandler(false); 1779 | } 1780 | objc_msgSend(alert, sel_registerName("release")); 1781 | } 1782 | 1783 | static void run_alert_panel(id self, SEL cmd, id webView, id message, id frame, 1784 | void (^completionHandler)(void)) { 1785 | id alert = 1786 | objc_msgSend((id)objc_getClass("NSAlert"), sel_registerName("new")); 1787 | objc_msgSend(alert, sel_registerName("setIcon:"), 1788 | objc_msgSend((id)objc_getClass("NSImage"), 1789 | sel_registerName("imageNamed:"), 1790 | get_nsstring("NSCaution"))); 1791 | objc_msgSend(alert, sel_registerName("setShowsHelp:"), 0); 1792 | objc_msgSend(alert, sel_registerName("setInformativeText:"), message); 1793 | objc_msgSend(alert, sel_registerName("addButtonWithTitle:"), 1794 | get_nsstring("OK")); 1795 | objc_msgSend(alert, sel_registerName("runModal")); 1796 | objc_msgSend(alert, sel_registerName("release")); 1797 | completionHandler(); 1798 | } 1799 | 1800 | static void download_failed(id self, SEL cmd, id download, id error) { 1801 | printf("%s", 1802 | (const char *)objc_msgSend( 1803 | objc_msgSend(error, sel_registerName("localizedDescription")), 1804 | sel_registerName("UTF8String"))); 1805 | } 1806 | 1807 | static void make_nav_policy_decision(id self, SEL cmd, id webView, id response, 1808 | void (^decisionHandler)(int)) { 1809 | if (objc_msgSend(response, sel_registerName("canShowMIMEType")) == 0) { 1810 | decisionHandler(WKNavigationActionPolicyDownload); 1811 | } else { 1812 | decisionHandler(WKNavigationResponsePolicyAllow); 1813 | } 1814 | } 1815 | 1816 | WEBVIEW_API int webview_init(struct webview *w) { 1817 | w->priv.pool = objc_msgSend((id)objc_getClass("NSAutoreleasePool"), 1818 | sel_registerName("new")); 1819 | objc_msgSend((id)objc_getClass("NSApplication"), 1820 | sel_registerName("sharedApplication")); 1821 | 1822 | Class __WKScriptMessageHandler = objc_allocateClassPair( 1823 | objc_getClass("NSObject"), "__WKScriptMessageHandler", 0); 1824 | class_addMethod( 1825 | __WKScriptMessageHandler, 1826 | sel_registerName("userContentController:didReceiveScriptMessage:"), 1827 | (IMP)webview_external_invoke, "v@:@@"); 1828 | objc_registerClassPair(__WKScriptMessageHandler); 1829 | 1830 | id scriptMessageHandler = 1831 | objc_msgSend((id)__WKScriptMessageHandler, sel_registerName("new")); 1832 | 1833 | /*** 1834 | _WKDownloadDelegate is an undocumented/private protocol with methods called 1835 | from WKNavigationDelegate 1836 | References: 1837 | https://github.com/WebKit/webkit/blob/master/Source/WebKit/UIProcess/API/Cocoa/_WKDownload.h 1838 | https://github.com/WebKit/webkit/blob/master/Source/WebKit/UIProcess/API/Cocoa/_WKDownloadDelegate.h 1839 | https://github.com/WebKit/webkit/blob/master/Tools/TestWebKitAPI/Tests/WebKitCocoa/Download.mm 1840 | ***/ 1841 | 1842 | Class __WKDownloadDelegate = objc_allocateClassPair( 1843 | objc_getClass("NSObject"), "__WKDownloadDelegate", 0); 1844 | class_addMethod( 1845 | __WKDownloadDelegate, 1846 | sel_registerName("_download:decideDestinationWithSuggestedFilename:" 1847 | "completionHandler:"), 1848 | (IMP)run_save_panel, "v@:@@?"); 1849 | class_addMethod(__WKDownloadDelegate, 1850 | sel_registerName("_download:didFailWithError:"), 1851 | (IMP)download_failed, "v@:@@"); 1852 | objc_registerClassPair(__WKDownloadDelegate); 1853 | id downloadDelegate = 1854 | objc_msgSend((id)__WKDownloadDelegate, sel_registerName("new")); 1855 | 1856 | Class __WKPreferences = objc_allocateClassPair(objc_getClass("WKPreferences"), 1857 | "__WKPreferences", 0); 1858 | objc_property_attribute_t type = {"T", "c"}; 1859 | objc_property_attribute_t ownership = {"N", ""}; 1860 | objc_property_attribute_t attrs[] = {type, ownership}; 1861 | class_replaceProperty(__WKPreferences, "developerExtrasEnabled", attrs, 2); 1862 | objc_registerClassPair(__WKPreferences); 1863 | id wkPref = objc_msgSend((id)__WKPreferences, sel_registerName("new")); 1864 | objc_msgSend(wkPref, sel_registerName("setValue:forKey:"), 1865 | objc_msgSend((id)objc_getClass("NSNumber"), 1866 | sel_registerName("numberWithBool:"), !!w->debug), 1867 | objc_msgSend((id)objc_getClass("NSString"), 1868 | sel_registerName("stringWithUTF8String:"), 1869 | "developerExtrasEnabled")); 1870 | 1871 | id userController = objc_msgSend((id)objc_getClass("WKUserContentController"), 1872 | sel_registerName("new")); 1873 | objc_setAssociatedObject(userController, "webview", (id)(w), 1874 | OBJC_ASSOCIATION_ASSIGN); 1875 | objc_msgSend( 1876 | userController, sel_registerName("addScriptMessageHandler:name:"), 1877 | scriptMessageHandler, 1878 | objc_msgSend((id)objc_getClass("NSString"), 1879 | sel_registerName("stringWithUTF8String:"), "invoke")); 1880 | 1881 | /*** 1882 | In order to maintain compatibility with the other 'webviews' we need to 1883 | override window.external.invoke to call 1884 | webkit.messageHandlers.invoke.postMessage 1885 | ***/ 1886 | 1887 | id windowExternalOverrideScript = objc_msgSend( 1888 | (id)objc_getClass("WKUserScript"), sel_registerName("alloc")); 1889 | objc_msgSend( 1890 | windowExternalOverrideScript, 1891 | sel_registerName("initWithSource:injectionTime:forMainFrameOnly:"), 1892 | get_nsstring("window.external = this; invoke = function(arg){ " 1893 | "webkit.messageHandlers.invoke.postMessage(arg); };"), 1894 | WKUserScriptInjectionTimeAtDocumentStart, 0); 1895 | 1896 | objc_msgSend(userController, sel_registerName("addUserScript:"), 1897 | windowExternalOverrideScript); 1898 | 1899 | id config = objc_msgSend((id)objc_getClass("WKWebViewConfiguration"), 1900 | sel_registerName("new")); 1901 | id processPool = objc_msgSend(config, sel_registerName("processPool")); 1902 | objc_msgSend(processPool, sel_registerName("_setDownloadDelegate:"), 1903 | downloadDelegate); 1904 | objc_msgSend(config, sel_registerName("setProcessPool:"), processPool); 1905 | objc_msgSend(config, sel_registerName("setUserContentController:"), 1906 | userController); 1907 | objc_msgSend(config, sel_registerName("setPreferences:"), wkPref); 1908 | 1909 | Class __NSWindowDelegate = objc_allocateClassPair(objc_getClass("NSObject"), 1910 | "__NSWindowDelegate", 0); 1911 | class_addProtocol(__NSWindowDelegate, objc_getProtocol("NSWindowDelegate")); 1912 | class_replaceMethod(__NSWindowDelegate, sel_registerName("windowWillClose:"), 1913 | (IMP)webview_window_will_close, "v@:@"); 1914 | objc_registerClassPair(__NSWindowDelegate); 1915 | 1916 | w->priv.windowDelegate = 1917 | objc_msgSend((id)__NSWindowDelegate, sel_registerName("new")); 1918 | 1919 | objc_setAssociatedObject(w->priv.windowDelegate, "webview", (id)(w), 1920 | OBJC_ASSOCIATION_ASSIGN); 1921 | 1922 | id nsTitle = 1923 | objc_msgSend((id)objc_getClass("NSString"), 1924 | sel_registerName("stringWithUTF8String:"), w->title); 1925 | 1926 | CGRect r = CGRectMake(0, 0, w->width, w->height); 1927 | 1928 | unsigned int style = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | 1929 | NSWindowStyleMaskMiniaturizable; 1930 | if (w->resizable) { 1931 | style = style | NSWindowStyleMaskResizable; 1932 | } 1933 | 1934 | w->priv.window = 1935 | objc_msgSend((id)objc_getClass("NSWindow"), sel_registerName("alloc")); 1936 | objc_msgSend(w->priv.window, 1937 | sel_registerName("initWithContentRect:styleMask:backing:defer:"), 1938 | r, style, NSBackingStoreBuffered, 0); 1939 | 1940 | objc_msgSend(w->priv.window, sel_registerName("autorelease")); 1941 | objc_msgSend(w->priv.window, sel_registerName("setTitle:"), nsTitle); 1942 | objc_msgSend(w->priv.window, sel_registerName("setDelegate:"), 1943 | w->priv.windowDelegate); 1944 | objc_msgSend(w->priv.window, sel_registerName("center")); 1945 | 1946 | Class __WKUIDelegate = 1947 | objc_allocateClassPair(objc_getClass("NSObject"), "__WKUIDelegate", 0); 1948 | class_addProtocol(__WKUIDelegate, objc_getProtocol("WKUIDelegate")); 1949 | class_addMethod(__WKUIDelegate, 1950 | sel_registerName("webView:runOpenPanelWithParameters:" 1951 | "initiatedByFrame:completionHandler:"), 1952 | (IMP)run_open_panel, "v@:@@@?"); 1953 | class_addMethod(__WKUIDelegate, 1954 | sel_registerName("webView:runJavaScriptAlertPanelWithMessage:" 1955 | "initiatedByFrame:completionHandler:"), 1956 | (IMP)run_alert_panel, "v@:@@@?"); 1957 | class_addMethod( 1958 | __WKUIDelegate, 1959 | sel_registerName("webView:runJavaScriptConfirmPanelWithMessage:" 1960 | "initiatedByFrame:completionHandler:"), 1961 | (IMP)run_confirmation_panel, "v@:@@@?"); 1962 | objc_registerClassPair(__WKUIDelegate); 1963 | id uiDel = objc_msgSend((id)__WKUIDelegate, sel_registerName("new")); 1964 | 1965 | Class __WKNavigationDelegate = objc_allocateClassPair( 1966 | objc_getClass("NSObject"), "__WKNavigationDelegate", 0); 1967 | class_addProtocol(__WKNavigationDelegate, 1968 | objc_getProtocol("WKNavigationDelegate")); 1969 | class_addMethod( 1970 | __WKNavigationDelegate, 1971 | sel_registerName( 1972 | "webView:decidePolicyForNavigationResponse:decisionHandler:"), 1973 | (IMP)make_nav_policy_decision, "v@:@@?"); 1974 | objc_registerClassPair(__WKNavigationDelegate); 1975 | id navDel = objc_msgSend((id)__WKNavigationDelegate, sel_registerName("new")); 1976 | 1977 | w->priv.webview = 1978 | objc_msgSend((id)objc_getClass("WKWebView"), sel_registerName("alloc")); 1979 | objc_msgSend(w->priv.webview, 1980 | sel_registerName("initWithFrame:configuration:"), r, config); 1981 | objc_msgSend(w->priv.webview, sel_registerName("setUIDelegate:"), uiDel); 1982 | objc_msgSend(w->priv.webview, sel_registerName("setNavigationDelegate:"), 1983 | navDel); 1984 | 1985 | id nsURL = objc_msgSend((id)objc_getClass("NSURL"), 1986 | sel_registerName("URLWithString:"), 1987 | get_nsstring(webview_check_url(w->url))); 1988 | 1989 | objc_msgSend(w->priv.webview, sel_registerName("loadRequest:"), 1990 | objc_msgSend((id)objc_getClass("NSURLRequest"), 1991 | sel_registerName("requestWithURL:"), nsURL)); 1992 | objc_msgSend(w->priv.webview, sel_registerName("setAutoresizesSubviews:"), 1); 1993 | objc_msgSend(w->priv.webview, sel_registerName("setAutoresizingMask:"), 1994 | (NSViewWidthSizable | NSViewHeightSizable)); 1995 | objc_msgSend(objc_msgSend(w->priv.window, sel_registerName("contentView")), 1996 | sel_registerName("addSubview:"), w->priv.webview); 1997 | objc_msgSend(w->priv.window, sel_registerName("orderFrontRegardless")); 1998 | 1999 | objc_msgSend(objc_msgSend((id)objc_getClass("NSApplication"), 2000 | sel_registerName("sharedApplication")), 2001 | sel_registerName("setActivationPolicy:"), 2002 | NSApplicationActivationPolicyRegular); 2003 | 2004 | objc_msgSend(objc_msgSend((id)objc_getClass("NSApplication"), 2005 | sel_registerName("sharedApplication")), 2006 | sel_registerName("finishLaunching")); 2007 | 2008 | objc_msgSend(objc_msgSend((id)objc_getClass("NSApplication"), 2009 | sel_registerName("sharedApplication")), 2010 | sel_registerName("activateIgnoringOtherApps:"), 1); 2011 | 2012 | id menubar = 2013 | objc_msgSend((id)objc_getClass("NSMenu"), sel_registerName("alloc")); 2014 | objc_msgSend(menubar, sel_registerName("initWithTitle:"), get_nsstring("")); 2015 | objc_msgSend(menubar, sel_registerName("autorelease")); 2016 | 2017 | id appName = objc_msgSend(objc_msgSend((id)objc_getClass("NSProcessInfo"), 2018 | sel_registerName("processInfo")), 2019 | sel_registerName("processName")); 2020 | 2021 | id appMenuItem = 2022 | objc_msgSend((id)objc_getClass("NSMenuItem"), sel_registerName("alloc")); 2023 | objc_msgSend(appMenuItem, 2024 | sel_registerName("initWithTitle:action:keyEquivalent:"), appName, 2025 | NULL, get_nsstring("")); 2026 | 2027 | id appMenu = 2028 | objc_msgSend((id)objc_getClass("NSMenu"), sel_registerName("alloc")); 2029 | objc_msgSend(appMenu, sel_registerName("initWithTitle:"), appName); 2030 | objc_msgSend(appMenu, sel_registerName("autorelease")); 2031 | 2032 | objc_msgSend(appMenuItem, sel_registerName("setSubmenu:"), appMenu); 2033 | objc_msgSend(menubar, sel_registerName("addItem:"), appMenuItem); 2034 | 2035 | id title = 2036 | objc_msgSend(get_nsstring("Hide "), 2037 | sel_registerName("stringByAppendingString:"), appName); 2038 | id item = create_menu_item(title, "hide:", "h"); 2039 | objc_msgSend(appMenu, sel_registerName("addItem:"), item); 2040 | 2041 | item = create_menu_item(get_nsstring("Hide Others"), 2042 | "hideOtherApplications:", "h"); 2043 | objc_msgSend(item, sel_registerName("setKeyEquivalentModifierMask:"), 2044 | (NSEventModifierFlagOption | NSEventModifierFlagCommand)); 2045 | objc_msgSend(appMenu, sel_registerName("addItem:"), item); 2046 | 2047 | item = 2048 | create_menu_item(get_nsstring("Show All"), "unhideAllApplications:", ""); 2049 | objc_msgSend(appMenu, sel_registerName("addItem:"), item); 2050 | 2051 | objc_msgSend(appMenu, sel_registerName("addItem:"), 2052 | objc_msgSend((id)objc_getClass("NSMenuItem"), 2053 | sel_registerName("separatorItem"))); 2054 | 2055 | title = objc_msgSend(get_nsstring("Quit "), 2056 | sel_registerName("stringByAppendingString:"), appName); 2057 | item = create_menu_item(title, "terminate:", "q"); 2058 | objc_msgSend(appMenu, sel_registerName("addItem:"), item); 2059 | 2060 | objc_msgSend(objc_msgSend((id)objc_getClass("NSApplication"), 2061 | sel_registerName("sharedApplication")), 2062 | sel_registerName("setMainMenu:"), menubar); 2063 | 2064 | w->priv.should_exit = 0; 2065 | return 0; 2066 | } 2067 | 2068 | WEBVIEW_API int webview_loop(struct webview *w, int blocking) { 2069 | id until = (blocking ? objc_msgSend((id)objc_getClass("NSDate"), 2070 | sel_registerName("distantFuture")) 2071 | : objc_msgSend((id)objc_getClass("NSDate"), 2072 | sel_registerName("distantPast"))); 2073 | 2074 | id event = objc_msgSend( 2075 | objc_msgSend((id)objc_getClass("NSApplication"), 2076 | sel_registerName("sharedApplication")), 2077 | sel_registerName("nextEventMatchingMask:untilDate:inMode:dequeue:"), 2078 | ULONG_MAX, until, 2079 | objc_msgSend((id)objc_getClass("NSString"), 2080 | sel_registerName("stringWithUTF8String:"), 2081 | "kCFRunLoopDefaultMode"), 2082 | true); 2083 | 2084 | if (event) { 2085 | objc_msgSend(objc_msgSend((id)objc_getClass("NSApplication"), 2086 | sel_registerName("sharedApplication")), 2087 | sel_registerName("sendEvent:"), event); 2088 | } 2089 | 2090 | return w->priv.should_exit; 2091 | } 2092 | 2093 | WEBVIEW_API int webview_eval(struct webview *w, const char *js) { 2094 | objc_msgSend(w->priv.webview, 2095 | sel_registerName("evaluateJavaScript:completionHandler:"), 2096 | get_nsstring(js), NULL); 2097 | 2098 | return 0; 2099 | } 2100 | 2101 | WEBVIEW_API void webview_set_title(struct webview *w, const char *title) { 2102 | objc_msgSend(w->priv.window, sel_registerName("setTitle"), 2103 | get_nsstring(title)); 2104 | } 2105 | 2106 | WEBVIEW_API void webview_set_fullscreen(struct webview *w, int fullscreen) { 2107 | unsigned long windowStyleMask = (unsigned long)objc_msgSend( 2108 | w->priv.window, sel_registerName("styleMask")); 2109 | int b = (((windowStyleMask & NSWindowStyleMaskFullScreen) == 2110 | NSWindowStyleMaskFullScreen) 2111 | ? 1 2112 | : 0); 2113 | if (b != fullscreen) { 2114 | objc_msgSend(w->priv.window, sel_registerName("toggleFullScreen:"), NULL); 2115 | } 2116 | } 2117 | 2118 | WEBVIEW_API void webview_set_color(struct webview *w, uint8_t r, uint8_t g, 2119 | uint8_t b, uint8_t a) { 2120 | 2121 | id color = objc_msgSend((id)objc_getClass("NSColor"), 2122 | sel_registerName("colorWithRed:green:blue:alpha:"), 2123 | (float)r / 255.0, (float)g / 255.0, (float)b / 255.0, 2124 | (float)a / 255.0); 2125 | 2126 | objc_msgSend(w->priv.window, sel_registerName("setBackgroundColor:"), color); 2127 | 2128 | if (0.5 >= ((r / 255.0 * 299.0) + (g / 255.0 * 587.0) + (b / 255.0 * 114.0)) / 2129 | 1000.0) { 2130 | objc_msgSend(w->priv.window, sel_registerName("setAppearance:"), 2131 | objc_msgSend((id)objc_getClass("NSAppearance"), 2132 | sel_registerName("appearanceNamed:"), 2133 | get_nsstring("NSAppearanceNameVibrantDark"))); 2134 | } else { 2135 | objc_msgSend(w->priv.window, sel_registerName("setAppearance:"), 2136 | objc_msgSend((id)objc_getClass("NSAppearance"), 2137 | sel_registerName("appearanceNamed:"), 2138 | get_nsstring("NSAppearanceNameVibrantLight"))); 2139 | } 2140 | objc_msgSend(w->priv.window, sel_registerName("setOpaque:"), 0); 2141 | objc_msgSend(w->priv.window, 2142 | sel_registerName("setTitlebarAppearsTransparent:"), 1); 2143 | } 2144 | 2145 | WEBVIEW_API void webview_dialog(struct webview *w, 2146 | enum webview_dialog_type dlgtype, int flags, 2147 | const char *title, const char *arg, 2148 | char *result, size_t resultsz) { 2149 | if (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN || 2150 | dlgtype == WEBVIEW_DIALOG_TYPE_SAVE) { 2151 | id panel = (id)objc_getClass("NSSavePanel"); 2152 | if (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN) { 2153 | id openPanel = objc_msgSend((id)objc_getClass("NSOpenPanel"), 2154 | sel_registerName("openPanel")); 2155 | if (flags & WEBVIEW_DIALOG_FLAG_DIRECTORY) { 2156 | objc_msgSend(openPanel, sel_registerName("setCanChooseFiles:"), 0); 2157 | objc_msgSend(openPanel, sel_registerName("setCanChooseDirectories:"), 2158 | 1); 2159 | } else { 2160 | objc_msgSend(openPanel, sel_registerName("setCanChooseFiles:"), 1); 2161 | objc_msgSend(openPanel, sel_registerName("setCanChooseDirectories:"), 2162 | 0); 2163 | } 2164 | objc_msgSend(openPanel, sel_registerName("setResolvesAliases:"), 0); 2165 | objc_msgSend(openPanel, sel_registerName("setAllowsMultipleSelection:"), 2166 | 0); 2167 | panel = openPanel; 2168 | } else { 2169 | panel = objc_msgSend((id)objc_getClass("NSSavePanel"), 2170 | sel_registerName("savePanel")); 2171 | } 2172 | 2173 | objc_msgSend(panel, sel_registerName("setCanCreateDirectories:"), 1); 2174 | objc_msgSend(panel, sel_registerName("setShowsHiddenFiles:"), 1); 2175 | objc_msgSend(panel, sel_registerName("setExtensionHidden:"), 0); 2176 | objc_msgSend(panel, sel_registerName("setCanSelectHiddenExtension:"), 0); 2177 | objc_msgSend(panel, sel_registerName("setTreatsFilePackagesAsDirectories:"), 2178 | 1); 2179 | objc_msgSend( 2180 | panel, sel_registerName("beginSheetModalForWindow:completionHandler:"), 2181 | w->priv.window, ^(id result) { 2182 | objc_msgSend(objc_msgSend((id)objc_getClass("NSApplication"), 2183 | sel_registerName("sharedApplication")), 2184 | sel_registerName("stopModalWithCode:"), result); 2185 | }); 2186 | 2187 | if (objc_msgSend(objc_msgSend((id)objc_getClass("NSApplication"), 2188 | sel_registerName("sharedApplication")), 2189 | sel_registerName("runModalForWindow:"), 2190 | panel) == (id)NSModalResponseOK) { 2191 | id url = objc_msgSend(panel, sel_registerName("URL")); 2192 | id path = objc_msgSend(url, sel_registerName("path")); 2193 | const char *filename = 2194 | (const char *)objc_msgSend(path, sel_registerName("UTF8String")); 2195 | strlcpy(result, filename, resultsz); 2196 | } 2197 | } else if (dlgtype == WEBVIEW_DIALOG_TYPE_ALERT) { 2198 | id a = objc_msgSend((id)objc_getClass("NSAlert"), sel_registerName("new")); 2199 | switch (flags & WEBVIEW_DIALOG_FLAG_ALERT_MASK) { 2200 | case WEBVIEW_DIALOG_FLAG_INFO: 2201 | objc_msgSend(a, sel_registerName("setAlertStyle:"), 2202 | NSAlertStyleInformational); 2203 | break; 2204 | case WEBVIEW_DIALOG_FLAG_WARNING: 2205 | printf("Warning\n"); 2206 | objc_msgSend(a, sel_registerName("setAlertStyle:"), NSAlertStyleWarning); 2207 | break; 2208 | case WEBVIEW_DIALOG_FLAG_ERROR: 2209 | printf("Error\n"); 2210 | objc_msgSend(a, sel_registerName("setAlertStyle:"), NSAlertStyleCritical); 2211 | break; 2212 | } 2213 | objc_msgSend(a, sel_registerName("setShowsHelp:"), 0); 2214 | objc_msgSend(a, sel_registerName("setShowsSuppressionButton:"), 0); 2215 | objc_msgSend(a, sel_registerName("setMessageText:"), get_nsstring(title)); 2216 | objc_msgSend(a, sel_registerName("setInformativeText:"), get_nsstring(arg)); 2217 | objc_msgSend(a, sel_registerName("addButtonWithTitle:"), 2218 | get_nsstring("OK")); 2219 | objc_msgSend(a, sel_registerName("runModal")); 2220 | objc_msgSend(a, sel_registerName("release")); 2221 | } 2222 | } 2223 | 2224 | static void webview_dispatch_cb(void *arg) { 2225 | struct webview_dispatch_arg *context = (struct webview_dispatch_arg *)arg; 2226 | (context->fn)(context->w, context->arg); 2227 | free(context); 2228 | } 2229 | 2230 | WEBVIEW_API void webview_dispatch(struct webview *w, webview_dispatch_fn fn, 2231 | void *arg) { 2232 | struct webview_dispatch_arg *context = (struct webview_dispatch_arg *)malloc( 2233 | sizeof(struct webview_dispatch_arg)); 2234 | context->w = w; 2235 | context->arg = arg; 2236 | context->fn = fn; 2237 | dispatch_async_f(dispatch_get_main_queue(), context, webview_dispatch_cb); 2238 | } 2239 | 2240 | WEBVIEW_API void webview_terminate(struct webview *w) { 2241 | w->priv.should_exit = 1; 2242 | } 2243 | 2244 | WEBVIEW_API void webview_exit(struct webview *w) { 2245 | id app = objc_msgSend((id)objc_getClass("NSApplication"), 2246 | sel_registerName("sharedApplication")); 2247 | objc_msgSend(app, sel_registerName("terminate:"), app); 2248 | } 2249 | 2250 | WEBVIEW_API void webview_print_log(const char *s) { printf("%s\n", s); } 2251 | 2252 | #endif /* WEBVIEW_COCOA */ 2253 | 2254 | #endif /* WEBVIEW_IMPLEMENTATION */ 2255 | 2256 | #ifdef __cplusplus 2257 | } 2258 | #endif 2259 | 2260 | #endif /* WEBVIEW_H */ 2261 | --------------------------------------------------------------------------------