├── MANIFEST.in ├── README.md ├── setup.py └── webview ├── webview.c └── webview.h /MANIFEST.in: -------------------------------------------------------------------------------- 1 | graft webview 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # webview 2 | 3 | Python extension that provides API for the [webview] library. 4 | 5 | ## Getting started 6 | 7 | Install the bindings: 8 | 9 | ```bash 10 | pip install webview 11 | ``` 12 | 13 | Try the following example: 14 | 15 | ```python 16 | import webview 17 | 18 | w = webview.WebView(width=320, height=240, title="Hello", url="https://google.com", resizable=True, debug=False) 19 | w.run() 20 | ``` 21 | 22 | You may use most of the webview APIs: 23 | 24 | ```python 25 | # Change window title 26 | w.set_title("New title") 27 | # Make window fullscreen 28 | w.set_fullscreen(True) 29 | # Change initial window background color 30 | w.set_color(255, 0, 0) 31 | # Inject some JS 32 | w.eval("alert('hello')") 33 | # Inject some CSS 34 | w.inject_css('* {background-color: yellow; }') 35 | # Show native OS dialog 36 | file_path = w.dialog(0, 0, "open file", "") 37 | # Post funciton to the UI thread 38 | w.dispatch(some_func) 39 | w.dispatch(lambda: some_func()) 40 | # Control run loop 41 | while w.loop(True): 42 | pass 43 | ``` 44 | 45 | Dispatch is currently only a stub and is implemented as direct function call. 46 | Also, proper Python-to-JS object mapping is not implemented yet, but is highly 47 | 48 | ## Development 49 | 50 | To build and install the library locally: 51 | 52 | ```bash 53 | python setup.py install 54 | ``` 55 | 56 | To upload a new version: 57 | 58 | ```bash 59 | python setup.py sdist 60 | twine upload dist/webview-*.tar.gz 61 | ``` 62 | 63 | To build and install it locally: 64 | 65 | ```bash 66 | python setup.py install 67 | ``` 68 | 69 | Please, ensure that all sources are formatted using `yapf`. 70 | 71 | 72 | [webview]: https://github.com/zserge/webview 73 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/bin/env python 2 | 3 | import os 4 | import subprocess 5 | import shutil 6 | 7 | from distutils.core import setup 8 | from distutils.extension import Extension 9 | from distutils.cmd import Command 10 | 11 | 12 | if hasattr(os, 'uname'): 13 | OSNAME = os.uname()[0] 14 | else: 15 | OSNAME = 'Windows' 16 | 17 | if OSNAME == 'Linux': 18 | 19 | def pkgconfig(flags): 20 | return subprocess.check_output( 21 | 'pkg-config %s gtk+-3.0 webkit2gtk-4.0' % flags, 22 | shell=True, 23 | stderr=subprocess.STDOUT).decode('utf-8') 24 | 25 | define_macros = [("WEBVIEW_GTK", '1')] 26 | extra_cflags = pkgconfig("--cflags").split() 27 | extra_ldflags = pkgconfig("--libs").split() 28 | elif OSNAME == 'Darwin': 29 | define_macros = [('WEBVIEW_COCOA', '1')] 30 | extra_cflags = "" 31 | extra_ldflags = ['-framework', 'CoreAudio'] 32 | elif OSNAME == 'Windows': 33 | define_macros = [('WEBVIEW_WINAPI', '1')] 34 | extra_cflags = "" 35 | extra_ldflags = ['-framework', 'CoreAudio'] 36 | 37 | webview = Extension( 38 | 'webview', 39 | sources=['webview/webview.c'], 40 | define_macros=define_macros, 41 | extra_compile_args=extra_cflags, 42 | extra_link_args=extra_ldflags, 43 | ) 44 | 45 | setup( 46 | name='webview', 47 | version='0.1.5', 48 | description='Python WebView bindings', 49 | author='Serge Zaitsev', 50 | author_email='zaitsev.serge@gmail.com', 51 | url='https://github.com/zserge/webview', 52 | keywords=[], 53 | license='MIT', 54 | classifiers=[], 55 | ext_modules=[webview], 56 | ) 57 | -------------------------------------------------------------------------------- /webview/webview.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "structmember.h" 4 | 5 | #define WEBVIEW_IMPLEMENTATION 6 | #include "webview.h" 7 | 8 | typedef struct { PyObject_HEAD struct webview w; } WebView; 9 | 10 | static void WebView_dealloc(WebView *self) { 11 | webview_exit(&self->w); 12 | Py_TYPE(self)->tp_free((PyObject *)self); 13 | } 14 | 15 | static PyObject *WebView_new(PyTypeObject *type, PyObject *args, 16 | PyObject *kwds) { 17 | WebView *self = (WebView *)type->tp_alloc(type, 0); 18 | if (self == NULL) { 19 | return NULL; 20 | } 21 | memset(&self->w, 0, sizeof(self->w)); 22 | return (PyObject *)self; 23 | } 24 | 25 | static int WebView_init(WebView *self, PyObject *args, PyObject *kwds) { 26 | const char *url = NULL; 27 | const char *title = NULL; 28 | static char *kwlist[] = {"width", "height", "resizable", "debug", 29 | "url", "title", NULL}; 30 | 31 | if (!PyArg_ParseTupleAndKeywords( 32 | args, kwds, "ii|iiss", kwlist, &self->w.width, &self->w.height, 33 | &self->w.resizable, &self->w.debug, &url, &title)) { 34 | return -1; 35 | } 36 | 37 | self->w.url = url; 38 | self->w.title = title; 39 | 40 | return webview_init(&self->w); 41 | } 42 | 43 | static PyObject *WebView_run(WebView *self) { 44 | while (webview_loop(&self->w, 1) == 0) 45 | ; 46 | Py_RETURN_NONE; 47 | } 48 | 49 | static PyObject *WebView_loop(WebView *self, PyObject *args, PyObject *kwds) { 50 | int blocking = 1; 51 | static char *kwlist[] = {"blocking", NULL}; 52 | if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, &blocking)) { 53 | return NULL; 54 | } 55 | if (webview_loop(&self->w, blocking) == 0) { 56 | Py_RETURN_TRUE; 57 | } else { 58 | Py_RETURN_FALSE; 59 | } 60 | } 61 | 62 | static PyObject *WebView_terminate(WebView *self) { 63 | webview_terminate(&self->w); 64 | Py_RETURN_NONE; 65 | } 66 | 67 | static PyObject *WebView_set_title(WebView *self, PyObject *args) { 68 | const char *title = ""; 69 | if (!PyArg_ParseTuple(args, "s", &title)) { 70 | return NULL; 71 | } 72 | webview_set_title(&self->w, title); 73 | Py_RETURN_NONE; 74 | } 75 | 76 | static PyObject *WebView_set_fullscreen(WebView *self, PyObject *args) { 77 | int fullscreen = 0; 78 | if (!PyArg_ParseTuple(args, "i", &fullscreen)) { 79 | return NULL; 80 | } 81 | webview_set_fullscreen(&self->w, fullscreen); 82 | Py_RETURN_NONE; 83 | } 84 | 85 | static PyObject *WebView_set_color(WebView *self, PyObject *args) { 86 | int r, g, b, a = 255; 87 | if (!PyArg_ParseTuple(args, "iii|i", &r, &g, &b, &a)) { 88 | return NULL; 89 | } 90 | webview_set_color(&self->w, r, g, b, a); 91 | Py_RETURN_NONE; 92 | } 93 | 94 | static PyObject *WebView_eval(WebView *self, PyObject *args) { 95 | const char *js = NULL; 96 | if (!PyArg_ParseTuple(args, "s", &js)) { 97 | return NULL; 98 | } 99 | webview_eval(&self->w, js); 100 | Py_RETURN_NONE; 101 | } 102 | 103 | static PyObject *WebView_inject_css(WebView *self, PyObject *args) { 104 | const char *css = NULL; 105 | if (!PyArg_ParseTuple(args, "s", &css)) { 106 | return NULL; 107 | } 108 | webview_inject_css(&self->w, css); 109 | Py_RETURN_NONE; 110 | } 111 | 112 | static PyObject *WebView_dialog(WebView *self, PyObject *args, PyObject *kwds) { 113 | int type = 0; 114 | int flags = 0; 115 | const char *title = NULL; 116 | const char *arg = NULL; 117 | char result[PATH_MAX]; 118 | static char *kwlist[] = {"type", "flags", "title", "arg", NULL}; 119 | if (!PyArg_ParseTupleAndKeywords(args, kwds, "iiss", kwlist, &type, &flags, 120 | &title, &arg)) { 121 | return NULL; 122 | } 123 | webview_dialog(&self->w, type, flags, title, arg, result, sizeof(result)); 124 | return PyUnicode_FromString(result); 125 | } 126 | 127 | static void webview_dispatch_cb(struct webview *w, void *arg) { 128 | PyObject *cb = (PyObject *)arg; 129 | /* TODO */ 130 | PyObject_CallObject(cb, NULL); 131 | Py_XINCREF(cb); 132 | } 133 | 134 | static PyObject *WebView_dispatch(WebView *self, PyObject *args) { 135 | PyObject *tmp; 136 | if (!PyArg_ParseTuple(args, "O:set_callback", &tmp)) { 137 | return NULL; 138 | } 139 | if (!PyCallable_Check(tmp)) { 140 | PyErr_SetString(PyExc_TypeError, "parameter must be callable"); 141 | return NULL; 142 | } 143 | Py_XINCREF(tmp); 144 | webview_dispatch(&self->w, webview_dispatch_cb, tmp); 145 | Py_RETURN_NONE; 146 | } 147 | 148 | static PyObject *WebView_bind(WebView *self) { 149 | /* TODO, very complex implementation */ 150 | Py_RETURN_NONE; 151 | } 152 | 153 | static PyMemberDef WebView_members[] = { 154 | {NULL} /* Sentinel */ 155 | }; 156 | static PyMethodDef WebView_methods[] = { 157 | {"run", (PyCFunction)WebView_run, METH_NOARGS, "..."}, 158 | {"loop", (PyCFunction)WebView_loop, METH_KEYWORDS | METH_VARARGS, "..."}, 159 | {"terminate", (PyCFunction)WebView_terminate, METH_NOARGS, "..."}, 160 | {"dispatch", (PyCFunction)WebView_dispatch, METH_VARARGS, "..."}, 161 | {"eval", (PyCFunction)WebView_eval, METH_VARARGS, "..."}, 162 | {"inject_css", (PyCFunction)WebView_inject_css, METH_VARARGS, "..."}, 163 | {"dialog", (PyCFunction)WebView_dialog, METH_KEYWORDS | METH_VARARGS, "..."}, 164 | {"set_title", (PyCFunction)WebView_set_title, METH_VARARGS, "..."}, 165 | {"set_fullscreen", (PyCFunction)WebView_set_fullscreen, METH_VARARGS, 166 | "..."}, 167 | {"set_color", (PyCFunction)WebView_set_color, METH_VARARGS, "..."}, 168 | {"bind", (PyCFunction)WebView_bind, METH_VARARGS, "..."}, 169 | {NULL} /* Sentinel */ 170 | }; 171 | 172 | static PyTypeObject WebViewType = { 173 | PyVarObject_HEAD_INIT(NULL, 0) "webview.WebView", /* tp_name */ 174 | sizeof(WebView), /* tp_basicsize */ 175 | 0, /* tp_itemsize */ 176 | (destructor)WebView_dealloc, /* tp_dealloc */ 177 | 0, /* tp_print */ 178 | 0, /* tp_getattr */ 179 | 0, /* tp_setattr */ 180 | 0, /* tp_compare */ 181 | 0, /* tp_repr */ 182 | 0, /* tp_as_number */ 183 | 0, /* tp_as_sequence */ 184 | 0, /* tp_as_mapping */ 185 | 0, /* tp_hash */ 186 | 0, /* tp_call */ 187 | 0, /* tp_str */ 188 | 0, /* tp_getattro */ 189 | 0, /* tp_setattro */ 190 | 0, /* tp_as_buffer */ 191 | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ 192 | "WebView objects", /* tp_doc */ 193 | 0, /* tp_traverse */ 194 | 0, /* tp_clear */ 195 | 0, /* tp_richcompare */ 196 | 0, /* tp_weaklistoffset */ 197 | 0, /* tp_iter */ 198 | 0, /* tp_iternext */ 199 | WebView_methods, /* tp_methods */ 200 | WebView_members, /* tp_members */ 201 | 0, /* tp_getset */ 202 | 0, /* tp_base */ 203 | 0, /* tp_dict */ 204 | 0, /* tp_descr_get */ 205 | 0, /* tp_descr_set */ 206 | 0, /* tp_dictoffset */ 207 | (initproc)WebView_init, /* tp_init */ 208 | 0, /* tp_alloc */ 209 | WebView_new, /* tp_new */ 210 | }; 211 | 212 | static PyMethodDef module_methods[] = { 213 | {NULL} /* Sentinel */ 214 | }; 215 | 216 | #if PY_MAJOR_VERSION >= 3 217 | static struct PyModuleDef moduledef = { 218 | PyModuleDef_HEAD_INIT, 219 | "webview", 220 | "Example module", 221 | 0, 222 | module_methods, 223 | NULL, 224 | NULL, 225 | NULL, 226 | NULL 227 | }; 228 | #define MODINIT_ERROR NULL 229 | #define MODINIT_NAME PyInit_webview 230 | #else 231 | #define MODINIT_ERROR 232 | #define MODINIT_NAME initwebview 233 | #endif 234 | PyMODINIT_FUNC MODINIT_NAME(void) { 235 | PyObject *m; 236 | 237 | if (PyType_Ready(&WebViewType) < 0) { 238 | return MODINIT_ERROR; 239 | } 240 | 241 | #if PY_MAJOR_VERSION >= 3 242 | m = PyModule_Create(&moduledef); 243 | #else 244 | m = Py_InitModule3("webview", module_methods, 245 | "Example module that creates an extension type."); 246 | #endif 247 | if (m == NULL) { 248 | return MODINIT_ERROR; 249 | } 250 | 251 | Py_INCREF(&WebViewType); 252 | PyModule_AddObject(m, "WebView", (PyObject *)&WebViewType); 253 | #if PY_MAJOR_VERSION >= 3 254 | return m; 255 | #endif 256 | } 257 | -------------------------------------------------------------------------------- /webview/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 | #import 78 | #import 79 | #import 80 | 81 | struct webview_priv { 82 | NSAutoreleasePool *pool; 83 | NSWindow *window; 84 | WebView *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 { IDocHostUIHandler ui; } _IDocHostUIHandlerEx; 508 | 509 | typedef struct { 510 | IOleClientSite client; 511 | _IOleInPlaceSiteEx inplace; 512 | _IDocHostUIHandlerEx ui; 513 | IDispatch external; 514 | } _IOleClientSiteEx; 515 | 516 | #ifdef __cplusplus 517 | #define iid_ref(x) &(x) 518 | #define iid_unref(x) *(x) 519 | #else 520 | #define iid_ref(x) (x) 521 | #define iid_unref(x) (x) 522 | #endif 523 | 524 | static inline WCHAR *webview_to_utf16(const char *s) { 525 | DWORD size = MultiByteToWideChar(CP_UTF8, 0, s, -1, 0, 0); 526 | WCHAR *ws = (WCHAR *)GlobalAlloc(GMEM_FIXED, sizeof(WCHAR) * size); 527 | if (ws == NULL) { 528 | return NULL; 529 | } 530 | MultiByteToWideChar(CP_UTF8, 0, s, -1, ws, size); 531 | return ws; 532 | } 533 | 534 | static inline char *webview_from_utf16(WCHAR *ws) { 535 | int n = WideCharToMultiByte(CP_UTF8, 0, ws, -1, NULL, 0, NULL, NULL); 536 | char *s = (char *)GlobalAlloc(GMEM_FIXED, n); 537 | if (s == NULL) { 538 | return NULL; 539 | } 540 | WideCharToMultiByte(CP_UTF8, 0, ws, -1, s, n, NULL, NULL); 541 | return s; 542 | } 543 | 544 | static int iid_eq(REFIID a, const IID *b) { 545 | return memcmp((const void *)iid_ref(a), (const void *)b, sizeof(GUID)) == 0; 546 | } 547 | 548 | static HRESULT STDMETHODCALLTYPE JS_QueryInterface(IDispatch FAR *This, 549 | REFIID riid, 550 | LPVOID FAR *ppvObj) { 551 | if (iid_eq(riid, &IID_IDispatch)) { 552 | *ppvObj = This; 553 | return S_OK; 554 | } 555 | *ppvObj = 0; 556 | return E_NOINTERFACE; 557 | } 558 | static ULONG STDMETHODCALLTYPE JS_AddRef(IDispatch FAR *This) { return 1; } 559 | static ULONG STDMETHODCALLTYPE JS_Release(IDispatch FAR *This) { return 1; } 560 | static HRESULT STDMETHODCALLTYPE JS_GetTypeInfoCount(IDispatch FAR *This, 561 | UINT *pctinfo) { 562 | return S_OK; 563 | } 564 | static HRESULT STDMETHODCALLTYPE JS_GetTypeInfo(IDispatch FAR *This, 565 | UINT iTInfo, LCID lcid, 566 | ITypeInfo **ppTInfo) { 567 | return S_OK; 568 | } 569 | #define WEBVIEW_JS_INVOKE_ID 0x1000 570 | static HRESULT STDMETHODCALLTYPE JS_GetIDsOfNames(IDispatch FAR *This, 571 | REFIID riid, 572 | LPOLESTR *rgszNames, 573 | UINT cNames, LCID lcid, 574 | DISPID *rgDispId) { 575 | if (cNames != 1) { 576 | return S_FALSE; 577 | } 578 | if (wcscmp(rgszNames[0], L"invoke") == 0) { 579 | rgDispId[0] = WEBVIEW_JS_INVOKE_ID; 580 | return S_OK; 581 | } 582 | return S_FALSE; 583 | } 584 | 585 | static HRESULT STDMETHODCALLTYPE 586 | JS_Invoke(IDispatch FAR *This, DISPID dispIdMember, REFIID riid, LCID lcid, 587 | WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, 588 | EXCEPINFO *pExcepInfo, UINT *puArgErr) { 589 | size_t offset = (size_t) & ((_IOleClientSiteEx *)NULL)->external; 590 | _IOleClientSiteEx *ex = (_IOleClientSiteEx *)((char *)(This)-offset); 591 | struct webview *w = (struct webview *)GetWindowLongPtr( 592 | ex->inplace.frame.window, GWLP_USERDATA); 593 | if (pDispParams->cArgs == 1 && pDispParams->rgvarg[0].vt == VT_BSTR) { 594 | BSTR bstr = pDispParams->rgvarg[0].bstrVal; 595 | char *s = webview_from_utf16(bstr); 596 | if (s != NULL) { 597 | if (dispIdMember == WEBVIEW_JS_INVOKE_ID) { 598 | if (w->external_invoke_cb != NULL) { 599 | w->external_invoke_cb(w, s); 600 | } 601 | } else { 602 | return S_FALSE; 603 | } 604 | GlobalFree(s); 605 | } 606 | } 607 | return S_OK; 608 | } 609 | 610 | static IDispatchVtbl ExternalDispatchTable = { 611 | JS_QueryInterface, JS_AddRef, JS_Release, JS_GetTypeInfoCount, 612 | JS_GetTypeInfo, JS_GetIDsOfNames, JS_Invoke}; 613 | 614 | static ULONG STDMETHODCALLTYPE Site_AddRef(IOleClientSite FAR *This) { 615 | return 1; 616 | } 617 | static ULONG STDMETHODCALLTYPE Site_Release(IOleClientSite FAR *This) { 618 | return 1; 619 | } 620 | static HRESULT STDMETHODCALLTYPE Site_SaveObject(IOleClientSite FAR *This) { 621 | return E_NOTIMPL; 622 | } 623 | static HRESULT STDMETHODCALLTYPE Site_GetMoniker(IOleClientSite FAR *This, 624 | DWORD dwAssign, 625 | DWORD dwWhichMoniker, 626 | IMoniker **ppmk) { 627 | return E_NOTIMPL; 628 | } 629 | static HRESULT STDMETHODCALLTYPE 630 | Site_GetContainer(IOleClientSite FAR *This, LPOLECONTAINER FAR *ppContainer) { 631 | *ppContainer = 0; 632 | return E_NOINTERFACE; 633 | } 634 | static HRESULT STDMETHODCALLTYPE Site_ShowObject(IOleClientSite FAR *This) { 635 | return NOERROR; 636 | } 637 | static HRESULT STDMETHODCALLTYPE Site_OnShowWindow(IOleClientSite FAR *This, 638 | BOOL fShow) { 639 | return E_NOTIMPL; 640 | } 641 | static HRESULT STDMETHODCALLTYPE 642 | Site_RequestNewObjectLayout(IOleClientSite FAR *This) { 643 | return E_NOTIMPL; 644 | } 645 | static HRESULT STDMETHODCALLTYPE Site_QueryInterface(IOleClientSite FAR *This, 646 | REFIID riid, 647 | void **ppvObject) { 648 | if (iid_eq(riid, &IID_IUnknown) || iid_eq(riid, &IID_IOleClientSite)) { 649 | *ppvObject = &((_IOleClientSiteEx *)This)->client; 650 | } else if (iid_eq(riid, &IID_IOleInPlaceSite)) { 651 | *ppvObject = &((_IOleClientSiteEx *)This)->inplace; 652 | } else if (iid_eq(riid, &IID_IDocHostUIHandler)) { 653 | *ppvObject = &((_IOleClientSiteEx *)This)->ui; 654 | } else { 655 | *ppvObject = 0; 656 | return (E_NOINTERFACE); 657 | } 658 | return S_OK; 659 | } 660 | static HRESULT STDMETHODCALLTYPE InPlace_QueryInterface( 661 | IOleInPlaceSite FAR *This, REFIID riid, LPVOID FAR *ppvObj) { 662 | return (Site_QueryInterface( 663 | (IOleClientSite *)((char *)This - sizeof(IOleClientSite)), riid, ppvObj)); 664 | } 665 | static ULONG STDMETHODCALLTYPE InPlace_AddRef(IOleInPlaceSite FAR *This) { 666 | return 1; 667 | } 668 | static ULONG STDMETHODCALLTYPE InPlace_Release(IOleInPlaceSite FAR *This) { 669 | return 1; 670 | } 671 | static HRESULT STDMETHODCALLTYPE InPlace_GetWindow(IOleInPlaceSite FAR *This, 672 | HWND FAR *lphwnd) { 673 | *lphwnd = ((_IOleInPlaceSiteEx FAR *)This)->frame.window; 674 | return S_OK; 675 | } 676 | static HRESULT STDMETHODCALLTYPE 677 | InPlace_ContextSensitiveHelp(IOleInPlaceSite FAR *This, BOOL fEnterMode) { 678 | return E_NOTIMPL; 679 | } 680 | static HRESULT STDMETHODCALLTYPE 681 | InPlace_CanInPlaceActivate(IOleInPlaceSite FAR *This) { 682 | return S_OK; 683 | } 684 | static HRESULT STDMETHODCALLTYPE 685 | InPlace_OnInPlaceActivate(IOleInPlaceSite FAR *This) { 686 | return S_OK; 687 | } 688 | static HRESULT STDMETHODCALLTYPE 689 | InPlace_OnUIActivate(IOleInPlaceSite FAR *This) { 690 | return S_OK; 691 | } 692 | static HRESULT STDMETHODCALLTYPE InPlace_GetWindowContext( 693 | IOleInPlaceSite FAR *This, LPOLEINPLACEFRAME FAR *lplpFrame, 694 | LPOLEINPLACEUIWINDOW FAR *lplpDoc, LPRECT lprcPosRect, LPRECT lprcClipRect, 695 | LPOLEINPLACEFRAMEINFO lpFrameInfo) { 696 | *lplpFrame = (LPOLEINPLACEFRAME) & ((_IOleInPlaceSiteEx *)This)->frame; 697 | *lplpDoc = 0; 698 | lpFrameInfo->fMDIApp = FALSE; 699 | lpFrameInfo->hwndFrame = ((_IOleInPlaceFrameEx *)*lplpFrame)->window; 700 | lpFrameInfo->haccel = 0; 701 | lpFrameInfo->cAccelEntries = 0; 702 | return S_OK; 703 | } 704 | static HRESULT STDMETHODCALLTYPE InPlace_Scroll(IOleInPlaceSite FAR *This, 705 | SIZE scrollExtent) { 706 | return E_NOTIMPL; 707 | } 708 | static HRESULT STDMETHODCALLTYPE 709 | InPlace_OnUIDeactivate(IOleInPlaceSite FAR *This, BOOL fUndoable) { 710 | return S_OK; 711 | } 712 | static HRESULT STDMETHODCALLTYPE 713 | InPlace_OnInPlaceDeactivate(IOleInPlaceSite FAR *This) { 714 | return S_OK; 715 | } 716 | static HRESULT STDMETHODCALLTYPE 717 | InPlace_DiscardUndoState(IOleInPlaceSite FAR *This) { 718 | return E_NOTIMPL; 719 | } 720 | static HRESULT STDMETHODCALLTYPE 721 | InPlace_DeactivateAndUndo(IOleInPlaceSite FAR *This) { 722 | return E_NOTIMPL; 723 | } 724 | static HRESULT STDMETHODCALLTYPE 725 | InPlace_OnPosRectChange(IOleInPlaceSite FAR *This, LPCRECT lprcPosRect) { 726 | IOleObject *browserObject; 727 | IOleInPlaceObject *inplace; 728 | browserObject = *((IOleObject **)((char *)This - sizeof(IOleObject *) - 729 | sizeof(IOleClientSite))); 730 | if (!browserObject->lpVtbl->QueryInterface(browserObject, 731 | iid_unref(&IID_IOleInPlaceObject), 732 | (void **)&inplace)) { 733 | inplace->lpVtbl->SetObjectRects(inplace, lprcPosRect, lprcPosRect); 734 | inplace->lpVtbl->Release(inplace); 735 | } 736 | return S_OK; 737 | } 738 | static HRESULT STDMETHODCALLTYPE Frame_QueryInterface( 739 | IOleInPlaceFrame FAR *This, REFIID riid, LPVOID FAR *ppvObj) { 740 | return E_NOTIMPL; 741 | } 742 | static ULONG STDMETHODCALLTYPE Frame_AddRef(IOleInPlaceFrame FAR *This) { 743 | return 1; 744 | } 745 | static ULONG STDMETHODCALLTYPE Frame_Release(IOleInPlaceFrame FAR *This) { 746 | return 1; 747 | } 748 | static HRESULT STDMETHODCALLTYPE Frame_GetWindow(IOleInPlaceFrame FAR *This, 749 | HWND FAR *lphwnd) { 750 | *lphwnd = ((_IOleInPlaceFrameEx *)This)->window; 751 | return S_OK; 752 | } 753 | static HRESULT STDMETHODCALLTYPE 754 | Frame_ContextSensitiveHelp(IOleInPlaceFrame FAR *This, BOOL fEnterMode) { 755 | return E_NOTIMPL; 756 | } 757 | static HRESULT STDMETHODCALLTYPE Frame_GetBorder(IOleInPlaceFrame FAR *This, 758 | LPRECT lprectBorder) { 759 | return E_NOTIMPL; 760 | } 761 | static HRESULT STDMETHODCALLTYPE Frame_RequestBorderSpace( 762 | IOleInPlaceFrame FAR *This, LPCBORDERWIDTHS pborderwidths) { 763 | return E_NOTIMPL; 764 | } 765 | static HRESULT STDMETHODCALLTYPE Frame_SetBorderSpace( 766 | IOleInPlaceFrame FAR *This, LPCBORDERWIDTHS pborderwidths) { 767 | return E_NOTIMPL; 768 | } 769 | static HRESULT STDMETHODCALLTYPE Frame_SetActiveObject( 770 | IOleInPlaceFrame FAR *This, IOleInPlaceActiveObject *pActiveObject, 771 | LPCOLESTR pszObjName) { 772 | return S_OK; 773 | } 774 | static HRESULT STDMETHODCALLTYPE 775 | Frame_InsertMenus(IOleInPlaceFrame FAR *This, HMENU hmenuShared, 776 | LPOLEMENUGROUPWIDTHS lpMenuWidths) { 777 | return E_NOTIMPL; 778 | } 779 | static HRESULT STDMETHODCALLTYPE Frame_SetMenu(IOleInPlaceFrame FAR *This, 780 | HMENU hmenuShared, 781 | HOLEMENU holemenu, 782 | HWND hwndActiveObject) { 783 | return S_OK; 784 | } 785 | static HRESULT STDMETHODCALLTYPE Frame_RemoveMenus(IOleInPlaceFrame FAR *This, 786 | HMENU hmenuShared) { 787 | return E_NOTIMPL; 788 | } 789 | static HRESULT STDMETHODCALLTYPE Frame_SetStatusText(IOleInPlaceFrame FAR *This, 790 | LPCOLESTR pszStatusText) { 791 | return S_OK; 792 | } 793 | static HRESULT STDMETHODCALLTYPE 794 | Frame_EnableModeless(IOleInPlaceFrame FAR *This, BOOL fEnable) { 795 | return S_OK; 796 | } 797 | static HRESULT STDMETHODCALLTYPE 798 | Frame_TranslateAccelerator(IOleInPlaceFrame FAR *This, LPMSG lpmsg, WORD wID) { 799 | return E_NOTIMPL; 800 | } 801 | static HRESULT STDMETHODCALLTYPE UI_QueryInterface(IDocHostUIHandler FAR *This, 802 | REFIID riid, 803 | LPVOID FAR *ppvObj) { 804 | return (Site_QueryInterface((IOleClientSite *)((char *)This - 805 | sizeof(IOleClientSite) - 806 | sizeof(_IOleInPlaceSiteEx)), 807 | riid, ppvObj)); 808 | } 809 | static ULONG STDMETHODCALLTYPE UI_AddRef(IDocHostUIHandler FAR *This) { 810 | return 1; 811 | } 812 | static ULONG STDMETHODCALLTYPE UI_Release(IDocHostUIHandler FAR *This) { 813 | return 1; 814 | } 815 | static HRESULT STDMETHODCALLTYPE UI_ShowContextMenu( 816 | IDocHostUIHandler FAR *This, DWORD dwID, POINT __RPC_FAR *ppt, 817 | IUnknown __RPC_FAR *pcmdtReserved, IDispatch __RPC_FAR *pdispReserved) { 818 | return S_OK; 819 | } 820 | static HRESULT STDMETHODCALLTYPE 821 | UI_GetHostInfo(IDocHostUIHandler FAR *This, DOCHOSTUIINFO __RPC_FAR *pInfo) { 822 | pInfo->cbSize = sizeof(DOCHOSTUIINFO); 823 | pInfo->dwFlags = DOCHOSTUIFLAG_NO3DBORDER; 824 | pInfo->dwDoubleClick = DOCHOSTUIDBLCLK_DEFAULT; 825 | return S_OK; 826 | } 827 | static HRESULT STDMETHODCALLTYPE UI_ShowUI( 828 | IDocHostUIHandler FAR *This, DWORD dwID, 829 | IOleInPlaceActiveObject __RPC_FAR *pActiveObject, 830 | IOleCommandTarget __RPC_FAR *pCommandTarget, 831 | IOleInPlaceFrame __RPC_FAR *pFrame, IOleInPlaceUIWindow __RPC_FAR *pDoc) { 832 | return S_OK; 833 | } 834 | static HRESULT STDMETHODCALLTYPE UI_HideUI(IDocHostUIHandler FAR *This) { 835 | return S_OK; 836 | } 837 | static HRESULT STDMETHODCALLTYPE UI_UpdateUI(IDocHostUIHandler FAR *This) { 838 | return S_OK; 839 | } 840 | static HRESULT STDMETHODCALLTYPE UI_EnableModeless(IDocHostUIHandler FAR *This, 841 | BOOL fEnable) { 842 | return S_OK; 843 | } 844 | static HRESULT STDMETHODCALLTYPE 845 | UI_OnDocWindowActivate(IDocHostUIHandler FAR *This, BOOL fActivate) { 846 | return S_OK; 847 | } 848 | static HRESULT STDMETHODCALLTYPE 849 | UI_OnFrameWindowActivate(IDocHostUIHandler FAR *This, BOOL fActivate) { 850 | return S_OK; 851 | } 852 | static HRESULT STDMETHODCALLTYPE 853 | UI_ResizeBorder(IDocHostUIHandler FAR *This, LPCRECT prcBorder, 854 | IOleInPlaceUIWindow __RPC_FAR *pUIWindow, BOOL fRameWindow) { 855 | return S_OK; 856 | } 857 | static HRESULT STDMETHODCALLTYPE 858 | UI_TranslateAccelerator(IDocHostUIHandler FAR *This, LPMSG lpMsg, 859 | const GUID __RPC_FAR *pguidCmdGroup, DWORD nCmdID) { 860 | return S_FALSE; 861 | } 862 | static HRESULT STDMETHODCALLTYPE UI_GetOptionKeyPath( 863 | IDocHostUIHandler FAR *This, LPOLESTR __RPC_FAR *pchKey, DWORD dw) { 864 | return S_FALSE; 865 | } 866 | static HRESULT STDMETHODCALLTYPE UI_GetDropTarget( 867 | IDocHostUIHandler FAR *This, IDropTarget __RPC_FAR *pDropTarget, 868 | IDropTarget __RPC_FAR *__RPC_FAR *ppDropTarget) { 869 | return S_FALSE; 870 | } 871 | static HRESULT STDMETHODCALLTYPE UI_GetExternal( 872 | IDocHostUIHandler FAR *This, IDispatch __RPC_FAR *__RPC_FAR *ppDispatch) { 873 | *ppDispatch = (IDispatch *)(This + 1); 874 | return S_OK; 875 | } 876 | static HRESULT STDMETHODCALLTYPE UI_TranslateUrl( 877 | IDocHostUIHandler FAR *This, DWORD dwTranslate, OLECHAR __RPC_FAR *pchURLIn, 878 | OLECHAR __RPC_FAR *__RPC_FAR *ppchURLOut) { 879 | *ppchURLOut = 0; 880 | return S_FALSE; 881 | } 882 | static HRESULT STDMETHODCALLTYPE 883 | UI_FilterDataObject(IDocHostUIHandler FAR *This, IDataObject __RPC_FAR *pDO, 884 | IDataObject __RPC_FAR *__RPC_FAR *ppDORet) { 885 | *ppDORet = 0; 886 | return S_FALSE; 887 | } 888 | 889 | static const TCHAR *classname = "WebView"; 890 | static const SAFEARRAYBOUND ArrayBound = {1, 0}; 891 | 892 | static IOleClientSiteVtbl MyIOleClientSiteTable = { 893 | Site_QueryInterface, Site_AddRef, Site_Release, 894 | Site_SaveObject, Site_GetMoniker, Site_GetContainer, 895 | Site_ShowObject, Site_OnShowWindow, Site_RequestNewObjectLayout}; 896 | static IOleInPlaceSiteVtbl MyIOleInPlaceSiteTable = { 897 | InPlace_QueryInterface, 898 | InPlace_AddRef, 899 | InPlace_Release, 900 | InPlace_GetWindow, 901 | InPlace_ContextSensitiveHelp, 902 | InPlace_CanInPlaceActivate, 903 | InPlace_OnInPlaceActivate, 904 | InPlace_OnUIActivate, 905 | InPlace_GetWindowContext, 906 | InPlace_Scroll, 907 | InPlace_OnUIDeactivate, 908 | InPlace_OnInPlaceDeactivate, 909 | InPlace_DiscardUndoState, 910 | InPlace_DeactivateAndUndo, 911 | InPlace_OnPosRectChange}; 912 | 913 | static IOleInPlaceFrameVtbl MyIOleInPlaceFrameTable = { 914 | Frame_QueryInterface, 915 | Frame_AddRef, 916 | Frame_Release, 917 | Frame_GetWindow, 918 | Frame_ContextSensitiveHelp, 919 | Frame_GetBorder, 920 | Frame_RequestBorderSpace, 921 | Frame_SetBorderSpace, 922 | Frame_SetActiveObject, 923 | Frame_InsertMenus, 924 | Frame_SetMenu, 925 | Frame_RemoveMenus, 926 | Frame_SetStatusText, 927 | Frame_EnableModeless, 928 | Frame_TranslateAccelerator}; 929 | 930 | static IDocHostUIHandlerVtbl MyIDocHostUIHandlerTable = { 931 | UI_QueryInterface, 932 | UI_AddRef, 933 | UI_Release, 934 | UI_ShowContextMenu, 935 | UI_GetHostInfo, 936 | UI_ShowUI, 937 | UI_HideUI, 938 | UI_UpdateUI, 939 | UI_EnableModeless, 940 | UI_OnDocWindowActivate, 941 | UI_OnFrameWindowActivate, 942 | UI_ResizeBorder, 943 | UI_TranslateAccelerator, 944 | UI_GetOptionKeyPath, 945 | UI_GetDropTarget, 946 | UI_GetExternal, 947 | UI_TranslateUrl, 948 | UI_FilterDataObject}; 949 | 950 | static void UnEmbedBrowserObject(struct webview *w) { 951 | if (w->priv.browser != NULL) { 952 | (*w->priv.browser)->lpVtbl->Close(*w->priv.browser, OLECLOSE_NOSAVE); 953 | (*w->priv.browser)->lpVtbl->Release(*w->priv.browser); 954 | GlobalFree(w->priv.browser); 955 | w->priv.browser = NULL; 956 | } 957 | } 958 | 959 | static int EmbedBrowserObject(struct webview *w) { 960 | RECT rect; 961 | IWebBrowser2 *webBrowser2 = NULL; 962 | LPCLASSFACTORY pClassFactory = NULL; 963 | _IOleClientSiteEx *_iOleClientSiteEx = NULL; 964 | IOleObject **browser = (IOleObject **)GlobalAlloc( 965 | GMEM_FIXED, sizeof(IOleObject *) + sizeof(_IOleClientSiteEx)); 966 | if (browser == NULL) { 967 | goto error; 968 | } 969 | w->priv.browser = browser; 970 | 971 | _iOleClientSiteEx = (_IOleClientSiteEx *)(browser + 1); 972 | _iOleClientSiteEx->client.lpVtbl = &MyIOleClientSiteTable; 973 | _iOleClientSiteEx->inplace.inplace.lpVtbl = &MyIOleInPlaceSiteTable; 974 | _iOleClientSiteEx->inplace.frame.frame.lpVtbl = &MyIOleInPlaceFrameTable; 975 | _iOleClientSiteEx->inplace.frame.window = w->priv.hwnd; 976 | _iOleClientSiteEx->ui.ui.lpVtbl = &MyIDocHostUIHandlerTable; 977 | _iOleClientSiteEx->external.lpVtbl = &ExternalDispatchTable; 978 | 979 | if (CoGetClassObject(iid_unref(&CLSID_WebBrowser), 980 | CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER, NULL, 981 | iid_unref(&IID_IClassFactory), 982 | (void **)&pClassFactory) != S_OK) { 983 | goto error; 984 | } 985 | 986 | if (pClassFactory == NULL) { 987 | goto error; 988 | } 989 | 990 | if (pClassFactory->lpVtbl->CreateInstance(pClassFactory, 0, 991 | iid_unref(&IID_IOleObject), 992 | (void **)browser) != S_OK) { 993 | goto error; 994 | } 995 | pClassFactory->lpVtbl->Release(pClassFactory); 996 | if ((*browser)->lpVtbl->SetClientSite( 997 | *browser, (IOleClientSite *)_iOleClientSiteEx) != S_OK) { 998 | goto error; 999 | } 1000 | (*browser)->lpVtbl->SetHostNames(*browser, L"My Host Name", 0); 1001 | 1002 | if (OleSetContainedObject((struct IUnknown *)(*browser), TRUE) != S_OK) { 1003 | goto error; 1004 | } 1005 | GetClientRect(w->priv.hwnd, &rect); 1006 | if ((*browser)->lpVtbl->DoVerb((*browser), OLEIVERB_SHOW, NULL, 1007 | (IOleClientSite *)_iOleClientSiteEx, -1, 1008 | w->priv.hwnd, &rect) != S_OK) { 1009 | goto error; 1010 | } 1011 | if ((*browser)->lpVtbl->QueryInterface((*browser), 1012 | iid_unref(&IID_IWebBrowser2), 1013 | (void **)&webBrowser2) != S_OK) { 1014 | goto error; 1015 | } 1016 | 1017 | webBrowser2->lpVtbl->put_Left(webBrowser2, 0); 1018 | webBrowser2->lpVtbl->put_Top(webBrowser2, 0); 1019 | webBrowser2->lpVtbl->put_Width(webBrowser2, rect.right); 1020 | webBrowser2->lpVtbl->put_Height(webBrowser2, rect.bottom); 1021 | webBrowser2->lpVtbl->Release(webBrowser2); 1022 | 1023 | return 0; 1024 | error: 1025 | UnEmbedBrowserObject(w); 1026 | if (pClassFactory != NULL) { 1027 | pClassFactory->lpVtbl->Release(pClassFactory); 1028 | } 1029 | if (browser != NULL) { 1030 | GlobalFree(browser); 1031 | } 1032 | return -1; 1033 | } 1034 | 1035 | #define WEBVIEW_DATA_URL_PREFIX "data:text/html," 1036 | static int DisplayHTMLPage(struct webview *w) { 1037 | IWebBrowser2 *webBrowser2; 1038 | VARIANT myURL; 1039 | LPDISPATCH lpDispatch; 1040 | IHTMLDocument2 *htmlDoc2; 1041 | BSTR bstr; 1042 | IOleObject *browserObject; 1043 | SAFEARRAY *sfArray; 1044 | VARIANT *pVar; 1045 | browserObject = *w->priv.browser; 1046 | int isDataURL = 0; 1047 | const char *webview_url = webview_check_url(w->url); 1048 | if (!browserObject->lpVtbl->QueryInterface( 1049 | browserObject, iid_unref(&IID_IWebBrowser2), (void **)&webBrowser2)) { 1050 | LPCSTR webPageName; 1051 | isDataURL = (strncmp(webview_url, WEBVIEW_DATA_URL_PREFIX, 1052 | strlen(WEBVIEW_DATA_URL_PREFIX)) == 0); 1053 | if (isDataURL) { 1054 | webPageName = "about:blank"; 1055 | } else { 1056 | webPageName = (LPCSTR)webview_url; 1057 | } 1058 | VariantInit(&myURL); 1059 | myURL.vt = VT_BSTR; 1060 | #ifndef UNICODE 1061 | { 1062 | wchar_t *buffer = webview_to_utf16(webPageName); 1063 | if (buffer == NULL) { 1064 | goto badalloc; 1065 | } 1066 | myURL.bstrVal = SysAllocString(buffer); 1067 | GlobalFree(buffer); 1068 | } 1069 | #else 1070 | myURL.bstrVal = SysAllocString(webPageName); 1071 | #endif 1072 | if (!myURL.bstrVal) { 1073 | badalloc: 1074 | webBrowser2->lpVtbl->Release(webBrowser2); 1075 | return (-6); 1076 | } 1077 | webBrowser2->lpVtbl->Navigate2(webBrowser2, &myURL, 0, 0, 0, 0); 1078 | VariantClear(&myURL); 1079 | if (!isDataURL) { 1080 | return 0; 1081 | } 1082 | 1083 | char *url = (char *)calloc(1, strlen(webview_url) + 1); 1084 | char *q = url; 1085 | for (const char *p = webview_url + strlen(WEBVIEW_DATA_URL_PREFIX); *q = *p; 1086 | p++, q++) { 1087 | if (*q == '%' && *(p + 1) && *(p + 2)) { 1088 | sscanf(p + 1, "%02x", q); 1089 | p = p + 2; 1090 | } 1091 | } 1092 | 1093 | if (webBrowser2->lpVtbl->get_Document(webBrowser2, &lpDispatch) == S_OK) { 1094 | if (lpDispatch->lpVtbl->QueryInterface(lpDispatch, 1095 | iid_unref(&IID_IHTMLDocument2), 1096 | (void **)&htmlDoc2) == S_OK) { 1097 | if ((sfArray = SafeArrayCreate(VT_VARIANT, 1, 1098 | (SAFEARRAYBOUND *)&ArrayBound))) { 1099 | if (!SafeArrayAccessData(sfArray, (void **)&pVar)) { 1100 | pVar->vt = VT_BSTR; 1101 | #ifndef UNICODE 1102 | { 1103 | wchar_t *buffer = webview_to_utf16(url); 1104 | if (buffer == NULL) { 1105 | goto release; 1106 | } 1107 | bstr = SysAllocString(buffer); 1108 | GlobalFree(buffer); 1109 | } 1110 | #else 1111 | bstr = SysAllocString(string); 1112 | #endif 1113 | if ((pVar->bstrVal = bstr)) { 1114 | htmlDoc2->lpVtbl->write(htmlDoc2, sfArray); 1115 | htmlDoc2->lpVtbl->close(htmlDoc2); 1116 | } 1117 | } 1118 | SafeArrayDestroy(sfArray); 1119 | } 1120 | release: 1121 | free(url); 1122 | htmlDoc2->lpVtbl->Release(htmlDoc2); 1123 | } 1124 | lpDispatch->lpVtbl->Release(lpDispatch); 1125 | } 1126 | webBrowser2->lpVtbl->Release(webBrowser2); 1127 | return (0); 1128 | } 1129 | return (-5); 1130 | } 1131 | 1132 | static LRESULT CALLBACK wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, 1133 | LPARAM lParam) { 1134 | struct webview *w = (struct webview *)GetWindowLongPtr(hwnd, GWLP_USERDATA); 1135 | switch (uMsg) { 1136 | case WM_CREATE: 1137 | w = (struct webview *)((CREATESTRUCT *)lParam)->lpCreateParams; 1138 | w->priv.hwnd = hwnd; 1139 | return EmbedBrowserObject(w); 1140 | case WM_DESTROY: 1141 | UnEmbedBrowserObject(w); 1142 | PostQuitMessage(0); 1143 | return TRUE; 1144 | case WM_SIZE: { 1145 | IWebBrowser2 *webBrowser2; 1146 | IOleObject *browser = *w->priv.browser; 1147 | if (browser->lpVtbl->QueryInterface(browser, iid_unref(&IID_IWebBrowser2), 1148 | (void **)&webBrowser2) == S_OK) { 1149 | RECT rect; 1150 | GetClientRect(hwnd, &rect); 1151 | webBrowser2->lpVtbl->put_Width(webBrowser2, rect.right); 1152 | webBrowser2->lpVtbl->put_Height(webBrowser2, rect.bottom); 1153 | } 1154 | return TRUE; 1155 | } 1156 | case WM_WEBVIEW_DISPATCH: { 1157 | webview_dispatch_fn f = (webview_dispatch_fn)wParam; 1158 | void *arg = (void *)lParam; 1159 | (*f)(w, arg); 1160 | return TRUE; 1161 | } 1162 | } 1163 | return DefWindowProc(hwnd, uMsg, wParam, lParam); 1164 | } 1165 | 1166 | #define WEBVIEW_KEY_FEATURE_BROWSER_EMULATION \ 1167 | "Software\\Microsoft\\Internet " \ 1168 | "Explorer\\Main\\FeatureControl\\FEATURE_BROWSER_EMULATION" 1169 | 1170 | static int webview_fix_ie_compat_mode() { 1171 | HKEY hKey; 1172 | DWORD ie_version = 11000; 1173 | TCHAR appname[MAX_PATH + 1]; 1174 | TCHAR *p; 1175 | if (GetModuleFileName(NULL, appname, MAX_PATH + 1) == 0) { 1176 | return -1; 1177 | } 1178 | for (p = &appname[strlen(appname) - 1]; p != appname && *p != '\\'; p--) { 1179 | } 1180 | p++; 1181 | if (RegCreateKey(HKEY_CURRENT_USER, WEBVIEW_KEY_FEATURE_BROWSER_EMULATION, 1182 | &hKey) != ERROR_SUCCESS) { 1183 | return -1; 1184 | } 1185 | if (RegSetValueEx(hKey, p, 0, REG_DWORD, (BYTE *)&ie_version, 1186 | sizeof(ie_version)) != ERROR_SUCCESS) { 1187 | RegCloseKey(hKey); 1188 | return -1; 1189 | } 1190 | RegCloseKey(hKey); 1191 | return 0; 1192 | } 1193 | 1194 | WEBVIEW_API int webview_init(struct webview *w) { 1195 | WNDCLASSEX wc; 1196 | HINSTANCE hInstance; 1197 | DWORD style; 1198 | RECT clientRect; 1199 | RECT rect; 1200 | 1201 | if (webview_fix_ie_compat_mode() < 0) { 1202 | return -1; 1203 | } 1204 | 1205 | hInstance = GetModuleHandle(NULL); 1206 | if (hInstance == NULL) { 1207 | return -1; 1208 | } 1209 | if (OleInitialize(NULL) != S_OK) { 1210 | return -1; 1211 | } 1212 | ZeroMemory(&wc, sizeof(WNDCLASSEX)); 1213 | wc.cbSize = sizeof(WNDCLASSEX); 1214 | wc.hInstance = hInstance; 1215 | wc.lpfnWndProc = wndproc; 1216 | wc.lpszClassName = classname; 1217 | RegisterClassEx(&wc); 1218 | 1219 | style = WS_OVERLAPPEDWINDOW; 1220 | if (!w->resizable) { 1221 | style = WS_OVERLAPPED | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU; 1222 | } 1223 | 1224 | rect.left = 0; 1225 | rect.top = 0; 1226 | rect.right = w->width; 1227 | rect.bottom = w->height; 1228 | AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, 0); 1229 | 1230 | GetClientRect(GetDesktopWindow(), &clientRect); 1231 | int left = (clientRect.right / 2) - ((rect.right - rect.left) / 2); 1232 | int top = (clientRect.bottom / 2) - ((rect.bottom - rect.top) / 2); 1233 | rect.right = rect.right - rect.left + left; 1234 | rect.left = left; 1235 | rect.bottom = rect.bottom - rect.top + top; 1236 | rect.top = top; 1237 | 1238 | w->priv.hwnd = 1239 | CreateWindowEx(0, classname, w->title, style, rect.left, rect.top, 1240 | rect.right - rect.left, rect.bottom - rect.top, 1241 | HWND_DESKTOP, NULL, hInstance, (void *)w); 1242 | if (w->priv.hwnd == 0) { 1243 | OleUninitialize(); 1244 | return -1; 1245 | } 1246 | 1247 | SetWindowLongPtr(w->priv.hwnd, GWLP_USERDATA, (LONG_PTR)w); 1248 | 1249 | DisplayHTMLPage(w); 1250 | 1251 | SetWindowText(w->priv.hwnd, w->title); 1252 | ShowWindow(w->priv.hwnd, SW_SHOWDEFAULT); 1253 | UpdateWindow(w->priv.hwnd); 1254 | SetFocus(w->priv.hwnd); 1255 | 1256 | return 0; 1257 | } 1258 | 1259 | WEBVIEW_API int webview_loop(struct webview *w, int blocking) { 1260 | MSG msg; 1261 | if (blocking) { 1262 | GetMessage(&msg, 0, 0, 0); 1263 | } else { 1264 | PeekMessage(&msg, 0, 0, 0, PM_REMOVE); 1265 | } 1266 | switch (msg.message) { 1267 | case WM_QUIT: 1268 | return -1; 1269 | case WM_COMMAND: 1270 | case WM_KEYDOWN: 1271 | case WM_KEYUP: { 1272 | HRESULT r = S_OK; 1273 | IWebBrowser2 *webBrowser2; 1274 | IOleObject *browser = *w->priv.browser; 1275 | if (browser->lpVtbl->QueryInterface(browser, iid_unref(&IID_IWebBrowser2), 1276 | (void **)&webBrowser2) == S_OK) { 1277 | IOleInPlaceActiveObject *pIOIPAO; 1278 | if (browser->lpVtbl->QueryInterface( 1279 | browser, iid_unref(&IID_IOleInPlaceActiveObject), 1280 | (void **)&pIOIPAO) == S_OK) { 1281 | r = pIOIPAO->lpVtbl->TranslateAccelerator(pIOIPAO, &msg); 1282 | pIOIPAO->lpVtbl->Release(pIOIPAO); 1283 | } 1284 | webBrowser2->lpVtbl->Release(webBrowser2); 1285 | } 1286 | if (r != S_FALSE) { 1287 | break; 1288 | } 1289 | } 1290 | default: 1291 | TranslateMessage(&msg); 1292 | DispatchMessage(&msg); 1293 | } 1294 | return 0; 1295 | } 1296 | 1297 | WEBVIEW_API int webview_eval(struct webview *w, const char *js) { 1298 | IWebBrowser2 *webBrowser2; 1299 | IHTMLDocument2 *htmlDoc2; 1300 | IDispatch *docDispatch; 1301 | IDispatch *scriptDispatch; 1302 | if ((*w->priv.browser) 1303 | ->lpVtbl->QueryInterface((*w->priv.browser), 1304 | iid_unref(&IID_IWebBrowser2), 1305 | (void **)&webBrowser2) != S_OK) { 1306 | return -1; 1307 | } 1308 | 1309 | if (webBrowser2->lpVtbl->get_Document(webBrowser2, &docDispatch) != S_OK) { 1310 | return -1; 1311 | } 1312 | if (docDispatch->lpVtbl->QueryInterface(docDispatch, 1313 | iid_unref(&IID_IHTMLDocument2), 1314 | (void **)&htmlDoc2) != S_OK) { 1315 | return -1; 1316 | } 1317 | if (htmlDoc2->lpVtbl->get_Script(htmlDoc2, &scriptDispatch) != S_OK) { 1318 | return -1; 1319 | } 1320 | DISPID dispid; 1321 | BSTR evalStr = SysAllocString(L"eval"); 1322 | if (scriptDispatch->lpVtbl->GetIDsOfNames( 1323 | scriptDispatch, iid_unref(&IID_NULL), &evalStr, 1, 1324 | LOCALE_SYSTEM_DEFAULT, &dispid) != S_OK) { 1325 | SysFreeString(evalStr); 1326 | return -1; 1327 | } 1328 | SysFreeString(evalStr); 1329 | 1330 | DISPPARAMS params; 1331 | VARIANT arg; 1332 | VARIANT result; 1333 | EXCEPINFO excepInfo; 1334 | UINT nArgErr = (UINT)-1; 1335 | params.cArgs = 1; 1336 | params.cNamedArgs = 0; 1337 | params.rgvarg = &arg; 1338 | arg.vt = VT_BSTR; 1339 | static const char *prologue = "(function(){"; 1340 | static const char *epilogue = ";})();"; 1341 | int n = strlen(prologue) + strlen(epilogue) + strlen(js) + 1; 1342 | char *eval = (char *)malloc(n); 1343 | snprintf(eval, n, "%s%s%s", prologue, js, epilogue); 1344 | wchar_t *buf = webview_to_utf16(eval); 1345 | if (buf == NULL) { 1346 | return -1; 1347 | } 1348 | arg.bstrVal = SysAllocString(buf); 1349 | if (scriptDispatch->lpVtbl->Invoke( 1350 | scriptDispatch, dispid, iid_unref(&IID_NULL), 0, DISPATCH_METHOD, 1351 | ¶ms, &result, &excepInfo, &nArgErr) != S_OK) { 1352 | return -1; 1353 | } 1354 | SysFreeString(arg.bstrVal); 1355 | free(eval); 1356 | scriptDispatch->lpVtbl->Release(scriptDispatch); 1357 | htmlDoc2->lpVtbl->Release(htmlDoc2); 1358 | docDispatch->lpVtbl->Release(docDispatch); 1359 | return 0; 1360 | } 1361 | 1362 | WEBVIEW_API void webview_dispatch(struct webview *w, webview_dispatch_fn fn, 1363 | void *arg) { 1364 | PostMessageW(w->priv.hwnd, WM_WEBVIEW_DISPATCH, (WPARAM)fn, (LPARAM)arg); 1365 | } 1366 | 1367 | WEBVIEW_API void webview_set_title(struct webview *w, const char *title) { 1368 | SetWindowText(w->priv.hwnd, title); 1369 | } 1370 | 1371 | WEBVIEW_API void webview_set_fullscreen(struct webview *w, int fullscreen) { 1372 | if (w->priv.is_fullscreen == !!fullscreen) { 1373 | return; 1374 | } 1375 | if (w->priv.is_fullscreen == 0) { 1376 | w->priv.saved_style = GetWindowLong(w->priv.hwnd, GWL_STYLE); 1377 | w->priv.saved_ex_style = GetWindowLong(w->priv.hwnd, GWL_EXSTYLE); 1378 | GetWindowRect(w->priv.hwnd, &w->priv.saved_rect); 1379 | } 1380 | w->priv.is_fullscreen = !!fullscreen; 1381 | if (fullscreen) { 1382 | MONITORINFO monitor_info; 1383 | SetWindowLong(w->priv.hwnd, GWL_STYLE, 1384 | w->priv.saved_style & ~(WS_CAPTION | WS_THICKFRAME)); 1385 | SetWindowLong(w->priv.hwnd, GWL_EXSTYLE, 1386 | w->priv.saved_ex_style & 1387 | ~(WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE | 1388 | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE)); 1389 | monitor_info.cbSize = sizeof(monitor_info); 1390 | GetMonitorInfo(MonitorFromWindow(w->priv.hwnd, MONITOR_DEFAULTTONEAREST), 1391 | &monitor_info); 1392 | RECT r; 1393 | r.left = monitor_info.rcMonitor.left; 1394 | r.top = monitor_info.rcMonitor.top; 1395 | r.right = monitor_info.rcMonitor.right; 1396 | r.bottom = monitor_info.rcMonitor.bottom; 1397 | SetWindowPos(w->priv.hwnd, NULL, r.left, r.top, r.right - r.left, 1398 | r.bottom - r.top, 1399 | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED); 1400 | } else { 1401 | SetWindowLong(w->priv.hwnd, GWL_STYLE, w->priv.saved_style); 1402 | SetWindowLong(w->priv.hwnd, GWL_EXSTYLE, w->priv.saved_ex_style); 1403 | SetWindowPos(w->priv.hwnd, NULL, w->priv.saved_rect.left, 1404 | w->priv.saved_rect.top, 1405 | w->priv.saved_rect.right - w->priv.saved_rect.left, 1406 | w->priv.saved_rect.bottom - w->priv.saved_rect.top, 1407 | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED); 1408 | } 1409 | } 1410 | 1411 | WEBVIEW_API void webview_set_color(struct webview *w, uint8_t r, uint8_t g, 1412 | uint8_t b, uint8_t a) { 1413 | HBRUSH brush = CreateSolidBrush(RGB(r, g, b)); 1414 | SetClassLongPtr(w->priv.hwnd, GCLP_HBRBACKGROUND, (LONG_PTR)brush); 1415 | } 1416 | 1417 | /* These are missing parts from MinGW */ 1418 | #ifndef __IFileDialog_INTERFACE_DEFINED__ 1419 | #define __IFileDialog_INTERFACE_DEFINED__ 1420 | enum _FILEOPENDIALOGOPTIONS { 1421 | FOS_OVERWRITEPROMPT = 0x2, 1422 | FOS_STRICTFILETYPES = 0x4, 1423 | FOS_NOCHANGEDIR = 0x8, 1424 | FOS_PICKFOLDERS = 0x20, 1425 | FOS_FORCEFILESYSTEM = 0x40, 1426 | FOS_ALLNONSTORAGEITEMS = 0x80, 1427 | FOS_NOVALIDATE = 0x100, 1428 | FOS_ALLOWMULTISELECT = 0x200, 1429 | FOS_PATHMUSTEXIST = 0x800, 1430 | FOS_FILEMUSTEXIST = 0x1000, 1431 | FOS_CREATEPROMPT = 0x2000, 1432 | FOS_SHAREAWARE = 0x4000, 1433 | FOS_NOREADONLYRETURN = 0x8000, 1434 | FOS_NOTESTFILECREATE = 0x10000, 1435 | FOS_HIDEMRUPLACES = 0x20000, 1436 | FOS_HIDEPINNEDPLACES = 0x40000, 1437 | FOS_NODEREFERENCELINKS = 0x100000, 1438 | FOS_DONTADDTORECENT = 0x2000000, 1439 | FOS_FORCESHOWHIDDEN = 0x10000000, 1440 | FOS_DEFAULTNOMINIMODE = 0x20000000, 1441 | FOS_FORCEPREVIEWPANEON = 0x40000000 1442 | }; 1443 | typedef DWORD FILEOPENDIALOGOPTIONS; 1444 | typedef enum FDAP { FDAP_BOTTOM = 0, FDAP_TOP = 1 } FDAP; 1445 | DEFINE_GUID(IID_IFileDialog, 0x42f85136, 0xdb7e, 0x439c, 0x85, 0xf1, 0xe4, 0x07, 1446 | 0x5d, 0x13, 0x5f, 0xc8); 1447 | typedef struct IFileDialogVtbl { 1448 | BEGIN_INTERFACE 1449 | HRESULT(STDMETHODCALLTYPE *QueryInterface) 1450 | (IFileDialog *This, REFIID riid, void **ppvObject); 1451 | ULONG(STDMETHODCALLTYPE *AddRef)(IFileDialog *This); 1452 | ULONG(STDMETHODCALLTYPE *Release)(IFileDialog *This); 1453 | HRESULT(STDMETHODCALLTYPE *Show)(IFileDialog *This, HWND hwndOwner); 1454 | HRESULT(STDMETHODCALLTYPE *SetFileTypes) 1455 | (IFileDialog *This, UINT cFileTypes, const COMDLG_FILTERSPEC *rgFilterSpec); 1456 | HRESULT(STDMETHODCALLTYPE *SetFileTypeIndex) 1457 | (IFileDialog *This, UINT iFileType); 1458 | HRESULT(STDMETHODCALLTYPE *GetFileTypeIndex) 1459 | (IFileDialog *This, UINT *piFileType); 1460 | HRESULT(STDMETHODCALLTYPE *Advise) 1461 | (IFileDialog *This, IFileDialogEvents *pfde, DWORD *pdwCookie); 1462 | HRESULT(STDMETHODCALLTYPE *Unadvise)(IFileDialog *This, DWORD dwCookie); 1463 | HRESULT(STDMETHODCALLTYPE *SetOptions) 1464 | (IFileDialog *This, FILEOPENDIALOGOPTIONS fos); 1465 | HRESULT(STDMETHODCALLTYPE *GetOptions) 1466 | (IFileDialog *This, FILEOPENDIALOGOPTIONS *pfos); 1467 | HRESULT(STDMETHODCALLTYPE *SetDefaultFolder) 1468 | (IFileDialog *This, IShellItem *psi); 1469 | HRESULT(STDMETHODCALLTYPE *SetFolder)(IFileDialog *This, IShellItem *psi); 1470 | HRESULT(STDMETHODCALLTYPE *GetFolder)(IFileDialog *This, IShellItem **ppsi); 1471 | HRESULT(STDMETHODCALLTYPE *GetCurrentSelection) 1472 | (IFileDialog *This, IShellItem **ppsi); 1473 | HRESULT(STDMETHODCALLTYPE *SetFileName)(IFileDialog *This, LPCWSTR pszName); 1474 | HRESULT(STDMETHODCALLTYPE *GetFileName)(IFileDialog *This, LPWSTR *pszName); 1475 | HRESULT(STDMETHODCALLTYPE *SetTitle)(IFileDialog *This, LPCWSTR pszTitle); 1476 | HRESULT(STDMETHODCALLTYPE *SetOkButtonLabel) 1477 | (IFileDialog *This, LPCWSTR pszText); 1478 | HRESULT(STDMETHODCALLTYPE *SetFileNameLabel) 1479 | (IFileDialog *This, LPCWSTR pszLabel); 1480 | HRESULT(STDMETHODCALLTYPE *GetResult)(IFileDialog *This, IShellItem **ppsi); 1481 | HRESULT(STDMETHODCALLTYPE *AddPlace) 1482 | (IFileDialog *This, IShellItem *psi, FDAP fdap); 1483 | HRESULT(STDMETHODCALLTYPE *SetDefaultExtension) 1484 | (IFileDialog *This, LPCWSTR pszDefaultExtension); 1485 | HRESULT(STDMETHODCALLTYPE *Close)(IFileDialog *This, HRESULT hr); 1486 | HRESULT(STDMETHODCALLTYPE *SetClientGuid)(IFileDialog *This, REFGUID guid); 1487 | HRESULT(STDMETHODCALLTYPE *ClearClientData)(IFileDialog *This); 1488 | HRESULT(STDMETHODCALLTYPE *SetFilter) 1489 | (IFileDialog *This, IShellItemFilter *pFilter); 1490 | END_INTERFACE 1491 | } IFileDialogVtbl; 1492 | interface IFileDialog { 1493 | CONST_VTBL IFileDialogVtbl *lpVtbl; 1494 | }; 1495 | DEFINE_GUID(IID_IFileOpenDialog, 0xd57c7288, 0xd4ad, 0x4768, 0xbe, 0x02, 0x9d, 1496 | 0x96, 0x95, 0x32, 0xd9, 0x60); 1497 | DEFINE_GUID(IID_IFileSaveDialog, 0x84bccd23, 0x5fde, 0x4cdb, 0xae, 0xa4, 0xaf, 1498 | 0x64, 0xb8, 0x3d, 0x78, 0xab); 1499 | #endif 1500 | 1501 | WEBVIEW_API void webview_dialog(struct webview *w, 1502 | enum webview_dialog_type dlgtype, int flags, 1503 | const char *title, const char *arg, 1504 | char *result, size_t resultsz) { 1505 | if (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN || 1506 | dlgtype == WEBVIEW_DIALOG_TYPE_SAVE) { 1507 | IFileDialog *dlg = NULL; 1508 | IShellItem *res = NULL; 1509 | WCHAR *ws = NULL; 1510 | char *s = NULL; 1511 | FILEOPENDIALOGOPTIONS opts, add_opts; 1512 | if (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN) { 1513 | if (CoCreateInstance( 1514 | iid_unref(&CLSID_FileOpenDialog), NULL, CLSCTX_INPROC_SERVER, 1515 | iid_unref(&IID_IFileOpenDialog), (void **)&dlg) != S_OK) { 1516 | goto error_dlg; 1517 | } 1518 | if (flags & WEBVIEW_DIALOG_FLAG_DIRECTORY) { 1519 | add_opts |= FOS_PICKFOLDERS; 1520 | } 1521 | add_opts |= FOS_NOCHANGEDIR | FOS_ALLNONSTORAGEITEMS | FOS_NOVALIDATE | 1522 | FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST | FOS_SHAREAWARE | 1523 | FOS_NOTESTFILECREATE | FOS_NODEREFERENCELINKS | 1524 | FOS_FORCESHOWHIDDEN | FOS_DEFAULTNOMINIMODE; 1525 | } else { 1526 | if (CoCreateInstance( 1527 | iid_unref(&CLSID_FileSaveDialog), NULL, CLSCTX_INPROC_SERVER, 1528 | iid_unref(&IID_IFileSaveDialog), (void **)&dlg) != S_OK) { 1529 | goto error_dlg; 1530 | } 1531 | add_opts |= FOS_OVERWRITEPROMPT | FOS_NOCHANGEDIR | 1532 | FOS_ALLNONSTORAGEITEMS | FOS_NOVALIDATE | FOS_SHAREAWARE | 1533 | FOS_NOTESTFILECREATE | FOS_NODEREFERENCELINKS | 1534 | FOS_FORCESHOWHIDDEN | FOS_DEFAULTNOMINIMODE; 1535 | } 1536 | if (dlg->lpVtbl->GetOptions(dlg, &opts) != S_OK) { 1537 | goto error_dlg; 1538 | } 1539 | opts &= ~FOS_NOREADONLYRETURN; 1540 | opts |= add_opts; 1541 | if (dlg->lpVtbl->SetOptions(dlg, opts) != S_OK) { 1542 | goto error_dlg; 1543 | } 1544 | if (dlg->lpVtbl->Show(dlg, w->priv.hwnd) != S_OK) { 1545 | goto error_dlg; 1546 | } 1547 | if (dlg->lpVtbl->GetResult(dlg, &res) != S_OK) { 1548 | goto error_dlg; 1549 | } 1550 | if (res->lpVtbl->GetDisplayName(res, SIGDN_FILESYSPATH, &ws) != S_OK) { 1551 | goto error_result; 1552 | } 1553 | s = webview_from_utf16(ws); 1554 | strncpy(result, s, resultsz); 1555 | result[resultsz - 1] = '\0'; 1556 | CoTaskMemFree(ws); 1557 | error_result: 1558 | res->lpVtbl->Release(res); 1559 | error_dlg: 1560 | dlg->lpVtbl->Release(dlg); 1561 | return; 1562 | } else if (dlgtype == WEBVIEW_DIALOG_TYPE_ALERT) { 1563 | #if 0 1564 | /* MinGW often doesn't contain TaskDialog, we'll use MessageBox for now */ 1565 | WCHAR *wtitle = webview_to_utf16(title); 1566 | WCHAR *warg = webview_to_utf16(arg); 1567 | TaskDialog(w->priv.hwnd, NULL, NULL, wtitle, warg, 0, NULL, NULL); 1568 | GlobalFree(warg); 1569 | GlobalFree(wtitle); 1570 | #else 1571 | UINT type = MB_OK; 1572 | switch (flags & WEBVIEW_DIALOG_FLAG_ALERT_MASK) { 1573 | case WEBVIEW_DIALOG_FLAG_INFO: 1574 | type |= MB_ICONINFORMATION; 1575 | break; 1576 | case WEBVIEW_DIALOG_FLAG_WARNING: 1577 | type |= MB_ICONWARNING; 1578 | break; 1579 | case WEBVIEW_DIALOG_FLAG_ERROR: 1580 | type |= MB_ICONERROR; 1581 | break; 1582 | } 1583 | MessageBox(w->priv.hwnd, arg, title, type); 1584 | #endif 1585 | } 1586 | } 1587 | 1588 | WEBVIEW_API void webview_terminate(struct webview *w) { PostQuitMessage(0); } 1589 | WEBVIEW_API void webview_exit(struct webview *w) { OleUninitialize(); } 1590 | WEBVIEW_API void webview_print_log(const char *s) { OutputDebugString(s); } 1591 | 1592 | #endif /* WEBVIEW_WINAPI */ 1593 | 1594 | #if defined(WEBVIEW_COCOA) 1595 | #if (!defined MAC_OS_X_VERSION_10_12) || \ 1596 | MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12 1597 | #define NSAlertStyleWarning NSWarningAlertStyle 1598 | #define NSAlertStyleCritical NSCriticalAlertStyle 1599 | #define NSWindowStyleMaskResizable NSResizableWindowMask 1600 | #define NSWindowStyleMaskMiniaturizable NSMiniaturizableWindowMask 1601 | #define NSWindowStyleMaskTitled NSTitledWindowMask 1602 | #define NSWindowStyleMaskClosable NSClosableWindowMask 1603 | #define NSWindowStyleMaskFullScreen NSFullScreenWindowMask 1604 | #define NSEventMaskAny NSAnyEventMask 1605 | #define NSEventModifierFlagCommand NSCommandKeyMask 1606 | #define NSEventModifierFlagOption NSAlternateKeyMask 1607 | #define NSAlertStyleInformational NSInformationalAlertStyle 1608 | #endif /* MAC_OS_X_VERSION_10_12 */ 1609 | #if (!defined MAC_OS_X_VERSION_10_13) || \ 1610 | MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_13 1611 | #define NSModalResponseOK NSFileHandlingPanelOKButton 1612 | #endif /* MAC_OS_X_VERSION_10_12, MAC_OS_X_VERSION_10_13 */ 1613 | static void webview_window_will_close(id self, SEL cmd, id notification) { 1614 | struct webview *w = 1615 | (struct webview *)objc_getAssociatedObject(self, "webview"); 1616 | webview_terminate(w); 1617 | } 1618 | 1619 | static BOOL webview_is_selector_excluded_from_web_script(id self, SEL cmd, 1620 | SEL selector) { 1621 | return selector != @selector(invoke:); 1622 | } 1623 | 1624 | static NSString *webview_webscript_name_for_selector(id self, SEL cmd, 1625 | SEL selector) { 1626 | return selector == @selector(invoke:) ? @"invoke" : nil; 1627 | } 1628 | 1629 | static void webview_did_clear_window_object(id self, SEL cmd, id webview, 1630 | id script, id frame) { 1631 | [script setValue:self forKey:@"external"]; 1632 | } 1633 | 1634 | static void webview_external_invoke(id self, SEL cmd, id arg) { 1635 | struct webview *w = 1636 | (struct webview *)objc_getAssociatedObject(self, "webview"); 1637 | if (w == NULL || w->external_invoke_cb == NULL) { 1638 | return; 1639 | } 1640 | if ([arg isKindOfClass:[NSString class]] == NO) { 1641 | return; 1642 | } 1643 | w->external_invoke_cb(w, [(NSString *)(arg)UTF8String]); 1644 | } 1645 | 1646 | WEBVIEW_API int webview_init(struct webview *w) { 1647 | w->priv.pool = [[NSAutoreleasePool alloc] init]; 1648 | [NSApplication sharedApplication]; 1649 | 1650 | Class webViewDelegateClass = 1651 | objc_allocateClassPair([NSObject class], "WebViewDelegate", 0); 1652 | class_addMethod(webViewDelegateClass, sel_registerName("windowWillClose:"), 1653 | (IMP)webview_window_will_close, "v@:@"); 1654 | class_addMethod(object_getClass(webViewDelegateClass), 1655 | sel_registerName("isSelectorExcludedFromWebScript:"), 1656 | (IMP)webview_is_selector_excluded_from_web_script, "c@::"); 1657 | class_addMethod(object_getClass(webViewDelegateClass), 1658 | sel_registerName("webScriptNameForSelector:"), 1659 | (IMP)webview_webscript_name_for_selector, "c@::"); 1660 | class_addMethod(webViewDelegateClass, 1661 | sel_registerName("webView:didClearWindowObject:forFrame:"), 1662 | (IMP)webview_did_clear_window_object, "v@:@@@"); 1663 | class_addMethod(webViewDelegateClass, sel_registerName("invoke:"), 1664 | (IMP)webview_external_invoke, "v@:@"); 1665 | objc_registerClassPair(webViewDelegateClass); 1666 | 1667 | w->priv.windowDelegate = [[webViewDelegateClass alloc] init]; 1668 | objc_setAssociatedObject(w->priv.windowDelegate, "webview", (id)(w), 1669 | OBJC_ASSOCIATION_ASSIGN); 1670 | 1671 | NSString *nsTitle = [NSString stringWithUTF8String:w->title]; 1672 | NSRect r = NSMakeRect(0, 0, w->width, w->height); 1673 | NSUInteger style = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | 1674 | NSWindowStyleMaskMiniaturizable; 1675 | if (w->resizable) { 1676 | style = style | NSWindowStyleMaskResizable; 1677 | } 1678 | w->priv.window = [[NSWindow alloc] initWithContentRect:r 1679 | styleMask:style 1680 | backing:NSBackingStoreBuffered 1681 | defer:NO]; 1682 | [w->priv.window autorelease]; 1683 | [w->priv.window setTitle:nsTitle]; 1684 | [w->priv.window setDelegate:w->priv.windowDelegate]; 1685 | [w->priv.window center]; 1686 | 1687 | [[NSUserDefaults standardUserDefaults] setBool:!!w->debug 1688 | forKey:@"WebKitDeveloperExtras"]; 1689 | [[NSUserDefaults standardUserDefaults] synchronize]; 1690 | w->priv.webview = 1691 | [[WebView alloc] initWithFrame:r frameName:@"WebView" groupName:nil]; 1692 | NSURL *nsURL = [NSURL 1693 | URLWithString:[NSString stringWithUTF8String:webview_check_url(w->url)]]; 1694 | [[w->priv.webview mainFrame] loadRequest:[NSURLRequest requestWithURL:nsURL]]; 1695 | 1696 | [w->priv.webview setAutoresizesSubviews:YES]; 1697 | [w->priv.webview 1698 | setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; 1699 | w->priv.webview.frameLoadDelegate = w->priv.windowDelegate; 1700 | [[w->priv.window contentView] addSubview:w->priv.webview]; 1701 | [w->priv.window orderFrontRegardless]; 1702 | 1703 | [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; 1704 | [NSApp finishLaunching]; 1705 | [NSApp activateIgnoringOtherApps:YES]; 1706 | 1707 | NSMenu *menubar = [[[NSMenu alloc] initWithTitle:@""] autorelease]; 1708 | 1709 | NSString *appName = [[NSProcessInfo processInfo] processName]; 1710 | NSMenuItem *appMenuItem = 1711 | [[[NSMenuItem alloc] initWithTitle:appName action:NULL keyEquivalent:@""] 1712 | autorelease]; 1713 | NSMenu *appMenu = [[[NSMenu alloc] initWithTitle:appName] autorelease]; 1714 | [appMenuItem setSubmenu:appMenu]; 1715 | [menubar addItem:appMenuItem]; 1716 | 1717 | NSString *title = [@"Hide " stringByAppendingString:appName]; 1718 | NSMenuItem *item = [[[NSMenuItem alloc] initWithTitle:title 1719 | action:@selector(hide:) 1720 | keyEquivalent:@"h"] autorelease]; 1721 | [appMenu addItem:item]; 1722 | item = [[[NSMenuItem alloc] initWithTitle:@"Hide Others" 1723 | action:@selector(hideOtherApplications:) 1724 | keyEquivalent:@"h"] autorelease]; 1725 | [item setKeyEquivalentModifierMask:(NSEventModifierFlagOption | 1726 | NSEventModifierFlagCommand)]; 1727 | [appMenu addItem:item]; 1728 | item = [[[NSMenuItem alloc] initWithTitle:@"Show All" 1729 | action:@selector(unhideAllApplications:) 1730 | keyEquivalent:@""] autorelease]; 1731 | [appMenu addItem:item]; 1732 | [appMenu addItem:[NSMenuItem separatorItem]]; 1733 | 1734 | title = [@"Quit " stringByAppendingString:appName]; 1735 | item = [[[NSMenuItem alloc] initWithTitle:title 1736 | action:@selector(terminate:) 1737 | keyEquivalent:@"q"] autorelease]; 1738 | [appMenu addItem:item]; 1739 | 1740 | [NSApp setMainMenu:menubar]; 1741 | 1742 | w->priv.should_exit = 0; 1743 | return 0; 1744 | } 1745 | 1746 | WEBVIEW_API int webview_loop(struct webview *w, int blocking) { 1747 | NSDate *until = (blocking ? [NSDate distantFuture] : [NSDate distantPast]); 1748 | NSEvent *event = [NSApp nextEventMatchingMask:NSEventMaskAny 1749 | untilDate:until 1750 | inMode:NSDefaultRunLoopMode 1751 | dequeue:YES]; 1752 | if (event) { 1753 | [NSApp sendEvent:event]; 1754 | } 1755 | return w->priv.should_exit; 1756 | } 1757 | 1758 | WEBVIEW_API int webview_eval(struct webview *w, const char *js) { 1759 | NSString *nsJS = [NSString stringWithUTF8String:js]; 1760 | [[w->priv.webview windowScriptObject] evaluateWebScript:nsJS]; 1761 | return 0; 1762 | } 1763 | 1764 | WEBVIEW_API void webview_set_title(struct webview *w, const char *title) { 1765 | NSString *nsTitle = [NSString stringWithUTF8String:title]; 1766 | [w->priv.window setTitle:nsTitle]; 1767 | } 1768 | 1769 | WEBVIEW_API void webview_set_fullscreen(struct webview *w, int fullscreen) { 1770 | int b = ((([w->priv.window styleMask] & NSWindowStyleMaskFullScreen) == 1771 | NSWindowStyleMaskFullScreen) 1772 | ? 1 1773 | : 0); 1774 | if (b != fullscreen) { 1775 | [w->priv.window toggleFullScreen:nil]; 1776 | } 1777 | } 1778 | 1779 | WEBVIEW_API void webview_set_color(struct webview *w, uint8_t r, uint8_t g, 1780 | uint8_t b, uint8_t a) { 1781 | [w->priv.window setBackgroundColor:[NSColor colorWithRed:(CGFloat)r / 255.0 1782 | green:(CGFloat)g / 255.0 1783 | blue:(CGFloat)b / 255.0 1784 | alpha:(CGFloat)a / 255.0]]; 1785 | if (0.5 >= ((r / 255.0 * 299.0) + (g / 255.0 * 587.0) + (b / 255.0 * 114.0)) / 1786 | 1000.0) { 1787 | [w->priv.window 1788 | setAppearance:[NSAppearance 1789 | appearanceNamed:NSAppearanceNameVibrantDark]]; 1790 | } else { 1791 | [w->priv.window 1792 | setAppearance:[NSAppearance 1793 | appearanceNamed:NSAppearanceNameVibrantLight]]; 1794 | } 1795 | [w->priv.window setOpaque:NO]; 1796 | [w->priv.window setTitlebarAppearsTransparent:YES]; 1797 | [w->priv.webview setDrawsBackground:NO]; 1798 | } 1799 | 1800 | WEBVIEW_API void webview_dialog(struct webview *w, 1801 | enum webview_dialog_type dlgtype, int flags, 1802 | const char *title, const char *arg, 1803 | char *result, size_t resultsz) { 1804 | if (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN || 1805 | dlgtype == WEBVIEW_DIALOG_TYPE_SAVE) { 1806 | NSSavePanel *panel; 1807 | if (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN) { 1808 | NSOpenPanel *openPanel = [NSOpenPanel openPanel]; 1809 | if (flags & WEBVIEW_DIALOG_FLAG_DIRECTORY) { 1810 | [openPanel setCanChooseFiles:NO]; 1811 | [openPanel setCanChooseDirectories:YES]; 1812 | } else { 1813 | [openPanel setCanChooseFiles:YES]; 1814 | [openPanel setCanChooseDirectories:NO]; 1815 | } 1816 | [openPanel setResolvesAliases:NO]; 1817 | [openPanel setAllowsMultipleSelection:NO]; 1818 | panel = openPanel; 1819 | } else { 1820 | panel = [NSSavePanel savePanel]; 1821 | } 1822 | [panel setCanCreateDirectories:YES]; 1823 | [panel setShowsHiddenFiles:YES]; 1824 | [panel setExtensionHidden:NO]; 1825 | [panel setCanSelectHiddenExtension:NO]; 1826 | [panel setTreatsFilePackagesAsDirectories:YES]; 1827 | [panel beginSheetModalForWindow:w->priv.window 1828 | completionHandler:^(NSInteger result) { 1829 | [NSApp stopModalWithCode:result]; 1830 | }]; 1831 | if ([NSApp runModalForWindow:panel] == NSModalResponseOK) { 1832 | const char *filename = [[[panel URL] path] UTF8String]; 1833 | strlcpy(result, filename, resultsz); 1834 | } 1835 | } else if (dlgtype == WEBVIEW_DIALOG_TYPE_ALERT) { 1836 | NSAlert *a = [NSAlert new]; 1837 | switch (flags & WEBVIEW_DIALOG_FLAG_ALERT_MASK) { 1838 | case WEBVIEW_DIALOG_FLAG_INFO: 1839 | [a setAlertStyle:NSAlertStyleInformational]; 1840 | break; 1841 | case WEBVIEW_DIALOG_FLAG_WARNING: 1842 | NSLog(@"warning"); 1843 | [a setAlertStyle:NSAlertStyleWarning]; 1844 | break; 1845 | case WEBVIEW_DIALOG_FLAG_ERROR: 1846 | NSLog(@"error"); 1847 | [a setAlertStyle:NSAlertStyleCritical]; 1848 | break; 1849 | } 1850 | [a setShowsHelp:NO]; 1851 | [a setShowsSuppressionButton:NO]; 1852 | [a setMessageText:[NSString stringWithUTF8String:title]]; 1853 | [a setInformativeText:[NSString stringWithUTF8String:arg]]; 1854 | [a addButtonWithTitle:@"OK"]; 1855 | [a runModal]; 1856 | [a release]; 1857 | } 1858 | } 1859 | 1860 | static void webview_dispatch_cb(void *arg) { 1861 | struct webview_dispatch_arg *context = (struct webview_dispatch_arg *)arg; 1862 | (context->fn)(context->w, context->arg); 1863 | free(context); 1864 | } 1865 | 1866 | WEBVIEW_API void webview_dispatch(struct webview *w, webview_dispatch_fn fn, 1867 | void *arg) { 1868 | struct webview_dispatch_arg *context = (struct webview_dispatch_arg *)malloc( 1869 | sizeof(struct webview_dispatch_arg)); 1870 | context->w = w; 1871 | context->arg = arg; 1872 | context->fn = fn; 1873 | dispatch_async_f(dispatch_get_main_queue(), context, webview_dispatch_cb); 1874 | } 1875 | 1876 | WEBVIEW_API void webview_terminate(struct webview *w) { 1877 | w->priv.should_exit = 1; 1878 | } 1879 | WEBVIEW_API void webview_exit(struct webview *w) { [NSApp terminate:NSApp]; } 1880 | WEBVIEW_API void webview_print_log(const char *s) { NSLog(@"%s", s); } 1881 | 1882 | #endif /* WEBVIEW_COCOA */ 1883 | 1884 | #endif /* WEBVIEW_IMPLEMENTATION */ 1885 | 1886 | #ifdef __cplusplus 1887 | } 1888 | #endif 1889 | 1890 | #endif /* WEBVIEW_H */ 1891 | --------------------------------------------------------------------------------