diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1a3e34c --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +.vscode/settings.json +build/ +dist/ +__pycache__ +*.pyc +*.egg-info +*.dist-info +*.pyd +*.so \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..5a6a3f3 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "webview"] + path = webview + url = https://github.com/webview/webview diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..0059440 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,19 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "WebView: Test", + "type": "python", + "request": "launch", + "program": "${workspaceFolder}/test.py", + "args": [ + "${file}" + ], + "console": "integratedTerminal", + "justMyCode": true + } + ] +} \ No newline at end of file diff --git a/.vscode/make_settings_json.py b/.vscode/make_settings_json.py new file mode 100644 index 0000000..86b1a35 --- /dev/null +++ b/.vscode/make_settings_json.py @@ -0,0 +1,16 @@ +#!/usr/bin/python3 +import os +import platform +import string +import sys +import sysconfig + +webview_platform = {'Linux': 'GTK', 'Darwin': 'COCOA', 'Windows': 'WINAPI'}.get(platform.system(), 'GTK') +python_include_dir = sysconfig.get_path('include').replace('\\', '\\\\') +py_major_version = sys.version_info[0] + +with open('settings.json.template') as f: + settings = string.Template(f.read()).safe_substitute(locals()) + +with open('settings.json', 'w') as f: + f.write(settings) diff --git a/.vscode/settings.json.template b/.vscode/settings.json.template new file mode 100644 index 0000000..6e36431 --- /dev/null +++ b/.vscode/settings.json.template @@ -0,0 +1,9 @@ +{ + "C_Cpp.default.defines": [ + "PY_MAJOR_VERSION=${py_major_version}", + "WEBVIEW_${webview_platform}" + ], + "C_Cpp.default.includePath": [ + "${python_include_dir}" + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..6c6c120 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,61 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "Build", + "type": "shell", + "options": { + "cwd": "${workspaceFolder}" + }, + "command": "${config:python.pythonPath} setup.py build_ext -f --inplace", + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": [ + "$msCompile", + "$gcc" + ] + }, + { + "label": "Build distributables", + "type": "shell", + "options": { + "cwd": "${workspaceFolder}" + }, + "command": "${config:python.pythonPath} setup.py sdist bdist_wheel", + "group": "build", + "problemMatcher": [] + }, + { + "label": "Test", + "type": "shell", + "options": { + "cwd": "${workspaceFolder}" + }, + "command": "${config:python.pythonPath} test.py", + "group": { + "kind": "test", + "isDefault": true + }, + "problemMatcher": [] + }, + { + "label": "Create settings.json", + "type": "shell", + "command": "${config:python.pythonPath}", + "args": [ + "make_settings_json.py" + ], + "options": { + "cwd": "${workspaceFolder}/.vscode" + }, + "runOptions": { + "runOn": "folderOpen" + }, + "problemMatcher": [] + } + ] +} \ No newline at end of file diff --git a/MANIFEST.in b/MANIFEST.in index db6bb26..ffd87cf 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1 +1 @@ -graft webview +graft webview \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d91d155 --- /dev/null +++ b/Makefile @@ -0,0 +1,27 @@ +ifeq ($(OS),Windows_NT) + PY = python +else + PY = python3 +endif + +all: dist + +build: + $(PY) setup.py build + +dist: + $(PY) setup.py sdist bdist_wheel + +install: + $(PY) -m pip install . + +test: + $(PY) test.py + +upload: + $(PY) -m twine upload dist/* + +clean: + $(RM) -r build dist *.egg-info *.dist-info __pycache__ *.pyc *.pyd *.so + +.PHONY: all build dist install test upload clean diff --git a/README.md b/README.md index 6062a8c..e1441c1 100644 --- a/README.md +++ b/README.md @@ -1,72 +1,10 @@ -# webview +# webview-python -Python extension that provides API for the [webview] library. +Python extension that provides API for the [webview](https://github.com/webview/webview) library. -## Getting started -Install the bindings: +## TODO -```bash -pip install webview -``` - -Try the following example: - -```python -import webview - -w = webview.WebView(width=320, height=240, title="Hello", url="https://google.com", resizable=True, debug=False) -w.run() -``` - -You may use most of the webview APIs: - -```python -# Change window title -w.set_title("New title") -# Make window fullscreen -w.set_fullscreen(True) -# Change initial window background color -w.set_color(255, 0, 0) -# Inject some JS -w.eval("alert('hello')") -# Inject some CSS -w.inject_css('* {background-color: yellow; }') -# Show native OS dialog -file_path = w.dialog(0, 0, "open file", "") -# Post funciton to the UI thread -w.dispatch(some_func) -w.dispatch(lambda: some_func()) -# Control run loop -while w.loop(True): - pass -``` - -Dispatch is currently only a stub and is implemented as direct function call. -Also, proper Python-to-JS object mapping is not implemented yet, but is highly - -## Development - -To build and install the library locally: - -```bash -python setup.py install -``` - -To upload a new version: - -```bash -python setup.py sdist -twine upload dist/webview-*.tar.gz -``` - -To build and install it locally: - -```bash -python setup.py install -``` - -Please, ensure that all sources are formatted using `yapf`. - - -[webview]: https://github.com/zserge/webview +- [ ] Figure out why `examples/with_server.py` doesn't work +- [ ] Better function binding support (automate `json.dumps` and `json.loads`) +- [ ] Remove support for Python 2.x diff --git a/examples/minimal.py b/examples/minimal.py new file mode 100644 index 0000000..f52bf88 --- /dev/null +++ b/examples/minimal.py @@ -0,0 +1,43 @@ +import json +import time +from urllib.parse import quote + +import webview + + +HTML = ''' +This is a test
+
Time should tick here

+ + +''' + + +def get_time(w, req): + fmt = json.loads(req)[0] + return json.dumps(time.strftime(fmt)) + + +def quit(w, req): + w.terminate() + + +def main(): + w = webview.WebView(width=320, height=240, resizable=True, title="My App", debug=True) + w.bind('get_time', get_time) + w.bind('quit', quit) + w.navigate('data:text/html,' + quote(HTML)) + w.run() + + +if __name__ == "__main__": + main() diff --git a/examples/with_server.py b/examples/with_server.py new file mode 100644 index 0000000..7a2865a --- /dev/null +++ b/examples/with_server.py @@ -0,0 +1,71 @@ +import json +import random +import threading +import time +from http import HTTPStatus +from http.server import BaseHTTPRequestHandler, HTTPServer +from urllib.parse import quote +from itertools import count + +import webview + + +HTML = ''' +This is a test
+''' + + +class MyHTTPRequestHandler(BaseHTTPRequestHandler): + def send_data(self, data, status=HTTPStatus.OK, mimetype='text/plain'): + if isinstance(data, str): + data = data.encode('utf8') + self.send_response(status) + self.send_header('Content-Type', mimetype) + self.send_header('Content-Length', len(data)) + self.end_headers() + self.wfile.write(data) + + def send_html(self, text): + self.send_data(text.encode('utf8'), mimetype='text/html') + + def do_GET(self): + self.send_html(HTML) + + +def run_server(): + while 1: + port = random.randint(20000, 30000) + try: + server = HTTPServer(('localhost', port), MyHTTPRequestHandler) + except Exception: + continue + break + threading.Thread(target=server.serve_forever, daemon=True).start() + return server + + +def callback(w, req): + # print(f'callback was called with {req!r}') + arg = json.loads(req)[0] + print(f'callback was called with {arg!r}') + + +def threadfunc(): + for n in count(): + print(n) + time.sleep(0.1) + + +def main(): + s = run_server() + url = f'http://127.0.0.1:{s.server_port}' + print(url) + w = webview.WebView(width=320, height=240, title="My App", resizable=True, debug=True) + w.bind('invoke', callback) + w.navigate(url) + w.run() + s.shutdown() + + +if __name__ == "__main__": + main() diff --git a/pywebview.py b/pywebview.py new file mode 100644 index 0000000..fa00175 --- /dev/null +++ b/pywebview.py @@ -0,0 +1,28 @@ +import argparse +import os + +import webview + + +def main(): + p = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter) + p.add_argument('url') + p.add_argument('-s', '--size', default='800x600', help='Window size in the form x') + p.add_argument('-f', '--fixed-size', action='store_true', help='Makes the window non-resizable') + p.add_argument('-t', '--title', default='Webview', help='Set the window title') + args = p.parse_args() + + try: + width, height = map(int, args.size.split('x')) + except Exception: + p.error('Size must be of the form x') + + if not os.path.exists(args.url) and '://' not in args.url: + args.url = 'http://' + args.url + + w = webview.WebView(width, height, resizable=not args.fixed_size, url=args.url, title=args.title) + w.run() + + +if __name__ == "__main__": + main() diff --git a/setup.py b/setup.py index 4084d94..cd70f25 100644 --- a/setup.py +++ b/setup.py @@ -1,42 +1,36 @@ -#!/bin/env python - -import os +import platform import subprocess -import shutil -from distutils.core import setup -from distutils.extension import Extension -from distutils.cmd import Command +from setuptools import Extension, setup -if hasattr(os, 'uname'): - OSNAME = os.uname()[0] -else: - OSNAME = 'Windows' +OSNAME = platform.system() if OSNAME == 'Linux': - def pkgconfig(flags): return subprocess.check_output( - 'pkg-config %s gtk+-3.0 webkit2gtk-4.0' % flags, + 'pkg-config {} gtk+-3.0 webkit2gtk-4.0'.format(flags), shell=True, - stderr=subprocess.STDOUT).decode('utf-8') + stderr=subprocess.STDOUT).decode() define_macros = [("WEBVIEW_GTK", '1')] extra_cflags = pkgconfig("--cflags").split() extra_ldflags = pkgconfig("--libs").split() + elif OSNAME == 'Darwin': define_macros = [('WEBVIEW_COCOA', '1')] - extra_cflags = "" - extra_ldflags = ['-framework', 'CoreAudio'] + extra_cflags = ['-std=c++11'] + extra_ldflags = ['-framework', 'WebKit'] + elif OSNAME == 'Windows': define_macros = [('WEBVIEW_WINAPI', '1')] - extra_cflags = "" - extra_ldflags = ['-framework', 'CoreAudio'] + extra_cflags = ['/std:c++17', '/Iwebview', '/Iwebview\\script'] + extra_ldflags = [R'webview\script\microsoft.web.webview2.1.0.664.37\build\native\x64\WebView2Loader.dll.lib'] + webview = Extension( 'webview', - sources=['webview/webview.c'], + sources=['webview.cpp'], define_macros=define_macros, extra_compile_args=extra_cflags, extra_link_args=extra_ldflags, @@ -51,6 +45,31 @@ def pkgconfig(flags): url='https://github.com/zserge/webview', keywords=[], license='MIT', - classifiers=[], + classifiers=[ + 'Development Status :: 3 - Alpha', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: MIT License', + 'Operating System :: MacOS :: MacOS X', + 'Operating System :: Microsoft :: Windows', + 'Operating System :: POSIX :: Linux', + 'Programming Language :: C', + 'Programming Language :: Python', + 'Topic :: Desktop Environment', + 'Topic :: Software Development :: Libraries', + 'Topic :: Software Development :: User Interfaces', + ], ext_modules=[webview], + py_modules=['pywebview'], + entry_points=dict( + gui_scripts=[ + 'pywv = pywebview:main', + ], + ), + extras_require=dict( + dev=[ + 'pip', + 'setuptools', + 'wheel', + ], + ), ) diff --git a/test.py b/test.py new file mode 100644 index 0000000..5b4edd2 --- /dev/null +++ b/test.py @@ -0,0 +1,28 @@ +import os +import runpy +import subprocess +import sys + + +def dirname(path): + return os.path.abspath(os.path.dirname(path)) + + +def main(): + base_dir = dirname(__file__) + + # Build the package (if necessary) + # subprocess.check_call([sys.executable, 'setup.py', 'build_ext', '--inplace'], cwd=base_dir) + + # Run code + if len(sys.argv) > 1: + fn = sys.argv[1] + else: + fn = os.path.join(base_dir, 'examples', 'minimal.py') + + os.chdir(base_dir) + runpy.run_path(fn, run_name='__main__') + + +if __name__ == "__main__": + main() diff --git a/webview b/webview new file mode 160000 index 0000000..7534ef6 --- /dev/null +++ b/webview @@ -0,0 +1 @@ +Subproject commit 7534ef6e476a1af6206ddb3d0efd3ebf131cba3c diff --git a/webview.cpp b/webview.cpp new file mode 100644 index 0000000..0ca5a20 --- /dev/null +++ b/webview.cpp @@ -0,0 +1,331 @@ +#include +#include "structmember.h" +#include "webview/webview.h" + +#ifndef PATH_MAX +#define PATH_MAX MAX_PATH +#endif + +typedef struct { + PyObject_HEAD + webview_t w; + PyObject *bindings; +} WebView; + +static void WebView_dealloc(WebView *self) { + webview_destroy(self->w); + PyDict_Clear(self->bindings); // TODO: What about refcounts for keys and values? + Py_DECREF(self->bindings); + Py_TYPE(self)->tp_free((PyObject *)self); +} + +static PyObject *WebView_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { + PyObject *bindings = PyDict_New(); // TODO: Should we call Py_INCREF(bindings)? + if (bindings == NULL) return NULL; + + WebView *self = (WebView *)type->tp_alloc(type, 0); + if (self == NULL) return NULL; + + self->bindings = bindings; + + return (PyObject *)self; +} + +static int WebView_init(WebView *self, PyObject *args, PyObject *kwds) { + int width = 0; + int height = 0; + int resizable = 1; + int debug = 0; + char *title = NULL; + char *kwlist[] = {"width", "height", "resizable", "debug", "title", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iiiis:WebView", kwlist, &width, &height, &resizable, &debug, &title)) { + return -1; + } + + // printf("size=%dx%d, resizable=%d, debug=%d, title=\"%s\"\n", width, height, resizable, debug, title); + + self->w = webview_create(debug, NULL); + + if (title) { + webview_set_title(self->w, title); + } + + if ((width && height) || !resizable) { + webview_set_size(self->w, width, height, resizable ? WEBVIEW_HINT_NONE: WEBVIEW_HINT_FIXED); + } + + return 0; +} + +static PyObject *WebView_run(WebView *self) { + // Py_BEGIN_ALLOW_THREADS + webview_run(self->w); + // Py_END_ALLOW_THREADS + Py_RETURN_NONE; +} + +static PyObject *WebView_terminate(WebView *self) { + webview_terminate(self->w); + Py_RETURN_NONE; +} + +static PyObject *WebView_set_title(WebView *self, PyObject *args) { + char *title = NULL; + if (!PyArg_ParseTuple(args, "s:set_title", &title)) { + return NULL; + } + webview_set_title(self->w, title); + Py_RETURN_NONE; +} + +static PyObject *WebView_set_size(WebView *self, PyObject *args) { + int width = 0; + int height = 0; + int hint = WEBVIEW_HINT_NONE; + + if (!PyArg_ParseTuple(args, "iii:set_size", &width, &height, &hint)) { + return NULL; + } + + webview_set_size(self->w, width, height, hint); + + Py_RETURN_NONE; +} + +static PyObject *WebView_navigate(WebView *self, PyObject *args) { + const char *url; + + if (!PyArg_ParseTuple(args, "s:navigate", &url)) { + return NULL; + } + + webview_navigate(self->w, url); + + Py_RETURN_NONE; +} + +static PyObject *WebView_init_js(WebView *self, PyObject *args) { + const char *js = NULL; + + if (!PyArg_ParseTuple(args, "s:init", &js)) { + return NULL; + } + + webview_init(self->w, js); + + Py_RETURN_NONE; +} + +static PyObject *WebView_eval(WebView *self, PyObject *args) { + const char *js = NULL; + + if (!PyArg_ParseTuple(args, "s:eval", &js)) { + return NULL; + } + + webview_eval(self->w, js); + + Py_RETURN_NONE; +} + +static void webview_bind_cb(const char *seq, const char *req, void *binding) { + WebView *self = (WebView *)PyTuple_GetItem((PyObject *)binding, 0); + PyObject *name = PyTuple_GetItem((PyObject *)binding, 1); + PyObject *func = PyTuple_GetItem((PyObject *)binding, 2); + + // printf("Calling %s(%s)\n", PyUnicode_AsUTF8(name), req); + + PyObject *ret = PyObject_CallFunction(func, "Os", self, req); + if (!ret) { + webview_return(self->w, seq, 1, NULL); // TODO: Return error JSON + PyErr_Print(); + return; + } + + PyObject *str = PyObject_Str(ret); + Py_DECREF(ret); + if (!str) { + webview_return(self->w, seq, 1, NULL); // TODO: Return error JSON + PyErr_Print(); + return; + } + + // printf("Returning %s\n", PyUnicode_AsUTF8(str)); + webview_return(self->w, seq, 0, PyUnicode_AsUTF8(str)); + + Py_DECREF(str); +} + +static PyObject *WebView_bind(WebView *self, PyObject *args) { + PyObject *name, *func, *tuple; + + if (!PyArg_ParseTuple(args, "OO:bind", &name, &func)) { // TODO: What about ref-counts? + return NULL; + } + + // printf("Got name=%s func=%s\n", PyUnicode_AsUTF8(PyObject_ASCII(name)), PyUnicode_AsUTF8(PyObject_ASCII(func))); + + if (!PyUnicode_Check(name)) { + PyErr_SetString(PyExc_TypeError, "name must be unicode"); + return NULL; + } + + if (!PyCallable_Check(func)) { + PyErr_SetString(PyExc_TypeError, "func must be callable"); + return NULL; + } + + if (PyDict_Contains(self->bindings, name)) { + PyErr_SetString(PyExc_ValueError, "name already exists"); + return NULL; + } + + tuple = PyTuple_Pack(3, self, name, func); + if (tuple == NULL) { + PyErr_SetString(PyExc_MemoryError, "Could not allocate binding tuple"); + return NULL; + } + + PyDict_SetItem(self->bindings, name, tuple); + + // printf("Bindings: %s\n", PyUnicode_AsUTF8(PyObject_ASCII(self->bindings))); + + webview_bind(self->w, PyUnicode_AsUTF8(name), webview_bind_cb, tuple); + + Py_RETURN_NONE; +} + +static void webview_dispatch_cb(webview_t w, void *arg) { + PyObject_CallObject((PyObject *)arg, NULL); +} + +static PyObject *WebView_dispatch(WebView *self, PyObject *args) { + PyObject *func; + + if (!PyArg_ParseTuple(args, "O:dispatch", &func)) { + return NULL; + } + + if (!PyCallable_Check(func)) { + PyErr_SetString(PyExc_TypeError, "parameter must be callable"); + return NULL; + } + + webview_dispatch(self->w, webview_dispatch_cb, func); + + Py_RETURN_NONE; +} + +static PyMemberDef WebView_members[] = { + {"bindings", T_OBJECT, offsetof(WebView, bindings), 0, "Debug access to internal bindings"}, + {NULL} /* Sentinel */ +}; +static PyMethodDef WebView_methods[] = { + {"run", (PyCFunction)WebView_run, METH_NOARGS, "WebView.run() -> None"}, + {"terminate", (PyCFunction)WebView_terminate, METH_NOARGS, "WebView.terminate() -> None"}, + {"set_title", (PyCFunction)WebView_set_title, METH_VARARGS, "WebView.set_title(title: str) -> None"}, + {"set_size", (PyCFunction)WebView_set_size, METH_VARARGS, "WebView.set_size(width: Optional[int], height: Optional[int], hints: Optional[int]) -> None"}, + {"navigate", (PyCFunction)WebView_navigate, METH_VARARGS, "WebView.navigate(url: str) -> None"}, + {"init", (PyCFunction)WebView_init_js, METH_VARARGS, "WebView.init(js: str) -> None"}, + {"eval", (PyCFunction)WebView_eval, METH_VARARGS, "WebView.eval(js: str) -> None"}, + {"bind", (PyCFunction)WebView_bind, METH_VARARGS, "WebView.bind(name: str, func: Callable) -> None"}, + {"dispatch", (PyCFunction)WebView_dispatch, METH_VARARGS, "WebView.dispatch(func: Callable) -> None"}, + {NULL} /* Sentinel */ +}; + +static PyTypeObject WebViewType = { + PyVarObject_HEAD_INIT(NULL, 0) "webview.WebView", /* tp_name */ + sizeof(WebView), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)WebView_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "webview.WebView(" + "width: int, height: int, " + "resizable: bool = False, " + "debug: bool = False, " + "title: str = '')", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + WebView_methods, /* tp_methods */ + WebView_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)WebView_init, /* tp_init */ + 0, /* tp_alloc */ + WebView_new, /* tp_new */ +}; + +static PyMethodDef module_methods[] = { + {NULL} /* Sentinel */ +}; + +#if PY_MAJOR_VERSION >= 3 +static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + "webview", + "Python bindings for the WebView C library.", + 0, + module_methods, + NULL, + NULL, + NULL, + NULL +}; +#define MODINIT_ERROR NULL +#define MODINIT_NAME PyInit_webview +#else +#define MODINIT_ERROR +#define MODINIT_NAME initwebview +#endif +PyMODINIT_FUNC MODINIT_NAME(void) { + PyObject *m; + + if (PyType_Ready(&WebViewType) < 0) { + return MODINIT_ERROR; + } + +#if PY_MAJOR_VERSION >= 3 + m = PyModule_Create(&moduledef); +#else + m = Py_InitModule3("webview", module_methods, "Python bindings for the WebView C library."); +#endif + + if (m == NULL) { + return MODINIT_ERROR; + } + + Py_INCREF(&WebViewType); + PyModule_AddObject(m, "WebView", (PyObject *)&WebViewType); + + PyModule_AddIntConstant(m, "SIZE_HINT_NONE", WEBVIEW_HINT_NONE); + PyModule_AddIntConstant(m, "SIZE_HINT_MIN", WEBVIEW_HINT_MIN); + PyModule_AddIntConstant(m, "SIZE_HINT_MAX", WEBVIEW_HINT_MAX); + PyModule_AddIntConstant(m, "SIZE_HINT_FIXED", WEBVIEW_HINT_FIXED); + +#if PY_MAJOR_VERSION >= 3 + return m; +#endif +} diff --git a/webview/webview.c b/webview/webview.c deleted file mode 100644 index 8ae7244..0000000 --- a/webview/webview.c +++ /dev/null @@ -1,256 +0,0 @@ -#include - -#include "structmember.h" - -#define WEBVIEW_IMPLEMENTATION -#include "webview.h" - -typedef struct { PyObject_HEAD struct webview w; } WebView; - -static void WebView_dealloc(WebView *self) { - webview_exit(&self->w); - Py_TYPE(self)->tp_free((PyObject *)self); -} - -static PyObject *WebView_new(PyTypeObject *type, PyObject *args, - PyObject *kwds) { - WebView *self = (WebView *)type->tp_alloc(type, 0); - if (self == NULL) { - return NULL; - } - memset(&self->w, 0, sizeof(self->w)); - return (PyObject *)self; -} - -static int WebView_init(WebView *self, PyObject *args, PyObject *kwds) { - const char *url = NULL; - const char *title = NULL; - static char *kwlist[] = {"width", "height", "resizable", "debug", - "url", "title", NULL}; - - if (!PyArg_ParseTupleAndKeywords( - args, kwds, "ii|iiss", kwlist, &self->w.width, &self->w.height, - &self->w.resizable, &self->w.debug, &url, &title)) { - return -1; - } - - self->w.url = url; - self->w.title = title; - - return webview_init(&self->w); -} - -static PyObject *WebView_run(WebView *self) { - while (webview_loop(&self->w, 1) == 0) - ; - Py_RETURN_NONE; -} - -static PyObject *WebView_loop(WebView *self, PyObject *args, PyObject *kwds) { - int blocking = 1; - static char *kwlist[] = {"blocking", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, &blocking)) { - return NULL; - } - if (webview_loop(&self->w, blocking) == 0) { - Py_RETURN_TRUE; - } else { - Py_RETURN_FALSE; - } -} - -static PyObject *WebView_terminate(WebView *self) { - webview_terminate(&self->w); - Py_RETURN_NONE; -} - -static PyObject *WebView_set_title(WebView *self, PyObject *args) { - const char *title = ""; - if (!PyArg_ParseTuple(args, "s", &title)) { - return NULL; - } - webview_set_title(&self->w, title); - Py_RETURN_NONE; -} - -static PyObject *WebView_set_fullscreen(WebView *self, PyObject *args) { - int fullscreen = 0; - if (!PyArg_ParseTuple(args, "i", &fullscreen)) { - return NULL; - } - webview_set_fullscreen(&self->w, fullscreen); - Py_RETURN_NONE; -} - -static PyObject *WebView_set_color(WebView *self, PyObject *args) { - int r, g, b, a = 255; - if (!PyArg_ParseTuple(args, "iii|i", &r, &g, &b, &a)) { - return NULL; - } - webview_set_color(&self->w, r, g, b, a); - Py_RETURN_NONE; -} - -static PyObject *WebView_eval(WebView *self, PyObject *args) { - const char *js = NULL; - if (!PyArg_ParseTuple(args, "s", &js)) { - return NULL; - } - webview_eval(&self->w, js); - Py_RETURN_NONE; -} - -static PyObject *WebView_inject_css(WebView *self, PyObject *args) { - const char *css = NULL; - if (!PyArg_ParseTuple(args, "s", &css)) { - return NULL; - } - webview_inject_css(&self->w, css); - Py_RETURN_NONE; -} - -static PyObject *WebView_dialog(WebView *self, PyObject *args, PyObject *kwds) { - int type = 0; - int flags = 0; - const char *title = NULL; - const char *arg = NULL; - char result[PATH_MAX]; - static char *kwlist[] = {"type", "flags", "title", "arg", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "iiss", kwlist, &type, &flags, - &title, &arg)) { - return NULL; - } - webview_dialog(&self->w, type, flags, title, arg, result, sizeof(result)); - return PyUnicode_FromString(result); -} - -static void webview_dispatch_cb(struct webview *w, void *arg) { - PyObject *cb = (PyObject *)arg; - /* TODO */ - PyObject_CallObject(cb, NULL); - Py_XINCREF(cb); -} - -static PyObject *WebView_dispatch(WebView *self, PyObject *args) { - PyObject *tmp; - if (!PyArg_ParseTuple(args, "O:set_callback", &tmp)) { - return NULL; - } - if (!PyCallable_Check(tmp)) { - PyErr_SetString(PyExc_TypeError, "parameter must be callable"); - return NULL; - } - Py_XINCREF(tmp); - webview_dispatch(&self->w, webview_dispatch_cb, tmp); - Py_RETURN_NONE; -} - -static PyObject *WebView_bind(WebView *self) { - /* TODO, very complex implementation */ - Py_RETURN_NONE; -} - -static PyMemberDef WebView_members[] = { - {NULL} /* Sentinel */ -}; -static PyMethodDef WebView_methods[] = { - {"run", (PyCFunction)WebView_run, METH_NOARGS, "..."}, - {"loop", (PyCFunction)WebView_loop, METH_KEYWORDS | METH_VARARGS, "..."}, - {"terminate", (PyCFunction)WebView_terminate, METH_NOARGS, "..."}, - {"dispatch", (PyCFunction)WebView_dispatch, METH_VARARGS, "..."}, - {"eval", (PyCFunction)WebView_eval, METH_VARARGS, "..."}, - {"inject_css", (PyCFunction)WebView_inject_css, METH_VARARGS, "..."}, - {"dialog", (PyCFunction)WebView_dialog, METH_KEYWORDS | METH_VARARGS, "..."}, - {"set_title", (PyCFunction)WebView_set_title, METH_VARARGS, "..."}, - {"set_fullscreen", (PyCFunction)WebView_set_fullscreen, METH_VARARGS, - "..."}, - {"set_color", (PyCFunction)WebView_set_color, METH_VARARGS, "..."}, - {"bind", (PyCFunction)WebView_bind, METH_VARARGS, "..."}, - {NULL} /* Sentinel */ -}; - -static PyTypeObject WebViewType = { - PyVarObject_HEAD_INIT(NULL, 0) "webview.WebView", /* tp_name */ - sizeof(WebView), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)WebView_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - "WebView objects", /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - WebView_methods, /* tp_methods */ - WebView_members, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc)WebView_init, /* tp_init */ - 0, /* tp_alloc */ - WebView_new, /* tp_new */ -}; - -static PyMethodDef module_methods[] = { - {NULL} /* Sentinel */ -}; - -#if PY_MAJOR_VERSION >= 3 -static struct PyModuleDef moduledef = { - PyModuleDef_HEAD_INIT, - "webview", - "Example module", - 0, - module_methods, - NULL, - NULL, - NULL, - NULL -}; -#define MODINIT_ERROR NULL -#define MODINIT_NAME PyInit_webview -#else -#define MODINIT_ERROR -#define MODINIT_NAME initwebview -#endif -PyMODINIT_FUNC MODINIT_NAME(void) { - PyObject *m; - - if (PyType_Ready(&WebViewType) < 0) { - return MODINIT_ERROR; - } - -#if PY_MAJOR_VERSION >= 3 - m = PyModule_Create(&moduledef); -#else - m = Py_InitModule3("webview", module_methods, - "Example module that creates an extension type."); -#endif - if (m == NULL) { - return MODINIT_ERROR; - } - - Py_INCREF(&WebViewType); - PyModule_AddObject(m, "WebView", (PyObject *)&WebViewType); -#if PY_MAJOR_VERSION >= 3 - return m; -#endif -} diff --git a/webview/webview.h b/webview/webview.h deleted file mode 100644 index d1d2a1c..0000000 --- a/webview/webview.h +++ /dev/null @@ -1,1890 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2017 Serge Zaitsev - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef WEBVIEW_H -#define WEBVIEW_H - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef WEBVIEW_STATIC -#define WEBVIEW_API static -#else -#define WEBVIEW_API extern -#endif - -#include -#include -#include - -#if defined(WEBVIEW_GTK) -#include -#include -#include - -struct webview_priv { - GtkWidget *window; - GtkWidget *scroller; - GtkWidget *webview; - GtkWidget *inspector_window; - GAsyncQueue *queue; - int ready; - int js_busy; - int should_exit; -}; -#elif defined(WEBVIEW_WINAPI) -#define CINTERFACE -#include - -#include -#include -#include -#include -#include - -#include - -struct webview_priv { - HWND hwnd; - IOleObject **browser; - BOOL is_fullscreen; - DWORD saved_style; - DWORD saved_ex_style; - RECT saved_rect; -}; -#elif defined(WEBVIEW_COCOA) -#import -#import -#import - -struct webview_priv { - NSAutoreleasePool *pool; - NSWindow *window; - WebView *webview; - id windowDelegate; - int should_exit; -}; -#else -#error "Define one of: WEBVIEW_GTK, WEBVIEW_COCOA or WEBVIEW_WINAPI" -#endif - -struct webview; - -typedef void (*webview_external_invoke_cb_t)(struct webview *w, - const char *arg); - -struct webview { - const char *url; - const char *title; - int width; - int height; - int resizable; - int debug; - webview_external_invoke_cb_t external_invoke_cb; - struct webview_priv priv; - void *userdata; -}; - -enum webview_dialog_type { - WEBVIEW_DIALOG_TYPE_OPEN = 0, - WEBVIEW_DIALOG_TYPE_SAVE = 1, - WEBVIEW_DIALOG_TYPE_ALERT = 2 -}; - -#define WEBVIEW_DIALOG_FLAG_FILE (0 << 0) -#define WEBVIEW_DIALOG_FLAG_DIRECTORY (1 << 0) - -#define WEBVIEW_DIALOG_FLAG_INFO (1 << 1) -#define WEBVIEW_DIALOG_FLAG_WARNING (2 << 1) -#define WEBVIEW_DIALOG_FLAG_ERROR (3 << 1) -#define WEBVIEW_DIALOG_FLAG_ALERT_MASK (3 << 1) - -typedef void (*webview_dispatch_fn)(struct webview *w, void *arg); - -struct webview_dispatch_arg { - webview_dispatch_fn fn; - struct webview *w; - void *arg; -}; - -#define DEFAULT_URL \ - "data:text/" \ - "html,%3C%21DOCTYPE%20html%3E%0A%3Chtml%20lang=%22en%22%3E%0A%3Chead%3E%" \ - "3Cmeta%20charset=%22utf-8%22%3E%3Cmeta%20http-equiv=%22X-UA-Compatible%22%" \ - "20content=%22IE=edge%22%3E%3C%2Fhead%3E%0A%3Cbody%3E%3Cdiv%20id=%22app%22%" \ - "3E%3C%2Fdiv%3E%3Cscript%20type=%22text%2Fjavascript%22%3E%3C%2Fscript%3E%" \ - "3C%2Fbody%3E%0A%3C%2Fhtml%3E" - -#define CSS_INJECT_FUNCTION \ - "(function(e){var " \ - "t=document.createElement('style'),d=document.head||document." \ - "getElementsByTagName('head')[0];t.setAttribute('type','text/" \ - "css'),t.styleSheet?t.styleSheet.cssText=e:t.appendChild(document." \ - "createTextNode(e)),d.appendChild(t)})" - -static const char *webview_check_url(const char *url) { - if (url == NULL || strlen(url) == 0) { - return DEFAULT_URL; - } - return url; -} - -WEBVIEW_API int webview(const char *title, const char *url, int width, - int height, int resizable); - -WEBVIEW_API int webview_init(struct webview *w); -WEBVIEW_API int webview_loop(struct webview *w, int blocking); -WEBVIEW_API int webview_eval(struct webview *w, const char *js); -WEBVIEW_API int webview_inject_css(struct webview *w, const char *css); -WEBVIEW_API void webview_set_title(struct webview *w, const char *title); -WEBVIEW_API void webview_set_fullscreen(struct webview *w, int fullscreen); -WEBVIEW_API void webview_set_color(struct webview *w, uint8_t r, uint8_t g, - uint8_t b, uint8_t a); -WEBVIEW_API void webview_dialog(struct webview *w, - enum webview_dialog_type dlgtype, int flags, - const char *title, const char *arg, - char *result, size_t resultsz); -WEBVIEW_API void webview_dispatch(struct webview *w, webview_dispatch_fn fn, - void *arg); -WEBVIEW_API void webview_terminate(struct webview *w); -WEBVIEW_API void webview_exit(struct webview *w); -WEBVIEW_API void webview_debug(const char *format, ...); -WEBVIEW_API void webview_print_log(const char *s); - -#ifdef WEBVIEW_IMPLEMENTATION -#undef WEBVIEW_IMPLEMENTATION - -WEBVIEW_API int webview(const char *title, const char *url, int width, - int height, int resizable) { - struct webview webview; - memset(&webview, 0, sizeof(webview)); - webview.title = title; - webview.url = url; - webview.width = width; - webview.height = height; - webview.resizable = resizable; - int r = webview_init(&webview); - if (r != 0) { - return r; - } - while (webview_loop(&webview, 1) == 0) { - } - webview_exit(&webview); - return 0; -} - -WEBVIEW_API void webview_debug(const char *format, ...) { - char buf[4096]; - va_list ap; - va_start(ap, format); - vsnprintf(buf, sizeof(buf), format, ap); - webview_print_log(buf); - va_end(ap); -} - -static int webview_js_encode(const char *s, char *esc, size_t n) { - int r = 1; /* At least one byte for trailing zero */ - for (; *s; s++) { - const unsigned char c = *s; - if (c >= 0x20 && c < 0x80 && strchr("<>\\'\"", c) == NULL) { - if (n > 0) { - *esc++ = c; - n--; - } - r++; - } else { - if (n > 0) { - snprintf(esc, n, "\\x%02x", (int)c); - esc += 4; - n -= 4; - } - r += 4; - } - } - return r; -} - -WEBVIEW_API int webview_inject_css(struct webview *w, const char *css) { - int n = webview_js_encode(css, NULL, 0); - char *esc = (char *)calloc(1, sizeof(CSS_INJECT_FUNCTION) + n + 4); - if (esc == NULL) { - return -1; - } - char *js = (char *)calloc(1, n); - webview_js_encode(css, js, n); - snprintf(esc, sizeof(CSS_INJECT_FUNCTION) + n + 4, "%s(\"%s\")", - CSS_INJECT_FUNCTION, js); - int r = webview_eval(w, esc); - free(js); - free(esc); - return r; -} - -#if defined(WEBVIEW_GTK) -static void external_message_received_cb(WebKitUserContentManager *m, - WebKitJavascriptResult *r, - gpointer arg) { - (void)m; - struct webview *w = (struct webview *)arg; - if (w->external_invoke_cb == NULL) { - return; - } - JSGlobalContextRef context = webkit_javascript_result_get_global_context(r); - JSValueRef value = webkit_javascript_result_get_value(r); - JSStringRef js = JSValueToStringCopy(context, value, NULL); - size_t n = JSStringGetMaximumUTF8CStringSize(js); - char *s = g_new(char, n); - JSStringGetUTF8CString(js, s, n); - w->external_invoke_cb(w, s); - JSStringRelease(js); - g_free(s); -} - -static void webview_load_changed_cb(WebKitWebView *webview, - WebKitLoadEvent event, gpointer arg) { - (void)webview; - struct webview *w = (struct webview *)arg; - if (event == WEBKIT_LOAD_FINISHED) { - w->priv.ready = 1; - } -} - -static void webview_destroy_cb(GtkWidget *widget, gpointer arg) { - (void)widget; - struct webview *w = (struct webview *)arg; - webview_terminate(w); -} - -static gboolean webview_context_menu_cb(WebKitWebView *webview, - GtkWidget *default_menu, - WebKitHitTestResult *hit_test_result, - gboolean triggered_with_keyboard, - gpointer userdata) { - (void)webview; - (void)default_menu; - (void)hit_test_result; - (void)triggered_with_keyboard; - (void)userdata; - return TRUE; -} - -WEBVIEW_API int webview_init(struct webview *w) { - if (gtk_init_check(0, NULL) == FALSE) { - return -1; - } - - w->priv.ready = 0; - w->priv.should_exit = 0; - w->priv.queue = g_async_queue_new(); - w->priv.window = gtk_window_new(GTK_WINDOW_TOPLEVEL); - gtk_window_set_title(GTK_WINDOW(w->priv.window), w->title); - - if (w->resizable) { - gtk_window_set_default_size(GTK_WINDOW(w->priv.window), w->width, - w->height); - } else { - gtk_widget_set_size_request(w->priv.window, w->width, w->height); - } - gtk_window_set_resizable(GTK_WINDOW(w->priv.window), !!w->resizable); - gtk_window_set_position(GTK_WINDOW(w->priv.window), GTK_WIN_POS_CENTER); - - w->priv.scroller = gtk_scrolled_window_new(NULL, NULL); - gtk_container_add(GTK_CONTAINER(w->priv.window), w->priv.scroller); - - WebKitUserContentManager *m = webkit_user_content_manager_new(); - webkit_user_content_manager_register_script_message_handler(m, "external"); - g_signal_connect(m, "script-message-received::external", - G_CALLBACK(external_message_received_cb), w); - - w->priv.webview = webkit_web_view_new_with_user_content_manager(m); - webkit_web_view_load_uri(WEBKIT_WEB_VIEW(w->priv.webview), - webview_check_url(w->url)); - g_signal_connect(G_OBJECT(w->priv.webview), "load-changed", - G_CALLBACK(webview_load_changed_cb), w); - gtk_container_add(GTK_CONTAINER(w->priv.scroller), w->priv.webview); - - if (w->debug) { - WebKitSettings *settings = - webkit_web_view_get_settings(WEBKIT_WEB_VIEW(w->priv.webview)); - webkit_settings_set_enable_write_console_messages_to_stdout(settings, true); - webkit_settings_set_enable_developer_extras(settings, true); - } else { - g_signal_connect(G_OBJECT(w->priv.webview), "context-menu", - G_CALLBACK(webview_context_menu_cb), w); - } - - gtk_widget_show_all(w->priv.window); - - webkit_web_view_run_javascript( - WEBKIT_WEB_VIEW(w->priv.webview), - "window.external={invoke:function(x){" - "window.webkit.messageHandlers.external.postMessage(x);}}", - NULL, NULL, NULL); - - g_signal_connect(G_OBJECT(w->priv.window), "destroy", - G_CALLBACK(webview_destroy_cb), w); - return 0; -} - -WEBVIEW_API int webview_loop(struct webview *w, int blocking) { - gtk_main_iteration_do(blocking); - return w->priv.should_exit; -} - -WEBVIEW_API void webview_set_title(struct webview *w, const char *title) { - gtk_window_set_title(GTK_WINDOW(w->priv.window), title); -} - -WEBVIEW_API void webview_set_fullscreen(struct webview *w, int fullscreen) { - if (fullscreen) { - gtk_window_fullscreen(GTK_WINDOW(w->priv.window)); - } else { - gtk_window_unfullscreen(GTK_WINDOW(w->priv.window)); - } -} - -WEBVIEW_API void webview_set_color(struct webview *w, uint8_t r, uint8_t g, - uint8_t b, uint8_t a) { - GdkRGBA color = {r / 255.0, g / 255.0, b / 255.0, a / 255.0}; - webkit_web_view_set_background_color(WEBKIT_WEB_VIEW(w->priv.webview), - &color); -} - -WEBVIEW_API void webview_dialog(struct webview *w, - enum webview_dialog_type dlgtype, int flags, - const char *title, const char *arg, - char *result, size_t resultsz) { - GtkWidget *dlg; - if (result != NULL) { - result[0] = '\0'; - } - if (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN || - dlgtype == WEBVIEW_DIALOG_TYPE_SAVE) { - dlg = gtk_file_chooser_dialog_new( - title, GTK_WINDOW(w->priv.window), - (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN - ? (flags & WEBVIEW_DIALOG_FLAG_DIRECTORY - ? GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER - : GTK_FILE_CHOOSER_ACTION_OPEN) - : GTK_FILE_CHOOSER_ACTION_SAVE), - "_Cancel", GTK_RESPONSE_CANCEL, - (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN ? "_Open" : "_Save"), - GTK_RESPONSE_ACCEPT, NULL); - gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(dlg), FALSE); - gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dlg), FALSE); - gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(dlg), TRUE); - gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dlg), TRUE); - gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER(dlg), TRUE); - gint response = gtk_dialog_run(GTK_DIALOG(dlg)); - if (response == GTK_RESPONSE_ACCEPT) { - gchar *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dlg)); - g_strlcpy(result, filename, resultsz); - g_free(filename); - } - gtk_widget_destroy(dlg); - } else if (dlgtype == WEBVIEW_DIALOG_TYPE_ALERT) { - GtkMessageType type = GTK_MESSAGE_OTHER; - switch (flags & WEBVIEW_DIALOG_FLAG_ALERT_MASK) { - case WEBVIEW_DIALOG_FLAG_INFO: - type = GTK_MESSAGE_INFO; - break; - case WEBVIEW_DIALOG_FLAG_WARNING: - type = GTK_MESSAGE_WARNING; - break; - case WEBVIEW_DIALOG_FLAG_ERROR: - type = GTK_MESSAGE_ERROR; - break; - } - dlg = gtk_message_dialog_new(GTK_WINDOW(w->priv.window), GTK_DIALOG_MODAL, - type, GTK_BUTTONS_OK, "%s", title); - gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dlg), "%s", - arg); - gtk_dialog_run(GTK_DIALOG(dlg)); - gtk_widget_destroy(dlg); - } -} - -static void webview_eval_finished(GObject *object, GAsyncResult *result, - gpointer userdata) { - (void) object; - (void) result; - struct webview *w = (struct webview *)userdata; - w->priv.js_busy = 0; -} - -WEBVIEW_API int webview_eval(struct webview *w, const char *js) { - while (w->priv.ready == 0) { - g_main_context_iteration(NULL, TRUE); - } - w->priv.js_busy = 1; - webkit_web_view_run_javascript(WEBKIT_WEB_VIEW(w->priv.webview), js, NULL, - webview_eval_finished, w); - while (w->priv.js_busy) { - g_main_context_iteration(NULL, TRUE); - } - return 0; -} - -static gboolean webview_dispatch_wrapper(gpointer userdata) { - struct webview *w = (struct webview *)userdata; - for (;;) { - struct webview_dispatch_arg *arg = - (struct webview_dispatch_arg *)g_async_queue_try_pop(w->priv.queue); - if (arg == NULL) { - break; - } - (arg->fn)(w, arg->arg); - g_free(arg); - } - return FALSE; -} - -WEBVIEW_API void webview_dispatch(struct webview *w, webview_dispatch_fn fn, - void *arg) { - struct webview_dispatch_arg *context = - (struct webview_dispatch_arg *)g_new(struct webview_dispatch_arg, 1); - context->w = w; - context->arg = arg; - context->fn = fn; - g_async_queue_lock(w->priv.queue); - g_async_queue_push_unlocked(w->priv.queue, context); - if (g_async_queue_length_unlocked(w->priv.queue) == 1) { - gdk_threads_add_idle(webview_dispatch_wrapper, w); - } - g_async_queue_unlock(w->priv.queue); -} - -WEBVIEW_API void webview_terminate(struct webview *w) { - w->priv.should_exit = 1; -} - -WEBVIEW_API void webview_exit(struct webview *w) { (void)w; } -WEBVIEW_API void webview_print_log(const char *s) { - fprintf(stderr, "%s\n", s); -} - -#endif /* WEBVIEW_GTK */ - -#if defined(WEBVIEW_WINAPI) - -#pragma comment(lib, "user32.lib") -#pragma comment(lib, "ole32.lib") -#pragma comment(lib, "oleaut32.lib") - -#define WM_WEBVIEW_DISPATCH (WM_APP + 1) - -typedef struct { - IOleInPlaceFrame frame; - HWND window; -} _IOleInPlaceFrameEx; - -typedef struct { - IOleInPlaceSite inplace; - _IOleInPlaceFrameEx frame; -} _IOleInPlaceSiteEx; - -typedef struct { IDocHostUIHandler ui; } _IDocHostUIHandlerEx; - -typedef struct { - IOleClientSite client; - _IOleInPlaceSiteEx inplace; - _IDocHostUIHandlerEx ui; - IDispatch external; -} _IOleClientSiteEx; - -#ifdef __cplusplus -#define iid_ref(x) &(x) -#define iid_unref(x) *(x) -#else -#define iid_ref(x) (x) -#define iid_unref(x) (x) -#endif - -static inline WCHAR *webview_to_utf16(const char *s) { - DWORD size = MultiByteToWideChar(CP_UTF8, 0, s, -1, 0, 0); - WCHAR *ws = (WCHAR *)GlobalAlloc(GMEM_FIXED, sizeof(WCHAR) * size); - if (ws == NULL) { - return NULL; - } - MultiByteToWideChar(CP_UTF8, 0, s, -1, ws, size); - return ws; -} - -static inline char *webview_from_utf16(WCHAR *ws) { - int n = WideCharToMultiByte(CP_UTF8, 0, ws, -1, NULL, 0, NULL, NULL); - char *s = (char *)GlobalAlloc(GMEM_FIXED, n); - if (s == NULL) { - return NULL; - } - WideCharToMultiByte(CP_UTF8, 0, ws, -1, s, n, NULL, NULL); - return s; -} - -static int iid_eq(REFIID a, const IID *b) { - return memcmp((const void *)iid_ref(a), (const void *)b, sizeof(GUID)) == 0; -} - -static HRESULT STDMETHODCALLTYPE JS_QueryInterface(IDispatch FAR *This, - REFIID riid, - LPVOID FAR *ppvObj) { - if (iid_eq(riid, &IID_IDispatch)) { - *ppvObj = This; - return S_OK; - } - *ppvObj = 0; - return E_NOINTERFACE; -} -static ULONG STDMETHODCALLTYPE JS_AddRef(IDispatch FAR *This) { return 1; } -static ULONG STDMETHODCALLTYPE JS_Release(IDispatch FAR *This) { return 1; } -static HRESULT STDMETHODCALLTYPE JS_GetTypeInfoCount(IDispatch FAR *This, - UINT *pctinfo) { - return S_OK; -} -static HRESULT STDMETHODCALLTYPE JS_GetTypeInfo(IDispatch FAR *This, - UINT iTInfo, LCID lcid, - ITypeInfo **ppTInfo) { - return S_OK; -} -#define WEBVIEW_JS_INVOKE_ID 0x1000 -static HRESULT STDMETHODCALLTYPE JS_GetIDsOfNames(IDispatch FAR *This, - REFIID riid, - LPOLESTR *rgszNames, - UINT cNames, LCID lcid, - DISPID *rgDispId) { - if (cNames != 1) { - return S_FALSE; - } - if (wcscmp(rgszNames[0], L"invoke") == 0) { - rgDispId[0] = WEBVIEW_JS_INVOKE_ID; - return S_OK; - } - return S_FALSE; -} - -static HRESULT STDMETHODCALLTYPE -JS_Invoke(IDispatch FAR *This, DISPID dispIdMember, REFIID riid, LCID lcid, - WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, - EXCEPINFO *pExcepInfo, UINT *puArgErr) { - size_t offset = (size_t) & ((_IOleClientSiteEx *)NULL)->external; - _IOleClientSiteEx *ex = (_IOleClientSiteEx *)((char *)(This)-offset); - struct webview *w = (struct webview *)GetWindowLongPtr( - ex->inplace.frame.window, GWLP_USERDATA); - if (pDispParams->cArgs == 1 && pDispParams->rgvarg[0].vt == VT_BSTR) { - BSTR bstr = pDispParams->rgvarg[0].bstrVal; - char *s = webview_from_utf16(bstr); - if (s != NULL) { - if (dispIdMember == WEBVIEW_JS_INVOKE_ID) { - if (w->external_invoke_cb != NULL) { - w->external_invoke_cb(w, s); - } - } else { - return S_FALSE; - } - GlobalFree(s); - } - } - return S_OK; -} - -static IDispatchVtbl ExternalDispatchTable = { - JS_QueryInterface, JS_AddRef, JS_Release, JS_GetTypeInfoCount, - JS_GetTypeInfo, JS_GetIDsOfNames, JS_Invoke}; - -static ULONG STDMETHODCALLTYPE Site_AddRef(IOleClientSite FAR *This) { - return 1; -} -static ULONG STDMETHODCALLTYPE Site_Release(IOleClientSite FAR *This) { - return 1; -} -static HRESULT STDMETHODCALLTYPE Site_SaveObject(IOleClientSite FAR *This) { - return E_NOTIMPL; -} -static HRESULT STDMETHODCALLTYPE Site_GetMoniker(IOleClientSite FAR *This, - DWORD dwAssign, - DWORD dwWhichMoniker, - IMoniker **ppmk) { - return E_NOTIMPL; -} -static HRESULT STDMETHODCALLTYPE -Site_GetContainer(IOleClientSite FAR *This, LPOLECONTAINER FAR *ppContainer) { - *ppContainer = 0; - return E_NOINTERFACE; -} -static HRESULT STDMETHODCALLTYPE Site_ShowObject(IOleClientSite FAR *This) { - return NOERROR; -} -static HRESULT STDMETHODCALLTYPE Site_OnShowWindow(IOleClientSite FAR *This, - BOOL fShow) { - return E_NOTIMPL; -} -static HRESULT STDMETHODCALLTYPE -Site_RequestNewObjectLayout(IOleClientSite FAR *This) { - return E_NOTIMPL; -} -static HRESULT STDMETHODCALLTYPE Site_QueryInterface(IOleClientSite FAR *This, - REFIID riid, - void **ppvObject) { - if (iid_eq(riid, &IID_IUnknown) || iid_eq(riid, &IID_IOleClientSite)) { - *ppvObject = &((_IOleClientSiteEx *)This)->client; - } else if (iid_eq(riid, &IID_IOleInPlaceSite)) { - *ppvObject = &((_IOleClientSiteEx *)This)->inplace; - } else if (iid_eq(riid, &IID_IDocHostUIHandler)) { - *ppvObject = &((_IOleClientSiteEx *)This)->ui; - } else { - *ppvObject = 0; - return (E_NOINTERFACE); - } - return S_OK; -} -static HRESULT STDMETHODCALLTYPE InPlace_QueryInterface( - IOleInPlaceSite FAR *This, REFIID riid, LPVOID FAR *ppvObj) { - return (Site_QueryInterface( - (IOleClientSite *)((char *)This - sizeof(IOleClientSite)), riid, ppvObj)); -} -static ULONG STDMETHODCALLTYPE InPlace_AddRef(IOleInPlaceSite FAR *This) { - return 1; -} -static ULONG STDMETHODCALLTYPE InPlace_Release(IOleInPlaceSite FAR *This) { - return 1; -} -static HRESULT STDMETHODCALLTYPE InPlace_GetWindow(IOleInPlaceSite FAR *This, - HWND FAR *lphwnd) { - *lphwnd = ((_IOleInPlaceSiteEx FAR *)This)->frame.window; - return S_OK; -} -static HRESULT STDMETHODCALLTYPE -InPlace_ContextSensitiveHelp(IOleInPlaceSite FAR *This, BOOL fEnterMode) { - return E_NOTIMPL; -} -static HRESULT STDMETHODCALLTYPE -InPlace_CanInPlaceActivate(IOleInPlaceSite FAR *This) { - return S_OK; -} -static HRESULT STDMETHODCALLTYPE -InPlace_OnInPlaceActivate(IOleInPlaceSite FAR *This) { - return S_OK; -} -static HRESULT STDMETHODCALLTYPE -InPlace_OnUIActivate(IOleInPlaceSite FAR *This) { - return S_OK; -} -static HRESULT STDMETHODCALLTYPE InPlace_GetWindowContext( - IOleInPlaceSite FAR *This, LPOLEINPLACEFRAME FAR *lplpFrame, - LPOLEINPLACEUIWINDOW FAR *lplpDoc, LPRECT lprcPosRect, LPRECT lprcClipRect, - LPOLEINPLACEFRAMEINFO lpFrameInfo) { - *lplpFrame = (LPOLEINPLACEFRAME) & ((_IOleInPlaceSiteEx *)This)->frame; - *lplpDoc = 0; - lpFrameInfo->fMDIApp = FALSE; - lpFrameInfo->hwndFrame = ((_IOleInPlaceFrameEx *)*lplpFrame)->window; - lpFrameInfo->haccel = 0; - lpFrameInfo->cAccelEntries = 0; - return S_OK; -} -static HRESULT STDMETHODCALLTYPE InPlace_Scroll(IOleInPlaceSite FAR *This, - SIZE scrollExtent) { - return E_NOTIMPL; -} -static HRESULT STDMETHODCALLTYPE -InPlace_OnUIDeactivate(IOleInPlaceSite FAR *This, BOOL fUndoable) { - return S_OK; -} -static HRESULT STDMETHODCALLTYPE -InPlace_OnInPlaceDeactivate(IOleInPlaceSite FAR *This) { - return S_OK; -} -static HRESULT STDMETHODCALLTYPE -InPlace_DiscardUndoState(IOleInPlaceSite FAR *This) { - return E_NOTIMPL; -} -static HRESULT STDMETHODCALLTYPE -InPlace_DeactivateAndUndo(IOleInPlaceSite FAR *This) { - return E_NOTIMPL; -} -static HRESULT STDMETHODCALLTYPE -InPlace_OnPosRectChange(IOleInPlaceSite FAR *This, LPCRECT lprcPosRect) { - IOleObject *browserObject; - IOleInPlaceObject *inplace; - browserObject = *((IOleObject **)((char *)This - sizeof(IOleObject *) - - sizeof(IOleClientSite))); - if (!browserObject->lpVtbl->QueryInterface(browserObject, - iid_unref(&IID_IOleInPlaceObject), - (void **)&inplace)) { - inplace->lpVtbl->SetObjectRects(inplace, lprcPosRect, lprcPosRect); - inplace->lpVtbl->Release(inplace); - } - return S_OK; -} -static HRESULT STDMETHODCALLTYPE Frame_QueryInterface( - IOleInPlaceFrame FAR *This, REFIID riid, LPVOID FAR *ppvObj) { - return E_NOTIMPL; -} -static ULONG STDMETHODCALLTYPE Frame_AddRef(IOleInPlaceFrame FAR *This) { - return 1; -} -static ULONG STDMETHODCALLTYPE Frame_Release(IOleInPlaceFrame FAR *This) { - return 1; -} -static HRESULT STDMETHODCALLTYPE Frame_GetWindow(IOleInPlaceFrame FAR *This, - HWND FAR *lphwnd) { - *lphwnd = ((_IOleInPlaceFrameEx *)This)->window; - return S_OK; -} -static HRESULT STDMETHODCALLTYPE -Frame_ContextSensitiveHelp(IOleInPlaceFrame FAR *This, BOOL fEnterMode) { - return E_NOTIMPL; -} -static HRESULT STDMETHODCALLTYPE Frame_GetBorder(IOleInPlaceFrame FAR *This, - LPRECT lprectBorder) { - return E_NOTIMPL; -} -static HRESULT STDMETHODCALLTYPE Frame_RequestBorderSpace( - IOleInPlaceFrame FAR *This, LPCBORDERWIDTHS pborderwidths) { - return E_NOTIMPL; -} -static HRESULT STDMETHODCALLTYPE Frame_SetBorderSpace( - IOleInPlaceFrame FAR *This, LPCBORDERWIDTHS pborderwidths) { - return E_NOTIMPL; -} -static HRESULT STDMETHODCALLTYPE Frame_SetActiveObject( - IOleInPlaceFrame FAR *This, IOleInPlaceActiveObject *pActiveObject, - LPCOLESTR pszObjName) { - return S_OK; -} -static HRESULT STDMETHODCALLTYPE -Frame_InsertMenus(IOleInPlaceFrame FAR *This, HMENU hmenuShared, - LPOLEMENUGROUPWIDTHS lpMenuWidths) { - return E_NOTIMPL; -} -static HRESULT STDMETHODCALLTYPE Frame_SetMenu(IOleInPlaceFrame FAR *This, - HMENU hmenuShared, - HOLEMENU holemenu, - HWND hwndActiveObject) { - return S_OK; -} -static HRESULT STDMETHODCALLTYPE Frame_RemoveMenus(IOleInPlaceFrame FAR *This, - HMENU hmenuShared) { - return E_NOTIMPL; -} -static HRESULT STDMETHODCALLTYPE Frame_SetStatusText(IOleInPlaceFrame FAR *This, - LPCOLESTR pszStatusText) { - return S_OK; -} -static HRESULT STDMETHODCALLTYPE -Frame_EnableModeless(IOleInPlaceFrame FAR *This, BOOL fEnable) { - return S_OK; -} -static HRESULT STDMETHODCALLTYPE -Frame_TranslateAccelerator(IOleInPlaceFrame FAR *This, LPMSG lpmsg, WORD wID) { - return E_NOTIMPL; -} -static HRESULT STDMETHODCALLTYPE UI_QueryInterface(IDocHostUIHandler FAR *This, - REFIID riid, - LPVOID FAR *ppvObj) { - return (Site_QueryInterface((IOleClientSite *)((char *)This - - sizeof(IOleClientSite) - - sizeof(_IOleInPlaceSiteEx)), - riid, ppvObj)); -} -static ULONG STDMETHODCALLTYPE UI_AddRef(IDocHostUIHandler FAR *This) { - return 1; -} -static ULONG STDMETHODCALLTYPE UI_Release(IDocHostUIHandler FAR *This) { - return 1; -} -static HRESULT STDMETHODCALLTYPE UI_ShowContextMenu( - IDocHostUIHandler FAR *This, DWORD dwID, POINT __RPC_FAR *ppt, - IUnknown __RPC_FAR *pcmdtReserved, IDispatch __RPC_FAR *pdispReserved) { - return S_OK; -} -static HRESULT STDMETHODCALLTYPE -UI_GetHostInfo(IDocHostUIHandler FAR *This, DOCHOSTUIINFO __RPC_FAR *pInfo) { - pInfo->cbSize = sizeof(DOCHOSTUIINFO); - pInfo->dwFlags = DOCHOSTUIFLAG_NO3DBORDER; - pInfo->dwDoubleClick = DOCHOSTUIDBLCLK_DEFAULT; - return S_OK; -} -static HRESULT STDMETHODCALLTYPE UI_ShowUI( - IDocHostUIHandler FAR *This, DWORD dwID, - IOleInPlaceActiveObject __RPC_FAR *pActiveObject, - IOleCommandTarget __RPC_FAR *pCommandTarget, - IOleInPlaceFrame __RPC_FAR *pFrame, IOleInPlaceUIWindow __RPC_FAR *pDoc) { - return S_OK; -} -static HRESULT STDMETHODCALLTYPE UI_HideUI(IDocHostUIHandler FAR *This) { - return S_OK; -} -static HRESULT STDMETHODCALLTYPE UI_UpdateUI(IDocHostUIHandler FAR *This) { - return S_OK; -} -static HRESULT STDMETHODCALLTYPE UI_EnableModeless(IDocHostUIHandler FAR *This, - BOOL fEnable) { - return S_OK; -} -static HRESULT STDMETHODCALLTYPE -UI_OnDocWindowActivate(IDocHostUIHandler FAR *This, BOOL fActivate) { - return S_OK; -} -static HRESULT STDMETHODCALLTYPE -UI_OnFrameWindowActivate(IDocHostUIHandler FAR *This, BOOL fActivate) { - return S_OK; -} -static HRESULT STDMETHODCALLTYPE -UI_ResizeBorder(IDocHostUIHandler FAR *This, LPCRECT prcBorder, - IOleInPlaceUIWindow __RPC_FAR *pUIWindow, BOOL fRameWindow) { - return S_OK; -} -static HRESULT STDMETHODCALLTYPE -UI_TranslateAccelerator(IDocHostUIHandler FAR *This, LPMSG lpMsg, - const GUID __RPC_FAR *pguidCmdGroup, DWORD nCmdID) { - return S_FALSE; -} -static HRESULT STDMETHODCALLTYPE UI_GetOptionKeyPath( - IDocHostUIHandler FAR *This, LPOLESTR __RPC_FAR *pchKey, DWORD dw) { - return S_FALSE; -} -static HRESULT STDMETHODCALLTYPE UI_GetDropTarget( - IDocHostUIHandler FAR *This, IDropTarget __RPC_FAR *pDropTarget, - IDropTarget __RPC_FAR *__RPC_FAR *ppDropTarget) { - return S_FALSE; -} -static HRESULT STDMETHODCALLTYPE UI_GetExternal( - IDocHostUIHandler FAR *This, IDispatch __RPC_FAR *__RPC_FAR *ppDispatch) { - *ppDispatch = (IDispatch *)(This + 1); - return S_OK; -} -static HRESULT STDMETHODCALLTYPE UI_TranslateUrl( - IDocHostUIHandler FAR *This, DWORD dwTranslate, OLECHAR __RPC_FAR *pchURLIn, - OLECHAR __RPC_FAR *__RPC_FAR *ppchURLOut) { - *ppchURLOut = 0; - return S_FALSE; -} -static HRESULT STDMETHODCALLTYPE -UI_FilterDataObject(IDocHostUIHandler FAR *This, IDataObject __RPC_FAR *pDO, - IDataObject __RPC_FAR *__RPC_FAR *ppDORet) { - *ppDORet = 0; - return S_FALSE; -} - -static const TCHAR *classname = "WebView"; -static const SAFEARRAYBOUND ArrayBound = {1, 0}; - -static IOleClientSiteVtbl MyIOleClientSiteTable = { - Site_QueryInterface, Site_AddRef, Site_Release, - Site_SaveObject, Site_GetMoniker, Site_GetContainer, - Site_ShowObject, Site_OnShowWindow, Site_RequestNewObjectLayout}; -static IOleInPlaceSiteVtbl MyIOleInPlaceSiteTable = { - InPlace_QueryInterface, - InPlace_AddRef, - InPlace_Release, - InPlace_GetWindow, - InPlace_ContextSensitiveHelp, - InPlace_CanInPlaceActivate, - InPlace_OnInPlaceActivate, - InPlace_OnUIActivate, - InPlace_GetWindowContext, - InPlace_Scroll, - InPlace_OnUIDeactivate, - InPlace_OnInPlaceDeactivate, - InPlace_DiscardUndoState, - InPlace_DeactivateAndUndo, - InPlace_OnPosRectChange}; - -static IOleInPlaceFrameVtbl MyIOleInPlaceFrameTable = { - Frame_QueryInterface, - Frame_AddRef, - Frame_Release, - Frame_GetWindow, - Frame_ContextSensitiveHelp, - Frame_GetBorder, - Frame_RequestBorderSpace, - Frame_SetBorderSpace, - Frame_SetActiveObject, - Frame_InsertMenus, - Frame_SetMenu, - Frame_RemoveMenus, - Frame_SetStatusText, - Frame_EnableModeless, - Frame_TranslateAccelerator}; - -static IDocHostUIHandlerVtbl MyIDocHostUIHandlerTable = { - UI_QueryInterface, - UI_AddRef, - UI_Release, - UI_ShowContextMenu, - UI_GetHostInfo, - UI_ShowUI, - UI_HideUI, - UI_UpdateUI, - UI_EnableModeless, - UI_OnDocWindowActivate, - UI_OnFrameWindowActivate, - UI_ResizeBorder, - UI_TranslateAccelerator, - UI_GetOptionKeyPath, - UI_GetDropTarget, - UI_GetExternal, - UI_TranslateUrl, - UI_FilterDataObject}; - -static void UnEmbedBrowserObject(struct webview *w) { - if (w->priv.browser != NULL) { - (*w->priv.browser)->lpVtbl->Close(*w->priv.browser, OLECLOSE_NOSAVE); - (*w->priv.browser)->lpVtbl->Release(*w->priv.browser); - GlobalFree(w->priv.browser); - w->priv.browser = NULL; - } -} - -static int EmbedBrowserObject(struct webview *w) { - RECT rect; - IWebBrowser2 *webBrowser2 = NULL; - LPCLASSFACTORY pClassFactory = NULL; - _IOleClientSiteEx *_iOleClientSiteEx = NULL; - IOleObject **browser = (IOleObject **)GlobalAlloc( - GMEM_FIXED, sizeof(IOleObject *) + sizeof(_IOleClientSiteEx)); - if (browser == NULL) { - goto error; - } - w->priv.browser = browser; - - _iOleClientSiteEx = (_IOleClientSiteEx *)(browser + 1); - _iOleClientSiteEx->client.lpVtbl = &MyIOleClientSiteTable; - _iOleClientSiteEx->inplace.inplace.lpVtbl = &MyIOleInPlaceSiteTable; - _iOleClientSiteEx->inplace.frame.frame.lpVtbl = &MyIOleInPlaceFrameTable; - _iOleClientSiteEx->inplace.frame.window = w->priv.hwnd; - _iOleClientSiteEx->ui.ui.lpVtbl = &MyIDocHostUIHandlerTable; - _iOleClientSiteEx->external.lpVtbl = &ExternalDispatchTable; - - if (CoGetClassObject(iid_unref(&CLSID_WebBrowser), - CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER, NULL, - iid_unref(&IID_IClassFactory), - (void **)&pClassFactory) != S_OK) { - goto error; - } - - if (pClassFactory == NULL) { - goto error; - } - - if (pClassFactory->lpVtbl->CreateInstance(pClassFactory, 0, - iid_unref(&IID_IOleObject), - (void **)browser) != S_OK) { - goto error; - } - pClassFactory->lpVtbl->Release(pClassFactory); - if ((*browser)->lpVtbl->SetClientSite( - *browser, (IOleClientSite *)_iOleClientSiteEx) != S_OK) { - goto error; - } - (*browser)->lpVtbl->SetHostNames(*browser, L"My Host Name", 0); - - if (OleSetContainedObject((struct IUnknown *)(*browser), TRUE) != S_OK) { - goto error; - } - GetClientRect(w->priv.hwnd, &rect); - if ((*browser)->lpVtbl->DoVerb((*browser), OLEIVERB_SHOW, NULL, - (IOleClientSite *)_iOleClientSiteEx, -1, - w->priv.hwnd, &rect) != S_OK) { - goto error; - } - if ((*browser)->lpVtbl->QueryInterface((*browser), - iid_unref(&IID_IWebBrowser2), - (void **)&webBrowser2) != S_OK) { - goto error; - } - - webBrowser2->lpVtbl->put_Left(webBrowser2, 0); - webBrowser2->lpVtbl->put_Top(webBrowser2, 0); - webBrowser2->lpVtbl->put_Width(webBrowser2, rect.right); - webBrowser2->lpVtbl->put_Height(webBrowser2, rect.bottom); - webBrowser2->lpVtbl->Release(webBrowser2); - - return 0; -error: - UnEmbedBrowserObject(w); - if (pClassFactory != NULL) { - pClassFactory->lpVtbl->Release(pClassFactory); - } - if (browser != NULL) { - GlobalFree(browser); - } - return -1; -} - -#define WEBVIEW_DATA_URL_PREFIX "data:text/html," -static int DisplayHTMLPage(struct webview *w) { - IWebBrowser2 *webBrowser2; - VARIANT myURL; - LPDISPATCH lpDispatch; - IHTMLDocument2 *htmlDoc2; - BSTR bstr; - IOleObject *browserObject; - SAFEARRAY *sfArray; - VARIANT *pVar; - browserObject = *w->priv.browser; - int isDataURL = 0; - const char *webview_url = webview_check_url(w->url); - if (!browserObject->lpVtbl->QueryInterface( - browserObject, iid_unref(&IID_IWebBrowser2), (void **)&webBrowser2)) { - LPCSTR webPageName; - isDataURL = (strncmp(webview_url, WEBVIEW_DATA_URL_PREFIX, - strlen(WEBVIEW_DATA_URL_PREFIX)) == 0); - if (isDataURL) { - webPageName = "about:blank"; - } else { - webPageName = (LPCSTR)webview_url; - } - VariantInit(&myURL); - myURL.vt = VT_BSTR; -#ifndef UNICODE - { - wchar_t *buffer = webview_to_utf16(webPageName); - if (buffer == NULL) { - goto badalloc; - } - myURL.bstrVal = SysAllocString(buffer); - GlobalFree(buffer); - } -#else - myURL.bstrVal = SysAllocString(webPageName); -#endif - if (!myURL.bstrVal) { - badalloc: - webBrowser2->lpVtbl->Release(webBrowser2); - return (-6); - } - webBrowser2->lpVtbl->Navigate2(webBrowser2, &myURL, 0, 0, 0, 0); - VariantClear(&myURL); - if (!isDataURL) { - return 0; - } - - char *url = (char *)calloc(1, strlen(webview_url) + 1); - char *q = url; - for (const char *p = webview_url + strlen(WEBVIEW_DATA_URL_PREFIX); *q = *p; - p++, q++) { - if (*q == '%' && *(p + 1) && *(p + 2)) { - sscanf(p + 1, "%02x", q); - p = p + 2; - } - } - - if (webBrowser2->lpVtbl->get_Document(webBrowser2, &lpDispatch) == S_OK) { - if (lpDispatch->lpVtbl->QueryInterface(lpDispatch, - iid_unref(&IID_IHTMLDocument2), - (void **)&htmlDoc2) == S_OK) { - if ((sfArray = SafeArrayCreate(VT_VARIANT, 1, - (SAFEARRAYBOUND *)&ArrayBound))) { - if (!SafeArrayAccessData(sfArray, (void **)&pVar)) { - pVar->vt = VT_BSTR; -#ifndef UNICODE - { - wchar_t *buffer = webview_to_utf16(url); - if (buffer == NULL) { - goto release; - } - bstr = SysAllocString(buffer); - GlobalFree(buffer); - } -#else - bstr = SysAllocString(string); -#endif - if ((pVar->bstrVal = bstr)) { - htmlDoc2->lpVtbl->write(htmlDoc2, sfArray); - htmlDoc2->lpVtbl->close(htmlDoc2); - } - } - SafeArrayDestroy(sfArray); - } - release: - free(url); - htmlDoc2->lpVtbl->Release(htmlDoc2); - } - lpDispatch->lpVtbl->Release(lpDispatch); - } - webBrowser2->lpVtbl->Release(webBrowser2); - return (0); - } - return (-5); -} - -static LRESULT CALLBACK wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, - LPARAM lParam) { - struct webview *w = (struct webview *)GetWindowLongPtr(hwnd, GWLP_USERDATA); - switch (uMsg) { - case WM_CREATE: - w = (struct webview *)((CREATESTRUCT *)lParam)->lpCreateParams; - w->priv.hwnd = hwnd; - return EmbedBrowserObject(w); - case WM_DESTROY: - UnEmbedBrowserObject(w); - PostQuitMessage(0); - return TRUE; - case WM_SIZE: { - IWebBrowser2 *webBrowser2; - IOleObject *browser = *w->priv.browser; - if (browser->lpVtbl->QueryInterface(browser, iid_unref(&IID_IWebBrowser2), - (void **)&webBrowser2) == S_OK) { - RECT rect; - GetClientRect(hwnd, &rect); - webBrowser2->lpVtbl->put_Width(webBrowser2, rect.right); - webBrowser2->lpVtbl->put_Height(webBrowser2, rect.bottom); - } - return TRUE; - } - case WM_WEBVIEW_DISPATCH: { - webview_dispatch_fn f = (webview_dispatch_fn)wParam; - void *arg = (void *)lParam; - (*f)(w, arg); - return TRUE; - } - } - return DefWindowProc(hwnd, uMsg, wParam, lParam); -} - -#define WEBVIEW_KEY_FEATURE_BROWSER_EMULATION \ - "Software\\Microsoft\\Internet " \ - "Explorer\\Main\\FeatureControl\\FEATURE_BROWSER_EMULATION" - -static int webview_fix_ie_compat_mode() { - HKEY hKey; - DWORD ie_version = 11000; - TCHAR appname[MAX_PATH + 1]; - TCHAR *p; - if (GetModuleFileName(NULL, appname, MAX_PATH + 1) == 0) { - return -1; - } - for (p = &appname[strlen(appname) - 1]; p != appname && *p != '\\'; p--) { - } - p++; - if (RegCreateKey(HKEY_CURRENT_USER, WEBVIEW_KEY_FEATURE_BROWSER_EMULATION, - &hKey) != ERROR_SUCCESS) { - return -1; - } - if (RegSetValueEx(hKey, p, 0, REG_DWORD, (BYTE *)&ie_version, - sizeof(ie_version)) != ERROR_SUCCESS) { - RegCloseKey(hKey); - return -1; - } - RegCloseKey(hKey); - return 0; -} - -WEBVIEW_API int webview_init(struct webview *w) { - WNDCLASSEX wc; - HINSTANCE hInstance; - DWORD style; - RECT clientRect; - RECT rect; - - if (webview_fix_ie_compat_mode() < 0) { - return -1; - } - - hInstance = GetModuleHandle(NULL); - if (hInstance == NULL) { - return -1; - } - if (OleInitialize(NULL) != S_OK) { - return -1; - } - ZeroMemory(&wc, sizeof(WNDCLASSEX)); - wc.cbSize = sizeof(WNDCLASSEX); - wc.hInstance = hInstance; - wc.lpfnWndProc = wndproc; - wc.lpszClassName = classname; - RegisterClassEx(&wc); - - style = WS_OVERLAPPEDWINDOW; - if (!w->resizable) { - style = WS_OVERLAPPED | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU; - } - - rect.left = 0; - rect.top = 0; - rect.right = w->width; - rect.bottom = w->height; - AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, 0); - - GetClientRect(GetDesktopWindow(), &clientRect); - int left = (clientRect.right / 2) - ((rect.right - rect.left) / 2); - int top = (clientRect.bottom / 2) - ((rect.bottom - rect.top) / 2); - rect.right = rect.right - rect.left + left; - rect.left = left; - rect.bottom = rect.bottom - rect.top + top; - rect.top = top; - - w->priv.hwnd = - CreateWindowEx(0, classname, w->title, style, rect.left, rect.top, - rect.right - rect.left, rect.bottom - rect.top, - HWND_DESKTOP, NULL, hInstance, (void *)w); - if (w->priv.hwnd == 0) { - OleUninitialize(); - return -1; - } - - SetWindowLongPtr(w->priv.hwnd, GWLP_USERDATA, (LONG_PTR)w); - - DisplayHTMLPage(w); - - SetWindowText(w->priv.hwnd, w->title); - ShowWindow(w->priv.hwnd, SW_SHOWDEFAULT); - UpdateWindow(w->priv.hwnd); - SetFocus(w->priv.hwnd); - - return 0; -} - -WEBVIEW_API int webview_loop(struct webview *w, int blocking) { - MSG msg; - if (blocking) { - GetMessage(&msg, 0, 0, 0); - } else { - PeekMessage(&msg, 0, 0, 0, PM_REMOVE); - } - switch (msg.message) { - case WM_QUIT: - return -1; - case WM_COMMAND: - case WM_KEYDOWN: - case WM_KEYUP: { - HRESULT r = S_OK; - IWebBrowser2 *webBrowser2; - IOleObject *browser = *w->priv.browser; - if (browser->lpVtbl->QueryInterface(browser, iid_unref(&IID_IWebBrowser2), - (void **)&webBrowser2) == S_OK) { - IOleInPlaceActiveObject *pIOIPAO; - if (browser->lpVtbl->QueryInterface( - browser, iid_unref(&IID_IOleInPlaceActiveObject), - (void **)&pIOIPAO) == S_OK) { - r = pIOIPAO->lpVtbl->TranslateAccelerator(pIOIPAO, &msg); - pIOIPAO->lpVtbl->Release(pIOIPAO); - } - webBrowser2->lpVtbl->Release(webBrowser2); - } - if (r != S_FALSE) { - break; - } - } - default: - TranslateMessage(&msg); - DispatchMessage(&msg); - } - return 0; -} - -WEBVIEW_API int webview_eval(struct webview *w, const char *js) { - IWebBrowser2 *webBrowser2; - IHTMLDocument2 *htmlDoc2; - IDispatch *docDispatch; - IDispatch *scriptDispatch; - if ((*w->priv.browser) - ->lpVtbl->QueryInterface((*w->priv.browser), - iid_unref(&IID_IWebBrowser2), - (void **)&webBrowser2) != S_OK) { - return -1; - } - - if (webBrowser2->lpVtbl->get_Document(webBrowser2, &docDispatch) != S_OK) { - return -1; - } - if (docDispatch->lpVtbl->QueryInterface(docDispatch, - iid_unref(&IID_IHTMLDocument2), - (void **)&htmlDoc2) != S_OK) { - return -1; - } - if (htmlDoc2->lpVtbl->get_Script(htmlDoc2, &scriptDispatch) != S_OK) { - return -1; - } - DISPID dispid; - BSTR evalStr = SysAllocString(L"eval"); - if (scriptDispatch->lpVtbl->GetIDsOfNames( - scriptDispatch, iid_unref(&IID_NULL), &evalStr, 1, - LOCALE_SYSTEM_DEFAULT, &dispid) != S_OK) { - SysFreeString(evalStr); - return -1; - } - SysFreeString(evalStr); - - DISPPARAMS params; - VARIANT arg; - VARIANT result; - EXCEPINFO excepInfo; - UINT nArgErr = (UINT)-1; - params.cArgs = 1; - params.cNamedArgs = 0; - params.rgvarg = &arg; - arg.vt = VT_BSTR; - static const char *prologue = "(function(){"; - static const char *epilogue = ";})();"; - int n = strlen(prologue) + strlen(epilogue) + strlen(js) + 1; - char *eval = (char *)malloc(n); - snprintf(eval, n, "%s%s%s", prologue, js, epilogue); - wchar_t *buf = webview_to_utf16(eval); - if (buf == NULL) { - return -1; - } - arg.bstrVal = SysAllocString(buf); - if (scriptDispatch->lpVtbl->Invoke( - scriptDispatch, dispid, iid_unref(&IID_NULL), 0, DISPATCH_METHOD, - ¶ms, &result, &excepInfo, &nArgErr) != S_OK) { - return -1; - } - SysFreeString(arg.bstrVal); - free(eval); - scriptDispatch->lpVtbl->Release(scriptDispatch); - htmlDoc2->lpVtbl->Release(htmlDoc2); - docDispatch->lpVtbl->Release(docDispatch); - return 0; -} - -WEBVIEW_API void webview_dispatch(struct webview *w, webview_dispatch_fn fn, - void *arg) { - PostMessageW(w->priv.hwnd, WM_WEBVIEW_DISPATCH, (WPARAM)fn, (LPARAM)arg); -} - -WEBVIEW_API void webview_set_title(struct webview *w, const char *title) { - SetWindowText(w->priv.hwnd, title); -} - -WEBVIEW_API void webview_set_fullscreen(struct webview *w, int fullscreen) { - if (w->priv.is_fullscreen == !!fullscreen) { - return; - } - if (w->priv.is_fullscreen == 0) { - w->priv.saved_style = GetWindowLong(w->priv.hwnd, GWL_STYLE); - w->priv.saved_ex_style = GetWindowLong(w->priv.hwnd, GWL_EXSTYLE); - GetWindowRect(w->priv.hwnd, &w->priv.saved_rect); - } - w->priv.is_fullscreen = !!fullscreen; - if (fullscreen) { - MONITORINFO monitor_info; - SetWindowLong(w->priv.hwnd, GWL_STYLE, - w->priv.saved_style & ~(WS_CAPTION | WS_THICKFRAME)); - SetWindowLong(w->priv.hwnd, GWL_EXSTYLE, - w->priv.saved_ex_style & - ~(WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE | - WS_EX_CLIENTEDGE | WS_EX_STATICEDGE)); - monitor_info.cbSize = sizeof(monitor_info); - GetMonitorInfo(MonitorFromWindow(w->priv.hwnd, MONITOR_DEFAULTTONEAREST), - &monitor_info); - RECT r; - r.left = monitor_info.rcMonitor.left; - r.top = monitor_info.rcMonitor.top; - r.right = monitor_info.rcMonitor.right; - r.bottom = monitor_info.rcMonitor.bottom; - SetWindowPos(w->priv.hwnd, NULL, r.left, r.top, r.right - r.left, - r.bottom - r.top, - SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED); - } else { - SetWindowLong(w->priv.hwnd, GWL_STYLE, w->priv.saved_style); - SetWindowLong(w->priv.hwnd, GWL_EXSTYLE, w->priv.saved_ex_style); - SetWindowPos(w->priv.hwnd, NULL, w->priv.saved_rect.left, - w->priv.saved_rect.top, - w->priv.saved_rect.right - w->priv.saved_rect.left, - w->priv.saved_rect.bottom - w->priv.saved_rect.top, - SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED); - } -} - -WEBVIEW_API void webview_set_color(struct webview *w, uint8_t r, uint8_t g, - uint8_t b, uint8_t a) { - HBRUSH brush = CreateSolidBrush(RGB(r, g, b)); - SetClassLongPtr(w->priv.hwnd, GCLP_HBRBACKGROUND, (LONG_PTR)brush); -} - -/* These are missing parts from MinGW */ -#ifndef __IFileDialog_INTERFACE_DEFINED__ -#define __IFileDialog_INTERFACE_DEFINED__ -enum _FILEOPENDIALOGOPTIONS { - FOS_OVERWRITEPROMPT = 0x2, - FOS_STRICTFILETYPES = 0x4, - FOS_NOCHANGEDIR = 0x8, - FOS_PICKFOLDERS = 0x20, - FOS_FORCEFILESYSTEM = 0x40, - FOS_ALLNONSTORAGEITEMS = 0x80, - FOS_NOVALIDATE = 0x100, - FOS_ALLOWMULTISELECT = 0x200, - FOS_PATHMUSTEXIST = 0x800, - FOS_FILEMUSTEXIST = 0x1000, - FOS_CREATEPROMPT = 0x2000, - FOS_SHAREAWARE = 0x4000, - FOS_NOREADONLYRETURN = 0x8000, - FOS_NOTESTFILECREATE = 0x10000, - FOS_HIDEMRUPLACES = 0x20000, - FOS_HIDEPINNEDPLACES = 0x40000, - FOS_NODEREFERENCELINKS = 0x100000, - FOS_DONTADDTORECENT = 0x2000000, - FOS_FORCESHOWHIDDEN = 0x10000000, - FOS_DEFAULTNOMINIMODE = 0x20000000, - FOS_FORCEPREVIEWPANEON = 0x40000000 -}; -typedef DWORD FILEOPENDIALOGOPTIONS; -typedef enum FDAP { FDAP_BOTTOM = 0, FDAP_TOP = 1 } FDAP; -DEFINE_GUID(IID_IFileDialog, 0x42f85136, 0xdb7e, 0x439c, 0x85, 0xf1, 0xe4, 0x07, - 0x5d, 0x13, 0x5f, 0xc8); -typedef struct IFileDialogVtbl { - BEGIN_INTERFACE - HRESULT(STDMETHODCALLTYPE *QueryInterface) - (IFileDialog *This, REFIID riid, void **ppvObject); - ULONG(STDMETHODCALLTYPE *AddRef)(IFileDialog *This); - ULONG(STDMETHODCALLTYPE *Release)(IFileDialog *This); - HRESULT(STDMETHODCALLTYPE *Show)(IFileDialog *This, HWND hwndOwner); - HRESULT(STDMETHODCALLTYPE *SetFileTypes) - (IFileDialog *This, UINT cFileTypes, const COMDLG_FILTERSPEC *rgFilterSpec); - HRESULT(STDMETHODCALLTYPE *SetFileTypeIndex) - (IFileDialog *This, UINT iFileType); - HRESULT(STDMETHODCALLTYPE *GetFileTypeIndex) - (IFileDialog *This, UINT *piFileType); - HRESULT(STDMETHODCALLTYPE *Advise) - (IFileDialog *This, IFileDialogEvents *pfde, DWORD *pdwCookie); - HRESULT(STDMETHODCALLTYPE *Unadvise)(IFileDialog *This, DWORD dwCookie); - HRESULT(STDMETHODCALLTYPE *SetOptions) - (IFileDialog *This, FILEOPENDIALOGOPTIONS fos); - HRESULT(STDMETHODCALLTYPE *GetOptions) - (IFileDialog *This, FILEOPENDIALOGOPTIONS *pfos); - HRESULT(STDMETHODCALLTYPE *SetDefaultFolder) - (IFileDialog *This, IShellItem *psi); - HRESULT(STDMETHODCALLTYPE *SetFolder)(IFileDialog *This, IShellItem *psi); - HRESULT(STDMETHODCALLTYPE *GetFolder)(IFileDialog *This, IShellItem **ppsi); - HRESULT(STDMETHODCALLTYPE *GetCurrentSelection) - (IFileDialog *This, IShellItem **ppsi); - HRESULT(STDMETHODCALLTYPE *SetFileName)(IFileDialog *This, LPCWSTR pszName); - HRESULT(STDMETHODCALLTYPE *GetFileName)(IFileDialog *This, LPWSTR *pszName); - HRESULT(STDMETHODCALLTYPE *SetTitle)(IFileDialog *This, LPCWSTR pszTitle); - HRESULT(STDMETHODCALLTYPE *SetOkButtonLabel) - (IFileDialog *This, LPCWSTR pszText); - HRESULT(STDMETHODCALLTYPE *SetFileNameLabel) - (IFileDialog *This, LPCWSTR pszLabel); - HRESULT(STDMETHODCALLTYPE *GetResult)(IFileDialog *This, IShellItem **ppsi); - HRESULT(STDMETHODCALLTYPE *AddPlace) - (IFileDialog *This, IShellItem *psi, FDAP fdap); - HRESULT(STDMETHODCALLTYPE *SetDefaultExtension) - (IFileDialog *This, LPCWSTR pszDefaultExtension); - HRESULT(STDMETHODCALLTYPE *Close)(IFileDialog *This, HRESULT hr); - HRESULT(STDMETHODCALLTYPE *SetClientGuid)(IFileDialog *This, REFGUID guid); - HRESULT(STDMETHODCALLTYPE *ClearClientData)(IFileDialog *This); - HRESULT(STDMETHODCALLTYPE *SetFilter) - (IFileDialog *This, IShellItemFilter *pFilter); - END_INTERFACE -} IFileDialogVtbl; -interface IFileDialog { - CONST_VTBL IFileDialogVtbl *lpVtbl; -}; -DEFINE_GUID(IID_IFileOpenDialog, 0xd57c7288, 0xd4ad, 0x4768, 0xbe, 0x02, 0x9d, - 0x96, 0x95, 0x32, 0xd9, 0x60); -DEFINE_GUID(IID_IFileSaveDialog, 0x84bccd23, 0x5fde, 0x4cdb, 0xae, 0xa4, 0xaf, - 0x64, 0xb8, 0x3d, 0x78, 0xab); -#endif - -WEBVIEW_API void webview_dialog(struct webview *w, - enum webview_dialog_type dlgtype, int flags, - const char *title, const char *arg, - char *result, size_t resultsz) { - if (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN || - dlgtype == WEBVIEW_DIALOG_TYPE_SAVE) { - IFileDialog *dlg = NULL; - IShellItem *res = NULL; - WCHAR *ws = NULL; - char *s = NULL; - FILEOPENDIALOGOPTIONS opts, add_opts; - if (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN) { - if (CoCreateInstance( - iid_unref(&CLSID_FileOpenDialog), NULL, CLSCTX_INPROC_SERVER, - iid_unref(&IID_IFileOpenDialog), (void **)&dlg) != S_OK) { - goto error_dlg; - } - if (flags & WEBVIEW_DIALOG_FLAG_DIRECTORY) { - add_opts |= FOS_PICKFOLDERS; - } - add_opts |= FOS_NOCHANGEDIR | FOS_ALLNONSTORAGEITEMS | FOS_NOVALIDATE | - FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST | FOS_SHAREAWARE | - FOS_NOTESTFILECREATE | FOS_NODEREFERENCELINKS | - FOS_FORCESHOWHIDDEN | FOS_DEFAULTNOMINIMODE; - } else { - if (CoCreateInstance( - iid_unref(&CLSID_FileSaveDialog), NULL, CLSCTX_INPROC_SERVER, - iid_unref(&IID_IFileSaveDialog), (void **)&dlg) != S_OK) { - goto error_dlg; - } - add_opts |= FOS_OVERWRITEPROMPT | FOS_NOCHANGEDIR | - FOS_ALLNONSTORAGEITEMS | FOS_NOVALIDATE | FOS_SHAREAWARE | - FOS_NOTESTFILECREATE | FOS_NODEREFERENCELINKS | - FOS_FORCESHOWHIDDEN | FOS_DEFAULTNOMINIMODE; - } - if (dlg->lpVtbl->GetOptions(dlg, &opts) != S_OK) { - goto error_dlg; - } - opts &= ~FOS_NOREADONLYRETURN; - opts |= add_opts; - if (dlg->lpVtbl->SetOptions(dlg, opts) != S_OK) { - goto error_dlg; - } - if (dlg->lpVtbl->Show(dlg, w->priv.hwnd) != S_OK) { - goto error_dlg; - } - if (dlg->lpVtbl->GetResult(dlg, &res) != S_OK) { - goto error_dlg; - } - if (res->lpVtbl->GetDisplayName(res, SIGDN_FILESYSPATH, &ws) != S_OK) { - goto error_result; - } - s = webview_from_utf16(ws); - strncpy(result, s, resultsz); - result[resultsz - 1] = '\0'; - CoTaskMemFree(ws); - error_result: - res->lpVtbl->Release(res); - error_dlg: - dlg->lpVtbl->Release(dlg); - return; - } else if (dlgtype == WEBVIEW_DIALOG_TYPE_ALERT) { -#if 0 - /* MinGW often doesn't contain TaskDialog, we'll use MessageBox for now */ - WCHAR *wtitle = webview_to_utf16(title); - WCHAR *warg = webview_to_utf16(arg); - TaskDialog(w->priv.hwnd, NULL, NULL, wtitle, warg, 0, NULL, NULL); - GlobalFree(warg); - GlobalFree(wtitle); -#else - UINT type = MB_OK; - switch (flags & WEBVIEW_DIALOG_FLAG_ALERT_MASK) { - case WEBVIEW_DIALOG_FLAG_INFO: - type |= MB_ICONINFORMATION; - break; - case WEBVIEW_DIALOG_FLAG_WARNING: - type |= MB_ICONWARNING; - break; - case WEBVIEW_DIALOG_FLAG_ERROR: - type |= MB_ICONERROR; - break; - } - MessageBox(w->priv.hwnd, arg, title, type); -#endif - } -} - -WEBVIEW_API void webview_terminate(struct webview *w) { PostQuitMessage(0); } -WEBVIEW_API void webview_exit(struct webview *w) { OleUninitialize(); } -WEBVIEW_API void webview_print_log(const char *s) { OutputDebugString(s); } - -#endif /* WEBVIEW_WINAPI */ - -#if defined(WEBVIEW_COCOA) -#if (!defined MAC_OS_X_VERSION_10_12) || \ - MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12 -#define NSAlertStyleWarning NSWarningAlertStyle -#define NSAlertStyleCritical NSCriticalAlertStyle -#define NSWindowStyleMaskResizable NSResizableWindowMask -#define NSWindowStyleMaskMiniaturizable NSMiniaturizableWindowMask -#define NSWindowStyleMaskTitled NSTitledWindowMask -#define NSWindowStyleMaskClosable NSClosableWindowMask -#define NSWindowStyleMaskFullScreen NSFullScreenWindowMask -#define NSEventMaskAny NSAnyEventMask -#define NSEventModifierFlagCommand NSCommandKeyMask -#define NSEventModifierFlagOption NSAlternateKeyMask -#define NSAlertStyleInformational NSInformationalAlertStyle -#endif /* MAC_OS_X_VERSION_10_12 */ -#if (!defined MAC_OS_X_VERSION_10_13) || \ - MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_13 -#define NSModalResponseOK NSFileHandlingPanelOKButton -#endif /* MAC_OS_X_VERSION_10_12, MAC_OS_X_VERSION_10_13 */ -static void webview_window_will_close(id self, SEL cmd, id notification) { - struct webview *w = - (struct webview *)objc_getAssociatedObject(self, "webview"); - webview_terminate(w); -} - -static BOOL webview_is_selector_excluded_from_web_script(id self, SEL cmd, - SEL selector) { - return selector != @selector(invoke:); -} - -static NSString *webview_webscript_name_for_selector(id self, SEL cmd, - SEL selector) { - return selector == @selector(invoke:) ? @"invoke" : nil; -} - -static void webview_did_clear_window_object(id self, SEL cmd, id webview, - id script, id frame) { - [script setValue:self forKey:@"external"]; -} - -static void webview_external_invoke(id self, SEL cmd, id arg) { - struct webview *w = - (struct webview *)objc_getAssociatedObject(self, "webview"); - if (w == NULL || w->external_invoke_cb == NULL) { - return; - } - if ([arg isKindOfClass:[NSString class]] == NO) { - return; - } - w->external_invoke_cb(w, [(NSString *)(arg)UTF8String]); -} - -WEBVIEW_API int webview_init(struct webview *w) { - w->priv.pool = [[NSAutoreleasePool alloc] init]; - [NSApplication sharedApplication]; - - Class webViewDelegateClass = - objc_allocateClassPair([NSObject class], "WebViewDelegate", 0); - class_addMethod(webViewDelegateClass, sel_registerName("windowWillClose:"), - (IMP)webview_window_will_close, "v@:@"); - class_addMethod(object_getClass(webViewDelegateClass), - sel_registerName("isSelectorExcludedFromWebScript:"), - (IMP)webview_is_selector_excluded_from_web_script, "c@::"); - class_addMethod(object_getClass(webViewDelegateClass), - sel_registerName("webScriptNameForSelector:"), - (IMP)webview_webscript_name_for_selector, "c@::"); - class_addMethod(webViewDelegateClass, - sel_registerName("webView:didClearWindowObject:forFrame:"), - (IMP)webview_did_clear_window_object, "v@:@@@"); - class_addMethod(webViewDelegateClass, sel_registerName("invoke:"), - (IMP)webview_external_invoke, "v@:@"); - objc_registerClassPair(webViewDelegateClass); - - w->priv.windowDelegate = [[webViewDelegateClass alloc] init]; - objc_setAssociatedObject(w->priv.windowDelegate, "webview", (id)(w), - OBJC_ASSOCIATION_ASSIGN); - - NSString *nsTitle = [NSString stringWithUTF8String:w->title]; - NSRect r = NSMakeRect(0, 0, w->width, w->height); - NSUInteger style = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | - NSWindowStyleMaskMiniaturizable; - if (w->resizable) { - style = style | NSWindowStyleMaskResizable; - } - w->priv.window = [[NSWindow alloc] initWithContentRect:r - styleMask:style - backing:NSBackingStoreBuffered - defer:NO]; - [w->priv.window autorelease]; - [w->priv.window setTitle:nsTitle]; - [w->priv.window setDelegate:w->priv.windowDelegate]; - [w->priv.window center]; - - [[NSUserDefaults standardUserDefaults] setBool:!!w->debug - forKey:@"WebKitDeveloperExtras"]; - [[NSUserDefaults standardUserDefaults] synchronize]; - w->priv.webview = - [[WebView alloc] initWithFrame:r frameName:@"WebView" groupName:nil]; - NSURL *nsURL = [NSURL - URLWithString:[NSString stringWithUTF8String:webview_check_url(w->url)]]; - [[w->priv.webview mainFrame] loadRequest:[NSURLRequest requestWithURL:nsURL]]; - - [w->priv.webview setAutoresizesSubviews:YES]; - [w->priv.webview - setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; - w->priv.webview.frameLoadDelegate = w->priv.windowDelegate; - [[w->priv.window contentView] addSubview:w->priv.webview]; - [w->priv.window orderFrontRegardless]; - - [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; - [NSApp finishLaunching]; - [NSApp activateIgnoringOtherApps:YES]; - - NSMenu *menubar = [[[NSMenu alloc] initWithTitle:@""] autorelease]; - - NSString *appName = [[NSProcessInfo processInfo] processName]; - NSMenuItem *appMenuItem = - [[[NSMenuItem alloc] initWithTitle:appName action:NULL keyEquivalent:@""] - autorelease]; - NSMenu *appMenu = [[[NSMenu alloc] initWithTitle:appName] autorelease]; - [appMenuItem setSubmenu:appMenu]; - [menubar addItem:appMenuItem]; - - NSString *title = [@"Hide " stringByAppendingString:appName]; - NSMenuItem *item = [[[NSMenuItem alloc] initWithTitle:title - action:@selector(hide:) - keyEquivalent:@"h"] autorelease]; - [appMenu addItem:item]; - item = [[[NSMenuItem alloc] initWithTitle:@"Hide Others" - action:@selector(hideOtherApplications:) - keyEquivalent:@"h"] autorelease]; - [item setKeyEquivalentModifierMask:(NSEventModifierFlagOption | - NSEventModifierFlagCommand)]; - [appMenu addItem:item]; - item = [[[NSMenuItem alloc] initWithTitle:@"Show All" - action:@selector(unhideAllApplications:) - keyEquivalent:@""] autorelease]; - [appMenu addItem:item]; - [appMenu addItem:[NSMenuItem separatorItem]]; - - title = [@"Quit " stringByAppendingString:appName]; - item = [[[NSMenuItem alloc] initWithTitle:title - action:@selector(terminate:) - keyEquivalent:@"q"] autorelease]; - [appMenu addItem:item]; - - [NSApp setMainMenu:menubar]; - - w->priv.should_exit = 0; - return 0; -} - -WEBVIEW_API int webview_loop(struct webview *w, int blocking) { - NSDate *until = (blocking ? [NSDate distantFuture] : [NSDate distantPast]); - NSEvent *event = [NSApp nextEventMatchingMask:NSEventMaskAny - untilDate:until - inMode:NSDefaultRunLoopMode - dequeue:YES]; - if (event) { - [NSApp sendEvent:event]; - } - return w->priv.should_exit; -} - -WEBVIEW_API int webview_eval(struct webview *w, const char *js) { - NSString *nsJS = [NSString stringWithUTF8String:js]; - [[w->priv.webview windowScriptObject] evaluateWebScript:nsJS]; - return 0; -} - -WEBVIEW_API void webview_set_title(struct webview *w, const char *title) { - NSString *nsTitle = [NSString stringWithUTF8String:title]; - [w->priv.window setTitle:nsTitle]; -} - -WEBVIEW_API void webview_set_fullscreen(struct webview *w, int fullscreen) { - int b = ((([w->priv.window styleMask] & NSWindowStyleMaskFullScreen) == - NSWindowStyleMaskFullScreen) - ? 1 - : 0); - if (b != fullscreen) { - [w->priv.window toggleFullScreen:nil]; - } -} - -WEBVIEW_API void webview_set_color(struct webview *w, uint8_t r, uint8_t g, - uint8_t b, uint8_t a) { - [w->priv.window setBackgroundColor:[NSColor colorWithRed:(CGFloat)r / 255.0 - green:(CGFloat)g / 255.0 - blue:(CGFloat)b / 255.0 - alpha:(CGFloat)a / 255.0]]; - if (0.5 >= ((r / 255.0 * 299.0) + (g / 255.0 * 587.0) + (b / 255.0 * 114.0)) / - 1000.0) { - [w->priv.window - setAppearance:[NSAppearance - appearanceNamed:NSAppearanceNameVibrantDark]]; - } else { - [w->priv.window - setAppearance:[NSAppearance - appearanceNamed:NSAppearanceNameVibrantLight]]; - } - [w->priv.window setOpaque:NO]; - [w->priv.window setTitlebarAppearsTransparent:YES]; - [w->priv.webview setDrawsBackground:NO]; -} - -WEBVIEW_API void webview_dialog(struct webview *w, - enum webview_dialog_type dlgtype, int flags, - const char *title, const char *arg, - char *result, size_t resultsz) { - if (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN || - dlgtype == WEBVIEW_DIALOG_TYPE_SAVE) { - NSSavePanel *panel; - if (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN) { - NSOpenPanel *openPanel = [NSOpenPanel openPanel]; - if (flags & WEBVIEW_DIALOG_FLAG_DIRECTORY) { - [openPanel setCanChooseFiles:NO]; - [openPanel setCanChooseDirectories:YES]; - } else { - [openPanel setCanChooseFiles:YES]; - [openPanel setCanChooseDirectories:NO]; - } - [openPanel setResolvesAliases:NO]; - [openPanel setAllowsMultipleSelection:NO]; - panel = openPanel; - } else { - panel = [NSSavePanel savePanel]; - } - [panel setCanCreateDirectories:YES]; - [panel setShowsHiddenFiles:YES]; - [panel setExtensionHidden:NO]; - [panel setCanSelectHiddenExtension:NO]; - [panel setTreatsFilePackagesAsDirectories:YES]; - [panel beginSheetModalForWindow:w->priv.window - completionHandler:^(NSInteger result) { - [NSApp stopModalWithCode:result]; - }]; - if ([NSApp runModalForWindow:panel] == NSModalResponseOK) { - const char *filename = [[[panel URL] path] UTF8String]; - strlcpy(result, filename, resultsz); - } - } else if (dlgtype == WEBVIEW_DIALOG_TYPE_ALERT) { - NSAlert *a = [NSAlert new]; - switch (flags & WEBVIEW_DIALOG_FLAG_ALERT_MASK) { - case WEBVIEW_DIALOG_FLAG_INFO: - [a setAlertStyle:NSAlertStyleInformational]; - break; - case WEBVIEW_DIALOG_FLAG_WARNING: - NSLog(@"warning"); - [a setAlertStyle:NSAlertStyleWarning]; - break; - case WEBVIEW_DIALOG_FLAG_ERROR: - NSLog(@"error"); - [a setAlertStyle:NSAlertStyleCritical]; - break; - } - [a setShowsHelp:NO]; - [a setShowsSuppressionButton:NO]; - [a setMessageText:[NSString stringWithUTF8String:title]]; - [a setInformativeText:[NSString stringWithUTF8String:arg]]; - [a addButtonWithTitle:@"OK"]; - [a runModal]; - [a release]; - } -} - -static void webview_dispatch_cb(void *arg) { - struct webview_dispatch_arg *context = (struct webview_dispatch_arg *)arg; - (context->fn)(context->w, context->arg); - free(context); -} - -WEBVIEW_API void webview_dispatch(struct webview *w, webview_dispatch_fn fn, - void *arg) { - struct webview_dispatch_arg *context = (struct webview_dispatch_arg *)malloc( - sizeof(struct webview_dispatch_arg)); - context->w = w; - context->arg = arg; - context->fn = fn; - dispatch_async_f(dispatch_get_main_queue(), context, webview_dispatch_cb); -} - -WEBVIEW_API void webview_terminate(struct webview *w) { - w->priv.should_exit = 1; -} -WEBVIEW_API void webview_exit(struct webview *w) { [NSApp terminate:NSApp]; } -WEBVIEW_API void webview_print_log(const char *s) { NSLog(@"%s", s); } - -#endif /* WEBVIEW_COCOA */ - -#endif /* WEBVIEW_IMPLEMENTATION */ - -#ifdef __cplusplus -} -#endif - -#endif /* WEBVIEW_H */