webview

Check-in [a8e6226dd4]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Initial working code
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: a8e6226dd4991db5bfa05cd6c1dff326d0b249388b7a6c0c967feec3a9c0d266
User & Date: murphy 2018-08-30 23:27:47
Context
2018-08-31
09:22
Primary module check-in: c46caad7e9 user: murphy tags: trunk
2018-08-30
23:27
Initial working code check-in: a8e6226dd4 user: murphy tags: trunk
09:28
initial empty check-in check-in: b7ac3d9813 user: murphy tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Added LICENSE.txt.

            1  +MIT License
            2  +
            3  +Copyright (c) 2017 Serge Zaitsev
            4  +Copyright (c) 2018 Thomas Chust
            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.

Added build-webview.

            1  +#!/bin/sh -e
            2  +if test "$(uname -s)" = "Darwin"; then
            3  +    "$CHICKEN_CSC" -O2 -d1 -C "$CFLAGS -DWEBVIEW_COCOA -xobjective-c" -L "$LDFLAGS -framework Cocoa" webview.impl.c "$@"
            4  +elif pkg-config --exists webkit2gtk-4.0; then
            5  +    "$CHICKEN_CSC" -O2 -d1 -C "$CFLAGS -DWEBVIEW_GTK=2 $(pkg-config --cflags glib-2.0 gtk+-3.0 webkit2gtk-4.0)" -L "$LDFLAGS $(pkg-config --libs glib-2.0 gtk+-3.0 webkit2gtk-4.0)" webview.impl.c "$@"
            6  +elif pkg-config --exists webkitgtk-3.0; then
            7  +    "$CHICKEN_CSC" -O2 -d1 -C "$CFLAGS -DWEBVIEW_GTK=1 $(pkg-config --cflags glib-2.0 gtk+-3.0 webkitgtk-3.0)" -L "$LDFLAGS $(pkg-config --libs glib-2.0 gtk+-3.0 webkitgtk-3.0)" webview.impl.c "$@"
            8  +fi

Added build-webview.bat.

            1  +@echo off
            2  +%CHICKEN_CSC% -O2 -d1 -C %CFLAGS% -C -DWEBVIEW_WINAPI -L -ladvapi32 -L %LDFLAGS% -L -luser32 -L -lgdi32 -L -lole32 -L -loleaut32 -L -luuid webview.impl.c %*

Added webview.egg.

            1  +((category ui)
            2  + (synopsis "Multi-platform HTML user interface shell")
            3  + (author "Thomas Chust")
            4  + (license "BSD")
            5  + (version "1.0.0")
            6  + (dependencies
            7  +   srfi-1
            8  +   srfi-13
            9  +   srfi-18
           10  +   srfi-69
           11  +   utf8)
           12  + (components
           13  +   (extension webview
           14  +     (custom-build "build-webview")
           15  +     (source-dependencies "webview.h" "webview.impl.c"))))
           16  +
           17  +;; vim: set ai et ts=4 sts=2 sw=2 ft=scheme: ;;

Added webview.h.

            1  +/*
            2  + * MIT License
            3  + *
            4  + * Copyright (c) 2017 Serge Zaitsev
            5  + * Copyright (c) 2018 Thomas Chust
            6  + *
            7  + * Permission is hereby granted, free of charge, to any person obtaining a copy
            8  + * of this software and associated documentation files (the "Software"), to deal
            9  + * in the Software without restriction, including without limitation the rights
           10  + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
           11  + * copies of the Software, and to permit persons to whom the Software is
           12  + * furnished to do so, subject to the following conditions:
           13  + *
           14  + * The above copyright notice and this permission notice shall be included in
           15  + * all copies or substantial portions of the Software.
           16  + *
           17  + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
           18  + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
           19  + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
           20  + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
           21  + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
           22  + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
           23  + * SOFTWARE.
           24  + */
           25  +#ifndef WEBVIEW_H
           26  +#define WEBVIEW_H
           27  +
           28  +#if defined(_WIN32)
           29  +#if defined(WEBVIEW_GTK) || defined(WEBVIEW_WINAPI)
           30  +#define WEBVIEW_API __declspec(dllexport)
           31  +#else
           32  +#define WEBVIEW_API __declspec(dllimport)
           33  +#endif
           34  +#else
           35  +#define WEBVIEW_API extern
           36  +#endif
           37  +
           38  +#include <stddef.h>
           39  +
           40  +typedef struct webview webview_t;
           41  +
           42  +typedef void (*webview_invoke_fn)(webview_t *w, const char *arg);
           43  +
           44  +typedef enum webview_dialog {
           45  +  WEBVIEW_DIALOG_TYPE_OPEN = 0,
           46  +  WEBVIEW_DIALOG_TYPE_SAVE = 1,
           47  +  WEBVIEW_DIALOG_TYPE_ALERT = 2
           48  +} webview_dialog_t;
           49  +
           50  +#define WEBVIEW_DIALOG_FLAG_FILE (0 << 0)
           51  +#define WEBVIEW_DIALOG_FLAG_DIRECTORY (1 << 0)
           52  +
           53  +#define WEBVIEW_DIALOG_FLAG_INFO (1 << 1)
           54  +#define WEBVIEW_DIALOG_FLAG_WARNING (2 << 1)
           55  +#define WEBVIEW_DIALOG_FLAG_ERROR (3 << 1)
           56  +#define WEBVIEW_DIALOG_FLAG_ALERT_MASK (3 << 1)
           57  +
           58  +typedef void (*webview_dispatch_fn)(webview_t *w, void *arg);
           59  +
           60  +WEBVIEW_API webview_t *webview_new(const char *title, const char *url, int width, int height, int resizable, int debug);
           61  +WEBVIEW_API void webview_destroy(webview_t *w);
           62  +
           63  +WEBVIEW_API int webview_loop(webview_t *w, int blocking);
           64  +WEBVIEW_API void webview_dispatch(webview_t *w, webview_dispatch_fn fn, void *arg);
           65  +WEBVIEW_API void webview_terminate(webview_t *w);
           66  +
           67  +WEBVIEW_API void webview_set_title(webview_t *w, const char *title);
           68  +WEBVIEW_API void webview_set_external_invoke_cb(webview_t *w, webview_invoke_fn external_invoke_cb);
           69  +WEBVIEW_API void webview_set_fullscreen(webview_t *w, int fullscreen);
           70  +
           71  +WEBVIEW_API int webview_eval(webview_t *w, const char *js);
           72  +WEBVIEW_API void webview_dialog(webview_t *w,
           73  +                                webview_dialog_t dlgtype, int flags,
           74  +                                const char *title, const char *arg,
           75  +                                char *result, size_t resultsz);
           76  +
           77  +WEBVIEW_API void webview_print_log(const char *s);
           78  +
           79  +#endif /* WEBVIEW_H */

Added webview.impl.c.

            1  +/*
            2  + * MIT License
            3  + *
            4  + * Copyright (c) 2017 Serge Zaitsev
            5  + * Copyright (c) 2018 Thomas Chust
            6  + *
            7  + * Permission is hereby granted, free of charge, to any person obtaining a copy
            8  + * of this software and associated documentation files (the "Software"), to deal
            9  + * in the Software without restriction, including without limitation the rights
           10  + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
           11  + * copies of the Software, and to permit persons to whom the Software is
           12  + * furnished to do so, subject to the following conditions:
           13  + *
           14  + * The above copyright notice and this permission notice shall be included in
           15  + * all copies or substantial portions of the Software.
           16  + *
           17  + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
           18  + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
           19  + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
           20  + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
           21  + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
           22  + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
           23  + * SOFTWARE.
           24  + */
           25  +#include "webview.h"
           26  +
           27  +#include <string.h>
           28  +#include <stdlib.h>
           29  +
           30  +struct webview_conf {
           31  +  const char *title;
           32  +  const char *url;
           33  +  int width;
           34  +  int height;
           35  +  int resizable;
           36  +  int debug;
           37  +};
           38  +
           39  +#if defined(WEBVIEW_GTK)
           40  +#include <glib.h>
           41  +#include <JavaScriptCore/JavaScript.h>
           42  +#include <gtk/gtk.h>
           43  +#if WEBVIEW_GTK >= 2
           44  +/* Link with `pkg-config --libs glib-2.0 gtk+-3.0 webkit2gtk-4.0` */
           45  +#include <webkit2/webkit2.h>
           46  +#else
           47  +/* Link with `pkg-config --libs glib-2.0 gtk+-3.0 webkitgtk-3.0` */
           48  +#include <webkit/webkit.h>
           49  +#endif
           50  +
           51  +struct webview_priv {
           52  +  int ready;
           53  +  GtkWidget *window;
           54  +  GtkWidget *webview;
           55  +  GtkWidget *inspector_window;
           56  +  GtkWidget *inspector_webview;
           57  +  int should_exit;
           58  +};
           59  +#elif defined(WEBVIEW_WINAPI)
           60  +/* Link with -luser32 -lgdi32 -lole32 -loleaut32 -luuid */
           61  +#define CINTERFACE
           62  +#include <windows.h>
           63  +
           64  +#include <commctrl.h>
           65  +#include <exdisp.h>
           66  +#include <mshtmhst.h>
           67  +#include <mshtml.h>
           68  +#include <shobjidl.h>
           69  +
           70  +#include <stdio.h>
           71  +
           72  +struct webview_priv {
           73  +  HWND hwnd;
           74  +  IOleObject **browser;
           75  +  BOOL is_fullscreen;
           76  +  DWORD saved_style;
           77  +  DWORD saved_ex_style;
           78  +  RECT saved_rect;
           79  +};
           80  +#elif defined(WEBVIEW_COCOA)
           81  +#import <Cocoa/Cocoa.h>
           82  +#import <WebKit/WebKit.h>
           83  +#import <objc/runtime.h>
           84  +
           85  +struct webview_priv {
           86  +  NSAutoreleasePool *pool;
           87  +  NSWindow *window;
           88  +  WebView *webview;
           89  +  id windowDelegate;
           90  +  int should_exit;
           91  +};
           92  +#else
           93  +#error "Define one of: WEBVIEW_GTK, WEBVIEW_COCOA or WEBVIEW_WINAPI"
           94  +#endif
           95  +
           96  +struct webview {
           97  +  webview_invoke_fn external_invoke_cb;
           98  +  struct webview_priv priv;
           99  +};
          100  +
          101  +struct webview_dispatch_arg {
          102  +  webview_dispatch_fn fn;
          103  +  webview_t *w;
          104  +  void *arg;
          105  +};
          106  +
          107  +#define DEFAULT_URL                                                     \
          108  +  "data:text/html,%3C%21DOCTYPE%20html%3E%0A%3Chtml%20lang=%22en%22%3E" \
          109  +  "%0A%3Chead%3E%3Cmeta%20charset=%22utf-8%22%3E%3Cmeta%20http-equiv=%" \
          110  +  "22X-UA-Compatible%22%20content=%22IE=edge%22%3E%3C%2Fhead%3E%0A%3Cb" \
          111  +  "ody%3E%3Cdiv%20id=%22app%22%3E%3C%2Fdiv%3E%3Cscript%20type=%22text%" \
          112  +  "2Fjavascript%22%3Ewindow.external.invoke%28%22load%22%29%3C%2Fscrip" \
          113  +  "t%3E%3C%2Fbody%3E%0A%3C%2Fhtml%3E"
          114  +
          115  +static int webview_init(webview_t *w, const struct webview_conf *c);
          116  +static void webview_exit(webview_t *w);
          117  +
          118  +webview_t *webview_new(const char *title, const char *url, int width, int height, int resizable, int debug) {
          119  +  struct webview_conf c = {0};
          120  +  c.title = title;
          121  +  c.url = url;
          122  +  c.width = width;
          123  +  c.height = height;
          124  +  c.resizable = resizable;
          125  +  c.debug = debug;
          126  +
          127  +  webview_t *w = (webview_t *)malloc(sizeof(struct webview));
          128  +  if (w != NULL) {
          129  +    memset(w, 0, sizeof(struct webview));
          130  +    w->external_invoke_cb = NULL;
          131  +    if (webview_init(w, &c) != 0) {
          132  +      free(w);
          133  +      w = NULL;
          134  +    }
          135  +  }
          136  +
          137  +  return w;
          138  +}
          139  +
          140  +void webview_destroy(webview_t *w) {
          141  +  if (w != NULL) {
          142  +    webview_exit(w);
          143  +    free(w);
          144  +  }
          145  +}
          146  +
          147  +void webview_set_external_invoke_cb(webview_t *w, webview_invoke_fn external_invoke_cb) {
          148  +  w->external_invoke_cb = external_invoke_cb;
          149  +}
          150  +
          151  +static const char *webview_check_url(const char *url) {
          152  +  if (url == NULL || *url == 0) {
          153  +    return DEFAULT_URL;
          154  +  }
          155  +  return url;
          156  +}
          157  +
          158  +#if defined(WEBVIEW_GTK)
          159  +#if WEBVIEW_GTK >= 2
          160  +static void external_message_received_cb(WebKitUserContentManager *m,
          161  +                                         WebKitJavascriptResult *r,
          162  +                                         gpointer arg) {
          163  +  (void)m;
          164  +  webview_t *w = (webview_t *)arg;
          165  +  if (w->external_invoke_cb == NULL) {
          166  +    return;
          167  +  }
          168  +  JSGlobalContextRef context = webkit_javascript_result_get_global_context(r);
          169  +  JSValueRef value = webkit_javascript_result_get_value(r);
          170  +  JSStringRef js = JSValueToStringCopy(context, value, NULL);
          171  +  size_t n = JSStringGetMaximumUTF8CStringSize(js);
          172  +  char *s = g_new(char, n);
          173  +  JSStringGetUTF8CString(js, s, n);
          174  +  w->external_invoke_cb(w, s);
          175  +  JSStringRelease(js);
          176  +  g_free(s);
          177  +}
          178  +
          179  +static void webview_load_changed_cb(WebKitWebView *webview,
          180  +                                    WebKitLoadEvent event, gpointer arg) {
          181  +  (void)webview;
          182  +  webview_t *w = (webview_t *)arg;
          183  +  if (event == WEBKIT_LOAD_FINISHED) {
          184  +    w->priv.ready = 1;
          185  +  }
          186  +}
          187  +#else
          188  +static void external_message_dispatch_cb(webview_t *w, gpointer arg) {
          189  +  char *s = (char *)arg;
          190  +  if (w->external_invoke_cb != NULL) {
          191  +    w->external_invoke_cb(w, s);
          192  +  }
          193  +  g_free(s);
          194  +}
          195  +
          196  +static JSValueRef external_message_received_cb(JSContextRef context,
          197  +                                               JSObjectRef function,
          198  +                                               JSObjectRef thisObject,
          199  +                                               size_t nargs, const JSValueRef args[],
          200  +                                               JSValueRef *exception) {
          201  +  webview_t *w = (webview_t *)JSObjectGetPrivate(thisObject);
          202  +  if (w->external_invoke_cb == NULL || nargs == 0) {
          203  +    return JSValueMakeUndefined(context);
          204  +  }
          205  +  JSStringRef js = JSValueToStringCopy(context, args[0], NULL);
          206  +  size_t n = JSStringGetMaximumUTF8CStringSize(js);
          207  +  char *s = g_new(char, n);
          208  +  JSStringGetUTF8CString(js, s, n);
          209  +  JSStringRelease(js);
          210  +  webview_dispatch(w, external_message_dispatch_cb, s);
          211  +  return JSValueMakeUndefined(context);
          212  +}
          213  +
          214  +static const JSStaticFunction external_methods[] = {
          215  +  {
          216  +    .name = "invoke",
          217  +    .callAsFunction = external_message_received_cb,
          218  +    .attributes = kJSPropertyAttributeNone
          219  +  },
          220  +  {0}
          221  +};
          222  +
          223  +static const JSClassDefinition external_class = {
          224  +  .version = 0,
          225  +  .attributes = kJSClassAttributeNone,
          226  +  .className = "external",
          227  +  .staticFunctions = external_methods
          228  +};
          229  +
          230  +static void webview_window_object_cleared(WebKitWebView *webview,
          231  +                                          WebKitWebFrame *frame,
          232  +                                          gpointer _context,
          233  +                                          gpointer _window,
          234  +                                          gpointer arg) {
          235  +  JSContextRef context = (JSContextRef)_context;
          236  +  JSObjectRef window = (JSObjectRef)_window;
          237  +  JSStringRef name = JSStringCreateWithUTF8CString("external");
          238  +  JSClassRef external = JSClassCreate(&external_class);
          239  +  JSObjectSetProperty(context, window, name, JSObjectMake(context, external, arg), kJSPropertyAttributeNone, NULL);
          240  +  JSStringRelease(name);
          241  +  JSClassRelease(external);
          242  +}
          243  +
          244  +static void webview_load_changed_cb(WebKitWebView *webview,
          245  +                                    GParamSpec *spec, gpointer arg) {
          246  +  WebKitLoadStatus event = webkit_web_view_get_load_status(webview);
          247  +  webview_t *w = (webview_t *)arg;
          248  +  if (event == WEBKIT_LOAD_FINISHED) {
          249  +    w->priv.ready = 1;
          250  +  }
          251  +}
          252  +
          253  +static void webview_destroy_inspector_cb(GtkWidget *widget, gpointer arg) {
          254  +  (void)widget;
          255  +  webview_t *w = (webview_t *)arg;
          256  +  w->priv.inspector_window = NULL;
          257  +  w->priv.inspector_webview = NULL;
          258  +}
          259  +
          260  +static WebKitWebView *webview_inspect_cb(WebKitWebInspector *inspector,
          261  +                                         WebKitWebView *webview,
          262  +                                         gpointer arg) {
          263  +  webview_t *w = (webview_t *)arg;
          264  +  if (w->priv.inspector_webview == NULL) {
          265  +    w->priv.inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
          266  +    gtk_window_set_title(GTK_WINDOW(w->priv.inspector_window), "Web Inspector");
          267  +    gtk_window_set_default_size(GTK_WINDOW(w->priv.inspector_window), 800, 600);
          268  +    gtk_window_set_resizable(GTK_WINDOW(w->priv.inspector_window), TRUE);
          269  +    gtk_window_set_position(GTK_WINDOW(w->priv.inspector_window), GTK_WIN_POS_CENTER);
          270  +
          271  +    GtkWidget *scroller = gtk_scrolled_window_new(NULL, NULL);
          272  +    gtk_container_add(GTK_CONTAINER(w->priv.inspector_window), scroller);
          273  +
          274  +    w->priv.inspector_webview = webkit_web_view_new();
          275  +    gtk_container_add(GTK_CONTAINER(scroller), w->priv.inspector_webview);
          276  +
          277  +    g_signal_connect(G_OBJECT(w->priv.inspector_window), "destroy",
          278  +                     G_CALLBACK(webview_destroy_inspector_cb), w);
          279  +  }
          280  +  return WEBKIT_WEB_VIEW(w->priv.inspector_webview);
          281  +}
          282  +
          283  +static gboolean webview_show_inspector_window(WebKitWebInspector *inspector,
          284  +                                              gpointer arg) {
          285  +  webview_t *w = (webview_t *)arg;
          286  +  if (w->priv.inspector_window != NULL) {
          287  +    gtk_widget_show_all(w->priv.inspector_window);
          288  +    return TRUE;
          289  +  }
          290  +  else {
          291  +    return FALSE;
          292  +  }
          293  +}
          294  +
          295  +static gboolean webview_close_inspector_window(WebKitWebInspector *inspector,
          296  +                                               gpointer arg) {
          297  +  webview_t *w = (webview_t *)arg;
          298  +  if (w->priv.inspector_window != NULL) {
          299  +    gtk_widget_hide(w->priv.inspector_window);
          300  +    return TRUE;
          301  +  }
          302  +  else {
          303  +    return FALSE;
          304  +  }
          305  +}
          306  +#endif
          307  +
          308  +static void webview_destroy_cb(GtkWidget *widget, gpointer arg) {
          309  +  (void)widget;
          310  +  webview_t *w = (webview_t *)arg;
          311  +  webview_terminate(w);
          312  +  if (w->priv.inspector_window != NULL) {
          313  +    gtk_widget_destroy(w->priv.inspector_window);
          314  +  }
          315  +}
          316  +
          317  +static gboolean webview_context_menu_cb(WebKitWebView *webview,
          318  +                                        GtkWidget *default_menu,
          319  +                                        WebKitHitTestResult *hit_test_result,
          320  +                                        gboolean triggered_with_keyboard,
          321  +                                        gpointer userdata) {
          322  +  (void)webview;
          323  +  (void)default_menu;
          324  +  (void)hit_test_result;
          325  +  (void)triggered_with_keyboard;
          326  +  (void)userdata;
          327  +  return TRUE;
          328  +}
          329  +
          330  +int webview_init(webview_t *w, const struct webview_conf *c) {
          331  +  if (gtk_init_check(0, NULL) == FALSE) {
          332  +    return -1;
          333  +  }
          334  +
          335  +  w->priv.ready = 0;
          336  +  w->priv.should_exit = 0;
          337  +  w->priv.window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
          338  +  gtk_window_set_title(GTK_WINDOW(w->priv.window), c->title);
          339  +
          340  +  if (c->resizable) {
          341  +    gtk_window_set_default_size(GTK_WINDOW(w->priv.window), c->width, c->height);
          342  +  } else {
          343  +    gtk_widget_set_size_request(w->priv.window, c->width, c->height);
          344  +  }
          345  +  gtk_window_set_resizable(GTK_WINDOW(w->priv.window), !!c->resizable);
          346  +  gtk_window_set_position(GTK_WINDOW(w->priv.window), GTK_WIN_POS_CENTER);
          347  +
          348  +  GtkWidget *scroller = gtk_scrolled_window_new(NULL, NULL);
          349  +  gtk_container_add(GTK_CONTAINER(w->priv.window), scroller);
          350  +
          351  +#if WEBVIEW_GTK >= 2
          352  +  WebKitUserContentManager *m = webkit_user_content_manager_new();
          353  +  webkit_user_content_manager_register_script_message_handler(m, "external");
          354  +  g_signal_connect(G_OBJECT(m), "script-message-received::external",
          355  +                   G_CALLBACK(external_message_received_cb), w);
          356  +
          357  +  w->priv.webview = webkit_web_view_new_with_user_content_manager(m);
          358  +  g_signal_connect(G_OBJECT(w->priv.webview), "load-changed",
          359  +                   G_CALLBACK(webview_load_changed_cb), w);
          360  +#else
          361  +  w->priv.webview = webkit_web_view_new();
          362  +  g_signal_connect(G_OBJECT(w->priv.webview), "window-object-cleared",
          363  +                   G_CALLBACK(webview_window_object_cleared), w);
          364  +  g_signal_connect(G_OBJECT(w->priv.webview), "notify::load-status",
          365  +                   G_CALLBACK(webview_load_changed_cb), w);
          366  +#endif
          367  +  webkit_web_view_load_uri(WEBKIT_WEB_VIEW(w->priv.webview),
          368  +                           webview_check_url(c->url));
          369  +  gtk_container_add(GTK_CONTAINER(scroller), w->priv.webview);
          370  +
          371  +  if (c->debug) {
          372  +#if WEBVIEW_GTK >= 2
          373  +    WebKitSettings *settings =
          374  +        webkit_web_view_get_settings(WEBKIT_WEB_VIEW(w->priv.webview));
          375  +    webkit_settings_set_enable_write_console_messages_to_stdout(settings, TRUE);
          376  +    webkit_settings_set_enable_developer_extras(settings, TRUE);
          377  +#else
          378  +    WebKitWebSettings *settings =
          379  +        webkit_web_view_get_settings(WEBKIT_WEB_VIEW(w->priv.webview));
          380  +    g_object_set(G_OBJECT(settings),
          381  +                 "enable-developer-extras", TRUE,
          382  +                 NULL);
          383  +    WebKitWebInspector *inspector =
          384  +        webkit_web_view_get_inspector(WEBKIT_WEB_VIEW(w->priv.webview));
          385  +    g_signal_connect(G_OBJECT(inspector), "inspect-web-view",
          386  +                     G_CALLBACK(webview_inspect_cb), w);
          387  +    g_signal_connect(G_OBJECT(inspector), "show-window",
          388  +                     G_CALLBACK(webview_show_inspector_window), w);
          389  +    g_signal_connect(G_OBJECT(inspector), "close-window",
          390  +                     G_CALLBACK(webview_close_inspector_window), w);
          391  +#endif
          392  +  } else {
          393  +    g_signal_connect(G_OBJECT(w->priv.webview), "context-menu",
          394  +                     G_CALLBACK(webview_context_menu_cb), w);
          395  +  }
          396  +
          397  +  gtk_widget_show_all(w->priv.window);
          398  +
          399  +#if WEBVIEW_GTK >= 2
          400  +  webkit_web_view_run_javascript(
          401  +      WEBKIT_WEB_VIEW(w->priv.webview),
          402  +      "window.external={invoke:function(x){"
          403  +      "window.webkit.messageHandlers.external.postMessage(x)}}",
          404  +      NULL, NULL, NULL);
          405  +#endif
          406  +
          407  +  g_signal_connect(G_OBJECT(w->priv.window), "destroy",
          408  +                   G_CALLBACK(webview_destroy_cb), w);
          409  +  return 0;
          410  +}
          411  +
          412  +int webview_loop(webview_t *w, int blocking) {
          413  +  gtk_main_iteration_do(blocking);
          414  +  return w->priv.should_exit;
          415  +}
          416  +
          417  +void webview_set_title(webview_t *w, const char *title) {
          418  +  gtk_window_set_title(GTK_WINDOW(w->priv.window), title);
          419  +}
          420  +
          421  +void webview_set_fullscreen(webview_t *w, int fullscreen) {
          422  +  if (fullscreen) {
          423  +    gtk_window_fullscreen(GTK_WINDOW(w->priv.window));
          424  +  } else {
          425  +    gtk_window_unfullscreen(GTK_WINDOW(w->priv.window));
          426  +  }
          427  +}
          428  +
          429  +void webview_dialog(webview_t *w,
          430  +                                webview_dialog_t dlgtype, int flags,
          431  +                                const char *title, const char *arg,
          432  +                                char *result, size_t resultsz) {
          433  +  GtkWidget *dlg;
          434  +  if (result != NULL) {
          435  +    result[0] = '\0';
          436  +  }
          437  +  if (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN ||
          438  +      dlgtype == WEBVIEW_DIALOG_TYPE_SAVE) {
          439  +    dlg = gtk_file_chooser_dialog_new(
          440  +        title, GTK_WINDOW(w->priv.window),
          441  +        (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN
          442  +             ? (flags & WEBVIEW_DIALOG_FLAG_DIRECTORY
          443  +                    ? GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
          444  +                    : GTK_FILE_CHOOSER_ACTION_OPEN)
          445  +             : GTK_FILE_CHOOSER_ACTION_SAVE),
          446  +        "_Cancel", GTK_RESPONSE_CANCEL,
          447  +        (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN ? "_Open" : "_Save"),
          448  +        GTK_RESPONSE_ACCEPT, NULL);
          449  +    gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(dlg), FALSE);
          450  +    gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dlg), FALSE);
          451  +    gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(dlg), TRUE);
          452  +    gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dlg), TRUE);
          453  +    gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER(dlg), TRUE);
          454  +    gint response = gtk_dialog_run(GTK_DIALOG(dlg));
          455  +    if (response == GTK_RESPONSE_ACCEPT) {
          456  +      gchar *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dlg));
          457  +      g_strlcpy(result, filename, resultsz);
          458  +      g_free(filename);
          459  +    }
          460  +    gtk_widget_destroy(dlg);
          461  +  } else if (dlgtype == WEBVIEW_DIALOG_TYPE_ALERT) {
          462  +    GtkMessageType type = GTK_MESSAGE_OTHER;
          463  +    switch (flags & WEBVIEW_DIALOG_FLAG_ALERT_MASK) {
          464  +    case WEBVIEW_DIALOG_FLAG_INFO:
          465  +      type = GTK_MESSAGE_INFO;
          466  +      break;
          467  +    case WEBVIEW_DIALOG_FLAG_WARNING:
          468  +      type = GTK_MESSAGE_WARNING;
          469  +      break;
          470  +    case WEBVIEW_DIALOG_FLAG_ERROR:
          471  +      type = GTK_MESSAGE_ERROR;
          472  +      break;
          473  +    }
          474  +    dlg = gtk_message_dialog_new(GTK_WINDOW(w->priv.window), GTK_DIALOG_MODAL,
          475  +                                 type, GTK_BUTTONS_OK, "%s", title);
          476  +    gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dlg), "%s",
          477  +                                             arg);
          478  +    gtk_dialog_run(GTK_DIALOG(dlg));
          479  +    gtk_widget_destroy(dlg);
          480  +  }
          481  +}
          482  +
          483  +int webview_eval(webview_t *w, const char *js) {
          484  +  while (w->priv.ready == 0) {
          485  +    g_main_context_iteration(0, FALSE);
          486  +  }
          487  +#if WEBVIEW_GTK >= 2
          488  +  webkit_web_view_run_javascript(WEBKIT_WEB_VIEW(w->priv.webview), js, NULL,
          489  +                                 NULL, NULL);
          490  +#else
          491  +  webkit_web_view_execute_script(WEBKIT_WEB_VIEW(w->priv.webview), js);
          492  +#endif
          493  +  return 0;
          494  +}
          495  +
          496  +static gboolean webview_dispatch_wrapper(gpointer userdata) {
          497  +  struct webview_dispatch_arg *arg = (struct webview_dispatch_arg *)userdata;
          498  +  (arg->fn)(arg->w, arg->arg);
          499  +  g_free(arg);
          500  +  return FALSE;
          501  +}
          502  +
          503  +void webview_dispatch(webview_t *w, webview_dispatch_fn fn,
          504  +                                  void *arg) {
          505  +  struct webview_dispatch_arg *context = g_new(struct webview_dispatch_arg, 1);
          506  +  context->w = w;
          507  +  context->arg = arg;
          508  +  context->fn = fn;
          509  +  gdk_threads_add_idle(webview_dispatch_wrapper, context);
          510  +}
          511  +
          512  +void webview_terminate(webview_t *w) {
          513  +  w->priv.should_exit = 1;
          514  +}
          515  +
          516  +void webview_exit(webview_t *w) { (void)w; }
          517  +void webview_print_log(const char *s) { g_message("%s", s); }
          518  +
          519  +#endif /* WEBVIEW_GTK */
          520  +
          521  +#if defined(WEBVIEW_WINAPI)
          522  +
          523  +#define WM_WEBVIEW_DISPATCH (WM_APP + 1)
          524  +
          525  +typedef struct {
          526  +  IOleInPlaceFrame frame;
          527  +  HWND window;
          528  +} _IOleInPlaceFrameEx;
          529  +
          530  +typedef struct {
          531  +  IOleInPlaceSite inplace;
          532  +  _IOleInPlaceFrameEx frame;
          533  +} _IOleInPlaceSiteEx;
          534  +
          535  +typedef struct { IDocHostUIHandler ui; } _IDocHostUIHandlerEx;
          536  +
          537  +typedef struct {
          538  +  IOleClientSite client;
          539  +  _IOleInPlaceSiteEx inplace;
          540  +  _IDocHostUIHandlerEx ui;
          541  +  IDispatch external;
          542  +} _IOleClientSiteEx;
          543  +
          544  +#ifdef __cplusplus
          545  +#define iid_ref(x) &(x)
          546  +#define iid_unref(x) *(x)
          547  +#else
          548  +#define iid_ref(x) (x)
          549  +#define iid_unref(x) (x)
          550  +#endif
          551  +
          552  +static inline WCHAR *webview_to_utf16(const char *s) {
          553  +  DWORD size = MultiByteToWideChar(CP_UTF8, 0, s, -1, 0, 0);
          554  +  WCHAR *ws = (WCHAR *)GlobalAlloc(GMEM_FIXED, sizeof(WCHAR) * size);
          555  +  if (ws == NULL) {
          556  +    return NULL;
          557  +  }
          558  +  MultiByteToWideChar(CP_UTF8, 0, s, -1, ws, size);
          559  +  return ws;
          560  +}
          561  +
          562  +static inline char *webview_from_utf16(WCHAR *ws) {
          563  +  int n = WideCharToMultiByte(CP_UTF8, 0, ws, -1, NULL, 0, NULL, NULL);
          564  +  char *s = (char *)GlobalAlloc(GMEM_FIXED, n);
          565  +  if (s == NULL) {
          566  +    return NULL;
          567  +  }
          568  +  WideCharToMultiByte(CP_UTF8, 0, ws, -1, s, n, NULL, NULL);
          569  +  return s;
          570  +}
          571  +
          572  +static int iid_eq(REFIID a, const IID *b) {
          573  +  return memcmp((const void *)iid_ref(a), (const void *)b, sizeof(GUID)) == 0;
          574  +}
          575  +
          576  +static HRESULT STDMETHODCALLTYPE JS_QueryInterface(IDispatch FAR *This,
          577  +                                                   REFIID riid,
          578  +                                                   LPVOID FAR *ppvObj) {
          579  +  if (iid_eq(riid, &IID_IDispatch)) {
          580  +    *ppvObj = This;
          581  +    return S_OK;
          582  +  }
          583  +  *ppvObj = 0;
          584  +  return E_NOINTERFACE;
          585  +}
          586  +static ULONG STDMETHODCALLTYPE JS_AddRef(IDispatch FAR *This) { return 1; }
          587  +static ULONG STDMETHODCALLTYPE JS_Release(IDispatch FAR *This) { return 1; }
          588  +static HRESULT STDMETHODCALLTYPE JS_GetTypeInfoCount(IDispatch FAR *This,
          589  +                                                     UINT *pctinfo) {
          590  +  return S_OK;
          591  +}
          592  +static HRESULT STDMETHODCALLTYPE JS_GetTypeInfo(IDispatch FAR *This,
          593  +                                                UINT iTInfo, LCID lcid,
          594  +                                                ITypeInfo **ppTInfo) {
          595  +  return S_OK;
          596  +}
          597  +#define WEBVIEW_JS_INVOKE_ID 0x1000
          598  +static HRESULT STDMETHODCALLTYPE JS_GetIDsOfNames(IDispatch FAR *This,
          599  +                                                  REFIID riid,
          600  +                                                  LPOLESTR *rgszNames,
          601  +                                                  UINT cNames, LCID lcid,
          602  +                                                  DISPID *rgDispId) {
          603  +  if (cNames != 1) {
          604  +    return S_FALSE;
          605  +  }
          606  +  if (wcscmp(rgszNames[0], L"invoke") == 0) {
          607  +    rgDispId[0] = WEBVIEW_JS_INVOKE_ID;
          608  +    return S_OK;
          609  +  }
          610  +  return S_FALSE;
          611  +}
          612  +
          613  +static HRESULT STDMETHODCALLTYPE
          614  +JS_Invoke(IDispatch FAR *This, DISPID dispIdMember, REFIID riid, LCID lcid,
          615  +          WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult,
          616  +          EXCEPINFO *pExcepInfo, UINT *puArgErr) {
          617  +  size_t offset = (size_t) & ((_IOleClientSiteEx *)NULL)->external;
          618  +  _IOleClientSiteEx *ex = (_IOleClientSiteEx *)((char *)(This)-offset);
          619  +  webview_t *w = (webview_t *)GetWindowLongPtr(
          620  +      ex->inplace.frame.window, GWLP_USERDATA);
          621  +  if (pDispParams->cArgs == 1 && pDispParams->rgvarg[0].vt == VT_BSTR) {
          622  +    BSTR bstr = pDispParams->rgvarg[0].bstrVal;
          623  +    char *s = webview_from_utf16(bstr);
          624  +    if (s != NULL) {
          625  +      if (dispIdMember == WEBVIEW_JS_INVOKE_ID) {
          626  +        if (w->external_invoke_cb != NULL) {
          627  +          w->external_invoke_cb(w, s);
          628  +        }
          629  +      } else {
          630  +        return S_FALSE;
          631  +      }
          632  +      GlobalFree(s);
          633  +    }
          634  +  }
          635  +  return S_OK;
          636  +}
          637  +
          638  +static IDispatchVtbl ExternalDispatchTable = {
          639  +    JS_QueryInterface, JS_AddRef,        JS_Release, JS_GetTypeInfoCount,
          640  +    JS_GetTypeInfo,    JS_GetIDsOfNames, JS_Invoke};
          641  +
          642  +static ULONG STDMETHODCALLTYPE Site_AddRef(IOleClientSite FAR *This) {
          643  +  return 1;
          644  +}
          645  +static ULONG STDMETHODCALLTYPE Site_Release(IOleClientSite FAR *This) {
          646  +  return 1;
          647  +}
          648  +static HRESULT STDMETHODCALLTYPE Site_SaveObject(IOleClientSite FAR *This) {
          649  +  return E_NOTIMPL;
          650  +}
          651  +static HRESULT STDMETHODCALLTYPE Site_GetMoniker(IOleClientSite FAR *This,
          652  +                                                 DWORD dwAssign,
          653  +                                                 DWORD dwWhichMoniker,
          654  +                                                 IMoniker **ppmk) {
          655  +  return E_NOTIMPL;
          656  +}
          657  +static HRESULT STDMETHODCALLTYPE
          658  +Site_GetContainer(IOleClientSite FAR *This, LPOLECONTAINER FAR *ppContainer) {
          659  +  *ppContainer = 0;
          660  +  return E_NOINTERFACE;
          661  +}
          662  +static HRESULT STDMETHODCALLTYPE Site_ShowObject(IOleClientSite FAR *This) {
          663  +  return NOERROR;
          664  +}
          665  +static HRESULT STDMETHODCALLTYPE Site_OnShowWindow(IOleClientSite FAR *This,
          666  +                                                   BOOL fShow) {
          667  +  return E_NOTIMPL;
          668  +}
          669  +static HRESULT STDMETHODCALLTYPE
          670  +Site_RequestNewObjectLayout(IOleClientSite FAR *This) {
          671  +  return E_NOTIMPL;
          672  +}
          673  +static HRESULT STDMETHODCALLTYPE Site_QueryInterface(IOleClientSite FAR *This,
          674  +                                                     REFIID riid,
          675  +                                                     void **ppvObject) {
          676  +  if (iid_eq(riid, &IID_IUnknown) || iid_eq(riid, &IID_IOleClientSite)) {
          677  +    *ppvObject = &((_IOleClientSiteEx *)This)->client;
          678  +  } else if (iid_eq(riid, &IID_IOleInPlaceSite)) {
          679  +    *ppvObject = &((_IOleClientSiteEx *)This)->inplace;
          680  +  } else if (iid_eq(riid, &IID_IDocHostUIHandler)) {
          681  +    *ppvObject = &((_IOleClientSiteEx *)This)->ui;
          682  +  } else {
          683  +    *ppvObject = 0;
          684  +    return (E_NOINTERFACE);
          685  +  }
          686  +  return S_OK;
          687  +}
          688  +static HRESULT STDMETHODCALLTYPE InPlace_QueryInterface(
          689  +    IOleInPlaceSite FAR *This, REFIID riid, LPVOID FAR *ppvObj) {
          690  +  return (Site_QueryInterface(
          691  +      (IOleClientSite *)((char *)This - sizeof(IOleClientSite)), riid, ppvObj));
          692  +}
          693  +static ULONG STDMETHODCALLTYPE InPlace_AddRef(IOleInPlaceSite FAR *This) {
          694  +  return 1;
          695  +}
          696  +static ULONG STDMETHODCALLTYPE InPlace_Release(IOleInPlaceSite FAR *This) {
          697  +  return 1;
          698  +}
          699  +static HRESULT STDMETHODCALLTYPE InPlace_GetWindow(IOleInPlaceSite FAR *This,
          700  +                                                   HWND FAR *lphwnd) {
          701  +  *lphwnd = ((_IOleInPlaceSiteEx FAR *)This)->frame.window;
          702  +  return S_OK;
          703  +}
          704  +static HRESULT STDMETHODCALLTYPE
          705  +InPlace_ContextSensitiveHelp(IOleInPlaceSite FAR *This, BOOL fEnterMode) {
          706  +  return E_NOTIMPL;
          707  +}
          708  +static HRESULT STDMETHODCALLTYPE
          709  +InPlace_CanInPlaceActivate(IOleInPlaceSite FAR *This) {
          710  +  return S_OK;
          711  +}
          712  +static HRESULT STDMETHODCALLTYPE
          713  +InPlace_OnInPlaceActivate(IOleInPlaceSite FAR *This) {
          714  +  return S_OK;
          715  +}
          716  +static HRESULT STDMETHODCALLTYPE
          717  +InPlace_OnUIActivate(IOleInPlaceSite FAR *This) {
          718  +  return S_OK;
          719  +}
          720  +static HRESULT STDMETHODCALLTYPE InPlace_GetWindowContext(
          721  +    IOleInPlaceSite FAR *This, LPOLEINPLACEFRAME FAR *lplpFrame,
          722  +    LPOLEINPLACEUIWINDOW FAR *lplpDoc, LPRECT lprcPosRect, LPRECT lprcClipRect,
          723  +    LPOLEINPLACEFRAMEINFO lpFrameInfo) {
          724  +  *lplpFrame = (LPOLEINPLACEFRAME) & ((_IOleInPlaceSiteEx *)This)->frame;
          725  +  *lplpDoc = 0;
          726  +  lpFrameInfo->fMDIApp = FALSE;
          727  +  lpFrameInfo->hwndFrame = ((_IOleInPlaceFrameEx *)*lplpFrame)->window;
          728  +  lpFrameInfo->haccel = 0;
          729  +  lpFrameInfo->cAccelEntries = 0;
          730  +  return S_OK;
          731  +}
          732  +static HRESULT STDMETHODCALLTYPE InPlace_Scroll(IOleInPlaceSite FAR *This,
          733  +                                                SIZE scrollExtent) {
          734  +  return E_NOTIMPL;
          735  +}
          736  +static HRESULT STDMETHODCALLTYPE
          737  +InPlace_OnUIDeactivate(IOleInPlaceSite FAR *This, BOOL fUndoable) {
          738  +  return S_OK;
          739  +}
          740  +static HRESULT STDMETHODCALLTYPE
          741  +InPlace_OnInPlaceDeactivate(IOleInPlaceSite FAR *This) {
          742  +  return S_OK;
          743  +}
          744  +static HRESULT STDMETHODCALLTYPE
          745  +InPlace_DiscardUndoState(IOleInPlaceSite FAR *This) {
          746  +  return E_NOTIMPL;
          747  +}
          748  +static HRESULT STDMETHODCALLTYPE
          749  +InPlace_DeactivateAndUndo(IOleInPlaceSite FAR *This) {
          750  +  return E_NOTIMPL;
          751  +}
          752  +static HRESULT STDMETHODCALLTYPE
          753  +InPlace_OnPosRectChange(IOleInPlaceSite FAR *This, LPCRECT lprcPosRect) {
          754  +  IOleObject *browserObject;
          755  +  IOleInPlaceObject *inplace;
          756  +  browserObject = *((IOleObject **)((char *)This - sizeof(IOleObject *) -
          757  +                                    sizeof(IOleClientSite)));
          758  +  if (!browserObject->lpVtbl->QueryInterface(browserObject,
          759  +                                             iid_unref(&IID_IOleInPlaceObject),
          760  +                                             (void **)&inplace)) {
          761  +    inplace->lpVtbl->SetObjectRects(inplace, lprcPosRect, lprcPosRect);
          762  +    inplace->lpVtbl->Release(inplace);
          763  +  }
          764  +  return S_OK;
          765  +}
          766  +static HRESULT STDMETHODCALLTYPE Frame_QueryInterface(
          767  +    IOleInPlaceFrame FAR *This, REFIID riid, LPVOID FAR *ppvObj) {
          768  +  return E_NOTIMPL;
          769  +}
          770  +static ULONG STDMETHODCALLTYPE Frame_AddRef(IOleInPlaceFrame FAR *This) {
          771  +  return 1;
          772  +}
          773  +static ULONG STDMETHODCALLTYPE Frame_Release(IOleInPlaceFrame FAR *This) {
          774  +  return 1;
          775  +}
          776  +static HRESULT STDMETHODCALLTYPE Frame_GetWindow(IOleInPlaceFrame FAR *This,
          777  +                                                 HWND FAR *lphwnd) {
          778  +  *lphwnd = ((_IOleInPlaceFrameEx *)This)->window;
          779  +  return S_OK;
          780  +}
          781  +static HRESULT STDMETHODCALLTYPE
          782  +Frame_ContextSensitiveHelp(IOleInPlaceFrame FAR *This, BOOL fEnterMode) {
          783  +  return E_NOTIMPL;
          784  +}
          785  +static HRESULT STDMETHODCALLTYPE Frame_GetBorder(IOleInPlaceFrame FAR *This,
          786  +                                                 LPRECT lprectBorder) {
          787  +  return E_NOTIMPL;
          788  +}
          789  +static HRESULT STDMETHODCALLTYPE Frame_RequestBorderSpace(
          790  +    IOleInPlaceFrame FAR *This, LPCBORDERWIDTHS pborderwidths) {
          791  +  return E_NOTIMPL;
          792  +}
          793  +static HRESULT STDMETHODCALLTYPE Frame_SetBorderSpace(
          794  +    IOleInPlaceFrame FAR *This, LPCBORDERWIDTHS pborderwidths) {
          795  +  return E_NOTIMPL;
          796  +}
          797  +static HRESULT STDMETHODCALLTYPE Frame_SetActiveObject(
          798  +    IOleInPlaceFrame FAR *This, IOleInPlaceActiveObject *pActiveObject,
          799  +    LPCOLESTR pszObjName) {
          800  +  return S_OK;
          801  +}
          802  +static HRESULT STDMETHODCALLTYPE
          803  +Frame_InsertMenus(IOleInPlaceFrame FAR *This, HMENU hmenuShared,
          804  +                  LPOLEMENUGROUPWIDTHS lpMenuWidths) {
          805  +  return E_NOTIMPL;
          806  +}
          807  +static HRESULT STDMETHODCALLTYPE Frame_SetMenu(IOleInPlaceFrame FAR *This,
          808  +                                               HMENU hmenuShared,
          809  +                                               HOLEMENU holemenu,
          810  +                                               HWND hwndActiveObject) {
          811  +  return S_OK;
          812  +}
          813  +static HRESULT STDMETHODCALLTYPE Frame_RemoveMenus(IOleInPlaceFrame FAR *This,
          814  +                                                   HMENU hmenuShared) {
          815  +  return E_NOTIMPL;
          816  +}
          817  +static HRESULT STDMETHODCALLTYPE Frame_SetStatusText(IOleInPlaceFrame FAR *This,
          818  +                                                     LPCOLESTR pszStatusText) {
          819  +  return S_OK;
          820  +}
          821  +static HRESULT STDMETHODCALLTYPE
          822  +Frame_EnableModeless(IOleInPlaceFrame FAR *This, BOOL fEnable) {
          823  +  return S_OK;
          824  +}
          825  +static HRESULT STDMETHODCALLTYPE
          826  +Frame_TranslateAccelerator(IOleInPlaceFrame FAR *This, LPMSG lpmsg, WORD wID) {
          827  +  return E_NOTIMPL;
          828  +}
          829  +static HRESULT STDMETHODCALLTYPE UI_QueryInterface(IDocHostUIHandler FAR *This,
          830  +                                                   REFIID riid,
          831  +                                                   LPVOID FAR *ppvObj) {
          832  +  return (Site_QueryInterface((IOleClientSite *)((char *)This -
          833  +                                                 sizeof(IOleClientSite) -
          834  +                                                 sizeof(_IOleInPlaceSiteEx)),
          835  +                              riid, ppvObj));
          836  +}
          837  +static ULONG STDMETHODCALLTYPE UI_AddRef(IDocHostUIHandler FAR *This) {
          838  +  return 1;
          839  +}
          840  +static ULONG STDMETHODCALLTYPE UI_Release(IDocHostUIHandler FAR *This) {
          841  +  return 1;
          842  +}
          843  +static HRESULT STDMETHODCALLTYPE UI_ShowContextMenu(
          844  +    IDocHostUIHandler FAR *This, DWORD dwID, POINT __RPC_FAR *ppt,
          845  +    IUnknown __RPC_FAR *pcmdtReserved, IDispatch __RPC_FAR *pdispReserved) {
          846  +  return S_OK;
          847  +}
          848  +static HRESULT STDMETHODCALLTYPE
          849  +UI_GetHostInfo(IDocHostUIHandler FAR *This, DOCHOSTUIINFO __RPC_FAR *pInfo) {
          850  +  pInfo->cbSize = sizeof(DOCHOSTUIINFO);
          851  +  pInfo->dwFlags = DOCHOSTUIFLAG_NO3DBORDER;
          852  +  pInfo->dwDoubleClick = DOCHOSTUIDBLCLK_DEFAULT;
          853  +  return S_OK;
          854  +}
          855  +static HRESULT STDMETHODCALLTYPE UI_ShowUI(
          856  +    IDocHostUIHandler FAR *This, DWORD dwID,
          857  +    IOleInPlaceActiveObject __RPC_FAR *pActiveObject,
          858  +    IOleCommandTarget __RPC_FAR *pCommandTarget,
          859  +    IOleInPlaceFrame __RPC_FAR *pFrame, IOleInPlaceUIWindow __RPC_FAR *pDoc) {
          860  +  return S_OK;
          861  +}
          862  +static HRESULT STDMETHODCALLTYPE UI_HideUI(IDocHostUIHandler FAR *This) {
          863  +  return S_OK;
          864  +}
          865  +static HRESULT STDMETHODCALLTYPE UI_UpdateUI(IDocHostUIHandler FAR *This) {
          866  +  return S_OK;
          867  +}
          868  +static HRESULT STDMETHODCALLTYPE UI_EnableModeless(IDocHostUIHandler FAR *This,
          869  +                                                   BOOL fEnable) {
          870  +  return S_OK;
          871  +}
          872  +static HRESULT STDMETHODCALLTYPE
          873  +UI_OnDocWindowActivate(IDocHostUIHandler FAR *This, BOOL fActivate) {
          874  +  return S_OK;
          875  +}
          876  +static HRESULT STDMETHODCALLTYPE
          877  +UI_OnFrameWindowActivate(IDocHostUIHandler FAR *This, BOOL fActivate) {
          878  +  return S_OK;
          879  +}
          880  +static HRESULT STDMETHODCALLTYPE
          881  +UI_ResizeBorder(IDocHostUIHandler FAR *This, LPCRECT prcBorder,
          882  +                IOleInPlaceUIWindow __RPC_FAR *pUIWindow, BOOL fRameWindow) {
          883  +  return S_OK;
          884  +}
          885  +static HRESULT STDMETHODCALLTYPE
          886  +UI_TranslateAccelerator(IDocHostUIHandler FAR *This, LPMSG lpMsg,
          887  +                        const GUID __RPC_FAR *pguidCmdGroup, DWORD nCmdID) {
          888  +  return S_FALSE;
          889  +}
          890  +static HRESULT STDMETHODCALLTYPE UI_GetOptionKeyPath(
          891  +    IDocHostUIHandler FAR *This, LPOLESTR __RPC_FAR *pchKey, DWORD dw) {
          892  +  return S_FALSE;
          893  +}
          894  +static HRESULT STDMETHODCALLTYPE UI_GetDropTarget(
          895  +    IDocHostUIHandler FAR *This, IDropTarget __RPC_FAR *pDropTarget,
          896  +    IDropTarget __RPC_FAR *__RPC_FAR *ppDropTarget) {
          897  +  return S_FALSE;
          898  +}
          899  +static HRESULT STDMETHODCALLTYPE UI_GetExternal(
          900  +    IDocHostUIHandler FAR *This, IDispatch __RPC_FAR *__RPC_FAR *ppDispatch) {
          901  +  *ppDispatch = (IDispatch *)(This + 1);
          902  +  return S_OK;
          903  +}
          904  +static HRESULT STDMETHODCALLTYPE UI_TranslateUrl(
          905  +    IDocHostUIHandler FAR *This, DWORD dwTranslate, OLECHAR __RPC_FAR *pchURLIn,
          906  +    OLECHAR __RPC_FAR *__RPC_FAR *ppchURLOut) {
          907  +  *ppchURLOut = 0;
          908  +  return S_FALSE;
          909  +}
          910  +static HRESULT STDMETHODCALLTYPE
          911  +UI_FilterDataObject(IDocHostUIHandler FAR *This, IDataObject __RPC_FAR *pDO,
          912  +                    IDataObject __RPC_FAR *__RPC_FAR *ppDORet) {
          913  +  *ppDORet = 0;
          914  +  return S_FALSE;
          915  +}
          916  +
          917  +static const TCHAR *classname = "WebView";
          918  +static const SAFEARRAYBOUND ArrayBound = {1, 0};
          919  +
          920  +static IOleClientSiteVtbl MyIOleClientSiteTable = {
          921  +    Site_QueryInterface, Site_AddRef,       Site_Release,
          922  +    Site_SaveObject,     Site_GetMoniker,   Site_GetContainer,
          923  +    Site_ShowObject,     Site_OnShowWindow, Site_RequestNewObjectLayout};
          924  +static IOleInPlaceSiteVtbl MyIOleInPlaceSiteTable = {
          925  +    InPlace_QueryInterface,
          926  +    InPlace_AddRef,
          927  +    InPlace_Release,
          928  +    InPlace_GetWindow,
          929  +    InPlace_ContextSensitiveHelp,
          930  +    InPlace_CanInPlaceActivate,
          931  +    InPlace_OnInPlaceActivate,
          932  +    InPlace_OnUIActivate,
          933  +    InPlace_GetWindowContext,
          934  +    InPlace_Scroll,
          935  +    InPlace_OnUIDeactivate,
          936  +    InPlace_OnInPlaceDeactivate,
          937  +    InPlace_DiscardUndoState,
          938  +    InPlace_DeactivateAndUndo,
          939  +    InPlace_OnPosRectChange};
          940  +
          941  +static IOleInPlaceFrameVtbl MyIOleInPlaceFrameTable = {
          942  +    Frame_QueryInterface,
          943  +    Frame_AddRef,
          944  +    Frame_Release,
          945  +    Frame_GetWindow,
          946  +    Frame_ContextSensitiveHelp,
          947  +    Frame_GetBorder,
          948  +    Frame_RequestBorderSpace,
          949  +    Frame_SetBorderSpace,
          950  +    Frame_SetActiveObject,
          951  +    Frame_InsertMenus,
          952  +    Frame_SetMenu,
          953  +    Frame_RemoveMenus,
          954  +    Frame_SetStatusText,
          955  +    Frame_EnableModeless,
          956  +    Frame_TranslateAccelerator};
          957  +
          958  +static IDocHostUIHandlerVtbl MyIDocHostUIHandlerTable = {
          959  +    UI_QueryInterface,
          960  +    UI_AddRef,
          961  +    UI_Release,
          962  +    UI_ShowContextMenu,
          963  +    UI_GetHostInfo,
          964  +    UI_ShowUI,
          965  +    UI_HideUI,
          966  +    UI_UpdateUI,
          967  +    UI_EnableModeless,
          968  +    UI_OnDocWindowActivate,
          969  +    UI_OnFrameWindowActivate,
          970  +    UI_ResizeBorder,
          971  +    UI_TranslateAccelerator,
          972  +    UI_GetOptionKeyPath,
          973  +    UI_GetDropTarget,
          974  +    UI_GetExternal,
          975  +    UI_TranslateUrl,
          976  +    UI_FilterDataObject};
          977  +
          978  +static void UnEmbedBrowserObject(webview_t *w) {
          979  +  if (w->priv.browser != NULL) {
          980  +    (*w->priv.browser)->lpVtbl->Close(*w->priv.browser, OLECLOSE_NOSAVE);
          981  +    (*w->priv.browser)->lpVtbl->Release(*w->priv.browser);
          982  +    GlobalFree(w->priv.browser);
          983  +    w->priv.browser = NULL;
          984  +  }
          985  +}
          986  +
          987  +static int EmbedBrowserObject(webview_t *w) {
          988  +  RECT rect;
          989  +  IWebBrowser2 *webBrowser2 = NULL;
          990  +  LPCLASSFACTORY pClassFactory = NULL;
          991  +  _IOleClientSiteEx *_iOleClientSiteEx = NULL;
          992  +  IOleObject **browser = (IOleObject **)GlobalAlloc(
          993  +      GMEM_FIXED, sizeof(IOleObject *) + sizeof(_IOleClientSiteEx));
          994  +  if (browser == NULL) {
          995  +    goto error;
          996  +  }
          997  +  w->priv.browser = browser;
          998  +
          999  +  _iOleClientSiteEx = (_IOleClientSiteEx *)(browser + 1);
         1000  +  _iOleClientSiteEx->client.lpVtbl = &MyIOleClientSiteTable;
         1001  +  _iOleClientSiteEx->inplace.inplace.lpVtbl = &MyIOleInPlaceSiteTable;
         1002  +  _iOleClientSiteEx->inplace.frame.frame.lpVtbl = &MyIOleInPlaceFrameTable;
         1003  +  _iOleClientSiteEx->inplace.frame.window = w->priv.hwnd;
         1004  +  _iOleClientSiteEx->ui.ui.lpVtbl = &MyIDocHostUIHandlerTable;
         1005  +  _iOleClientSiteEx->external.lpVtbl = &ExternalDispatchTable;
         1006  +
         1007  +  if (CoGetClassObject(iid_unref(&CLSID_WebBrowser),
         1008  +                       CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER, NULL,
         1009  +                       iid_unref(&IID_IClassFactory),
         1010  +                       (void **)&pClassFactory) != S_OK) {
         1011  +    goto error;
         1012  +  }
         1013  +
         1014  +  if (pClassFactory == NULL) {
         1015  +    goto error;
         1016  +  }
         1017  +
         1018  +  if (pClassFactory->lpVtbl->CreateInstance(pClassFactory, 0,
         1019  +                                            iid_unref(&IID_IOleObject),
         1020  +                                            (void **)browser) != S_OK) {
         1021  +    goto error;
         1022  +  }
         1023  +  pClassFactory->lpVtbl->Release(pClassFactory);
         1024  +  if ((*browser)->lpVtbl->SetClientSite(
         1025  +          *browser, (IOleClientSite *)_iOleClientSiteEx) != S_OK) {
         1026  +    goto error;
         1027  +  }
         1028  +  (*browser)->lpVtbl->SetHostNames(*browser, L"My Host Name", 0);
         1029  +
         1030  +  if (OleSetContainedObject((struct IUnknown *)(*browser), TRUE) != S_OK) {
         1031  +    goto error;
         1032  +  }
         1033  +  GetClientRect(w->priv.hwnd, &rect);
         1034  +  if ((*browser)->lpVtbl->DoVerb((*browser), OLEIVERB_SHOW, NULL,
         1035  +                                 (IOleClientSite *)_iOleClientSiteEx, -1,
         1036  +                                 w->priv.hwnd, &rect) != S_OK) {
         1037  +    goto error;
         1038  +  }
         1039  +  if ((*browser)->lpVtbl->QueryInterface((*browser),
         1040  +                                         iid_unref(&IID_IWebBrowser2),
         1041  +                                         (void **)&webBrowser2) != S_OK) {
         1042  +    goto error;
         1043  +  }
         1044  +
         1045  +  webBrowser2->lpVtbl->put_Left(webBrowser2, 0);
         1046  +  webBrowser2->lpVtbl->put_Top(webBrowser2, 0);
         1047  +  webBrowser2->lpVtbl->put_Width(webBrowser2, rect.right);
         1048  +  webBrowser2->lpVtbl->put_Height(webBrowser2, rect.bottom);
         1049  +  webBrowser2->lpVtbl->Release(webBrowser2);
         1050  +
         1051  +  return 0;
         1052  +error:
         1053  +  UnEmbedBrowserObject(w);
         1054  +  if (pClassFactory != NULL) {
         1055  +    pClassFactory->lpVtbl->Release(pClassFactory);
         1056  +  }
         1057  +  if (browser != NULL) {
         1058  +    GlobalFree(browser);
         1059  +  }
         1060  +  return -1;
         1061  +}
         1062  +
         1063  +#define WEBVIEW_DATA_URL_PREFIX "data:text/html,"
         1064  +static int DisplayHTMLPage(webview_t *w, const struct webview_conf *c) {
         1065  +  IWebBrowser2 *webBrowser2;
         1066  +  VARIANT myURL;
         1067  +  LPDISPATCH lpDispatch;
         1068  +  IHTMLDocument2 *htmlDoc2;
         1069  +  BSTR bstr;
         1070  +  IOleObject *browserObject;
         1071  +  SAFEARRAY *sfArray;
         1072  +  VARIANT *pVar;
         1073  +  browserObject = *w->priv.browser;
         1074  +  int isDataURL = 0;
         1075  +  const char *webview_url = webview_check_url(c->url);
         1076  +  if (!browserObject->lpVtbl->QueryInterface(
         1077  +          browserObject, iid_unref(&IID_IWebBrowser2), (void **)&webBrowser2)) {
         1078  +    LPCSTR webPageName;
         1079  +    isDataURL = (strncmp(webview_url, WEBVIEW_DATA_URL_PREFIX,
         1080  +                         strlen(WEBVIEW_DATA_URL_PREFIX)) == 0);
         1081  +    if (isDataURL) {
         1082  +      webPageName = "about:blank";
         1083  +    } else {
         1084  +      webPageName = (LPCSTR)webview_url;
         1085  +    }
         1086  +    VariantInit(&myURL);
         1087  +    myURL.vt = VT_BSTR;
         1088  +#ifndef UNICODE
         1089  +    {
         1090  +      wchar_t *buffer = webview_to_utf16(webPageName);
         1091  +      if (buffer == NULL) {
         1092  +        goto badalloc;
         1093  +      }
         1094  +      myURL.bstrVal = SysAllocString(buffer);
         1095  +      GlobalFree(buffer);
         1096  +    }
         1097  +#else
         1098  +    myURL.bstrVal = SysAllocString(webPageName);
         1099  +#endif
         1100  +    if (!myURL.bstrVal) {
         1101  +    badalloc:
         1102  +      webBrowser2->lpVtbl->Release(webBrowser2);
         1103  +      return (-6);
         1104  +    }
         1105  +    webBrowser2->lpVtbl->Navigate2(webBrowser2, &myURL, 0, 0, 0, 0);
         1106  +    VariantClear(&myURL);
         1107  +    if (!isDataURL) {
         1108  +      return 0;
         1109  +    }
         1110  +
         1111  +    const char *p = webview_url + strlen(WEBVIEW_DATA_URL_PREFIX);
         1112  +    char *url = (char *)calloc(1, strlen(webview_url) + 1);
         1113  +    char *q = url;
         1114  +    do {
         1115  +      if (*p == '%' && *(p + 1) && *(p + 2)) {
         1116  +        unsigned it = 0;
         1117  +        if (sscanf(p + 1, "%02x", &it) == 1) {
         1118  +          p += 3;
         1119  +          *q++ = it;
         1120  +          continue;
         1121  +        }
         1122  +      }
         1123  +      *q++ = *p++;
         1124  +    }
         1125  +    while (*p);
         1126  +
         1127  +    if (webBrowser2->lpVtbl->get_Document(webBrowser2, &lpDispatch) == S_OK) {
         1128  +      if (lpDispatch->lpVtbl->QueryInterface(lpDispatch,
         1129  +                                             iid_unref(&IID_IHTMLDocument2),
         1130  +                                             (void **)&htmlDoc2) == S_OK) {
         1131  +        if ((sfArray = SafeArrayCreate(VT_VARIANT, 1,
         1132  +                                       (SAFEARRAYBOUND *)&ArrayBound))) {
         1133  +          if (!SafeArrayAccessData(sfArray, (void **)&pVar)) {
         1134  +            pVar->vt = VT_BSTR;
         1135  +#ifndef UNICODE
         1136  +            {
         1137  +              wchar_t *buffer = webview_to_utf16(url);
         1138  +              if (buffer == NULL) {
         1139  +                goto release;
         1140  +              }
         1141  +              bstr = SysAllocString(buffer);
         1142  +              GlobalFree(buffer);
         1143  +            }
         1144  +#else
         1145  +            bstr = SysAllocString(string);
         1146  +#endif
         1147  +            if ((pVar->bstrVal = bstr)) {
         1148  +              htmlDoc2->lpVtbl->write(htmlDoc2, sfArray);
         1149  +              htmlDoc2->lpVtbl->close(htmlDoc2);
         1150  +            }
         1151  +          }
         1152  +          SafeArrayDestroy(sfArray);
         1153  +        }
         1154  +      release:
         1155  +        free(url);
         1156  +        htmlDoc2->lpVtbl->Release(htmlDoc2);
         1157  +      }
         1158  +      lpDispatch->lpVtbl->Release(lpDispatch);
         1159  +    }
         1160  +    webBrowser2->lpVtbl->Release(webBrowser2);
         1161  +    return (0);
         1162  +  }
         1163  +  return (-5);
         1164  +}
         1165  +
         1166  +static LRESULT CALLBACK wndproc(HWND hwnd, UINT uMsg, WPARAM wParam,
         1167  +                                LPARAM lParam) {
         1168  +  webview_t *w = (webview_t *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
         1169  +  switch (uMsg) {
         1170  +  case WM_CREATE:
         1171  +    w = (webview_t *)((CREATESTRUCT *)lParam)->lpCreateParams;
         1172  +    w->priv.hwnd = hwnd;
         1173  +    return EmbedBrowserObject(w);
         1174  +  case WM_DESTROY:
         1175  +    UnEmbedBrowserObject(w);
         1176  +    PostQuitMessage(0);
         1177  +    return TRUE;
         1178  +  case WM_SIZE: {
         1179  +    IWebBrowser2 *webBrowser2;
         1180  +    IOleObject *browser = *w->priv.browser;
         1181  +    if (browser->lpVtbl->QueryInterface(browser, iid_unref(&IID_IWebBrowser2),
         1182  +                                        (void **)&webBrowser2) == S_OK) {
         1183  +      RECT rect;
         1184  +      GetClientRect(hwnd, &rect);
         1185  +      webBrowser2->lpVtbl->put_Width(webBrowser2, rect.right);
         1186  +      webBrowser2->lpVtbl->put_Height(webBrowser2, rect.bottom);
         1187  +    }
         1188  +    return TRUE;
         1189  +  }
         1190  +  case WM_WEBVIEW_DISPATCH: {
         1191  +    webview_dispatch_fn f = (webview_dispatch_fn)wParam;
         1192  +    void *arg = (void *)lParam;
         1193  +    (*f)(w, arg);
         1194  +    return TRUE;
         1195  +  }
         1196  +  }
         1197  +  return DefWindowProc(hwnd, uMsg, wParam, lParam);
         1198  +}
         1199  +
         1200  +#define WEBVIEW_KEY_FEATURE_BROWSER_EMULATION                                  \
         1201  +  "Software\\Microsoft\\Internet "                                             \
         1202  +  "Explorer\\Main\\FeatureControl\\FEATURE_BROWSER_EMULATION"
         1203  +
         1204  +static int webview_fix_ie_compat_mode() {
         1205  +  HKEY hKey;
         1206  +  DWORD ie_version = 11000;
         1207  +  TCHAR appname[MAX_PATH + 1];
         1208  +  TCHAR *p;
         1209  +  if (GetModuleFileName(NULL, appname, MAX_PATH + 1) == 0) {
         1210  +    return -1;
         1211  +  }
         1212  +  for (p = &appname[strlen(appname) - 1]; p != appname && *p != '\\'; p--) {
         1213  +  }
         1214  +  p++;
         1215  +  if (RegCreateKey(HKEY_CURRENT_USER, WEBVIEW_KEY_FEATURE_BROWSER_EMULATION,
         1216  +                   &hKey) != ERROR_SUCCESS) {
         1217  +    return -1;
         1218  +  }
         1219  +  if (RegSetValueEx(hKey, p, 0, REG_DWORD, (BYTE *)&ie_version,
         1220  +                    sizeof(ie_version)) != ERROR_SUCCESS) {
         1221  +    RegCloseKey(hKey);
         1222  +    return -1;
         1223  +  }
         1224  +  RegCloseKey(hKey);
         1225  +  return 0;
         1226  +}
         1227  +
         1228  +int webview_init(webview_t *w, const struct webview_conf *c) {
         1229  +  WNDCLASSEX wc;
         1230  +  HINSTANCE hInstance;
         1231  +  STARTUPINFO info;
         1232  +  DWORD style;
         1233  +  RECT clientRect;
         1234  +  RECT rect;
         1235  +
         1236  +  if (webview_fix_ie_compat_mode() < 0) {
         1237  +    return -1;
         1238  +  }
         1239  +
         1240  +  hInstance = GetModuleHandle(NULL);
         1241  +  if (hInstance == NULL) {
         1242  +    return -1;
         1243  +  }
         1244  +  GetStartupInfo(&info);
         1245  +  if (OleInitialize(NULL) != S_OK) {
         1246  +    return -1;
         1247  +  }
         1248  +  ZeroMemory(&wc, sizeof(WNDCLASSEX));
         1249  +  wc.cbSize = sizeof(WNDCLASSEX);
         1250  +  wc.hInstance = hInstance;
         1251  +  wc.lpfnWndProc = wndproc;
         1252  +  wc.lpszClassName = classname;
         1253  +  RegisterClassEx(&wc);
         1254  +
         1255  +  style = WS_OVERLAPPEDWINDOW;
         1256  +  if (!c->resizable) {
         1257  +    style = WS_OVERLAPPED | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU;
         1258  +  }
         1259  +
         1260  +  rect.left = 0;
         1261  +  rect.top = 0;
         1262  +  rect.right = c->width;
         1263  +  rect.bottom = c->height;
         1264  +  AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, 0);
         1265  +
         1266  +  GetClientRect(GetDesktopWindow(), &clientRect);
         1267  +  int left = (clientRect.right / 2) - ((rect.right - rect.left) / 2);
         1268  +  int top = (clientRect.bottom / 2) - ((rect.bottom - rect.top) / 2);
         1269  +  rect.right = rect.right - rect.left + left;
         1270  +  rect.left = left;
         1271  +  rect.bottom = rect.bottom - rect.top + top;
         1272  +  rect.top = top;
         1273  +
         1274  +  w->priv.hwnd =
         1275  +      CreateWindowEx(0, classname, c->title, style, rect.left, rect.top,
         1276  +                     rect.right - rect.left, rect.bottom - rect.top,
         1277  +                     HWND_DESKTOP, NULL, hInstance, (void *)w);
         1278  +  if (w->priv.hwnd == 0) {
         1279  +    OleUninitialize();
         1280  +    return -1;
         1281  +  }
         1282  +
         1283  +  SetWindowLongPtr(w->priv.hwnd, GWLP_USERDATA, (LONG_PTR)w);
         1284  +
         1285  +  DisplayHTMLPage(w, c);
         1286  +
         1287  +  SetWindowText(w->priv.hwnd, c->title);
         1288  +  ShowWindow(w->priv.hwnd, info.wShowWindow);
         1289  +  UpdateWindow(w->priv.hwnd);
         1290  +  SetFocus(w->priv.hwnd);
         1291  +
         1292  +  return 0;
         1293  +}
         1294  +
         1295  +int webview_loop(webview_t *w, int blocking) {
         1296  +  MSG msg;
         1297  +  if (blocking) {
         1298  +    GetMessage(&msg, 0, 0, 0);
         1299  +  } else {
         1300  +    PeekMessage(&msg, 0, 0, 0, PM_REMOVE);
         1301  +  }
         1302  +  switch (msg.message) {
         1303  +  case WM_QUIT:
         1304  +    return -1;
         1305  +  case WM_COMMAND:
         1306  +  case WM_KEYDOWN:
         1307  +  case WM_KEYUP: {
         1308  +    HRESULT r = S_OK;
         1309  +    IWebBrowser2 *webBrowser2;
         1310  +    IOleObject *browser = *w->priv.browser;
         1311  +    if (browser->lpVtbl->QueryInterface(browser, iid_unref(&IID_IWebBrowser2),
         1312  +                                        (void **)&webBrowser2) == S_OK) {
         1313  +      IOleInPlaceActiveObject *pIOIPAO;
         1314  +      if (browser->lpVtbl->QueryInterface(
         1315  +              browser, iid_unref(&IID_IOleInPlaceActiveObject),
         1316  +              (void **)&pIOIPAO) == S_OK) {
         1317  +        r = pIOIPAO->lpVtbl->TranslateAccelerator(pIOIPAO, &msg);
         1318  +        pIOIPAO->lpVtbl->Release(pIOIPAO);
         1319  +      }
         1320  +      webBrowser2->lpVtbl->Release(webBrowser2);
         1321  +    }
         1322  +    if (r != S_FALSE) {
         1323  +      break;
         1324  +    }
         1325  +  }
         1326  +  default:
         1327  +    TranslateMessage(&msg);
         1328  +    DispatchMessage(&msg);
         1329  +  }
         1330  +  return 0;
         1331  +}
         1332  +
         1333  +int webview_eval(webview_t *w, const char *js) {
         1334  +  IWebBrowser2 *webBrowser2;
         1335  +  IHTMLDocument2 *htmlDoc2;
         1336  +  IDispatch *docDispatch;
         1337  +  IDispatch *scriptDispatch;
         1338  +  if ((*w->priv.browser)
         1339  +          ->lpVtbl->QueryInterface((*w->priv.browser),
         1340  +                                   iid_unref(&IID_IWebBrowser2),
         1341  +                                   (void **)&webBrowser2) != S_OK) {
         1342  +    return -1;
         1343  +  }
         1344  +
         1345  +  if (webBrowser2->lpVtbl->get_Document(webBrowser2, &docDispatch) != S_OK) {
         1346  +    return -1;
         1347  +  }
         1348  +  if (docDispatch->lpVtbl->QueryInterface(docDispatch,
         1349  +                                          iid_unref(&IID_IHTMLDocument2),
         1350  +                                          (void **)&htmlDoc2) != S_OK) {
         1351  +    return -1;
         1352  +  }
         1353  +  if (htmlDoc2->lpVtbl->get_Script(htmlDoc2, &scriptDispatch) != S_OK) {
         1354  +    return -1;
         1355  +  }
         1356  +  DISPID dispid;
         1357  +  BSTR evalStr = SysAllocString(L"eval");
         1358  +  if (scriptDispatch->lpVtbl->GetIDsOfNames(
         1359  +          scriptDispatch, iid_unref(&IID_NULL), &evalStr, 1,
         1360  +          LOCALE_SYSTEM_DEFAULT, &dispid) != S_OK) {
         1361  +    SysFreeString(evalStr);
         1362  +    return -1;
         1363  +  }
         1364  +  SysFreeString(evalStr);
         1365  +
         1366  +  DISPPARAMS params;
         1367  +  VARIANT arg;
         1368  +  VARIANT result;
         1369  +  EXCEPINFO excepInfo;
         1370  +  UINT nArgErr = (UINT)-1;
         1371  +  params.cArgs = 1;
         1372  +  params.cNamedArgs = 0;
         1373  +  params.rgvarg = &arg;
         1374  +  arg.vt = VT_BSTR;
         1375  +  static const char *prologue = "(function(){";
         1376  +  static const char *epilogue = ";})();";
         1377  +  int n = strlen(prologue) + strlen(epilogue) + strlen(js) + 1;
         1378  +  char *eval = (char *)malloc(n);
         1379  +  snprintf(eval, n, "%s%s%s", prologue, js, epilogue);
         1380  +  wchar_t *buf = webview_to_utf16(eval);
         1381  +  if (buf == NULL) {
         1382  +    return -1;
         1383  +  }
         1384  +  arg.bstrVal = SysAllocString(buf);
         1385  +  if (scriptDispatch->lpVtbl->Invoke(
         1386  +          scriptDispatch, dispid, iid_unref(&IID_NULL), 0, DISPATCH_METHOD,
         1387  +          &params, &result, &excepInfo, &nArgErr) != S_OK) {
         1388  +    return -1;
         1389  +  }
         1390  +  SysFreeString(arg.bstrVal);
         1391  +  free(eval);
         1392  +  scriptDispatch->lpVtbl->Release(scriptDispatch);
         1393  +  htmlDoc2->lpVtbl->Release(htmlDoc2);
         1394  +  docDispatch->lpVtbl->Release(docDispatch);
         1395  +  return 0;
         1396  +}
         1397  +
         1398  +void webview_dispatch(webview_t *w, webview_dispatch_fn fn,
         1399  +                                  void *arg) {
         1400  +  PostMessageW(w->priv.hwnd, WM_WEBVIEW_DISPATCH, (WPARAM)fn, (LPARAM)arg);
         1401  +}
         1402  +
         1403  +void webview_set_title(webview_t *w, const char *title) {
         1404  +  SetWindowText(w->priv.hwnd, title);
         1405  +}
         1406  +
         1407  +void webview_set_fullscreen(webview_t *w, int fullscreen) {
         1408  +  if (w->priv.is_fullscreen == !!fullscreen) {
         1409  +    return;
         1410  +  }
         1411  +  if (w->priv.is_fullscreen == 0) {
         1412  +    w->priv.saved_style = GetWindowLong(w->priv.hwnd, GWL_STYLE);
         1413  +    w->priv.saved_ex_style = GetWindowLong(w->priv.hwnd, GWL_EXSTYLE);
         1414  +    GetWindowRect(w->priv.hwnd, &w->priv.saved_rect);
         1415  +  }
         1416  +  w->priv.is_fullscreen = !!fullscreen;
         1417  +  if (fullscreen) {
         1418  +    MONITORINFO monitor_info;
         1419  +    SetWindowLong(w->priv.hwnd, GWL_STYLE,
         1420  +                  w->priv.saved_style & ~(WS_CAPTION | WS_THICKFRAME));
         1421  +    SetWindowLong(w->priv.hwnd, GWL_EXSTYLE,
         1422  +                  w->priv.saved_ex_style &
         1423  +                      ~(WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE |
         1424  +                        WS_EX_CLIENTEDGE | WS_EX_STATICEDGE));
         1425  +    monitor_info.cbSize = sizeof(monitor_info);
         1426  +    GetMonitorInfo(MonitorFromWindow(w->priv.hwnd, MONITOR_DEFAULTTONEAREST),
         1427  +                   &monitor_info);
         1428  +    RECT r;
         1429  +    r.left = monitor_info.rcMonitor.left;
         1430  +    r.top = monitor_info.rcMonitor.top;
         1431  +    r.right = monitor_info.rcMonitor.right;
         1432  +    r.bottom = monitor_info.rcMonitor.bottom;
         1433  +    SetWindowPos(w->priv.hwnd, NULL, r.left, r.top, r.right - r.left,
         1434  +                 r.bottom - r.top,
         1435  +                 SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
         1436  +  } else {
         1437  +    SetWindowLong(w->priv.hwnd, GWL_STYLE, w->priv.saved_style);
         1438  +    SetWindowLong(w->priv.hwnd, GWL_EXSTYLE, w->priv.saved_ex_style);
         1439  +    SetWindowPos(w->priv.hwnd, NULL, w->priv.saved_rect.left,
         1440  +                 w->priv.saved_rect.top,
         1441  +                 w->priv.saved_rect.right - w->priv.saved_rect.left,
         1442  +                 w->priv.saved_rect.bottom - w->priv.saved_rect.top,
         1443  +                 SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
         1444  +  }
         1445  +}
         1446  +
         1447  +/* These are missing parts from MinGW */
         1448  +#ifndef __IFileDialog_INTERFACE_DEFINED__
         1449  +#define __IFileDialog_INTERFACE_DEFINED__
         1450  +enum _FILEOPENDIALOGOPTIONS {
         1451  +  FOS_OVERWRITEPROMPT = 0x2,
         1452  +  FOS_STRICTFILETYPES = 0x4,
         1453  +  FOS_NOCHANGEDIR = 0x8,
         1454  +  FOS_PICKFOLDERS = 0x20,
         1455  +  FOS_FORCEFILESYSTEM = 0x40,
         1456  +  FOS_ALLNONSTORAGEITEMS = 0x80,
         1457  +  FOS_NOVALIDATE = 0x100,
         1458  +  FOS_ALLOWMULTISELECT = 0x200,
         1459  +  FOS_PATHMUSTEXIST = 0x800,
         1460  +  FOS_FILEMUSTEXIST = 0x1000,
         1461  +  FOS_CREATEPROMPT = 0x2000,
         1462  +  FOS_SHAREAWARE = 0x4000,
         1463  +  FOS_NOREADONLYRETURN = 0x8000,
         1464  +  FOS_NOTESTFILECREATE = 0x10000,
         1465  +  FOS_HIDEMRUPLACES = 0x20000,
         1466  +  FOS_HIDEPINNEDPLACES = 0x40000,
         1467  +  FOS_NODEREFERENCELINKS = 0x100000,
         1468  +  FOS_DONTADDTORECENT = 0x2000000,
         1469  +  FOS_FORCESHOWHIDDEN = 0x10000000,
         1470  +  FOS_DEFAULTNOMINIMODE = 0x20000000,
         1471  +  FOS_FORCEPREVIEWPANEON = 0x40000000
         1472  +};
         1473  +typedef DWORD FILEOPENDIALOGOPTIONS;
         1474  +typedef enum FDAP { FDAP_BOTTOM = 0, FDAP_TOP = 1 } FDAP;
         1475  +DEFINE_GUID(IID_IFileDialog, 0x42f85136, 0xdb7e, 0x439c, 0x85, 0xf1, 0xe4, 0x07,
         1476  +            0x5d, 0x13, 0x5f, 0xc8);
         1477  +typedef struct IFileDialogVtbl {
         1478  +  BEGIN_INTERFACE
         1479  +  HRESULT(STDMETHODCALLTYPE *QueryInterface)
         1480  +  (IFileDialog *This, REFIID riid, void **ppvObject);
         1481  +  ULONG(STDMETHODCALLTYPE *AddRef)(IFileDialog *This);
         1482  +  ULONG(STDMETHODCALLTYPE *Release)(IFileDialog *This);
         1483  +  HRESULT(STDMETHODCALLTYPE *Show)(IFileDialog *This, HWND hwndOwner);
         1484  +  HRESULT(STDMETHODCALLTYPE *SetFileTypes)
         1485  +  (IFileDialog *This, UINT cFileTypes, const COMDLG_FILTERSPEC *rgFilterSpec);
         1486  +  HRESULT(STDMETHODCALLTYPE *SetFileTypeIndex)
         1487  +  (IFileDialog *This, UINT iFileType);
         1488  +  HRESULT(STDMETHODCALLTYPE *GetFileTypeIndex)
         1489  +  (IFileDialog *This, UINT *piFileType);
         1490  +  HRESULT(STDMETHODCALLTYPE *Advise)
         1491  +  (IFileDialog *This, IFileDialogEvents *pfde, DWORD *pdwCookie);
         1492  +  HRESULT(STDMETHODCALLTYPE *Unadvise)(IFileDialog *This, DWORD dwCookie);
         1493  +  HRESULT(STDMETHODCALLTYPE *SetOptions)
         1494  +  (IFileDialog *This, FILEOPENDIALOGOPTIONS fos);
         1495  +  HRESULT(STDMETHODCALLTYPE *GetOptions)
         1496  +  (IFileDialog *This, FILEOPENDIALOGOPTIONS *pfos);
         1497  +  HRESULT(STDMETHODCALLTYPE *SetDefaultFolder)
         1498  +  (IFileDialog *This, IShellItem *psi);
         1499  +  HRESULT(STDMETHODCALLTYPE *SetFolder)(IFileDialog *This, IShellItem *psi);
         1500  +  HRESULT(STDMETHODCALLTYPE *GetFolder)(IFileDialog *This, IShellItem **ppsi);
         1501  +  HRESULT(STDMETHODCALLTYPE *GetCurrentSelection)
         1502  +  (IFileDialog *This, IShellItem **ppsi);
         1503  +  HRESULT(STDMETHODCALLTYPE *SetFileName)(IFileDialog *This, LPCWSTR pszName);
         1504  +  HRESULT(STDMETHODCALLTYPE *GetFileName)(IFileDialog *This, LPWSTR *pszName);
         1505  +  HRESULT(STDMETHODCALLTYPE *SetTitle)(IFileDialog *This, LPCWSTR pszTitle);
         1506  +  HRESULT(STDMETHODCALLTYPE *SetOkButtonLabel)
         1507  +  (IFileDialog *This, LPCWSTR pszText);
         1508  +  HRESULT(STDMETHODCALLTYPE *SetFileNameLabel)
         1509  +  (IFileDialog *This, LPCWSTR pszLabel);
         1510  +  HRESULT(STDMETHODCALLTYPE *GetResult)(IFileDialog *This, IShellItem **ppsi);
         1511  +  HRESULT(STDMETHODCALLTYPE *AddPlace)
         1512  +  (IFileDialog *This, IShellItem *psi, FDAP fdap);
         1513  +  HRESULT(STDMETHODCALLTYPE *SetDefaultExtension)
         1514  +  (IFileDialog *This, LPCWSTR pszDefaultExtension);
         1515  +  HRESULT(STDMETHODCALLTYPE *Close)(IFileDialog *This, HRESULT hr);
         1516  +  HRESULT(STDMETHODCALLTYPE *SetClientGuid)(IFileDialog *This, REFGUID guid);
         1517  +  HRESULT(STDMETHODCALLTYPE *ClearClientData)(IFileDialog *This);
         1518  +  HRESULT(STDMETHODCALLTYPE *SetFilter)
         1519  +  (IFileDialog *This, IShellItemFilter *pFilter);
         1520  +  END_INTERFACE
         1521  +} IFileDialogVtbl;
         1522  +interface IFileDialog {
         1523  +  CONST_VTBL IFileDialogVtbl *lpVtbl;
         1524  +};
         1525  +DEFINE_GUID(IID_IFileOpenDialog, 0xd57c7288, 0xd4ad, 0x4768, 0xbe, 0x02, 0x9d,
         1526  +            0x96, 0x95, 0x32, 0xd9, 0x60);
         1527  +DEFINE_GUID(IID_IFileSaveDialog, 0x84bccd23, 0x5fde, 0x4cdb, 0xae, 0xa4, 0xaf,
         1528  +            0x64, 0xb8, 0x3d, 0x78, 0xab);
         1529  +#endif
         1530  +
         1531  +void webview_dialog(webview_t *w,
         1532  +                                webview_dialog_t dlgtype, int flags,
         1533  +                                const char *title, const char *arg,
         1534  +                                char *result, size_t resultsz) {
         1535  +  if (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN ||
         1536  +      dlgtype == WEBVIEW_DIALOG_TYPE_SAVE) {
         1537  +    IFileDialog *dlg = NULL;
         1538  +    IShellItem *res = NULL;
         1539  +    WCHAR *ws = NULL;
         1540  +    char *s = NULL;
         1541  +    FILEOPENDIALOGOPTIONS opts, add_opts = 0;
         1542  +    if (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN) {
         1543  +      if (CoCreateInstance(
         1544  +              iid_unref(&CLSID_FileOpenDialog), NULL, CLSCTX_INPROC_SERVER,
         1545  +              iid_unref(&IID_IFileOpenDialog), (void **)&dlg) != S_OK) {
         1546  +        goto error_dlg;
         1547  +      }
         1548  +      if (flags & WEBVIEW_DIALOG_FLAG_DIRECTORY) {
         1549  +        add_opts |= FOS_PICKFOLDERS;
         1550  +      }
         1551  +      add_opts |= FOS_NOCHANGEDIR | FOS_ALLNONSTORAGEITEMS | FOS_NOVALIDATE |
         1552  +                  FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST | FOS_SHAREAWARE |
         1553  +                  FOS_NOTESTFILECREATE | FOS_NODEREFERENCELINKS |
         1554  +                  FOS_FORCESHOWHIDDEN | FOS_DEFAULTNOMINIMODE;
         1555  +    } else {
         1556  +      if (CoCreateInstance(
         1557  +              iid_unref(&CLSID_FileSaveDialog), NULL, CLSCTX_INPROC_SERVER,
         1558  +              iid_unref(&IID_IFileSaveDialog), (void **)&dlg) != S_OK) {
         1559  +        goto error_dlg;
         1560  +      }
         1561  +      add_opts |= FOS_OVERWRITEPROMPT | FOS_NOCHANGEDIR |
         1562  +                  FOS_ALLNONSTORAGEITEMS | FOS_NOVALIDATE | FOS_SHAREAWARE |
         1563  +                  FOS_NOTESTFILECREATE | FOS_NODEREFERENCELINKS |
         1564  +                  FOS_FORCESHOWHIDDEN | FOS_DEFAULTNOMINIMODE;
         1565  +    }
         1566  +    if (dlg->lpVtbl->GetOptions(dlg, &opts) != S_OK) {
         1567  +      goto error_dlg;
         1568  +    }
         1569  +    opts &= ~FOS_NOREADONLYRETURN;
         1570  +    opts |= add_opts;
         1571  +    if (dlg->lpVtbl->SetOptions(dlg, opts) != S_OK) {
         1572  +      goto error_dlg;
         1573  +    }
         1574  +    if (dlg->lpVtbl->Show(dlg, w->priv.hwnd) != S_OK) {
         1575  +      goto error_dlg;
         1576  +    }
         1577  +    if (dlg->lpVtbl->GetResult(dlg, &res) != S_OK) {
         1578  +      goto error_dlg;
         1579  +    }
         1580  +    if (res->lpVtbl->GetDisplayName(res, SIGDN_FILESYSPATH, &ws) != S_OK) {
         1581  +      goto error_result;
         1582  +    }
         1583  +    s = webview_from_utf16(ws);
         1584  +    strncpy(result, s, resultsz);
         1585  +    result[resultsz - 1] = '\0';
         1586  +    CoTaskMemFree(ws);
         1587  +  error_result:
         1588  +    res->lpVtbl->Release(res);
         1589  +  error_dlg:
         1590  +    dlg->lpVtbl->Release(dlg);
         1591  +    return;
         1592  +  } else if (dlgtype == WEBVIEW_DIALOG_TYPE_ALERT) {
         1593  +#if 0
         1594  +    /* MinGW often doesn't contain TaskDialog, we'll use MessageBox for now */
         1595  +    WCHAR *wtitle = webview_to_utf16(title);
         1596  +    WCHAR *warg = webview_to_utf16(arg);
         1597  +    TaskDialog(w->priv.hwnd, NULL, NULL, wtitle, warg, 0, NULL, NULL);
         1598  +    GlobalFree(warg);
         1599  +    GlobalFree(wtitle);
         1600  +#else
         1601  +    UINT type = MB_OK;
         1602  +    switch (flags & WEBVIEW_DIALOG_FLAG_ALERT_MASK) {
         1603  +    case WEBVIEW_DIALOG_FLAG_INFO:
         1604  +      type |= MB_ICONINFORMATION;
         1605  +      break;
         1606  +    case WEBVIEW_DIALOG_FLAG_WARNING:
         1607  +      type |= MB_ICONWARNING;
         1608  +      break;
         1609  +    case WEBVIEW_DIALOG_FLAG_ERROR:
         1610  +      type |= MB_ICONERROR;
         1611  +      break;
         1612  +    }
         1613  +    MessageBox(w->priv.hwnd, arg, title, type);
         1614  +#endif
         1615  +  }
         1616  +}
         1617  +
         1618  +void webview_terminate(webview_t *w) { PostQuitMessage(0); }
         1619  +void webview_exit(webview_t *w) { OleUninitialize(); }
         1620  +void webview_print_log(const char *s) { OutputDebugString(s); }
         1621  +
         1622  +#endif /* WEBVIEW_WINAPI */
         1623  +
         1624  +#if defined(WEBVIEW_COCOA)
         1625  +#if (!defined MAC_OS_X_VERSION_10_12) ||                                       \
         1626  +    MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12
         1627  +#define NSWindowStyleMaskResizable NSResizableWindowMask
         1628  +#define NSWindowStyleMaskMiniaturizable NSMiniaturizableWindowMask
         1629  +#define NSWindowStyleMaskTitled NSTitledWindowMask
         1630  +#define NSWindowStyleMaskClosable NSClosableWindowMask
         1631  +#define NSWindowStyleMaskFullScreen NSFullScreenWindowMask
         1632  +#define NSEventMaskAny NSAnyEventMask
         1633  +#define NSEventModifierFlagCommand NSCommandKeyMask
         1634  +#define NSEventModifierFlagOption NSAlternateKeyMask
         1635  +#define NSAlertStyleInformational NSInformationalAlertStyle
         1636  +#endif /* MAC_OS_X_VERSION_10_12 */
         1637  +#if (!defined MAC_OS_X_VERSION_10_13) ||                                       \
         1638  +    MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_13
         1639  +#define NSModalResponseOK NSFileHandlingPanelOKButton
         1640  +#endif /* MAC_OS_X_VERSION_10_12, MAC_OS_X_VERSION_10_13 */
         1641  +static void webview_window_will_close(id self, SEL cmd, id notification) {
         1642  +  webview_t *w =
         1643  +      (webview_t *)objc_getAssociatedObject(self, "webview");
         1644  +  webview_terminate(w);
         1645  +}
         1646  +
         1647  +static BOOL webview_is_selector_excluded_from_web_script(id self, SEL cmd,
         1648  +                                                         SEL selector) {
         1649  +  return selector != @selector(invoke:);
         1650  +}
         1651  +
         1652  +static NSString *webview_webscript_name_for_selector(id self, SEL cmd,
         1653  +                                                     SEL selector) {
         1654  +  return selector == @selector(invoke:) ? @"invoke" : nil;
         1655  +}
         1656  +
         1657  +static void webview_did_clear_window_object(id self, SEL cmd, id webview,
         1658  +                                            id script, id frame) {
         1659  +  [script setValue:self forKey:@"external"];
         1660  +}
         1661  +
         1662  +static void webview_external_invoke(id self, SEL cmd, id arg) {
         1663  +  webview_t *w =
         1664  +      (webview_t *)objc_getAssociatedObject(self, "webview");
         1665  +  if (w == NULL || w->external_invoke_cb == NULL) {
         1666  +    return;
         1667  +  }
         1668  +  if ([arg isKindOfClass:[NSString class]] == NO) {
         1669  +    return;
         1670  +  }
         1671  +  w->external_invoke_cb(w, [(NSString *)(arg)UTF8String]);
         1672  +}
         1673  +
         1674  +int webview_init(webview_t *w, const struct webview_conf *c) {
         1675  +  w->priv.pool = [[NSAutoreleasePool alloc] init];
         1676  +  [NSApplication sharedApplication];
         1677  +
         1678  +  Class webViewDelegateClass =
         1679  +      objc_allocateClassPair([NSObject class], "WebViewDelegate", 0);
         1680  +  class_addMethod(webViewDelegateClass, sel_registerName("windowWillClose:"),
         1681  +                  (IMP)webview_window_will_close, "v@:@");
         1682  +  class_addMethod(object_getClass(webViewDelegateClass),
         1683  +                  sel_registerName("isSelectorExcludedFromWebScript:"),
         1684  +                  (IMP)webview_is_selector_excluded_from_web_script, "c@::");
         1685  +  class_addMethod(object_getClass(webViewDelegateClass),
         1686  +                  sel_registerName("webScriptNameForSelector:"),
         1687  +                  (IMP)webview_webscript_name_for_selector, "c@::");
         1688  +  class_addMethod(webViewDelegateClass,
         1689  +                  sel_registerName("webView:didClearWindowObject:forFrame:"),
         1690  +                  (IMP)webview_did_clear_window_object, "v@:@@@");
         1691  +  class_addMethod(webViewDelegateClass, sel_registerName("invoke:"),
         1692  +                  (IMP)webview_external_invoke, "v@:@");
         1693  +  objc_registerClassPair(webViewDelegateClass);
         1694  +
         1695  +  w->priv.windowDelegate = [[webViewDelegateClass alloc] init];
         1696  +  objc_setAssociatedObject(w->priv.windowDelegate, "webview", (id)(w),
         1697  +                           OBJC_ASSOCIATION_ASSIGN);
         1698  +
         1699  +  NSString *nsTitle = [NSString stringWithUTF8String:c->title];
         1700  +  NSRect r = NSMakeRect(0, 0, c->width, c->height);
         1701  +  NSUInteger style = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable |
         1702  +                     NSWindowStyleMaskMiniaturizable;
         1703  +  if (c->resizable) {
         1704  +    style = style | NSWindowStyleMaskResizable;
         1705  +  }
         1706  +  w->priv.window = [[NSWindow alloc] initWithContentRect:r
         1707  +                                               styleMask:style
         1708  +                                                 backing:NSBackingStoreBuffered
         1709  +                                                   defer:NO];
         1710  +  [w->priv.window autorelease];
         1711  +  [w->priv.window setTitle:nsTitle];
         1712  +  [w->priv.window setDelegate:w->priv.windowDelegate];
         1713  +  [w->priv.window center];
         1714  +
         1715  +  [[NSUserDefaults standardUserDefaults] setBool:!!c->debug
         1716  +                                          forKey:@"WebKitDeveloperExtras"];
         1717  +  [[NSUserDefaults standardUserDefaults] synchronize];
         1718  +  w->priv.webview =
         1719  +      [[WebView alloc] initWithFrame:r frameName:@"WebView" groupName:nil];
         1720  +  NSURL *nsURL = [NSURL
         1721  +      URLWithString:[NSString stringWithUTF8String:webview_check_url(c->url)]];
         1722  +  [[w->priv.webview mainFrame] loadRequest:[NSURLRequest requestWithURL:nsURL]];
         1723  +
         1724  +  [w->priv.webview setAutoresizesSubviews:YES];
         1725  +  [w->priv.webview
         1726  +      setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
         1727  +  w->priv.webview.frameLoadDelegate = w->priv.windowDelegate;
         1728  +  [[w->priv.window contentView] addSubview:w->priv.webview];
         1729  +  [w->priv.window orderFrontRegardless];
         1730  +
         1731  +  [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
         1732  +  [NSApp finishLaunching];
         1733  +  [NSApp activateIgnoringOtherApps:YES];
         1734  +
         1735  +  NSMenu *menubar = [[[NSMenu alloc] initWithTitle:@""] autorelease];
         1736  +
         1737  +  NSString *appName = [[NSProcessInfo processInfo] processName];
         1738  +  NSMenuItem *appMenuItem =
         1739  +      [[[NSMenuItem alloc] initWithTitle:appName action:NULL keyEquivalent:@""]
         1740  +          autorelease];
         1741  +  NSMenu *appMenu = [[[NSMenu alloc] initWithTitle:appName] autorelease];
         1742  +  [appMenuItem setSubmenu:appMenu];
         1743  +  [menubar addItem:appMenuItem];
         1744  +
         1745  +  NSString *title = [@"Hide " stringByAppendingString:appName];
         1746  +  NSMenuItem *item = [[[NSMenuItem alloc] initWithTitle:title
         1747  +                                                 action:@selector(hide:)
         1748  +                                          keyEquivalent:@"h"] autorelease];
         1749  +  [appMenu addItem:item];
         1750  +  item = [[[NSMenuItem alloc] initWithTitle:@"Hide Others"
         1751  +                                     action:@selector(hideOtherApplications:)
         1752  +                              keyEquivalent:@"h"] autorelease];
         1753  +  [item setKeyEquivalentModifierMask:(NSEventModifierFlagOption |
         1754  +                                      NSEventModifierFlagCommand)];
         1755  +  [appMenu addItem:item];
         1756  +  item = [[[NSMenuItem alloc] initWithTitle:@"Show All"
         1757  +                                     action:@selector(unhideAllApplications:)
         1758  +                              keyEquivalent:@""] autorelease];
         1759  +  [appMenu addItem:item];
         1760  +  [appMenu addItem:[NSMenuItem separatorItem]];
         1761  +
         1762  +  title = [@"Quit " stringByAppendingString:appName];
         1763  +  item = [[[NSMenuItem alloc] initWithTitle:title
         1764  +                                     action:@selector(terminate:)
         1765  +                              keyEquivalent:@"q"] autorelease];
         1766  +  [appMenu addItem:item];
         1767  +
         1768  +  [NSApp setMainMenu:menubar];
         1769  +
         1770  +  w->priv.should_exit = 0;
         1771  +  return 0;
         1772  +}
         1773  +
         1774  +int webview_loop(webview_t *w, int blocking) {
         1775  +  NSDate *until = (blocking ? [NSDate distantFuture] : [NSDate distantPast]);
         1776  +  NSEvent *event = [NSApp nextEventMatchingMask:NSEventMaskAny
         1777  +                                      untilDate:until
         1778  +                                         inMode:NSDefaultRunLoopMode
         1779  +                                        dequeue:YES];
         1780  +  if (event) {
         1781  +    [NSApp sendEvent:event];
         1782  +  }
         1783  +  return w->priv.should_exit;
         1784  +}
         1785  +
         1786  +int webview_eval(webview_t *w, const char *js) {
         1787  +  NSString *nsJS = [NSString stringWithUTF8String:js];
         1788  +  id u = [[w->priv.webview windowScriptObject] evaluateWebScript:nsJS];
         1789  +  if ([u isKindOfClass:[WebUndefined class]] == YES) {
         1790  +    return -1;
         1791  +  }
         1792  +  return 0;
         1793  +}
         1794  +
         1795  +void webview_set_title(webview_t *w, const char *title) {
         1796  +  NSString *nsTitle = [NSString stringWithUTF8String:title];
         1797  +  [w->priv.window setTitle:nsTitle];
         1798  +}
         1799  +
         1800  +void webview_set_fullscreen(webview_t *w, int fullscreen) {
         1801  +  int b = ((([w->priv.window styleMask] & NSWindowStyleMaskFullScreen) ==
         1802  +            NSWindowStyleMaskFullScreen)
         1803  +               ? 1
         1804  +               : 0);
         1805  +  if (b != fullscreen) {
         1806  +    [w->priv.window toggleFullScreen:nil];
         1807  +  }
         1808  +}
         1809  +
         1810  +void webview_dialog(webview_t *w,
         1811  +                                webview_dialog_t dlgtype, int flags,
         1812  +                                const char *title, const char *arg,
         1813  +                                char *result, size_t resultsz) {
         1814  +  if (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN ||
         1815  +      dlgtype == WEBVIEW_DIALOG_TYPE_SAVE) {
         1816  +    NSSavePanel *panel;
         1817  +    if (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN) {
         1818  +      NSOpenPanel *openPanel = [NSOpenPanel openPanel];
         1819  +      if (flags & WEBVIEW_DIALOG_FLAG_DIRECTORY) {
         1820  +        [openPanel setCanChooseFiles:NO];
         1821  +        [openPanel setCanChooseDirectories:YES];
         1822  +      } else {
         1823  +        [openPanel setCanChooseFiles:YES];
         1824  +        [openPanel setCanChooseDirectories:NO];
         1825  +      }
         1826  +      [openPanel setResolvesAliases:NO];
         1827  +      [openPanel setAllowsMultipleSelection:NO];
         1828  +      panel = openPanel;
         1829  +    } else {
         1830  +      panel = [NSSavePanel savePanel];
         1831  +    }
         1832  +    [panel setCanCreateDirectories:YES];
         1833  +    [panel setShowsHiddenFiles:YES];
         1834  +    [panel setExtensionHidden:NO];
         1835  +    [panel setCanSelectHiddenExtension:NO];
         1836  +    [panel setTreatsFilePackagesAsDirectories:YES];
         1837  +    [panel beginSheetModalForWindow:w->priv.window
         1838  +                  completionHandler:^(NSInteger result) {
         1839  +                    [NSApp stopModalWithCode:result];
         1840  +                  }];
         1841  +    if ([NSApp runModalForWindow:panel] == NSModalResponseOK) {
         1842  +      const char *filename = [[[panel URL] path] UTF8String];
         1843  +      strlcpy(result, filename, resultsz);
         1844  +    }
         1845  +  } else if (dlgtype == WEBVIEW_DIALOG_TYPE_ALERT) {
         1846  +    NSAlert *a = [NSAlert new];
         1847  +    switch (flags & WEBVIEW_DIALOG_FLAG_ALERT_MASK) {
         1848  +    case WEBVIEW_DIALOG_FLAG_INFO:
         1849  +      [a setAlertStyle:NSAlertStyleInformational];
         1850  +      break;
         1851  +    case WEBVIEW_DIALOG_FLAG_WARNING:
         1852  +      NSLog(@"warning");
         1853  +      [a setAlertStyle:NSAlertStyleWarning];
         1854  +      break;
         1855  +    case WEBVIEW_DIALOG_FLAG_ERROR:
         1856  +      NSLog(@"error");
         1857  +      [a setAlertStyle:NSAlertStyleCritical];
         1858  +      break;
         1859  +    }
         1860  +    [a setShowsHelp:NO];
         1861  +    [a setShowsSuppressionButton:NO];
         1862  +    [a setMessageText:[NSString stringWithUTF8String:title]];
         1863  +    [a setInformativeText:[NSString stringWithUTF8String:arg]];
         1864  +    [a addButtonWithTitle:@"OK"];
         1865  +    [a runModal];
         1866  +    [a release];
         1867  +  }
         1868  +}
         1869  +
         1870  +static void webview_dispatch_cb(void *arg) {
         1871  +  struct webview_dispatch_arg *context = (struct webview_dispatch_arg *)arg;
         1872  +  (context->fn)(context->w, context->arg);
         1873  +  free(context);
         1874  +}
         1875  +
         1876  +void webview_dispatch(webview_t *w, webview_dispatch_fn fn,
         1877  +                                  void *arg) {
         1878  +  struct webview_dispatch_arg *context = (struct webview_dispatch_arg *)malloc(
         1879  +      sizeof(struct webview_dispatch_arg));
         1880  +  context->w = w;
         1881  +  context->arg = arg;
         1882  +  context->fn = fn;
         1883  +  dispatch_async_f(dispatch_get_main_queue(), context, webview_dispatch_cb);
         1884  +}
         1885  +
         1886  +void webview_terminate(webview_t *w) {
         1887  +  w->priv.should_exit = 1;
         1888  +}
         1889  +void webview_exit(webview_t *w) { [NSApp terminate:NSApp]; }
         1890  +void webview_print_log(const char *s) { NSLog(@"%s", s); }
         1891  +
         1892  +#endif /* WEBVIEW_COCOA */