diff --git a/bindings/rust/tauri-sys/build.rs b/bindings/rust/tauri-sys/build.rs index 962275bc0..0efc0d11b 100644 --- a/bindings/rust/tauri-sys/build.rs +++ b/bindings/rust/tauri-sys/build.rs @@ -2,12 +2,12 @@ use std::{env, path::PathBuf}; fn main() { let tauri_path = PathBuf::from("../../../ui"); + let mut tauri_impl_path = tauri_path.clone(); let mut build = cc::Build::new(); build .include(&tauri_path) - .file("tauri.c") .flag_if_supported("-std=c11") .flag_if_supported("-w"); @@ -20,11 +20,13 @@ fn main() { let target = env::var("TARGET").unwrap(); if target.contains("windows") { + tauri_impl_path.push("tauri-windows.c"); build.define("WEBVIEW_WINAPI", None); for &lib in &["ole32", "comctl32", "oleaut32", "uuid", "gdi32"] { println!("cargo:rustc-link-lib={}", lib); } } else if target.contains("linux") || target.contains("bsd") { + tauri_impl_path.push("tauri-gtk.c"); let webkit = pkg_config::Config::new() .atleast_version("2.8") .probe("webkit2gtk-4.0") @@ -35,6 +37,7 @@ fn main() { } build.define("WEBVIEW_GTK", None); } else if target.contains("apple") { + tauri_impl_path.push("tauri-cocoa.m"); build .define("WEBVIEW_COCOA", None) .flag("-x") @@ -45,5 +48,9 @@ fn main() { panic!("unsupported target"); } + build + .file(tauri_impl_path.into_os_string().into_string().unwrap()) + .file("tauri.c"); + build.compile("tauri"); } diff --git a/bindings/rust/tauri-sys/tauri.c b/bindings/rust/tauri-sys/tauri.c index 6c41a0dff..fbfe4ce08 100644 --- a/bindings/rust/tauri-sys/tauri.c +++ b/bindings/rust/tauri-sys/tauri.c @@ -1,3 +1,14 @@ +#if defined(WEBVIEW_GTK) +#include "tauri-gtk-webview.h" +#elif defined(WEBVIEW_WINAPI) +#define CINTERFACE +#include "tauri-windows-webview.h" +#elif defined(WEBVIEW_COCOA) +#include "tauri-cocoa-webview.h" +#else +#error "Define one of: WEBVIEW_GTK, WEBVIEW_COCOA or WEBVIEW_WINAPI" +#endif + #define WEBVIEW_IMPLEMENTATION #include "tauri.h" diff --git a/ui/tauri-cocoa-webview.h b/ui/tauri-cocoa-webview.h new file mode 100644 index 000000000..f5150bac3 --- /dev/null +++ b/ui/tauri-cocoa-webview.h @@ -0,0 +1,11 @@ +#include +#include +#include + +struct webview_priv { + id pool; + id window; + id webview; + id windowDelegate; + int should_exit; +}; diff --git a/ui/tauri-cocoa.m b/ui/tauri-cocoa.m new file mode 100644 index 000000000..2cee6f923 --- /dev/null +++ b/ui/tauri-cocoa.m @@ -0,0 +1,636 @@ +#include "tauri-cocoa-webview.h" +#include "tauri.h" + +#define NSAlertStyleWarning 0 +#define NSAlertStyleCritical 2 +#define NSWindowStyleMaskResizable 8 +#define NSWindowStyleMaskMiniaturizable 4 +#define NSWindowStyleMaskTitled 1 +#define NSWindowStyleMaskClosable 2 +#define NSWindowStyleMaskFullScreen (1 << 14) +#define NSViewWidthSizable 2 +#define NSViewHeightSizable 16 +#define NSBackingStoreBuffered 2 +#define NSEventMaskAny ULONG_MAX +#define NSEventModifierFlagCommand (1 << 20) +#define NSEventModifierFlagOption (1 << 19) +#define NSAlertStyleInformational 1 +#define NSAlertFirstButtonReturn 1000 +#define WKNavigationActionPolicyDownload 2 +#define NSModalResponseOK 1 +#define WKNavigationActionPolicyDownload 2 +#define WKNavigationResponsePolicyAllow 1 +#define WKUserScriptInjectionTimeAtDocumentStart 0 +#define NSApplicationActivationPolicyRegular 0 + +static id get_nsstring(const char *c_str) { + return objc_msgSend((id)objc_getClass("NSString"), + sel_registerName("stringWithUTF8String:"), c_str); +} + +static id create_menu_item(id title, const char *action, const char *key) { + id item = + objc_msgSend((id)objc_getClass("NSMenuItem"), sel_registerName("alloc")); + objc_msgSend(item, sel_registerName("initWithTitle:action:keyEquivalent:"), + title, sel_registerName(action), get_nsstring(key)); + objc_msgSend(item, sel_registerName("autorelease")); + + return item; +} + +static void webview_window_will_close(id self, SEL cmd, id notification) { + struct webview *w = + (struct webview *)objc_getAssociatedObject(self, "webview"); + webview_terminate(w); +} + +static void webview_external_invoke(id self, SEL cmd, id contentController, + id message) { + struct webview *w = + (struct webview *)objc_getAssociatedObject(contentController, "webview"); + if (w == NULL || w->external_invoke_cb == NULL) { + return; + } + + w->external_invoke_cb(w, (const char *)objc_msgSend( + objc_msgSend(message, sel_registerName("body")), + sel_registerName("UTF8String"))); +} + +static void run_open_panel(id self, SEL cmd, id webView, id parameters, + id frame, void (^completionHandler)(id)) { + + id openPanel = objc_msgSend((id)objc_getClass("NSOpenPanel"), + sel_registerName("openPanel")); + + objc_msgSend( + openPanel, sel_registerName("setAllowsMultipleSelection:"), + objc_msgSend(parameters, sel_registerName("allowsMultipleSelection"))); + + objc_msgSend(openPanel, sel_registerName("setCanChooseFiles:"), 1); + objc_msgSend( + openPanel, sel_registerName("beginWithCompletionHandler:"), ^(id result) { + if (result == (id)NSModalResponseOK) { + completionHandler(objc_msgSend(openPanel, sel_registerName("URLs"))); + } else { + completionHandler(nil); + } + }); +} + +static void run_save_panel(id self, SEL cmd, id download, id filename, + void (^completionHandler)(int allowOverwrite, + id destination)) { + id savePanel = objc_msgSend((id)objc_getClass("NSSavePanel"), + sel_registerName("savePanel")); + objc_msgSend(savePanel, sel_registerName("setCanCreateDirectories:"), 1); + objc_msgSend(savePanel, sel_registerName("setNameFieldStringValue:"), + filename); + objc_msgSend(savePanel, sel_registerName("beginWithCompletionHandler:"), + ^(id result) { + if (result == (id)NSModalResponseOK) { + id url = objc_msgSend(savePanel, sel_registerName("URL")); + id path = objc_msgSend(url, sel_registerName("path")); + completionHandler(1, path); + } else { + completionHandler(NO, nil); + } + }); +} + +static void run_confirmation_panel(id self, SEL cmd, id webView, id message, + id frame, void (^completionHandler)(bool)) { + + id alert = + objc_msgSend((id)objc_getClass("NSAlert"), sel_registerName("new")); + objc_msgSend(alert, sel_registerName("setIcon:"), + objc_msgSend((id)objc_getClass("NSImage"), + sel_registerName("imageNamed:"), + get_nsstring("NSCaution"))); + objc_msgSend(alert, sel_registerName("setShowsHelp:"), 0); + objc_msgSend(alert, sel_registerName("setInformativeText:"), message); + objc_msgSend(alert, sel_registerName("addButtonWithTitle:"), + get_nsstring("OK")); + objc_msgSend(alert, sel_registerName("addButtonWithTitle:"), + get_nsstring("Cancel")); + if (objc_msgSend(alert, sel_registerName("runModal")) == + (id)NSAlertFirstButtonReturn) { + completionHandler(true); + } else { + completionHandler(false); + } + objc_msgSend(alert, sel_registerName("release")); +} + +static void run_alert_panel(id self, SEL cmd, id webView, id message, id frame, + void (^completionHandler)(void)) { + id alert = + objc_msgSend((id)objc_getClass("NSAlert"), sel_registerName("new")); + objc_msgSend(alert, sel_registerName("setIcon:"), + objc_msgSend((id)objc_getClass("NSImage"), + sel_registerName("imageNamed:"), + get_nsstring("NSCaution"))); + objc_msgSend(alert, sel_registerName("setShowsHelp:"), 0); + objc_msgSend(alert, sel_registerName("setInformativeText:"), message); + objc_msgSend(alert, sel_registerName("addButtonWithTitle:"), + get_nsstring("OK")); + objc_msgSend(alert, sel_registerName("runModal")); + objc_msgSend(alert, sel_registerName("release")); + completionHandler(); +} + +static void download_failed(id self, SEL cmd, id download, id error) { + printf("%s", + (const char *)objc_msgSend( + objc_msgSend(error, sel_registerName("localizedDescription")), + sel_registerName("UTF8String"))); +} + +static void make_nav_policy_decision(id self, SEL cmd, id webView, id response, + void (^decisionHandler)(int)) { + if (objc_msgSend(response, sel_registerName("canShowMIMEType")) == 0) { + decisionHandler(WKNavigationActionPolicyDownload); + } else { + decisionHandler(WKNavigationResponsePolicyAllow); + } +} + +WEBVIEW_API int webview_init(struct webview *w) { + w->priv.pool = objc_msgSend((id)objc_getClass("NSAutoreleasePool"), + sel_registerName("new")); + objc_msgSend((id)objc_getClass("NSApplication"), + sel_registerName("sharedApplication")); + + Class __WKScriptMessageHandler = objc_allocateClassPair( + objc_getClass("NSObject"), "__WKScriptMessageHandler", 0); + class_addMethod( + __WKScriptMessageHandler, + sel_registerName("userContentController:didReceiveScriptMessage:"), + (IMP)webview_external_invoke, "v@:@@"); + objc_registerClassPair(__WKScriptMessageHandler); + + id scriptMessageHandler = + objc_msgSend((id)__WKScriptMessageHandler, sel_registerName("new")); + + /*** + _WKDownloadDelegate is an undocumented/private protocol with methods called + from WKNavigationDelegate + References: + https://github.com/WebKit/webkit/blob/master/Source/WebKit/UIProcess/API/Cocoa/_WKDownload.h + https://github.com/WebKit/webkit/blob/master/Source/WebKit/UIProcess/API/Cocoa/_WKDownloadDelegate.h + https://github.com/WebKit/webkit/blob/master/Tools/TestWebKitAPI/Tests/WebKitCocoa/Download.mm + ***/ + + Class __WKDownloadDelegate = objc_allocateClassPair( + objc_getClass("NSObject"), "__WKDownloadDelegate", 0); + class_addMethod( + __WKDownloadDelegate, + sel_registerName("_download:decideDestinationWithSuggestedFilename:" + "completionHandler:"), + (IMP)run_save_panel, "v@:@@?"); + class_addMethod(__WKDownloadDelegate, + sel_registerName("_download:didFailWithError:"), + (IMP)download_failed, "v@:@@"); + objc_registerClassPair(__WKDownloadDelegate); + id downloadDelegate = + objc_msgSend((id)__WKDownloadDelegate, sel_registerName("new")); + + Class __WKPreferences = objc_allocateClassPair(objc_getClass("WKPreferences"), + "__WKPreferences", 0); + objc_property_attribute_t type = {"T", "c"}; + objc_property_attribute_t ownership = {"N", ""}; + objc_property_attribute_t attrs[] = {type, ownership}; + class_replaceProperty(__WKPreferences, "developerExtrasEnabled", attrs, 2); + objc_registerClassPair(__WKPreferences); + id wkPref = objc_msgSend((id)__WKPreferences, sel_registerName("new")); + objc_msgSend(wkPref, sel_registerName("setValue:forKey:"), + objc_msgSend((id)objc_getClass("NSNumber"), + sel_registerName("numberWithBool:"), !!w->debug), + objc_msgSend((id)objc_getClass("NSString"), + sel_registerName("stringWithUTF8String:"), + "developerExtrasEnabled")); + + id userController = objc_msgSend((id)objc_getClass("WKUserContentController"), + sel_registerName("new")); + objc_setAssociatedObject(userController, "webview", (id)(w), + OBJC_ASSOCIATION_ASSIGN); + objc_msgSend( + userController, sel_registerName("addScriptMessageHandler:name:"), + scriptMessageHandler, + objc_msgSend((id)objc_getClass("NSString"), + sel_registerName("stringWithUTF8String:"), "invoke")); + + /*** + In order to maintain compatibility with the other 'webviews' we need to + override window.external.invoke to call + webkit.messageHandlers.invoke.postMessage + ***/ + + id windowExternalOverrideScript = objc_msgSend( + (id)objc_getClass("WKUserScript"), sel_registerName("alloc")); + objc_msgSend( + windowExternalOverrideScript, + sel_registerName("initWithSource:injectionTime:forMainFrameOnly:"), + get_nsstring("window.external = this; invoke = function(arg){ " + "webkit.messageHandlers.invoke.postMessage(arg); };"), + WKUserScriptInjectionTimeAtDocumentStart, 0); + + objc_msgSend(userController, sel_registerName("addUserScript:"), + windowExternalOverrideScript); + + id config = objc_msgSend((id)objc_getClass("WKWebViewConfiguration"), + sel_registerName("new")); + id processPool = objc_msgSend(config, sel_registerName("processPool")); + objc_msgSend(processPool, sel_registerName("_setDownloadDelegate:"), + downloadDelegate); + objc_msgSend(config, sel_registerName("setProcessPool:"), processPool); + objc_msgSend(config, sel_registerName("setUserContentController:"), + userController); + objc_msgSend(config, sel_registerName("setPreferences:"), wkPref); + + Class __NSWindowDelegate = objc_allocateClassPair(objc_getClass("NSObject"), + "__NSWindowDelegate", 0); + class_addProtocol(__NSWindowDelegate, objc_getProtocol("NSWindowDelegate")); + class_replaceMethod(__NSWindowDelegate, sel_registerName("windowWillClose:"), + (IMP)webview_window_will_close, "v@:@"); + objc_registerClassPair(__NSWindowDelegate); + + w->priv.windowDelegate = + objc_msgSend((id)__NSWindowDelegate, sel_registerName("new")); + + objc_setAssociatedObject(w->priv.windowDelegate, "webview", (id)(w), + OBJC_ASSOCIATION_ASSIGN); + + id nsTitle = + objc_msgSend((id)objc_getClass("NSString"), + sel_registerName("stringWithUTF8String:"), w->title); + + CGRect r = CGRectMake(0, 0, w->width, w->height); + + unsigned int style = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | + NSWindowStyleMaskMiniaturizable; + if (w->resizable) { + style = style | NSWindowStyleMaskResizable; + } + + w->priv.window = + objc_msgSend((id)objc_getClass("NSWindow"), sel_registerName("alloc")); + objc_msgSend(w->priv.window, + sel_registerName("initWithContentRect:styleMask:backing:defer:"), + r, style, NSBackingStoreBuffered, 0); + + objc_msgSend(w->priv.window, sel_registerName("autorelease")); + objc_msgSend(w->priv.window, sel_registerName("setTitle:"), nsTitle); + objc_msgSend(w->priv.window, sel_registerName("setDelegate:"), + w->priv.windowDelegate); + objc_msgSend(w->priv.window, sel_registerName("center")); + + Class __WKUIDelegate = + objc_allocateClassPair(objc_getClass("NSObject"), "__WKUIDelegate", 0); + class_addProtocol(__WKUIDelegate, objc_getProtocol("WKUIDelegate")); + class_addMethod(__WKUIDelegate, + sel_registerName("webView:runOpenPanelWithParameters:" + "initiatedByFrame:completionHandler:"), + (IMP)run_open_panel, "v@:@@@?"); + class_addMethod(__WKUIDelegate, + sel_registerName("webView:runJavaScriptAlertPanelWithMessage:" + "initiatedByFrame:completionHandler:"), + (IMP)run_alert_panel, "v@:@@@?"); + class_addMethod( + __WKUIDelegate, + sel_registerName("webView:runJavaScriptConfirmPanelWithMessage:" + "initiatedByFrame:completionHandler:"), + (IMP)run_confirmation_panel, "v@:@@@?"); + objc_registerClassPair(__WKUIDelegate); + id uiDel = objc_msgSend((id)__WKUIDelegate, sel_registerName("new")); + + Class __WKNavigationDelegate = objc_allocateClassPair( + objc_getClass("NSObject"), "__WKNavigationDelegate", 0); + class_addProtocol(__WKNavigationDelegate, + objc_getProtocol("WKNavigationDelegate")); + class_addMethod( + __WKNavigationDelegate, + sel_registerName( + "webView:decidePolicyForNavigationResponse:decisionHandler:"), + (IMP)make_nav_policy_decision, "v@:@@?"); + objc_registerClassPair(__WKNavigationDelegate); + id navDel = objc_msgSend((id)__WKNavigationDelegate, sel_registerName("new")); + + w->priv.webview = + objc_msgSend((id)objc_getClass("WKWebView"), sel_registerName("alloc")); + objc_msgSend(w->priv.webview, + sel_registerName("initWithFrame:configuration:"), r, config); + objc_msgSend(w->priv.webview, sel_registerName("setUIDelegate:"), uiDel); + objc_msgSend(w->priv.webview, sel_registerName("setNavigationDelegate:"), + navDel); + + id nsURL = objc_msgSend((id)objc_getClass("NSURL"), + sel_registerName("URLWithString:"), + get_nsstring(webview_check_url(w->url))); + + objc_msgSend(w->priv.webview, sel_registerName("loadRequest:"), + objc_msgSend((id)objc_getClass("NSURLRequest"), + sel_registerName("requestWithURL:"), nsURL)); + objc_msgSend(w->priv.webview, sel_registerName("setAutoresizesSubviews:"), 1); + objc_msgSend(w->priv.webview, sel_registerName("setAutoresizingMask:"), + (NSViewWidthSizable | NSViewHeightSizable)); + objc_msgSend(objc_msgSend(w->priv.window, sel_registerName("contentView")), + sel_registerName("addSubview:"), w->priv.webview); + objc_msgSend(w->priv.window, sel_registerName("orderFrontRegardless")); + + objc_msgSend(objc_msgSend((id)objc_getClass("NSApplication"), + sel_registerName("sharedApplication")), + sel_registerName("setActivationPolicy:"), + NSApplicationActivationPolicyRegular); + + objc_msgSend(objc_msgSend((id)objc_getClass("NSApplication"), + sel_registerName("sharedApplication")), + sel_registerName("finishLaunching")); + + objc_msgSend(objc_msgSend((id)objc_getClass("NSApplication"), + sel_registerName("sharedApplication")), + sel_registerName("activateIgnoringOtherApps:"), 1); + + id menubar = + objc_msgSend((id)objc_getClass("NSMenu"), sel_registerName("alloc")); + objc_msgSend(menubar, sel_registerName("initWithTitle:"), get_nsstring("")); + objc_msgSend(menubar, sel_registerName("autorelease")); + + id appName = objc_msgSend(objc_msgSend((id)objc_getClass("NSProcessInfo"), + sel_registerName("processInfo")), + sel_registerName("processName")); + + id appMenuItem = + objc_msgSend((id)objc_getClass("NSMenuItem"), sel_registerName("alloc")); + objc_msgSend(appMenuItem, + sel_registerName("initWithTitle:action:keyEquivalent:"), appName, + NULL, get_nsstring("")); + + id appMenu = + objc_msgSend((id)objc_getClass("NSMenu"), sel_registerName("alloc")); + objc_msgSend(appMenu, sel_registerName("initWithTitle:"), appName); + objc_msgSend(appMenu, sel_registerName("autorelease")); + + objc_msgSend(appMenuItem, sel_registerName("setSubmenu:"), appMenu); + objc_msgSend(menubar, sel_registerName("addItem:"), appMenuItem); + + id title = + objc_msgSend(get_nsstring("Hide "), + sel_registerName("stringByAppendingString:"), appName); + id item = create_menu_item(title, "hide:", "h"); + objc_msgSend(appMenu, sel_registerName("addItem:"), item); + + item = create_menu_item(get_nsstring("Hide Others"), + "hideOtherApplications:", "h"); + objc_msgSend(item, sel_registerName("setKeyEquivalentModifierMask:"), + (NSEventModifierFlagOption | NSEventModifierFlagCommand)); + objc_msgSend(appMenu, sel_registerName("addItem:"), item); + + item = + create_menu_item(get_nsstring("Show All"), "unhideAllApplications:", ""); + objc_msgSend(appMenu, sel_registerName("addItem:"), item); + + objc_msgSend(appMenu, sel_registerName("addItem:"), + objc_msgSend((id)objc_getClass("NSMenuItem"), + sel_registerName("separatorItem"))); + + title = objc_msgSend(get_nsstring("Quit "), + sel_registerName("stringByAppendingString:"), appName); + item = create_menu_item(title, "terminate:", "q"); + objc_msgSend(appMenu, sel_registerName("addItem:"), item); + +id editMenuItem = + objc_msgSend((id)objc_getClass("NSMenuItem"), sel_registerName("alloc")); + objc_msgSend(editMenuItem, + sel_registerName("initWithTitle:action:keyEquivalent:"), get_nsstring("Edit"), + NULL, get_nsstring("")); + + /*** + Edit menu + ***/ + +id editMenu = + objc_msgSend((id)objc_getClass("NSMenu"), sel_registerName("alloc")); + objc_msgSend(editMenu, sel_registerName("initWithTitle:"), get_nsstring("Edit")); + objc_msgSend(editMenu, sel_registerName("autorelease")); + + objc_msgSend(editMenuItem, sel_registerName("setSubmenu:"), editMenu); + objc_msgSend(menubar, sel_registerName("addItem:"), editMenuItem); + + item = create_menu_item(get_nsstring("Undo"), "undo:", "z"); + objc_msgSend(editMenu, sel_registerName("addItem:"), item); + + item = create_menu_item(get_nsstring("Redo"), "redo:", "y"); + objc_msgSend(editMenu, sel_registerName("addItem:"), item); + + item = objc_msgSend((id)objc_getClass("NSMenuItem"), sel_registerName("separatorItem")); + objc_msgSend(editMenu, sel_registerName("addItem:"), item); + + item = create_menu_item(get_nsstring("Cut"), "cut:", "x"); + objc_msgSend(editMenu, sel_registerName("addItem:"), item); + + item = create_menu_item(get_nsstring("Copy"), "copy:", "c"); + objc_msgSend(editMenu, sel_registerName("addItem:"), item); + + item = create_menu_item(get_nsstring("Paste"), "paste:", "v"); + objc_msgSend(editMenu, sel_registerName("addItem:"), item); + + item = create_menu_item(get_nsstring("Select All"), "selectAll:", "a"); + objc_msgSend(editMenu, sel_registerName("addItem:"), item); + + /*** + Finalize menubar + ***/ + + objc_msgSend(objc_msgSend((id)objc_getClass("NSApplication"), + sel_registerName("sharedApplication")), + sel_registerName("setMainMenu:"), menubar); + + w->priv.should_exit = 0; + return 0; +} + +WEBVIEW_API int webview_loop(struct webview *w, int blocking) { + id until = (blocking ? objc_msgSend((id)objc_getClass("NSDate"), + sel_registerName("distantFuture")) + : objc_msgSend((id)objc_getClass("NSDate"), + sel_registerName("distantPast"))); + + id event = objc_msgSend( + objc_msgSend((id)objc_getClass("NSApplication"), + sel_registerName("sharedApplication")), + sel_registerName("nextEventMatchingMask:untilDate:inMode:dequeue:"), + ULONG_MAX, until, + objc_msgSend((id)objc_getClass("NSString"), + sel_registerName("stringWithUTF8String:"), + "kCFRunLoopDefaultMode"), + true); + + if (event) { + objc_msgSend(objc_msgSend((id)objc_getClass("NSApplication"), + sel_registerName("sharedApplication")), + sel_registerName("sendEvent:"), event); + } + + return w->priv.should_exit; +} + +WEBVIEW_API int webview_eval(struct webview *w, const char *js) { + objc_msgSend(w->priv.webview, + sel_registerName("evaluateJavaScript:completionHandler:"), + get_nsstring(js), NULL); + + return 0; +} + +WEBVIEW_API void webview_set_title(struct webview *w, const char *title) { + objc_msgSend(w->priv.window, sel_registerName("setTitle"), + get_nsstring(title)); +} + +WEBVIEW_API void webview_set_fullscreen(struct webview *w, int fullscreen) { + unsigned long windowStyleMask = (unsigned long)objc_msgSend( + w->priv.window, sel_registerName("styleMask")); + int b = (((windowStyleMask & NSWindowStyleMaskFullScreen) == + NSWindowStyleMaskFullScreen) + ? 1 + : 0); + if (b != fullscreen) { + objc_msgSend(w->priv.window, sel_registerName("toggleFullScreen:"), NULL); + } +} + +WEBVIEW_API void webview_set_color(struct webview *w, uint8_t r, uint8_t g, + uint8_t b, uint8_t a) { + + id color = objc_msgSend((id)objc_getClass("NSColor"), + sel_registerName("colorWithRed:green:blue:alpha:"), + (float)r / 255.0, (float)g / 255.0, (float)b / 255.0, + (float)a / 255.0); + + objc_msgSend(w->priv.window, sel_registerName("setBackgroundColor:"), color); + + if (0.5 >= ((r / 255.0 * 299.0) + (g / 255.0 * 587.0) + (b / 255.0 * 114.0)) / + 1000.0) { + objc_msgSend(w->priv.window, sel_registerName("setAppearance:"), + objc_msgSend((id)objc_getClass("NSAppearance"), + sel_registerName("appearanceNamed:"), + get_nsstring("NSAppearanceNameVibrantDark"))); + } else { + objc_msgSend(w->priv.window, sel_registerName("setAppearance:"), + objc_msgSend((id)objc_getClass("NSAppearance"), + sel_registerName("appearanceNamed:"), + get_nsstring("NSAppearanceNameVibrantLight"))); + } + objc_msgSend(w->priv.window, sel_registerName("setOpaque:"), 0); + objc_msgSend(w->priv.window, + sel_registerName("setTitlebarAppearsTransparent:"), 1); + objc_msgSend(w->priv.webview, sel_registerName("_setDrawsBackground:"), 0); +} + +WEBVIEW_API void webview_dialog(struct webview *w, + enum webview_dialog_type dlgtype, int flags, + const char *title, const char *arg, + char *result, size_t resultsz) { + if (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN || + dlgtype == WEBVIEW_DIALOG_TYPE_SAVE) { + id panel = (id)objc_getClass("NSSavePanel"); + if (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN) { + id openPanel = objc_msgSend((id)objc_getClass("NSOpenPanel"), + sel_registerName("openPanel")); + if (flags & WEBVIEW_DIALOG_FLAG_DIRECTORY) { + objc_msgSend(openPanel, sel_registerName("setCanChooseFiles:"), 0); + objc_msgSend(openPanel, sel_registerName("setCanChooseDirectories:"), + 1); + } else { + objc_msgSend(openPanel, sel_registerName("setCanChooseFiles:"), 1); + objc_msgSend(openPanel, sel_registerName("setCanChooseDirectories:"), + 0); + } + objc_msgSend(openPanel, sel_registerName("setResolvesAliases:"), 0); + objc_msgSend(openPanel, sel_registerName("setAllowsMultipleSelection:"), + 0); + panel = openPanel; + } else { + panel = objc_msgSend((id)objc_getClass("NSSavePanel"), + sel_registerName("savePanel")); + } + + objc_msgSend(panel, sel_registerName("setCanCreateDirectories:"), 1); + objc_msgSend(panel, sel_registerName("setShowsHiddenFiles:"), 1); + objc_msgSend(panel, sel_registerName("setExtensionHidden:"), 0); + objc_msgSend(panel, sel_registerName("setCanSelectHiddenExtension:"), 0); + objc_msgSend(panel, sel_registerName("setTreatsFilePackagesAsDirectories:"), + 1); + objc_msgSend( + panel, sel_registerName("beginSheetModalForWindow:completionHandler:"), + w->priv.window, ^(id result) { + objc_msgSend(objc_msgSend((id)objc_getClass("NSApplication"), + sel_registerName("sharedApplication")), + sel_registerName("stopModalWithCode:"), result); + }); + + if (objc_msgSend(objc_msgSend((id)objc_getClass("NSApplication"), + sel_registerName("sharedApplication")), + sel_registerName("runModalForWindow:"), + panel) == (id)NSModalResponseOK) { + id url = objc_msgSend(panel, sel_registerName("URL")); + id path = objc_msgSend(url, sel_registerName("path")); + const char *filename = + (const char *)objc_msgSend(path, sel_registerName("UTF8String")); + strlcpy(result, filename, resultsz); + } + } else if (dlgtype == WEBVIEW_DIALOG_TYPE_ALERT) { + id a = objc_msgSend((id)objc_getClass("NSAlert"), sel_registerName("new")); + switch (flags & WEBVIEW_DIALOG_FLAG_ALERT_MASK) { + case WEBVIEW_DIALOG_FLAG_INFO: + objc_msgSend(a, sel_registerName("setAlertStyle:"), + NSAlertStyleInformational); + break; + case WEBVIEW_DIALOG_FLAG_WARNING: + printf("Warning\n"); + objc_msgSend(a, sel_registerName("setAlertStyle:"), NSAlertStyleWarning); + break; + case WEBVIEW_DIALOG_FLAG_ERROR: + printf("Error\n"); + objc_msgSend(a, sel_registerName("setAlertStyle:"), NSAlertStyleCritical); + break; + } + objc_msgSend(a, sel_registerName("setShowsHelp:"), 0); + objc_msgSend(a, sel_registerName("setShowsSuppressionButton:"), 0); + objc_msgSend(a, sel_registerName("setMessageText:"), get_nsstring(title)); + objc_msgSend(a, sel_registerName("setInformativeText:"), get_nsstring(arg)); + objc_msgSend(a, sel_registerName("addButtonWithTitle:"), + get_nsstring("OK")); + objc_msgSend(a, sel_registerName("runModal")); + objc_msgSend(a, sel_registerName("release")); + } +} + +static void webview_dispatch_cb(void *arg) { + struct webview_dispatch_arg *context = (struct webview_dispatch_arg *)arg; + (context->fn)(context->w, context->arg); + free(context); +} + +WEBVIEW_API void webview_dispatch(struct webview *w, webview_dispatch_fn fn, + void *arg) { + struct webview_dispatch_arg *context = (struct webview_dispatch_arg *)malloc( + sizeof(struct webview_dispatch_arg)); + context->w = w; + context->arg = arg; + context->fn = fn; + dispatch_async_f(dispatch_get_main_queue(), context, webview_dispatch_cb); +} + +WEBVIEW_API void webview_terminate(struct webview *w) { + w->priv.should_exit = 1; +} + +WEBVIEW_API void webview_exit(struct webview *w) { + id app = objc_msgSend((id)objc_getClass("NSApplication"), + sel_registerName("sharedApplication")); + objc_msgSend(app, sel_registerName("terminate:"), app); +} + +WEBVIEW_API void webview_print_log(const char *s) { printf("%s\n", s); } diff --git a/ui/tauri-gtk-webview.h b/ui/tauri-gtk-webview.h new file mode 100644 index 000000000..11578385d --- /dev/null +++ b/ui/tauri-gtk-webview.h @@ -0,0 +1,14 @@ +#include +#include +#include + +struct webview_priv { + GtkWidget *window; + GtkWidget *scroller; + GtkWidget *webview; + GtkWidget *inspector_window; + GAsyncQueue *queue; + int ready; + int js_busy; + int should_exit; +}; diff --git a/ui/tauri-gtk.c b/ui/tauri-gtk.c new file mode 100644 index 000000000..c90518b6f --- /dev/null +++ b/ui/tauri-gtk.c @@ -0,0 +1,244 @@ +#include "tauri-gtk-webview.h" +#include "tauri.h" + +static void external_message_received_cb(WebKitUserContentManager *m, + WebKitJavascriptResult *r, + gpointer arg) { + (void)m; + struct webview *w = (struct webview *)arg; + if (w->external_invoke_cb == NULL) { + return; + } + JSGlobalContextRef context = webkit_javascript_result_get_global_context(r); + JSValueRef value = webkit_javascript_result_get_value(r); + JSStringRef js = JSValueToStringCopy(context, value, NULL); + size_t n = JSStringGetMaximumUTF8CStringSize(js); + char *s = g_new(char, n); + JSStringGetUTF8CString(js, s, n); + w->external_invoke_cb(w, s); + JSStringRelease(js); + g_free(s); +} + +static void webview_load_changed_cb(WebKitWebView *webview, + WebKitLoadEvent event, gpointer arg) { + (void)webview; + struct webview *w = (struct webview *)arg; + if (event == WEBKIT_LOAD_FINISHED) { + w->priv.ready = 1; + } +} + +static void webview_destroy_cb(GtkWidget *widget, gpointer arg) { + (void)widget; + struct webview *w = (struct webview *)arg; + webview_terminate(w); +} + +static gboolean webview_context_menu_cb(WebKitWebView *webview, + GtkWidget *default_menu, + WebKitHitTestResult *hit_test_result, + gboolean triggered_with_keyboard, + gpointer userdata) { + (void)webview; + (void)default_menu; + (void)hit_test_result; + (void)triggered_with_keyboard; + (void)userdata; + return TRUE; +} + +WEBVIEW_API int webview_init(struct webview *w) { + if (gtk_init_check(0, NULL) == FALSE) { + return -1; + } + + w->priv.ready = 0; + w->priv.should_exit = 0; + w->priv.queue = g_async_queue_new(); + w->priv.window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(w->priv.window), w->title); + + if (w->resizable) { + gtk_window_set_default_size(GTK_WINDOW(w->priv.window), w->width, + w->height); + } else { + gtk_widget_set_size_request(w->priv.window, w->width, w->height); + } + gtk_window_set_resizable(GTK_WINDOW(w->priv.window), !!w->resizable); + gtk_window_set_position(GTK_WINDOW(w->priv.window), GTK_WIN_POS_CENTER); + + w->priv.scroller = gtk_scrolled_window_new(NULL, NULL); + gtk_container_add(GTK_CONTAINER(w->priv.window), w->priv.scroller); + + WebKitUserContentManager *m = webkit_user_content_manager_new(); + webkit_user_content_manager_register_script_message_handler(m, "external"); + g_signal_connect(m, "script-message-received::external", + G_CALLBACK(external_message_received_cb), w); + + w->priv.webview = webkit_web_view_new_with_user_content_manager(m); + webkit_web_view_load_uri(WEBKIT_WEB_VIEW(w->priv.webview), + webview_check_url(w->url)); + g_signal_connect(G_OBJECT(w->priv.webview), "load-changed", + G_CALLBACK(webview_load_changed_cb), w); + gtk_container_add(GTK_CONTAINER(w->priv.scroller), w->priv.webview); + + if (w->debug) { + WebKitSettings *settings = + webkit_web_view_get_settings(WEBKIT_WEB_VIEW(w->priv.webview)); + webkit_settings_set_enable_write_console_messages_to_stdout(settings, true); + webkit_settings_set_enable_developer_extras(settings, true); + } else { + g_signal_connect(G_OBJECT(w->priv.webview), "context-menu", + G_CALLBACK(webview_context_menu_cb), w); + } + + gtk_widget_show_all(w->priv.window); + + webkit_web_view_run_javascript( + WEBKIT_WEB_VIEW(w->priv.webview), + "window.external={invoke:function(x){" + "window.webkit.messageHandlers.external.postMessage(x);}}", + NULL, NULL, NULL); + + g_signal_connect(G_OBJECT(w->priv.window), "destroy", + G_CALLBACK(webview_destroy_cb), w); + return 0; +} + +WEBVIEW_API int webview_loop(struct webview *w, int blocking) { + gtk_main_iteration_do(blocking); + return w->priv.should_exit; +} + +WEBVIEW_API void webview_set_title(struct webview *w, const char *title) { + gtk_window_set_title(GTK_WINDOW(w->priv.window), title); +} + +WEBVIEW_API void webview_set_fullscreen(struct webview *w, int fullscreen) { + if (fullscreen) { + gtk_window_fullscreen(GTK_WINDOW(w->priv.window)); + } else { + gtk_window_unfullscreen(GTK_WINDOW(w->priv.window)); + } +} + +WEBVIEW_API void webview_set_color(struct webview *w, uint8_t r, uint8_t g, + uint8_t b, uint8_t a) { + GdkRGBA color = {r / 255.0, g / 255.0, b / 255.0, a / 255.0}; + webkit_web_view_set_background_color(WEBKIT_WEB_VIEW(w->priv.webview), + &color); +} + +WEBVIEW_API void webview_dialog(struct webview *w, + enum webview_dialog_type dlgtype, int flags, + const char *title, const char *arg, + char *result, size_t resultsz) { + GtkWidget *dlg; + if (result != NULL) { + result[0] = '\0'; + } + if (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN || + dlgtype == WEBVIEW_DIALOG_TYPE_SAVE) { + dlg = gtk_file_chooser_dialog_new( + title, GTK_WINDOW(w->priv.window), + (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN + ? (flags & WEBVIEW_DIALOG_FLAG_DIRECTORY + ? GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER + : GTK_FILE_CHOOSER_ACTION_OPEN) + : GTK_FILE_CHOOSER_ACTION_SAVE), + "_Cancel", GTK_RESPONSE_CANCEL, + (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN ? "_Open" : "_Save"), + GTK_RESPONSE_ACCEPT, NULL); + gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(dlg), FALSE); + gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dlg), FALSE); + gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(dlg), TRUE); + gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dlg), TRUE); + gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER(dlg), TRUE); + gint response = gtk_dialog_run(GTK_DIALOG(dlg)); + if (response == GTK_RESPONSE_ACCEPT) { + gchar *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dlg)); + g_strlcpy(result, filename, resultsz); + g_free(filename); + } + gtk_widget_destroy(dlg); + } else if (dlgtype == WEBVIEW_DIALOG_TYPE_ALERT) { + GtkMessageType type = GTK_MESSAGE_OTHER; + switch (flags & WEBVIEW_DIALOG_FLAG_ALERT_MASK) { + case WEBVIEW_DIALOG_FLAG_INFO: + type = GTK_MESSAGE_INFO; + break; + case WEBVIEW_DIALOG_FLAG_WARNING: + type = GTK_MESSAGE_WARNING; + break; + case WEBVIEW_DIALOG_FLAG_ERROR: + type = GTK_MESSAGE_ERROR; + break; + } + dlg = gtk_message_dialog_new(GTK_WINDOW(w->priv.window), GTK_DIALOG_MODAL, + type, GTK_BUTTONS_OK, "%s", title); + gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dlg), "%s", + arg); + gtk_dialog_run(GTK_DIALOG(dlg)); + gtk_widget_destroy(dlg); + } +} + +static void webview_eval_finished(GObject *object, GAsyncResult *result, + gpointer userdata) { + (void)object; + (void)result; + struct webview *w = (struct webview *)userdata; + w->priv.js_busy = 0; +} + +WEBVIEW_API int webview_eval(struct webview *w, const char *js) { + while (w->priv.ready == 0) { + g_main_context_iteration(NULL, TRUE); + } + w->priv.js_busy = 1; + webkit_web_view_run_javascript(WEBKIT_WEB_VIEW(w->priv.webview), js, NULL, + webview_eval_finished, w); + while (w->priv.js_busy) { + g_main_context_iteration(NULL, TRUE); + } + return 0; +} + +static gboolean webview_dispatch_wrapper(gpointer userdata) { + struct webview *w = (struct webview *)userdata; + for (;;) { + struct webview_dispatch_arg *arg = + (struct webview_dispatch_arg *)g_async_queue_try_pop(w->priv.queue); + if (arg == NULL) { + break; + } + (arg->fn)(w, arg->arg); + g_free(arg); + } + return FALSE; +} + +WEBVIEW_API void webview_dispatch(struct webview *w, webview_dispatch_fn fn, + void *arg) { + struct webview_dispatch_arg *context = + (struct webview_dispatch_arg *)g_new(struct webview_dispatch_arg, 1); + context->w = w; + context->arg = arg; + context->fn = fn; + g_async_queue_lock(w->priv.queue); + g_async_queue_push_unlocked(w->priv.queue, context); + if (g_async_queue_length_unlocked(w->priv.queue) == 1) { + gdk_threads_add_idle(webview_dispatch_wrapper, w); + } + g_async_queue_unlock(w->priv.queue); +} + +WEBVIEW_API void webview_terminate(struct webview *w) { + w->priv.should_exit = 1; +} + +WEBVIEW_API void webview_exit(struct webview *w) { (void)w; } +WEBVIEW_API void webview_print_log(const char *s) { + fprintf(stderr, "%s\n", s); +} diff --git a/ui/tauri-windows-webview.h b/ui/tauri-windows-webview.h new file mode 100644 index 000000000..b9eb742e5 --- /dev/null +++ b/ui/tauri-windows-webview.h @@ -0,0 +1,19 @@ +#define CINTERFACE +#include + +#include +#include +#include +#include +#include + +#include + +struct webview_priv { + HWND hwnd; + IOleObject **browser; + BOOL is_fullscreen; + DWORD saved_style; + DWORD saved_ex_style; + RECT saved_rect; +}; diff --git a/ui/tauri-windows.c b/ui/tauri-windows.c new file mode 100644 index 000000000..83cbf54b9 --- /dev/null +++ b/ui/tauri-windows.c @@ -0,0 +1,1175 @@ +#include "tauri-windows-webview.h" +#include "tauri.h" + +#pragma comment(lib, "user32.lib") +#pragma comment(lib, "ole32.lib") +#pragma comment(lib, "oleaut32.lib") + +#define WM_WEBVIEW_DISPATCH (WM_APP + 1) + +typedef struct { + IOleInPlaceFrame frame; + HWND window; +} _IOleInPlaceFrameEx; + +typedef struct { + IOleInPlaceSite inplace; + _IOleInPlaceFrameEx frame; +} _IOleInPlaceSiteEx; + +typedef struct { + IDocHostUIHandler ui; +} _IDocHostUIHandlerEx; + +typedef struct { + IInternetSecurityManager mgr; +} _IInternetSecurityManagerEx; + +typedef struct { + IServiceProvider provider; + _IInternetSecurityManagerEx mgr; +} _IServiceProviderEx; + +typedef struct { + IOleClientSite client; + _IOleInPlaceSiteEx inplace; + _IDocHostUIHandlerEx ui; + IDispatch external; + _IServiceProviderEx provider; +} _IOleClientSiteEx; + +#ifdef __cplusplus +#define iid_ref(x) &(x) +#define iid_unref(x) *(x) +#else +#define iid_ref(x) (x) +#define iid_unref(x) (x) +#endif + +static inline WCHAR *webview_to_utf16(const char *s) { + DWORD size = MultiByteToWideChar(CP_UTF8, 0, s, -1, 0, 0); + WCHAR *ws = (WCHAR *)GlobalAlloc(GMEM_FIXED, sizeof(WCHAR) * size); + if (ws == NULL) { + return NULL; + } + MultiByteToWideChar(CP_UTF8, 0, s, -1, ws, size); + return ws; +} + +static inline char *webview_from_utf16(WCHAR *ws) { + int n = WideCharToMultiByte(CP_UTF8, 0, ws, -1, NULL, 0, NULL, NULL); + char *s = (char *)GlobalAlloc(GMEM_FIXED, n); + if (s == NULL) { + return NULL; + } + WideCharToMultiByte(CP_UTF8, 0, ws, -1, s, n, NULL, NULL); + return s; +} + +static int iid_eq(REFIID a, const IID *b) { + return memcmp((const void *)iid_ref(a), (const void *)b, sizeof(GUID)) == 0; +} + +static HRESULT STDMETHODCALLTYPE JS_QueryInterface(IDispatch FAR *This, + REFIID riid, + LPVOID FAR *ppvObj) { + if (iid_eq(riid, &IID_IDispatch)) { + *ppvObj = This; + return S_OK; + } + *ppvObj = 0; + return E_NOINTERFACE; +} +static ULONG STDMETHODCALLTYPE JS_AddRef(IDispatch FAR *This) { return 1; } +static ULONG STDMETHODCALLTYPE JS_Release(IDispatch FAR *This) { return 1; } +static HRESULT STDMETHODCALLTYPE JS_GetTypeInfoCount(IDispatch FAR *This, + UINT *pctinfo) { + return S_OK; +} +static HRESULT STDMETHODCALLTYPE JS_GetTypeInfo(IDispatch FAR *This, + UINT iTInfo, LCID lcid, + ITypeInfo **ppTInfo) { + return S_OK; +} +#define WEBVIEW_JS_INVOKE_ID 0x1000 +static HRESULT STDMETHODCALLTYPE JS_GetIDsOfNames(IDispatch FAR *This, + REFIID riid, + LPOLESTR *rgszNames, + UINT cNames, LCID lcid, + DISPID *rgDispId) { + if (cNames != 1) { + return S_FALSE; + } + if (wcscmp(rgszNames[0], L"invoke") == 0) { + rgDispId[0] = WEBVIEW_JS_INVOKE_ID; + return S_OK; + } + return S_FALSE; +} + +static HRESULT STDMETHODCALLTYPE +JS_Invoke(IDispatch FAR *This, DISPID dispIdMember, REFIID riid, LCID lcid, + WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, + EXCEPINFO *pExcepInfo, UINT *puArgErr) { + size_t offset = (size_t) & ((_IOleClientSiteEx *)NULL)->external; + _IOleClientSiteEx *ex = (_IOleClientSiteEx *)((char *)(This)-offset); + struct webview *w = (struct webview *)GetWindowLongPtr( + ex->inplace.frame.window, GWLP_USERDATA); + if (pDispParams->cArgs == 1 && pDispParams->rgvarg[0].vt == VT_BSTR) { + BSTR bstr = pDispParams->rgvarg[0].bstrVal; + char *s = webview_from_utf16(bstr); + if (s != NULL) { + if (dispIdMember == WEBVIEW_JS_INVOKE_ID) { + if (w->external_invoke_cb != NULL) { + w->external_invoke_cb(w, s); + } + } else { + return S_FALSE; + } + GlobalFree(s); + } + } + return S_OK; +} + +static IDispatchVtbl ExternalDispatchTable = { + JS_QueryInterface, JS_AddRef, JS_Release, JS_GetTypeInfoCount, + JS_GetTypeInfo, JS_GetIDsOfNames, JS_Invoke}; + +static ULONG STDMETHODCALLTYPE Site_AddRef(IOleClientSite FAR *This) { + return 1; +} +static ULONG STDMETHODCALLTYPE Site_Release(IOleClientSite FAR *This) { + return 1; +} +static HRESULT STDMETHODCALLTYPE Site_SaveObject(IOleClientSite FAR *This) { + return E_NOTIMPL; +} +static HRESULT STDMETHODCALLTYPE Site_GetMoniker(IOleClientSite FAR *This, + DWORD dwAssign, + DWORD dwWhichMoniker, + IMoniker **ppmk) { + return E_NOTIMPL; +} +static HRESULT STDMETHODCALLTYPE +Site_GetContainer(IOleClientSite FAR *This, LPOLECONTAINER FAR *ppContainer) { + *ppContainer = 0; + return E_NOINTERFACE; +} +static HRESULT STDMETHODCALLTYPE Site_ShowObject(IOleClientSite FAR *This) { + return NOERROR; +} +static HRESULT STDMETHODCALLTYPE Site_OnShowWindow(IOleClientSite FAR *This, + BOOL fShow) { + return E_NOTIMPL; +} +static HRESULT STDMETHODCALLTYPE +Site_RequestNewObjectLayout(IOleClientSite FAR *This) { + return E_NOTIMPL; +} +static HRESULT STDMETHODCALLTYPE Site_QueryInterface(IOleClientSite FAR *This, + REFIID riid, + void **ppvObject) { + if (iid_eq(riid, &IID_IUnknown) || iid_eq(riid, &IID_IOleClientSite)) { + *ppvObject = &((_IOleClientSiteEx *)This)->client; + } else if (iid_eq(riid, &IID_IOleInPlaceSite)) { + *ppvObject = &((_IOleClientSiteEx *)This)->inplace; + } else if (iid_eq(riid, &IID_IDocHostUIHandler)) { + *ppvObject = &((_IOleClientSiteEx *)This)->ui; + } else if (iid_eq(riid, &IID_IServiceProvider)) { + *ppvObject = &((_IOleClientSiteEx *)This)->provider; + } else { + *ppvObject = 0; + return (E_NOINTERFACE); + } + return S_OK; +} +static HRESULT STDMETHODCALLTYPE InPlace_QueryInterface( + IOleInPlaceSite FAR *This, REFIID riid, LPVOID FAR *ppvObj) { + return (Site_QueryInterface( + (IOleClientSite *)((char *)This - sizeof(IOleClientSite)), riid, ppvObj)); +} +static ULONG STDMETHODCALLTYPE InPlace_AddRef(IOleInPlaceSite FAR *This) { + return 1; +} +static ULONG STDMETHODCALLTYPE InPlace_Release(IOleInPlaceSite FAR *This) { + return 1; +} +static HRESULT STDMETHODCALLTYPE InPlace_GetWindow(IOleInPlaceSite FAR *This, + HWND FAR *lphwnd) { + *lphwnd = ((_IOleInPlaceSiteEx FAR *)This)->frame.window; + return S_OK; +} +static HRESULT STDMETHODCALLTYPE +InPlace_ContextSensitiveHelp(IOleInPlaceSite FAR *This, BOOL fEnterMode) { + return E_NOTIMPL; +} +static HRESULT STDMETHODCALLTYPE +InPlace_CanInPlaceActivate(IOleInPlaceSite FAR *This) { + return S_OK; +} +static HRESULT STDMETHODCALLTYPE +InPlace_OnInPlaceActivate(IOleInPlaceSite FAR *This) { + return S_OK; +} +static HRESULT STDMETHODCALLTYPE +InPlace_OnUIActivate(IOleInPlaceSite FAR *This) { + return S_OK; +} +static HRESULT STDMETHODCALLTYPE InPlace_GetWindowContext( + IOleInPlaceSite FAR *This, LPOLEINPLACEFRAME FAR *lplpFrame, + LPOLEINPLACEUIWINDOW FAR *lplpDoc, LPRECT lprcPosRect, LPRECT lprcClipRect, + LPOLEINPLACEFRAMEINFO lpFrameInfo) { + *lplpFrame = (LPOLEINPLACEFRAME) & ((_IOleInPlaceSiteEx *)This)->frame; + *lplpDoc = 0; + lpFrameInfo->fMDIApp = FALSE; + lpFrameInfo->hwndFrame = ((_IOleInPlaceFrameEx *)*lplpFrame)->window; + lpFrameInfo->haccel = 0; + lpFrameInfo->cAccelEntries = 0; + return S_OK; +} +static HRESULT STDMETHODCALLTYPE InPlace_Scroll(IOleInPlaceSite FAR *This, + SIZE scrollExtent) { + return E_NOTIMPL; +} +static HRESULT STDMETHODCALLTYPE +InPlace_OnUIDeactivate(IOleInPlaceSite FAR *This, BOOL fUndoable) { + return S_OK; +} +static HRESULT STDMETHODCALLTYPE +InPlace_OnInPlaceDeactivate(IOleInPlaceSite FAR *This) { + return S_OK; +} +static HRESULT STDMETHODCALLTYPE +InPlace_DiscardUndoState(IOleInPlaceSite FAR *This) { + return E_NOTIMPL; +} +static HRESULT STDMETHODCALLTYPE +InPlace_DeactivateAndUndo(IOleInPlaceSite FAR *This) { + return E_NOTIMPL; +} +static HRESULT STDMETHODCALLTYPE +InPlace_OnPosRectChange(IOleInPlaceSite FAR *This, LPCRECT lprcPosRect) { + IOleObject *browserObject; + IOleInPlaceObject *inplace; + browserObject = *((IOleObject **)((char *)This - sizeof(IOleObject *) - + sizeof(IOleClientSite))); + if (!browserObject->lpVtbl->QueryInterface(browserObject, + iid_unref(&IID_IOleInPlaceObject), + (void **)&inplace)) { + inplace->lpVtbl->SetObjectRects(inplace, lprcPosRect, lprcPosRect); + inplace->lpVtbl->Release(inplace); + } + return S_OK; +} +static HRESULT STDMETHODCALLTYPE Frame_QueryInterface( + IOleInPlaceFrame FAR *This, REFIID riid, LPVOID FAR *ppvObj) { + return E_NOTIMPL; +} +static ULONG STDMETHODCALLTYPE Frame_AddRef(IOleInPlaceFrame FAR *This) { + return 1; +} +static ULONG STDMETHODCALLTYPE Frame_Release(IOleInPlaceFrame FAR *This) { + return 1; +} +static HRESULT STDMETHODCALLTYPE Frame_GetWindow(IOleInPlaceFrame FAR *This, + HWND FAR *lphwnd) { + *lphwnd = ((_IOleInPlaceFrameEx *)This)->window; + return S_OK; +} +static HRESULT STDMETHODCALLTYPE +Frame_ContextSensitiveHelp(IOleInPlaceFrame FAR *This, BOOL fEnterMode) { + return E_NOTIMPL; +} +static HRESULT STDMETHODCALLTYPE Frame_GetBorder(IOleInPlaceFrame FAR *This, + LPRECT lprectBorder) { + return E_NOTIMPL; +} +static HRESULT STDMETHODCALLTYPE Frame_RequestBorderSpace( + IOleInPlaceFrame FAR *This, LPCBORDERWIDTHS pborderwidths) { + return E_NOTIMPL; +} +static HRESULT STDMETHODCALLTYPE Frame_SetBorderSpace( + IOleInPlaceFrame FAR *This, LPCBORDERWIDTHS pborderwidths) { + return E_NOTIMPL; +} +static HRESULT STDMETHODCALLTYPE Frame_SetActiveObject( + IOleInPlaceFrame FAR *This, IOleInPlaceActiveObject *pActiveObject, + LPCOLESTR pszObjName) { + return S_OK; +} +static HRESULT STDMETHODCALLTYPE +Frame_InsertMenus(IOleInPlaceFrame FAR *This, HMENU hmenuShared, + LPOLEMENUGROUPWIDTHS lpMenuWidths) { + return E_NOTIMPL; +} +static HRESULT STDMETHODCALLTYPE Frame_SetMenu(IOleInPlaceFrame FAR *This, + HMENU hmenuShared, + HOLEMENU holemenu, + HWND hwndActiveObject) { + return S_OK; +} +static HRESULT STDMETHODCALLTYPE Frame_RemoveMenus(IOleInPlaceFrame FAR *This, + HMENU hmenuShared) { + return E_NOTIMPL; +} +static HRESULT STDMETHODCALLTYPE Frame_SetStatusText(IOleInPlaceFrame FAR *This, + LPCOLESTR pszStatusText) { + return S_OK; +} +static HRESULT STDMETHODCALLTYPE +Frame_EnableModeless(IOleInPlaceFrame FAR *This, BOOL fEnable) { + return S_OK; +} +static HRESULT STDMETHODCALLTYPE +Frame_TranslateAccelerator(IOleInPlaceFrame FAR *This, LPMSG lpmsg, WORD wID) { + return E_NOTIMPL; +} +static HRESULT STDMETHODCALLTYPE UI_QueryInterface(IDocHostUIHandler FAR *This, + REFIID riid, + LPVOID FAR *ppvObj) { + return (Site_QueryInterface((IOleClientSite *)((char *)This - + sizeof(IOleClientSite) - + sizeof(_IOleInPlaceSiteEx)), + riid, ppvObj)); +} +static ULONG STDMETHODCALLTYPE UI_AddRef(IDocHostUIHandler FAR *This) { + return 1; +} +static ULONG STDMETHODCALLTYPE UI_Release(IDocHostUIHandler FAR *This) { + return 1; +} +static HRESULT STDMETHODCALLTYPE UI_ShowContextMenu( + IDocHostUIHandler FAR *This, DWORD dwID, POINT __RPC_FAR *ppt, + IUnknown __RPC_FAR *pcmdtReserved, IDispatch __RPC_FAR *pdispReserved) { + return S_OK; +} +static HRESULT STDMETHODCALLTYPE +UI_GetHostInfo(IDocHostUIHandler FAR *This, DOCHOSTUIINFO __RPC_FAR *pInfo) { + pInfo->cbSize = sizeof(DOCHOSTUIINFO); + pInfo->dwFlags = DOCHOSTUIFLAG_NO3DBORDER; + pInfo->dwDoubleClick = DOCHOSTUIDBLCLK_DEFAULT; + return S_OK; +} +static HRESULT STDMETHODCALLTYPE UI_ShowUI( + IDocHostUIHandler FAR *This, DWORD dwID, + IOleInPlaceActiveObject __RPC_FAR *pActiveObject, + IOleCommandTarget __RPC_FAR *pCommandTarget, + IOleInPlaceFrame __RPC_FAR *pFrame, IOleInPlaceUIWindow __RPC_FAR *pDoc) { + return S_OK; +} +static HRESULT STDMETHODCALLTYPE UI_HideUI(IDocHostUIHandler FAR *This) { + return S_OK; +} +static HRESULT STDMETHODCALLTYPE UI_UpdateUI(IDocHostUIHandler FAR *This) { + return S_OK; +} +static HRESULT STDMETHODCALLTYPE UI_EnableModeless(IDocHostUIHandler FAR *This, + BOOL fEnable) { + return S_OK; +} +static HRESULT STDMETHODCALLTYPE +UI_OnDocWindowActivate(IDocHostUIHandler FAR *This, BOOL fActivate) { + return S_OK; +} +static HRESULT STDMETHODCALLTYPE +UI_OnFrameWindowActivate(IDocHostUIHandler FAR *This, BOOL fActivate) { + return S_OK; +} +static HRESULT STDMETHODCALLTYPE +UI_ResizeBorder(IDocHostUIHandler FAR *This, LPCRECT prcBorder, + IOleInPlaceUIWindow __RPC_FAR *pUIWindow, BOOL fRameWindow) { + return S_OK; +} +static HRESULT STDMETHODCALLTYPE +UI_TranslateAccelerator(IDocHostUIHandler FAR *This, LPMSG lpMsg, + const GUID __RPC_FAR *pguidCmdGroup, DWORD nCmdID) { + return S_FALSE; +} +static HRESULT STDMETHODCALLTYPE UI_GetOptionKeyPath( + IDocHostUIHandler FAR *This, LPOLESTR __RPC_FAR *pchKey, DWORD dw) { + return S_FALSE; +} +static HRESULT STDMETHODCALLTYPE UI_GetDropTarget( + IDocHostUIHandler FAR *This, IDropTarget __RPC_FAR *pDropTarget, + IDropTarget __RPC_FAR *__RPC_FAR *ppDropTarget) { + return S_FALSE; +} +static HRESULT STDMETHODCALLTYPE UI_GetExternal( + IDocHostUIHandler FAR *This, IDispatch __RPC_FAR *__RPC_FAR *ppDispatch) { + *ppDispatch = (IDispatch *)(This + 1); + return S_OK; +} +static HRESULT STDMETHODCALLTYPE UI_TranslateUrl( + IDocHostUIHandler FAR *This, DWORD dwTranslate, OLECHAR __RPC_FAR *pchURLIn, + OLECHAR __RPC_FAR *__RPC_FAR *ppchURLOut) { + *ppchURLOut = 0; + return S_FALSE; +} +static HRESULT STDMETHODCALLTYPE +UI_FilterDataObject(IDocHostUIHandler FAR *This, IDataObject __RPC_FAR *pDO, + IDataObject __RPC_FAR *__RPC_FAR *ppDORet) { + *ppDORet = 0; + return S_FALSE; +} + +static const TCHAR *classname = "WebView"; +static const SAFEARRAYBOUND ArrayBound = {1, 0}; + +static IOleClientSiteVtbl MyIOleClientSiteTable = { + Site_QueryInterface, Site_AddRef, Site_Release, + Site_SaveObject, Site_GetMoniker, Site_GetContainer, + Site_ShowObject, Site_OnShowWindow, Site_RequestNewObjectLayout}; +static IOleInPlaceSiteVtbl MyIOleInPlaceSiteTable = { + InPlace_QueryInterface, + InPlace_AddRef, + InPlace_Release, + InPlace_GetWindow, + InPlace_ContextSensitiveHelp, + InPlace_CanInPlaceActivate, + InPlace_OnInPlaceActivate, + InPlace_OnUIActivate, + InPlace_GetWindowContext, + InPlace_Scroll, + InPlace_OnUIDeactivate, + InPlace_OnInPlaceDeactivate, + InPlace_DiscardUndoState, + InPlace_DeactivateAndUndo, + InPlace_OnPosRectChange}; + +static IOleInPlaceFrameVtbl MyIOleInPlaceFrameTable = { + Frame_QueryInterface, + Frame_AddRef, + Frame_Release, + Frame_GetWindow, + Frame_ContextSensitiveHelp, + Frame_GetBorder, + Frame_RequestBorderSpace, + Frame_SetBorderSpace, + Frame_SetActiveObject, + Frame_InsertMenus, + Frame_SetMenu, + Frame_RemoveMenus, + Frame_SetStatusText, + Frame_EnableModeless, + Frame_TranslateAccelerator}; + +static IDocHostUIHandlerVtbl MyIDocHostUIHandlerTable = { + UI_QueryInterface, + UI_AddRef, + UI_Release, + UI_ShowContextMenu, + UI_GetHostInfo, + UI_ShowUI, + UI_HideUI, + UI_UpdateUI, + UI_EnableModeless, + UI_OnDocWindowActivate, + UI_OnFrameWindowActivate, + UI_ResizeBorder, + UI_TranslateAccelerator, + UI_GetOptionKeyPath, + UI_GetDropTarget, + UI_GetExternal, + UI_TranslateUrl, + UI_FilterDataObject}; + + + +static HRESULT STDMETHODCALLTYPE IS_QueryInterface(IInternetSecurityManager FAR *This, REFIID riid, void **ppvObject) { + return E_NOTIMPL; +} +static ULONG STDMETHODCALLTYPE IS_AddRef(IInternetSecurityManager FAR *This) { return 1; } +static ULONG STDMETHODCALLTYPE IS_Release(IInternetSecurityManager FAR *This) { return 1; } +static HRESULT STDMETHODCALLTYPE IS_SetSecuritySite(IInternetSecurityManager FAR *This, IInternetSecurityMgrSite *pSited) { + return INET_E_DEFAULT_ACTION; +} +static HRESULT STDMETHODCALLTYPE IS_GetSecuritySite(IInternetSecurityManager FAR *This, IInternetSecurityMgrSite **ppSite) { + return INET_E_DEFAULT_ACTION; +} +static HRESULT STDMETHODCALLTYPE IS_MapUrlToZone(IInternetSecurityManager FAR *This, LPCWSTR pwszUrl, DWORD *pdwZone, DWORD dwFlags) { + *pdwZone = URLZONE_LOCAL_MACHINE; + return S_OK; +} +static HRESULT STDMETHODCALLTYPE IS_GetSecurityId(IInternetSecurityManager FAR *This, LPCWSTR pwszUrl, BYTE *pbSecurityId, DWORD *pcbSecurityId, DWORD_PTR dwReserved) { + return INET_E_DEFAULT_ACTION; +} +static HRESULT STDMETHODCALLTYPE IS_ProcessUrlAction(IInternetSecurityManager FAR *This, LPCWSTR pwszUrl, DWORD dwAction, BYTE *pPolicy, DWORD cbPolicy, BYTE *pContext, DWORD cbContext, DWORD dwFlags, DWORD dwReserved) { + return INET_E_DEFAULT_ACTION; +} +static HRESULT STDMETHODCALLTYPE IS_QueryCustomPolicy(IInternetSecurityManager FAR *This, LPCWSTR pwszUrl, REFGUID guidKey, BYTE **ppPolicy, DWORD *pcbPolicy, BYTE *pContext, DWORD cbContext, DWORD dwReserved) { + return INET_E_DEFAULT_ACTION; +} +static HRESULT STDMETHODCALLTYPE IS_SetZoneMapping(IInternetSecurityManager FAR *This, DWORD dwZone, LPCWSTR lpszPattern, DWORD dwFlags) { + return INET_E_DEFAULT_ACTION; +} +static HRESULT STDMETHODCALLTYPE IS_GetZoneMappings(IInternetSecurityManager FAR *This, DWORD dwZone, IEnumString **ppenumString, DWORD dwFlags) { + return INET_E_DEFAULT_ACTION; +} +static IInternetSecurityManagerVtbl MyInternetSecurityManagerTable = {IS_QueryInterface, IS_AddRef, IS_Release, IS_SetSecuritySite, IS_GetSecuritySite, IS_MapUrlToZone, IS_GetSecurityId, IS_ProcessUrlAction, IS_QueryCustomPolicy, IS_SetZoneMapping, IS_GetZoneMappings}; + +static HRESULT STDMETHODCALLTYPE SP_QueryInterface(IServiceProvider FAR *This, REFIID riid, void **ppvObject) { + return (Site_QueryInterface( + (IOleClientSite *)((char *)This - sizeof(IOleClientSite) - sizeof(_IOleInPlaceSiteEx) - sizeof(_IDocHostUIHandlerEx) - sizeof(IDispatch)), riid, ppvObject)); +} +static ULONG STDMETHODCALLTYPE SP_AddRef(IServiceProvider FAR *This) { return 1; } +static ULONG STDMETHODCALLTYPE SP_Release(IServiceProvider FAR *This) { return 1; } +static HRESULT STDMETHODCALLTYPE SP_QueryService(IServiceProvider FAR *This, REFGUID siid, REFIID riid, void **ppvObject) { + if (iid_eq(siid, &IID_IInternetSecurityManager) && iid_eq(riid, &IID_IInternetSecurityManager)) { + *ppvObject = &((_IServiceProviderEx *)This)->mgr; + } else { + *ppvObject = 0; + return (E_NOINTERFACE); + } + return S_OK; +} +static IServiceProviderVtbl MyServiceProviderTable = {SP_QueryInterface, SP_AddRef, SP_Release, SP_QueryService}; + +static void UnEmbedBrowserObject(struct webview *w) { + if (w->priv.browser != NULL) { + (*w->priv.browser)->lpVtbl->Close(*w->priv.browser, OLECLOSE_NOSAVE); + (*w->priv.browser)->lpVtbl->Release(*w->priv.browser); + GlobalFree(w->priv.browser); + w->priv.browser = NULL; + } +} + +static int EmbedBrowserObject(struct webview *w) { + RECT rect; + IWebBrowser2 *webBrowser2 = NULL; + LPCLASSFACTORY pClassFactory = NULL; + _IOleClientSiteEx *_iOleClientSiteEx = NULL; + IOleObject **browser = (IOleObject **)GlobalAlloc( + GMEM_FIXED, sizeof(IOleObject *) + sizeof(_IOleClientSiteEx)); + if (browser == NULL) { + goto error; + } + w->priv.browser = browser; + + _iOleClientSiteEx = (_IOleClientSiteEx *)(browser + 1); + _iOleClientSiteEx->client.lpVtbl = &MyIOleClientSiteTable; + _iOleClientSiteEx->inplace.inplace.lpVtbl = &MyIOleInPlaceSiteTable; + _iOleClientSiteEx->inplace.frame.frame.lpVtbl = &MyIOleInPlaceFrameTable; + _iOleClientSiteEx->inplace.frame.window = w->priv.hwnd; + _iOleClientSiteEx->ui.ui.lpVtbl = &MyIDocHostUIHandlerTable; + _iOleClientSiteEx->external.lpVtbl = &ExternalDispatchTable; + _iOleClientSiteEx->provider.provider.lpVtbl = &MyServiceProviderTable; + _iOleClientSiteEx->provider.mgr.mgr.lpVtbl = &MyInternetSecurityManagerTable; + + if (CoGetClassObject(iid_unref(&CLSID_WebBrowser), + CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER, NULL, + iid_unref(&IID_IClassFactory), + (void **)&pClassFactory) != S_OK) { + goto error; + } + + if (pClassFactory == NULL) { + goto error; + } + + if (pClassFactory->lpVtbl->CreateInstance(pClassFactory, 0, + iid_unref(&IID_IOleObject), + (void **)browser) != S_OK) { + goto error; + } + pClassFactory->lpVtbl->Release(pClassFactory); + if ((*browser)->lpVtbl->SetClientSite( + *browser, (IOleClientSite *)_iOleClientSiteEx) != S_OK) { + goto error; + } + (*browser)->lpVtbl->SetHostNames(*browser, L"My Host Name", 0); + + if (OleSetContainedObject((struct IUnknown *)(*browser), TRUE) != S_OK) { + goto error; + } + GetClientRect(w->priv.hwnd, &rect); + if ((*browser)->lpVtbl->DoVerb((*browser), OLEIVERB_SHOW, NULL, + (IOleClientSite *)_iOleClientSiteEx, -1, + w->priv.hwnd, &rect) != S_OK) { + goto error; + } + if ((*browser)->lpVtbl->QueryInterface((*browser), + iid_unref(&IID_IWebBrowser2), + (void **)&webBrowser2) != S_OK) { + goto error; + } + + webBrowser2->lpVtbl->put_Left(webBrowser2, 0); + webBrowser2->lpVtbl->put_Top(webBrowser2, 0); + webBrowser2->lpVtbl->put_Width(webBrowser2, rect.right); + webBrowser2->lpVtbl->put_Height(webBrowser2, rect.bottom); + webBrowser2->lpVtbl->Release(webBrowser2); + + return 0; +error: + UnEmbedBrowserObject(w); + if (pClassFactory != NULL) { + pClassFactory->lpVtbl->Release(pClassFactory); + } + if (browser != NULL) { + GlobalFree(browser); + } + return -1; +} + +#define WEBVIEW_DATA_URL_PREFIX "data:text/html," +static int DisplayHTMLPage(struct webview *w) { + IWebBrowser2 *webBrowser2; + VARIANT myURL; + LPDISPATCH lpDispatch; + IHTMLDocument2 *htmlDoc2; + BSTR bstr; + IOleObject *browserObject; + SAFEARRAY *sfArray; + VARIANT *pVar; + browserObject = *w->priv.browser; + int isDataURL = 0; + const char *webview_url = webview_check_url(w->url); + if (!browserObject->lpVtbl->QueryInterface( + browserObject, iid_unref(&IID_IWebBrowser2), (void **)&webBrowser2)) { + LPCSTR webPageName; + isDataURL = (strncmp(webview_url, WEBVIEW_DATA_URL_PREFIX, + strlen(WEBVIEW_DATA_URL_PREFIX)) == 0); + if (isDataURL) { + webPageName = "about:blank"; + } else { + webPageName = (LPCSTR)webview_url; + } + VariantInit(&myURL); + myURL.vt = VT_BSTR; +#ifndef UNICODE + { + wchar_t *buffer = webview_to_utf16(webPageName); + if (buffer == NULL) { + goto badalloc; + } + myURL.bstrVal = SysAllocString(buffer); + GlobalFree(buffer); + } +#else + myURL.bstrVal = SysAllocString(webPageName); +#endif + if (!myURL.bstrVal) { + badalloc: + webBrowser2->lpVtbl->Release(webBrowser2); + return (-6); + } + webBrowser2->lpVtbl->Navigate2(webBrowser2, &myURL, 0, 0, 0, 0); + VariantClear(&myURL); + if (!isDataURL) { + return 0; + } + + char *url = (char *)calloc(1, strlen(webview_url) + 1); + char *q = url; + for (const char *p = webview_url + strlen(WEBVIEW_DATA_URL_PREFIX); *q = *p; + p++, q++) { + if (*q == '%' && *(p + 1) && *(p + 2)) { + sscanf(p + 1, "%02x", q); + p = p + 2; + } + } + + if (webBrowser2->lpVtbl->get_Document(webBrowser2, &lpDispatch) == S_OK) { + if (lpDispatch->lpVtbl->QueryInterface(lpDispatch, + iid_unref(&IID_IHTMLDocument2), + (void **)&htmlDoc2) == S_OK) { + if ((sfArray = SafeArrayCreate(VT_VARIANT, 1, + (SAFEARRAYBOUND *)&ArrayBound))) { + if (!SafeArrayAccessData(sfArray, (void **)&pVar)) { + pVar->vt = VT_BSTR; +#ifndef UNICODE + { + wchar_t *buffer = webview_to_utf16(url); + if (buffer == NULL) { + goto release; + } + bstr = SysAllocString(buffer); + GlobalFree(buffer); + } +#else + bstr = SysAllocString(string); +#endif + if ((pVar->bstrVal = bstr)) { + htmlDoc2->lpVtbl->write(htmlDoc2, sfArray); + htmlDoc2->lpVtbl->close(htmlDoc2); + } + } + SafeArrayDestroy(sfArray); + } + release: + free(url); + htmlDoc2->lpVtbl->Release(htmlDoc2); + } + lpDispatch->lpVtbl->Release(lpDispatch); + } + webBrowser2->lpVtbl->Release(webBrowser2); + return (0); + } + return (-5); +} + +static LRESULT CALLBACK wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, + LPARAM lParam) { + struct webview *w = (struct webview *)GetWindowLongPtr(hwnd, GWLP_USERDATA); + switch (uMsg) { + case WM_CREATE: + w = (struct webview *)((CREATESTRUCT *)lParam)->lpCreateParams; + w->priv.hwnd = hwnd; + return EmbedBrowserObject(w); + case WM_DESTROY: + UnEmbedBrowserObject(w); + PostQuitMessage(0); + return TRUE; + case WM_SIZE: { + IWebBrowser2 *webBrowser2; + IOleObject *browser = *w->priv.browser; + if (browser->lpVtbl->QueryInterface(browser, iid_unref(&IID_IWebBrowser2), + (void **)&webBrowser2) == S_OK) { + RECT rect; + GetClientRect(hwnd, &rect); + webBrowser2->lpVtbl->put_Width(webBrowser2, rect.right); + webBrowser2->lpVtbl->put_Height(webBrowser2, rect.bottom); + } + return TRUE; + } + case WM_WEBVIEW_DISPATCH: { + webview_dispatch_fn f = (webview_dispatch_fn)wParam; + void *arg = (void *)lParam; + (*f)(w, arg); + return TRUE; + } + } + return DefWindowProc(hwnd, uMsg, wParam, lParam); +} + +#define WEBVIEW_KEY_FEATURE_BROWSER_EMULATION \ + "Software\\Microsoft\\Internet " \ + "Explorer\\Main\\FeatureControl\\FEATURE_BROWSER_EMULATION" + +static int webview_fix_ie_compat_mode() { + HKEY hKey; + DWORD ie_version = 11000; + TCHAR appname[MAX_PATH + 1]; + TCHAR *p; + if (GetModuleFileName(NULL, appname, MAX_PATH + 1) == 0) { + return -1; + } + for (p = &appname[strlen(appname) - 1]; p != appname && *p != '\\'; p--) { + } + p++; + if (RegCreateKey(HKEY_CURRENT_USER, WEBVIEW_KEY_FEATURE_BROWSER_EMULATION, + &hKey) != ERROR_SUCCESS) { + return -1; + } + if (RegSetValueEx(hKey, p, 0, REG_DWORD, (BYTE *)&ie_version, + sizeof(ie_version)) != ERROR_SUCCESS) { + RegCloseKey(hKey); + return -1; + } + RegCloseKey(hKey); + return 0; +} + +WEBVIEW_API int webview_init(struct webview *w) { + WNDCLASSEX wc; + HINSTANCE hInstance; + DWORD style; + RECT clientRect; + RECT rect; + + if (webview_fix_ie_compat_mode() < 0) { + return -1; + } + + hInstance = GetModuleHandle(NULL); + if (hInstance == NULL) { + return -1; + } + if (OleInitialize(NULL) != S_OK) { + return -1; + } + ZeroMemory(&wc, sizeof(WNDCLASSEX)); + wc.cbSize = sizeof(WNDCLASSEX); + wc.hInstance = hInstance; + wc.lpfnWndProc = wndproc; + wc.lpszClassName = classname; + RegisterClassEx(&wc); + + style = WS_OVERLAPPEDWINDOW; + if (!w->resizable) { + style = WS_OVERLAPPED | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU; + } + + rect.left = 0; + rect.top = 0; + rect.right = w->width; + rect.bottom = w->height; + AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, 0); + + GetClientRect(GetDesktopWindow(), &clientRect); + int left = (clientRect.right / 2) - ((rect.right - rect.left) / 2); + int top = (clientRect.bottom / 2) - ((rect.bottom - rect.top) / 2); + rect.right = rect.right - rect.left + left; + rect.left = left; + rect.bottom = rect.bottom - rect.top + top; + rect.top = top; + + w->priv.hwnd = + CreateWindowEx(0, classname, w->title, style, rect.left, rect.top, + rect.right - rect.left, rect.bottom - rect.top, + HWND_DESKTOP, NULL, hInstance, (void *)w); + if (w->priv.hwnd == 0) { + OleUninitialize(); + return -1; + } + + SetWindowLongPtr(w->priv.hwnd, GWLP_USERDATA, (LONG_PTR)w); + + DisplayHTMLPage(w); + + SetWindowText(w->priv.hwnd, w->title); + ShowWindow(w->priv.hwnd, SW_SHOWDEFAULT); + UpdateWindow(w->priv.hwnd); + SetFocus(w->priv.hwnd); + + return 0; +} + +WEBVIEW_API int webview_loop(struct webview *w, int blocking) { + MSG msg; + if (blocking) { + GetMessage(&msg, 0, 0, 0); + } else { + PeekMessage(&msg, 0, 0, 0, PM_REMOVE); + } + switch (msg.message) { + case WM_QUIT: + return -1; + case WM_COMMAND: + case WM_KEYDOWN: + case WM_KEYUP: { + HRESULT r = S_OK; + IWebBrowser2 *webBrowser2; + IOleObject *browser = *w->priv.browser; + if (browser->lpVtbl->QueryInterface(browser, iid_unref(&IID_IWebBrowser2), + (void **)&webBrowser2) == S_OK) { + IOleInPlaceActiveObject *pIOIPAO; + if (browser->lpVtbl->QueryInterface( + browser, iid_unref(&IID_IOleInPlaceActiveObject), + (void **)&pIOIPAO) == S_OK) { + r = pIOIPAO->lpVtbl->TranslateAccelerator(pIOIPAO, &msg); + pIOIPAO->lpVtbl->Release(pIOIPAO); + } + webBrowser2->lpVtbl->Release(webBrowser2); + } + if (r != S_FALSE) { + break; + } + } + default: + TranslateMessage(&msg); + DispatchMessage(&msg); + } + return 0; +} + +WEBVIEW_API int webview_eval(struct webview *w, const char *js) { + IWebBrowser2 *webBrowser2; + IHTMLDocument2 *htmlDoc2; + IDispatch *docDispatch; + IDispatch *scriptDispatch; + if ((*w->priv.browser) + ->lpVtbl->QueryInterface((*w->priv.browser), + iid_unref(&IID_IWebBrowser2), + (void **)&webBrowser2) != S_OK) { + return -1; + } + + if (webBrowser2->lpVtbl->get_Document(webBrowser2, &docDispatch) != S_OK) { + return -1; + } + if (docDispatch->lpVtbl->QueryInterface(docDispatch, + iid_unref(&IID_IHTMLDocument2), + (void **)&htmlDoc2) != S_OK) { + return -1; + } + if (htmlDoc2->lpVtbl->get_Script(htmlDoc2, &scriptDispatch) != S_OK) { + return -1; + } + DISPID dispid; + BSTR evalStr = SysAllocString(L"eval"); + if (scriptDispatch->lpVtbl->GetIDsOfNames( + scriptDispatch, iid_unref(&IID_NULL), &evalStr, 1, + LOCALE_SYSTEM_DEFAULT, &dispid) != S_OK) { + SysFreeString(evalStr); + return -1; + } + SysFreeString(evalStr); + + DISPPARAMS params; + VARIANT arg; + VARIANT result; + EXCEPINFO excepInfo; + UINT nArgErr = (UINT)-1; + params.cArgs = 1; + params.cNamedArgs = 0; + params.rgvarg = &arg; + arg.vt = VT_BSTR; + static const char *prologue = "(function(){"; + static const char *epilogue = ";})();"; + int n = strlen(prologue) + strlen(epilogue) + strlen(js) + 1; + char *eval = (char *)malloc(n); + snprintf(eval, n, "%s%s%s", prologue, js, epilogue); + wchar_t *buf = webview_to_utf16(eval); + if (buf == NULL) { + return -1; + } + arg.bstrVal = SysAllocString(buf); + if (scriptDispatch->lpVtbl->Invoke( + scriptDispatch, dispid, iid_unref(&IID_NULL), 0, DISPATCH_METHOD, + ¶ms, &result, &excepInfo, &nArgErr) != S_OK) { + return -1; + } + SysFreeString(arg.bstrVal); + free(eval); + scriptDispatch->lpVtbl->Release(scriptDispatch); + htmlDoc2->lpVtbl->Release(htmlDoc2); + docDispatch->lpVtbl->Release(docDispatch); + return 0; +} + +WEBVIEW_API void webview_dispatch(struct webview *w, webview_dispatch_fn fn, + void *arg) { + PostMessageW(w->priv.hwnd, WM_WEBVIEW_DISPATCH, (WPARAM)fn, (LPARAM)arg); +} + +WEBVIEW_API void webview_set_title(struct webview *w, const char *title) { + SetWindowText(w->priv.hwnd, title); +} + +WEBVIEW_API void webview_set_fullscreen(struct webview *w, int fullscreen) { + if (w->priv.is_fullscreen == !!fullscreen) { + return; + } + if (w->priv.is_fullscreen == 0) { + w->priv.saved_style = GetWindowLong(w->priv.hwnd, GWL_STYLE); + w->priv.saved_ex_style = GetWindowLong(w->priv.hwnd, GWL_EXSTYLE); + GetWindowRect(w->priv.hwnd, &w->priv.saved_rect); + } + w->priv.is_fullscreen = !!fullscreen; + if (fullscreen) { + MONITORINFO monitor_info; + SetWindowLong(w->priv.hwnd, GWL_STYLE, + w->priv.saved_style & ~(WS_CAPTION | WS_THICKFRAME)); + SetWindowLong(w->priv.hwnd, GWL_EXSTYLE, + w->priv.saved_ex_style & + ~(WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE | + WS_EX_CLIENTEDGE | WS_EX_STATICEDGE)); + monitor_info.cbSize = sizeof(monitor_info); + GetMonitorInfo(MonitorFromWindow(w->priv.hwnd, MONITOR_DEFAULTTONEAREST), + &monitor_info); + RECT r; + r.left = monitor_info.rcMonitor.left; + r.top = monitor_info.rcMonitor.top; + r.right = monitor_info.rcMonitor.right; + r.bottom = monitor_info.rcMonitor.bottom; + SetWindowPos(w->priv.hwnd, NULL, r.left, r.top, r.right - r.left, + r.bottom - r.top, + SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED); + } else { + SetWindowLong(w->priv.hwnd, GWL_STYLE, w->priv.saved_style); + SetWindowLong(w->priv.hwnd, GWL_EXSTYLE, w->priv.saved_ex_style); + SetWindowPos(w->priv.hwnd, NULL, w->priv.saved_rect.left, + w->priv.saved_rect.top, + w->priv.saved_rect.right - w->priv.saved_rect.left, + w->priv.saved_rect.bottom - w->priv.saved_rect.top, + SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED); + } +} + +WEBVIEW_API void webview_set_color(struct webview *w, uint8_t r, uint8_t g, + uint8_t b, uint8_t a) { + HBRUSH brush = CreateSolidBrush(RGB(r, g, b)); + SetClassLongPtr(w->priv.hwnd, GCLP_HBRBACKGROUND, (LONG_PTR)brush); +} + +/* These are missing parts from MinGW */ +#ifndef __IFileDialog_INTERFACE_DEFINED__ +#define __IFileDialog_INTERFACE_DEFINED__ +enum _FILEOPENDIALOGOPTIONS { + FOS_OVERWRITEPROMPT = 0x2, + FOS_STRICTFILETYPES = 0x4, + FOS_NOCHANGEDIR = 0x8, + FOS_PICKFOLDERS = 0x20, + FOS_FORCEFILESYSTEM = 0x40, + FOS_ALLNONSTORAGEITEMS = 0x80, + FOS_NOVALIDATE = 0x100, + FOS_ALLOWMULTISELECT = 0x200, + FOS_PATHMUSTEXIST = 0x800, + FOS_FILEMUSTEXIST = 0x1000, + FOS_CREATEPROMPT = 0x2000, + FOS_SHAREAWARE = 0x4000, + FOS_NOREADONLYRETURN = 0x8000, + FOS_NOTESTFILECREATE = 0x10000, + FOS_HIDEMRUPLACES = 0x20000, + FOS_HIDEPINNEDPLACES = 0x40000, + FOS_NODEREFERENCELINKS = 0x100000, + FOS_DONTADDTORECENT = 0x2000000, + FOS_FORCESHOWHIDDEN = 0x10000000, + FOS_DEFAULTNOMINIMODE = 0x20000000, + FOS_FORCEPREVIEWPANEON = 0x40000000 +}; +typedef DWORD FILEOPENDIALOGOPTIONS; +typedef enum FDAP { FDAP_BOTTOM = 0, FDAP_TOP = 1 } FDAP; +DEFINE_GUID(IID_IFileDialog, 0x42f85136, 0xdb7e, 0x439c, 0x85, 0xf1, 0xe4, 0x07, + 0x5d, 0x13, 0x5f, 0xc8); +typedef struct IFileDialogVtbl { + BEGIN_INTERFACE + HRESULT(STDMETHODCALLTYPE *QueryInterface) + (IFileDialog *This, REFIID riid, void **ppvObject); + ULONG(STDMETHODCALLTYPE *AddRef)(IFileDialog *This); + ULONG(STDMETHODCALLTYPE *Release)(IFileDialog *This); + HRESULT(STDMETHODCALLTYPE *Show)(IFileDialog *This, HWND hwndOwner); + HRESULT(STDMETHODCALLTYPE *SetFileTypes) + (IFileDialog *This, UINT cFileTypes, const COMDLG_FILTERSPEC *rgFilterSpec); + HRESULT(STDMETHODCALLTYPE *SetFileTypeIndex) + (IFileDialog *This, UINT iFileType); + HRESULT(STDMETHODCALLTYPE *GetFileTypeIndex) + (IFileDialog *This, UINT *piFileType); + HRESULT(STDMETHODCALLTYPE *Advise) + (IFileDialog *This, IFileDialogEvents *pfde, DWORD *pdwCookie); + HRESULT(STDMETHODCALLTYPE *Unadvise)(IFileDialog *This, DWORD dwCookie); + HRESULT(STDMETHODCALLTYPE *SetOptions) + (IFileDialog *This, FILEOPENDIALOGOPTIONS fos); + HRESULT(STDMETHODCALLTYPE *GetOptions) + (IFileDialog *This, FILEOPENDIALOGOPTIONS *pfos); + HRESULT(STDMETHODCALLTYPE *SetDefaultFolder) + (IFileDialog *This, IShellItem *psi); + HRESULT(STDMETHODCALLTYPE *SetFolder)(IFileDialog *This, IShellItem *psi); + HRESULT(STDMETHODCALLTYPE *GetFolder)(IFileDialog *This, IShellItem **ppsi); + HRESULT(STDMETHODCALLTYPE *GetCurrentSelection) + (IFileDialog *This, IShellItem **ppsi); + HRESULT(STDMETHODCALLTYPE *SetFileName)(IFileDialog *This, LPCWSTR pszName); + HRESULT(STDMETHODCALLTYPE *GetFileName)(IFileDialog *This, LPWSTR *pszName); + HRESULT(STDMETHODCALLTYPE *SetTitle)(IFileDialog *This, LPCWSTR pszTitle); + HRESULT(STDMETHODCALLTYPE *SetOkButtonLabel) + (IFileDialog *This, LPCWSTR pszText); + HRESULT(STDMETHODCALLTYPE *SetFileNameLabel) + (IFileDialog *This, LPCWSTR pszLabel); + HRESULT(STDMETHODCALLTYPE *GetResult)(IFileDialog *This, IShellItem **ppsi); + HRESULT(STDMETHODCALLTYPE *AddPlace) + (IFileDialog *This, IShellItem *psi, FDAP fdap); + HRESULT(STDMETHODCALLTYPE *SetDefaultExtension) + (IFileDialog *This, LPCWSTR pszDefaultExtension); + HRESULT(STDMETHODCALLTYPE *Close)(IFileDialog *This, HRESULT hr); + HRESULT(STDMETHODCALLTYPE *SetClientGuid)(IFileDialog *This, REFGUID guid); + HRESULT(STDMETHODCALLTYPE *ClearClientData)(IFileDialog *This); + HRESULT(STDMETHODCALLTYPE *SetFilter) + (IFileDialog *This, IShellItemFilter *pFilter); + END_INTERFACE +} IFileDialogVtbl; +interface IFileDialog { + CONST_VTBL IFileDialogVtbl *lpVtbl; +}; +DEFINE_GUID(IID_IFileOpenDialog, 0xd57c7288, 0xd4ad, 0x4768, 0xbe, 0x02, 0x9d, + 0x96, 0x95, 0x32, 0xd9, 0x60); +DEFINE_GUID(IID_IFileSaveDialog, 0x84bccd23, 0x5fde, 0x4cdb, 0xae, 0xa4, 0xaf, + 0x64, 0xb8, 0x3d, 0x78, 0xab); +#endif + +WEBVIEW_API void webview_dialog(struct webview *w, + enum webview_dialog_type dlgtype, int flags, + const char *title, const char *arg, + char *result, size_t resultsz) { + if (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN || + dlgtype == WEBVIEW_DIALOG_TYPE_SAVE) { + IFileDialog *dlg = NULL; + IShellItem *res = NULL; + WCHAR *ws = NULL; + char *s = NULL; + FILEOPENDIALOGOPTIONS opts = 0, add_opts = 0; + if (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN) { + if (CoCreateInstance( + iid_unref(&CLSID_FileOpenDialog), NULL, CLSCTX_INPROC_SERVER, + iid_unref(&IID_IFileOpenDialog), (void **)&dlg) != S_OK) { + goto error_dlg; + } + if (flags & WEBVIEW_DIALOG_FLAG_DIRECTORY) { + add_opts |= FOS_PICKFOLDERS; + } + add_opts |= FOS_NOCHANGEDIR | FOS_ALLNONSTORAGEITEMS | FOS_NOVALIDATE | + FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST | FOS_SHAREAWARE | + FOS_NOTESTFILECREATE | FOS_NODEREFERENCELINKS | + FOS_FORCESHOWHIDDEN | FOS_DEFAULTNOMINIMODE; + } else { + if (CoCreateInstance( + iid_unref(&CLSID_FileSaveDialog), NULL, CLSCTX_INPROC_SERVER, + iid_unref(&IID_IFileSaveDialog), (void **)&dlg) != S_OK) { + goto error_dlg; + } + add_opts |= FOS_OVERWRITEPROMPT | FOS_NOCHANGEDIR | + FOS_ALLNONSTORAGEITEMS | FOS_NOVALIDATE | FOS_SHAREAWARE | + FOS_NOTESTFILECREATE | FOS_NODEREFERENCELINKS | + FOS_FORCESHOWHIDDEN | FOS_DEFAULTNOMINIMODE; + } + if (dlg->lpVtbl->GetOptions(dlg, &opts) != S_OK) { + goto error_dlg; + } + opts &= ~FOS_NOREADONLYRETURN; + opts |= add_opts; + if (dlg->lpVtbl->SetOptions(dlg, opts) != S_OK) { + goto error_dlg; + } + if (dlg->lpVtbl->Show(dlg, w->priv.hwnd) != S_OK) { + goto error_dlg; + } + if (dlg->lpVtbl->GetResult(dlg, &res) != S_OK) { + goto error_dlg; + } + if (res->lpVtbl->GetDisplayName(res, SIGDN_FILESYSPATH, &ws) != S_OK) { + goto error_result; + } + s = webview_from_utf16(ws); + strncpy(result, s, resultsz); + result[resultsz - 1] = '\0'; + CoTaskMemFree(ws); + error_result: + res->lpVtbl->Release(res); + error_dlg: + dlg->lpVtbl->Release(dlg); + return; + } else if (dlgtype == WEBVIEW_DIALOG_TYPE_ALERT) { +#if 0 + /* MinGW often doesn't contain TaskDialog, we'll use MessageBox for now */ + WCHAR *wtitle = webview_to_utf16(title); + WCHAR *warg = webview_to_utf16(arg); + TaskDialog(w->priv.hwnd, NULL, NULL, wtitle, warg, 0, NULL, NULL); + GlobalFree(warg); + GlobalFree(wtitle); +#else + UINT type = MB_OK; + switch (flags & WEBVIEW_DIALOG_FLAG_ALERT_MASK) { + case WEBVIEW_DIALOG_FLAG_INFO: + type |= MB_ICONINFORMATION; + break; + case WEBVIEW_DIALOG_FLAG_WARNING: + type |= MB_ICONWARNING; + break; + case WEBVIEW_DIALOG_FLAG_ERROR: + type |= MB_ICONERROR; + break; + } + MessageBox(w->priv.hwnd, arg, title, type); +#endif + } +} + +WEBVIEW_API void webview_terminate(struct webview *w) { PostQuitMessage(0); } + +WEBVIEW_API void webview_exit(struct webview *w) { + DestroyWindow(w->priv.hwnd); + OleUninitialize(); +} + +WEBVIEW_API void webview_print_log(const char *s) { OutputDebugString(s); } diff --git a/ui/tauri.h b/ui/tauri.h index b2805c7fa..35afd5f0f 100644 --- a/ui/tauri.h +++ b/ui/tauri.h @@ -37,57 +37,7 @@ extern "C" { #include #include #include - -#if defined(WEBVIEW_GTK) -#include -#include -#include - -struct webview_priv { - GtkWidget *window; - GtkWidget *scroller; - GtkWidget *webview; - GtkWidget *inspector_window; - GAsyncQueue *queue; - int ready; - int js_busy; - int should_exit; -}; -#elif defined(WEBVIEW_WINAPI) -#define CINTERFACE -#include - -#include -#include -#include -#include -#include - -#include - -struct webview_priv { - HWND hwnd; - IOleObject **browser; - BOOL is_fullscreen; - DWORD saved_style; - DWORD saved_ex_style; - RECT saved_rect; -}; -#elif defined(WEBVIEW_COCOA) -#include -#include -#include - -struct webview_priv { - id pool; - id window; - id webview; - id windowDelegate; - int should_exit; -}; -#else -#error "Define one of: WEBVIEW_GTK, WEBVIEW_COCOA or WEBVIEW_WINAPI" -#endif +#include struct webview; @@ -241,2065 +191,6 @@ WEBVIEW_API int webview_inject_css(struct webview *w, const char *css) { return r; } -#if defined(WEBVIEW_GTK) -static void external_message_received_cb(WebKitUserContentManager *m, - WebKitJavascriptResult *r, - gpointer arg) { - (void)m; - struct webview *w = (struct webview *)arg; - if (w->external_invoke_cb == NULL) { - return; - } - JSGlobalContextRef context = webkit_javascript_result_get_global_context(r); - JSValueRef value = webkit_javascript_result_get_value(r); - JSStringRef js = JSValueToStringCopy(context, value, NULL); - size_t n = JSStringGetMaximumUTF8CStringSize(js); - char *s = g_new(char, n); - JSStringGetUTF8CString(js, s, n); - w->external_invoke_cb(w, s); - JSStringRelease(js); - g_free(s); -} - -static void webview_load_changed_cb(WebKitWebView *webview, - WebKitLoadEvent event, gpointer arg) { - (void)webview; - struct webview *w = (struct webview *)arg; - if (event == WEBKIT_LOAD_FINISHED) { - w->priv.ready = 1; - } -} - -static void webview_destroy_cb(GtkWidget *widget, gpointer arg) { - (void)widget; - struct webview *w = (struct webview *)arg; - webview_terminate(w); -} - -static gboolean webview_context_menu_cb(WebKitWebView *webview, - GtkWidget *default_menu, - WebKitHitTestResult *hit_test_result, - gboolean triggered_with_keyboard, - gpointer userdata) { - (void)webview; - (void)default_menu; - (void)hit_test_result; - (void)triggered_with_keyboard; - (void)userdata; - return TRUE; -} - -WEBVIEW_API int webview_init(struct webview *w) { - if (gtk_init_check(0, NULL) == FALSE) { - return -1; - } - - w->priv.ready = 0; - w->priv.should_exit = 0; - w->priv.queue = g_async_queue_new(); - w->priv.window = gtk_window_new(GTK_WINDOW_TOPLEVEL); - gtk_window_set_title(GTK_WINDOW(w->priv.window), w->title); - - if (w->resizable) { - gtk_window_set_default_size(GTK_WINDOW(w->priv.window), w->width, - w->height); - } else { - gtk_widget_set_size_request(w->priv.window, w->width, w->height); - } - gtk_window_set_resizable(GTK_WINDOW(w->priv.window), !!w->resizable); - gtk_window_set_position(GTK_WINDOW(w->priv.window), GTK_WIN_POS_CENTER); - - w->priv.scroller = gtk_scrolled_window_new(NULL, NULL); - gtk_container_add(GTK_CONTAINER(w->priv.window), w->priv.scroller); - - WebKitUserContentManager *m = webkit_user_content_manager_new(); - webkit_user_content_manager_register_script_message_handler(m, "external"); - g_signal_connect(m, "script-message-received::external", - G_CALLBACK(external_message_received_cb), w); - - w->priv.webview = webkit_web_view_new_with_user_content_manager(m); - webkit_web_view_load_uri(WEBKIT_WEB_VIEW(w->priv.webview), - webview_check_url(w->url)); - g_signal_connect(G_OBJECT(w->priv.webview), "load-changed", - G_CALLBACK(webview_load_changed_cb), w); - gtk_container_add(GTK_CONTAINER(w->priv.scroller), w->priv.webview); - - if (w->debug) { - WebKitSettings *settings = - webkit_web_view_get_settings(WEBKIT_WEB_VIEW(w->priv.webview)); - webkit_settings_set_enable_write_console_messages_to_stdout(settings, true); - webkit_settings_set_enable_developer_extras(settings, true); - } else { - g_signal_connect(G_OBJECT(w->priv.webview), "context-menu", - G_CALLBACK(webview_context_menu_cb), w); - } - - gtk_widget_show_all(w->priv.window); - - webkit_web_view_run_javascript( - WEBKIT_WEB_VIEW(w->priv.webview), - "window.external={invoke:function(x){" - "window.webkit.messageHandlers.external.postMessage(x);}}", - NULL, NULL, NULL); - - g_signal_connect(G_OBJECT(w->priv.window), "destroy", - G_CALLBACK(webview_destroy_cb), w); - return 0; -} - -WEBVIEW_API int webview_loop(struct webview *w, int blocking) { - gtk_main_iteration_do(blocking); - return w->priv.should_exit; -} - -WEBVIEW_API void webview_set_title(struct webview *w, const char *title) { - gtk_window_set_title(GTK_WINDOW(w->priv.window), title); -} - -WEBVIEW_API void webview_set_fullscreen(struct webview *w, int fullscreen) { - if (fullscreen) { - gtk_window_fullscreen(GTK_WINDOW(w->priv.window)); - } else { - gtk_window_unfullscreen(GTK_WINDOW(w->priv.window)); - } -} - -WEBVIEW_API void webview_set_color(struct webview *w, uint8_t r, uint8_t g, - uint8_t b, uint8_t a) { - GdkRGBA color = {r / 255.0, g / 255.0, b / 255.0, a / 255.0}; - webkit_web_view_set_background_color(WEBKIT_WEB_VIEW(w->priv.webview), - &color); -} - -WEBVIEW_API void webview_dialog(struct webview *w, - enum webview_dialog_type dlgtype, int flags, - const char *title, const char *arg, - char *result, size_t resultsz) { - GtkWidget *dlg; - if (result != NULL) { - result[0] = '\0'; - } - if (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN || - dlgtype == WEBVIEW_DIALOG_TYPE_SAVE) { - dlg = gtk_file_chooser_dialog_new( - title, GTK_WINDOW(w->priv.window), - (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN - ? (flags & WEBVIEW_DIALOG_FLAG_DIRECTORY - ? GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER - : GTK_FILE_CHOOSER_ACTION_OPEN) - : GTK_FILE_CHOOSER_ACTION_SAVE), - "_Cancel", GTK_RESPONSE_CANCEL, - (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN ? "_Open" : "_Save"), - GTK_RESPONSE_ACCEPT, NULL); - gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(dlg), FALSE); - gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dlg), FALSE); - gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(dlg), TRUE); - gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dlg), TRUE); - gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER(dlg), TRUE); - gint response = gtk_dialog_run(GTK_DIALOG(dlg)); - if (response == GTK_RESPONSE_ACCEPT) { - gchar *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dlg)); - g_strlcpy(result, filename, resultsz); - g_free(filename); - } - gtk_widget_destroy(dlg); - } else if (dlgtype == WEBVIEW_DIALOG_TYPE_ALERT) { - GtkMessageType type = GTK_MESSAGE_OTHER; - switch (flags & WEBVIEW_DIALOG_FLAG_ALERT_MASK) { - case WEBVIEW_DIALOG_FLAG_INFO: - type = GTK_MESSAGE_INFO; - break; - case WEBVIEW_DIALOG_FLAG_WARNING: - type = GTK_MESSAGE_WARNING; - break; - case WEBVIEW_DIALOG_FLAG_ERROR: - type = GTK_MESSAGE_ERROR; - break; - } - dlg = gtk_message_dialog_new(GTK_WINDOW(w->priv.window), GTK_DIALOG_MODAL, - type, GTK_BUTTONS_OK, "%s", title); - gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dlg), "%s", - arg); - gtk_dialog_run(GTK_DIALOG(dlg)); - gtk_widget_destroy(dlg); - } -} - -static void webview_eval_finished(GObject *object, GAsyncResult *result, - gpointer userdata) { - (void)object; - (void)result; - struct webview *w = (struct webview *)userdata; - w->priv.js_busy = 0; -} - -WEBVIEW_API int webview_eval(struct webview *w, const char *js) { - while (w->priv.ready == 0) { - g_main_context_iteration(NULL, TRUE); - } - w->priv.js_busy = 1; - webkit_web_view_run_javascript(WEBKIT_WEB_VIEW(w->priv.webview), js, NULL, - webview_eval_finished, w); - while (w->priv.js_busy) { - g_main_context_iteration(NULL, TRUE); - } - return 0; -} - -static gboolean webview_dispatch_wrapper(gpointer userdata) { - struct webview *w = (struct webview *)userdata; - for (;;) { - struct webview_dispatch_arg *arg = - (struct webview_dispatch_arg *)g_async_queue_try_pop(w->priv.queue); - if (arg == NULL) { - break; - } - (arg->fn)(w, arg->arg); - g_free(arg); - } - return FALSE; -} - -WEBVIEW_API void webview_dispatch(struct webview *w, webview_dispatch_fn fn, - void *arg) { - struct webview_dispatch_arg *context = - (struct webview_dispatch_arg *)g_new(struct webview_dispatch_arg, 1); - context->w = w; - context->arg = arg; - context->fn = fn; - g_async_queue_lock(w->priv.queue); - g_async_queue_push_unlocked(w->priv.queue, context); - if (g_async_queue_length_unlocked(w->priv.queue) == 1) { - gdk_threads_add_idle(webview_dispatch_wrapper, w); - } - g_async_queue_unlock(w->priv.queue); -} - -WEBVIEW_API void webview_terminate(struct webview *w) { - w->priv.should_exit = 1; -} - -WEBVIEW_API void webview_exit(struct webview *w) { (void)w; } -WEBVIEW_API void webview_print_log(const char *s) { - fprintf(stderr, "%s\n", s); -} - -#endif /* WEBVIEW_GTK */ - -#if defined(WEBVIEW_WINAPI) - -#pragma comment(lib, "user32.lib") -#pragma comment(lib, "ole32.lib") -#pragma comment(lib, "oleaut32.lib") - -#define WM_WEBVIEW_DISPATCH (WM_APP + 1) - -typedef struct { - IOleInPlaceFrame frame; - HWND window; -} _IOleInPlaceFrameEx; - -typedef struct { - IOleInPlaceSite inplace; - _IOleInPlaceFrameEx frame; -} _IOleInPlaceSiteEx; - -typedef struct { - IDocHostUIHandler ui; -} _IDocHostUIHandlerEx; - -typedef struct { - IInternetSecurityManager mgr; -} _IInternetSecurityManagerEx; - -typedef struct { - IServiceProvider provider; - _IInternetSecurityManagerEx mgr; -} _IServiceProviderEx; - -typedef struct { - IOleClientSite client; - _IOleInPlaceSiteEx inplace; - _IDocHostUIHandlerEx ui; - IDispatch external; - _IServiceProviderEx provider; -} _IOleClientSiteEx; - -#ifdef __cplusplus -#define iid_ref(x) &(x) -#define iid_unref(x) *(x) -#else -#define iid_ref(x) (x) -#define iid_unref(x) (x) -#endif - -static inline WCHAR *webview_to_utf16(const char *s) { - DWORD size = MultiByteToWideChar(CP_UTF8, 0, s, -1, 0, 0); - WCHAR *ws = (WCHAR *)GlobalAlloc(GMEM_FIXED, sizeof(WCHAR) * size); - if (ws == NULL) { - return NULL; - } - MultiByteToWideChar(CP_UTF8, 0, s, -1, ws, size); - return ws; -} - -static inline char *webview_from_utf16(WCHAR *ws) { - int n = WideCharToMultiByte(CP_UTF8, 0, ws, -1, NULL, 0, NULL, NULL); - char *s = (char *)GlobalAlloc(GMEM_FIXED, n); - if (s == NULL) { - return NULL; - } - WideCharToMultiByte(CP_UTF8, 0, ws, -1, s, n, NULL, NULL); - return s; -} - -static int iid_eq(REFIID a, const IID *b) { - return memcmp((const void *)iid_ref(a), (const void *)b, sizeof(GUID)) == 0; -} - -static HRESULT STDMETHODCALLTYPE JS_QueryInterface(IDispatch FAR *This, - REFIID riid, - LPVOID FAR *ppvObj) { - if (iid_eq(riid, &IID_IDispatch)) { - *ppvObj = This; - return S_OK; - } - *ppvObj = 0; - return E_NOINTERFACE; -} -static ULONG STDMETHODCALLTYPE JS_AddRef(IDispatch FAR *This) { return 1; } -static ULONG STDMETHODCALLTYPE JS_Release(IDispatch FAR *This) { return 1; } -static HRESULT STDMETHODCALLTYPE JS_GetTypeInfoCount(IDispatch FAR *This, - UINT *pctinfo) { - return S_OK; -} -static HRESULT STDMETHODCALLTYPE JS_GetTypeInfo(IDispatch FAR *This, - UINT iTInfo, LCID lcid, - ITypeInfo **ppTInfo) { - return S_OK; -} -#define WEBVIEW_JS_INVOKE_ID 0x1000 -static HRESULT STDMETHODCALLTYPE JS_GetIDsOfNames(IDispatch FAR *This, - REFIID riid, - LPOLESTR *rgszNames, - UINT cNames, LCID lcid, - DISPID *rgDispId) { - if (cNames != 1) { - return S_FALSE; - } - if (wcscmp(rgszNames[0], L"invoke") == 0) { - rgDispId[0] = WEBVIEW_JS_INVOKE_ID; - return S_OK; - } - return S_FALSE; -} - -static HRESULT STDMETHODCALLTYPE -JS_Invoke(IDispatch FAR *This, DISPID dispIdMember, REFIID riid, LCID lcid, - WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, - EXCEPINFO *pExcepInfo, UINT *puArgErr) { - size_t offset = (size_t) & ((_IOleClientSiteEx *)NULL)->external; - _IOleClientSiteEx *ex = (_IOleClientSiteEx *)((char *)(This)-offset); - struct webview *w = (struct webview *)GetWindowLongPtr( - ex->inplace.frame.window, GWLP_USERDATA); - if (pDispParams->cArgs == 1 && pDispParams->rgvarg[0].vt == VT_BSTR) { - BSTR bstr = pDispParams->rgvarg[0].bstrVal; - char *s = webview_from_utf16(bstr); - if (s != NULL) { - if (dispIdMember == WEBVIEW_JS_INVOKE_ID) { - if (w->external_invoke_cb != NULL) { - w->external_invoke_cb(w, s); - } - } else { - return S_FALSE; - } - GlobalFree(s); - } - } - return S_OK; -} - -static IDispatchVtbl ExternalDispatchTable = { - JS_QueryInterface, JS_AddRef, JS_Release, JS_GetTypeInfoCount, - JS_GetTypeInfo, JS_GetIDsOfNames, JS_Invoke}; - -static ULONG STDMETHODCALLTYPE Site_AddRef(IOleClientSite FAR *This) { - return 1; -} -static ULONG STDMETHODCALLTYPE Site_Release(IOleClientSite FAR *This) { - return 1; -} -static HRESULT STDMETHODCALLTYPE Site_SaveObject(IOleClientSite FAR *This) { - return E_NOTIMPL; -} -static HRESULT STDMETHODCALLTYPE Site_GetMoniker(IOleClientSite FAR *This, - DWORD dwAssign, - DWORD dwWhichMoniker, - IMoniker **ppmk) { - return E_NOTIMPL; -} -static HRESULT STDMETHODCALLTYPE -Site_GetContainer(IOleClientSite FAR *This, LPOLECONTAINER FAR *ppContainer) { - *ppContainer = 0; - return E_NOINTERFACE; -} -static HRESULT STDMETHODCALLTYPE Site_ShowObject(IOleClientSite FAR *This) { - return NOERROR; -} -static HRESULT STDMETHODCALLTYPE Site_OnShowWindow(IOleClientSite FAR *This, - BOOL fShow) { - return E_NOTIMPL; -} -static HRESULT STDMETHODCALLTYPE -Site_RequestNewObjectLayout(IOleClientSite FAR *This) { - return E_NOTIMPL; -} -static HRESULT STDMETHODCALLTYPE Site_QueryInterface(IOleClientSite FAR *This, - REFIID riid, - void **ppvObject) { - if (iid_eq(riid, &IID_IUnknown) || iid_eq(riid, &IID_IOleClientSite)) { - *ppvObject = &((_IOleClientSiteEx *)This)->client; - } else if (iid_eq(riid, &IID_IOleInPlaceSite)) { - *ppvObject = &((_IOleClientSiteEx *)This)->inplace; - } else if (iid_eq(riid, &IID_IDocHostUIHandler)) { - *ppvObject = &((_IOleClientSiteEx *)This)->ui; - } else if (iid_eq(riid, &IID_IServiceProvider)) { - *ppvObject = &((_IOleClientSiteEx *)This)->provider; - } else { - *ppvObject = 0; - return (E_NOINTERFACE); - } - return S_OK; -} -static HRESULT STDMETHODCALLTYPE InPlace_QueryInterface( - IOleInPlaceSite FAR *This, REFIID riid, LPVOID FAR *ppvObj) { - return (Site_QueryInterface( - (IOleClientSite *)((char *)This - sizeof(IOleClientSite)), riid, ppvObj)); -} -static ULONG STDMETHODCALLTYPE InPlace_AddRef(IOleInPlaceSite FAR *This) { - return 1; -} -static ULONG STDMETHODCALLTYPE InPlace_Release(IOleInPlaceSite FAR *This) { - return 1; -} -static HRESULT STDMETHODCALLTYPE InPlace_GetWindow(IOleInPlaceSite FAR *This, - HWND FAR *lphwnd) { - *lphwnd = ((_IOleInPlaceSiteEx FAR *)This)->frame.window; - return S_OK; -} -static HRESULT STDMETHODCALLTYPE -InPlace_ContextSensitiveHelp(IOleInPlaceSite FAR *This, BOOL fEnterMode) { - return E_NOTIMPL; -} -static HRESULT STDMETHODCALLTYPE -InPlace_CanInPlaceActivate(IOleInPlaceSite FAR *This) { - return S_OK; -} -static HRESULT STDMETHODCALLTYPE -InPlace_OnInPlaceActivate(IOleInPlaceSite FAR *This) { - return S_OK; -} -static HRESULT STDMETHODCALLTYPE -InPlace_OnUIActivate(IOleInPlaceSite FAR *This) { - return S_OK; -} -static HRESULT STDMETHODCALLTYPE InPlace_GetWindowContext( - IOleInPlaceSite FAR *This, LPOLEINPLACEFRAME FAR *lplpFrame, - LPOLEINPLACEUIWINDOW FAR *lplpDoc, LPRECT lprcPosRect, LPRECT lprcClipRect, - LPOLEINPLACEFRAMEINFO lpFrameInfo) { - *lplpFrame = (LPOLEINPLACEFRAME) & ((_IOleInPlaceSiteEx *)This)->frame; - *lplpDoc = 0; - lpFrameInfo->fMDIApp = FALSE; - lpFrameInfo->hwndFrame = ((_IOleInPlaceFrameEx *)*lplpFrame)->window; - lpFrameInfo->haccel = 0; - lpFrameInfo->cAccelEntries = 0; - return S_OK; -} -static HRESULT STDMETHODCALLTYPE InPlace_Scroll(IOleInPlaceSite FAR *This, - SIZE scrollExtent) { - return E_NOTIMPL; -} -static HRESULT STDMETHODCALLTYPE -InPlace_OnUIDeactivate(IOleInPlaceSite FAR *This, BOOL fUndoable) { - return S_OK; -} -static HRESULT STDMETHODCALLTYPE -InPlace_OnInPlaceDeactivate(IOleInPlaceSite FAR *This) { - return S_OK; -} -static HRESULT STDMETHODCALLTYPE -InPlace_DiscardUndoState(IOleInPlaceSite FAR *This) { - return E_NOTIMPL; -} -static HRESULT STDMETHODCALLTYPE -InPlace_DeactivateAndUndo(IOleInPlaceSite FAR *This) { - return E_NOTIMPL; -} -static HRESULT STDMETHODCALLTYPE -InPlace_OnPosRectChange(IOleInPlaceSite FAR *This, LPCRECT lprcPosRect) { - IOleObject *browserObject; - IOleInPlaceObject *inplace; - browserObject = *((IOleObject **)((char *)This - sizeof(IOleObject *) - - sizeof(IOleClientSite))); - if (!browserObject->lpVtbl->QueryInterface(browserObject, - iid_unref(&IID_IOleInPlaceObject), - (void **)&inplace)) { - inplace->lpVtbl->SetObjectRects(inplace, lprcPosRect, lprcPosRect); - inplace->lpVtbl->Release(inplace); - } - return S_OK; -} -static HRESULT STDMETHODCALLTYPE Frame_QueryInterface( - IOleInPlaceFrame FAR *This, REFIID riid, LPVOID FAR *ppvObj) { - return E_NOTIMPL; -} -static ULONG STDMETHODCALLTYPE Frame_AddRef(IOleInPlaceFrame FAR *This) { - return 1; -} -static ULONG STDMETHODCALLTYPE Frame_Release(IOleInPlaceFrame FAR *This) { - return 1; -} -static HRESULT STDMETHODCALLTYPE Frame_GetWindow(IOleInPlaceFrame FAR *This, - HWND FAR *lphwnd) { - *lphwnd = ((_IOleInPlaceFrameEx *)This)->window; - return S_OK; -} -static HRESULT STDMETHODCALLTYPE -Frame_ContextSensitiveHelp(IOleInPlaceFrame FAR *This, BOOL fEnterMode) { - return E_NOTIMPL; -} -static HRESULT STDMETHODCALLTYPE Frame_GetBorder(IOleInPlaceFrame FAR *This, - LPRECT lprectBorder) { - return E_NOTIMPL; -} -static HRESULT STDMETHODCALLTYPE Frame_RequestBorderSpace( - IOleInPlaceFrame FAR *This, LPCBORDERWIDTHS pborderwidths) { - return E_NOTIMPL; -} -static HRESULT STDMETHODCALLTYPE Frame_SetBorderSpace( - IOleInPlaceFrame FAR *This, LPCBORDERWIDTHS pborderwidths) { - return E_NOTIMPL; -} -static HRESULT STDMETHODCALLTYPE Frame_SetActiveObject( - IOleInPlaceFrame FAR *This, IOleInPlaceActiveObject *pActiveObject, - LPCOLESTR pszObjName) { - return S_OK; -} -static HRESULT STDMETHODCALLTYPE -Frame_InsertMenus(IOleInPlaceFrame FAR *This, HMENU hmenuShared, - LPOLEMENUGROUPWIDTHS lpMenuWidths) { - return E_NOTIMPL; -} -static HRESULT STDMETHODCALLTYPE Frame_SetMenu(IOleInPlaceFrame FAR *This, - HMENU hmenuShared, - HOLEMENU holemenu, - HWND hwndActiveObject) { - return S_OK; -} -static HRESULT STDMETHODCALLTYPE Frame_RemoveMenus(IOleInPlaceFrame FAR *This, - HMENU hmenuShared) { - return E_NOTIMPL; -} -static HRESULT STDMETHODCALLTYPE Frame_SetStatusText(IOleInPlaceFrame FAR *This, - LPCOLESTR pszStatusText) { - return S_OK; -} -static HRESULT STDMETHODCALLTYPE -Frame_EnableModeless(IOleInPlaceFrame FAR *This, BOOL fEnable) { - return S_OK; -} -static HRESULT STDMETHODCALLTYPE -Frame_TranslateAccelerator(IOleInPlaceFrame FAR *This, LPMSG lpmsg, WORD wID) { - return E_NOTIMPL; -} -static HRESULT STDMETHODCALLTYPE UI_QueryInterface(IDocHostUIHandler FAR *This, - REFIID riid, - LPVOID FAR *ppvObj) { - return (Site_QueryInterface((IOleClientSite *)((char *)This - - sizeof(IOleClientSite) - - sizeof(_IOleInPlaceSiteEx)), - riid, ppvObj)); -} -static ULONG STDMETHODCALLTYPE UI_AddRef(IDocHostUIHandler FAR *This) { - return 1; -} -static ULONG STDMETHODCALLTYPE UI_Release(IDocHostUIHandler FAR *This) { - return 1; -} -static HRESULT STDMETHODCALLTYPE UI_ShowContextMenu( - IDocHostUIHandler FAR *This, DWORD dwID, POINT __RPC_FAR *ppt, - IUnknown __RPC_FAR *pcmdtReserved, IDispatch __RPC_FAR *pdispReserved) { - return S_OK; -} -static HRESULT STDMETHODCALLTYPE -UI_GetHostInfo(IDocHostUIHandler FAR *This, DOCHOSTUIINFO __RPC_FAR *pInfo) { - pInfo->cbSize = sizeof(DOCHOSTUIINFO); - pInfo->dwFlags = DOCHOSTUIFLAG_NO3DBORDER; - pInfo->dwDoubleClick = DOCHOSTUIDBLCLK_DEFAULT; - return S_OK; -} -static HRESULT STDMETHODCALLTYPE UI_ShowUI( - IDocHostUIHandler FAR *This, DWORD dwID, - IOleInPlaceActiveObject __RPC_FAR *pActiveObject, - IOleCommandTarget __RPC_FAR *pCommandTarget, - IOleInPlaceFrame __RPC_FAR *pFrame, IOleInPlaceUIWindow __RPC_FAR *pDoc) { - return S_OK; -} -static HRESULT STDMETHODCALLTYPE UI_HideUI(IDocHostUIHandler FAR *This) { - return S_OK; -} -static HRESULT STDMETHODCALLTYPE UI_UpdateUI(IDocHostUIHandler FAR *This) { - return S_OK; -} -static HRESULT STDMETHODCALLTYPE UI_EnableModeless(IDocHostUIHandler FAR *This, - BOOL fEnable) { - return S_OK; -} -static HRESULT STDMETHODCALLTYPE -UI_OnDocWindowActivate(IDocHostUIHandler FAR *This, BOOL fActivate) { - return S_OK; -} -static HRESULT STDMETHODCALLTYPE -UI_OnFrameWindowActivate(IDocHostUIHandler FAR *This, BOOL fActivate) { - return S_OK; -} -static HRESULT STDMETHODCALLTYPE -UI_ResizeBorder(IDocHostUIHandler FAR *This, LPCRECT prcBorder, - IOleInPlaceUIWindow __RPC_FAR *pUIWindow, BOOL fRameWindow) { - return S_OK; -} -static HRESULT STDMETHODCALLTYPE -UI_TranslateAccelerator(IDocHostUIHandler FAR *This, LPMSG lpMsg, - const GUID __RPC_FAR *pguidCmdGroup, DWORD nCmdID) { - return S_FALSE; -} -static HRESULT STDMETHODCALLTYPE UI_GetOptionKeyPath( - IDocHostUIHandler FAR *This, LPOLESTR __RPC_FAR *pchKey, DWORD dw) { - return S_FALSE; -} -static HRESULT STDMETHODCALLTYPE UI_GetDropTarget( - IDocHostUIHandler FAR *This, IDropTarget __RPC_FAR *pDropTarget, - IDropTarget __RPC_FAR *__RPC_FAR *ppDropTarget) { - return S_FALSE; -} -static HRESULT STDMETHODCALLTYPE UI_GetExternal( - IDocHostUIHandler FAR *This, IDispatch __RPC_FAR *__RPC_FAR *ppDispatch) { - *ppDispatch = (IDispatch *)(This + 1); - return S_OK; -} -static HRESULT STDMETHODCALLTYPE UI_TranslateUrl( - IDocHostUIHandler FAR *This, DWORD dwTranslate, OLECHAR __RPC_FAR *pchURLIn, - OLECHAR __RPC_FAR *__RPC_FAR *ppchURLOut) { - *ppchURLOut = 0; - return S_FALSE; -} -static HRESULT STDMETHODCALLTYPE -UI_FilterDataObject(IDocHostUIHandler FAR *This, IDataObject __RPC_FAR *pDO, - IDataObject __RPC_FAR *__RPC_FAR *ppDORet) { - *ppDORet = 0; - return S_FALSE; -} - -static const TCHAR *classname = "WebView"; -static const SAFEARRAYBOUND ArrayBound = {1, 0}; - -static IOleClientSiteVtbl MyIOleClientSiteTable = { - Site_QueryInterface, Site_AddRef, Site_Release, - Site_SaveObject, Site_GetMoniker, Site_GetContainer, - Site_ShowObject, Site_OnShowWindow, Site_RequestNewObjectLayout}; -static IOleInPlaceSiteVtbl MyIOleInPlaceSiteTable = { - InPlace_QueryInterface, - InPlace_AddRef, - InPlace_Release, - InPlace_GetWindow, - InPlace_ContextSensitiveHelp, - InPlace_CanInPlaceActivate, - InPlace_OnInPlaceActivate, - InPlace_OnUIActivate, - InPlace_GetWindowContext, - InPlace_Scroll, - InPlace_OnUIDeactivate, - InPlace_OnInPlaceDeactivate, - InPlace_DiscardUndoState, - InPlace_DeactivateAndUndo, - InPlace_OnPosRectChange}; - -static IOleInPlaceFrameVtbl MyIOleInPlaceFrameTable = { - Frame_QueryInterface, - Frame_AddRef, - Frame_Release, - Frame_GetWindow, - Frame_ContextSensitiveHelp, - Frame_GetBorder, - Frame_RequestBorderSpace, - Frame_SetBorderSpace, - Frame_SetActiveObject, - Frame_InsertMenus, - Frame_SetMenu, - Frame_RemoveMenus, - Frame_SetStatusText, - Frame_EnableModeless, - Frame_TranslateAccelerator}; - -static IDocHostUIHandlerVtbl MyIDocHostUIHandlerTable = { - UI_QueryInterface, - UI_AddRef, - UI_Release, - UI_ShowContextMenu, - UI_GetHostInfo, - UI_ShowUI, - UI_HideUI, - UI_UpdateUI, - UI_EnableModeless, - UI_OnDocWindowActivate, - UI_OnFrameWindowActivate, - UI_ResizeBorder, - UI_TranslateAccelerator, - UI_GetOptionKeyPath, - UI_GetDropTarget, - UI_GetExternal, - UI_TranslateUrl, - UI_FilterDataObject}; - - - -static HRESULT STDMETHODCALLTYPE IS_QueryInterface(IInternetSecurityManager FAR *This, REFIID riid, void **ppvObject) { - return E_NOTIMPL; -} -static ULONG STDMETHODCALLTYPE IS_AddRef(IInternetSecurityManager FAR *This) { return 1; } -static ULONG STDMETHODCALLTYPE IS_Release(IInternetSecurityManager FAR *This) { return 1; } -static HRESULT STDMETHODCALLTYPE IS_SetSecuritySite(IInternetSecurityManager FAR *This, IInternetSecurityMgrSite *pSited) { - return INET_E_DEFAULT_ACTION; -} -static HRESULT STDMETHODCALLTYPE IS_GetSecuritySite(IInternetSecurityManager FAR *This, IInternetSecurityMgrSite **ppSite) { - return INET_E_DEFAULT_ACTION; -} -static HRESULT STDMETHODCALLTYPE IS_MapUrlToZone(IInternetSecurityManager FAR *This, LPCWSTR pwszUrl, DWORD *pdwZone, DWORD dwFlags) { - *pdwZone = URLZONE_LOCAL_MACHINE; - return S_OK; -} -static HRESULT STDMETHODCALLTYPE IS_GetSecurityId(IInternetSecurityManager FAR *This, LPCWSTR pwszUrl, BYTE *pbSecurityId, DWORD *pcbSecurityId, DWORD_PTR dwReserved) { - return INET_E_DEFAULT_ACTION; -} -static HRESULT STDMETHODCALLTYPE IS_ProcessUrlAction(IInternetSecurityManager FAR *This, LPCWSTR pwszUrl, DWORD dwAction, BYTE *pPolicy, DWORD cbPolicy, BYTE *pContext, DWORD cbContext, DWORD dwFlags, DWORD dwReserved) { - return INET_E_DEFAULT_ACTION; -} -static HRESULT STDMETHODCALLTYPE IS_QueryCustomPolicy(IInternetSecurityManager FAR *This, LPCWSTR pwszUrl, REFGUID guidKey, BYTE **ppPolicy, DWORD *pcbPolicy, BYTE *pContext, DWORD cbContext, DWORD dwReserved) { - return INET_E_DEFAULT_ACTION; -} -static HRESULT STDMETHODCALLTYPE IS_SetZoneMapping(IInternetSecurityManager FAR *This, DWORD dwZone, LPCWSTR lpszPattern, DWORD dwFlags) { - return INET_E_DEFAULT_ACTION; -} -static HRESULT STDMETHODCALLTYPE IS_GetZoneMappings(IInternetSecurityManager FAR *This, DWORD dwZone, IEnumString **ppenumString, DWORD dwFlags) { - return INET_E_DEFAULT_ACTION; -} -static IInternetSecurityManagerVtbl MyInternetSecurityManagerTable = {IS_QueryInterface, IS_AddRef, IS_Release, IS_SetSecuritySite, IS_GetSecuritySite, IS_MapUrlToZone, IS_GetSecurityId, IS_ProcessUrlAction, IS_QueryCustomPolicy, IS_SetZoneMapping, IS_GetZoneMappings}; - -static HRESULT STDMETHODCALLTYPE SP_QueryInterface(IServiceProvider FAR *This, REFIID riid, void **ppvObject) { - return (Site_QueryInterface( - (IOleClientSite *)((char *)This - sizeof(IOleClientSite) - sizeof(_IOleInPlaceSiteEx) - sizeof(_IDocHostUIHandlerEx) - sizeof(IDispatch)), riid, ppvObject)); -} -static ULONG STDMETHODCALLTYPE SP_AddRef(IServiceProvider FAR *This) { return 1; } -static ULONG STDMETHODCALLTYPE SP_Release(IServiceProvider FAR *This) { return 1; } -static HRESULT STDMETHODCALLTYPE SP_QueryService(IServiceProvider FAR *This, REFGUID siid, REFIID riid, void **ppvObject) { - if (iid_eq(siid, &IID_IInternetSecurityManager) && iid_eq(riid, &IID_IInternetSecurityManager)) { - *ppvObject = &((_IServiceProviderEx *)This)->mgr; - } else { - *ppvObject = 0; - return (E_NOINTERFACE); - } - return S_OK; -} -static IServiceProviderVtbl MyServiceProviderTable = {SP_QueryInterface, SP_AddRef, SP_Release, SP_QueryService}; - -static void UnEmbedBrowserObject(struct webview *w) { - if (w->priv.browser != NULL) { - (*w->priv.browser)->lpVtbl->Close(*w->priv.browser, OLECLOSE_NOSAVE); - (*w->priv.browser)->lpVtbl->Release(*w->priv.browser); - GlobalFree(w->priv.browser); - w->priv.browser = NULL; - } -} - -static int EmbedBrowserObject(struct webview *w) { - RECT rect; - IWebBrowser2 *webBrowser2 = NULL; - LPCLASSFACTORY pClassFactory = NULL; - _IOleClientSiteEx *_iOleClientSiteEx = NULL; - IOleObject **browser = (IOleObject **)GlobalAlloc( - GMEM_FIXED, sizeof(IOleObject *) + sizeof(_IOleClientSiteEx)); - if (browser == NULL) { - goto error; - } - w->priv.browser = browser; - - _iOleClientSiteEx = (_IOleClientSiteEx *)(browser + 1); - _iOleClientSiteEx->client.lpVtbl = &MyIOleClientSiteTable; - _iOleClientSiteEx->inplace.inplace.lpVtbl = &MyIOleInPlaceSiteTable; - _iOleClientSiteEx->inplace.frame.frame.lpVtbl = &MyIOleInPlaceFrameTable; - _iOleClientSiteEx->inplace.frame.window = w->priv.hwnd; - _iOleClientSiteEx->ui.ui.lpVtbl = &MyIDocHostUIHandlerTable; - _iOleClientSiteEx->external.lpVtbl = &ExternalDispatchTable; - _iOleClientSiteEx->provider.provider.lpVtbl = &MyServiceProviderTable; - _iOleClientSiteEx->provider.mgr.mgr.lpVtbl = &MyInternetSecurityManagerTable; - - if (CoGetClassObject(iid_unref(&CLSID_WebBrowser), - CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER, NULL, - iid_unref(&IID_IClassFactory), - (void **)&pClassFactory) != S_OK) { - goto error; - } - - if (pClassFactory == NULL) { - goto error; - } - - if (pClassFactory->lpVtbl->CreateInstance(pClassFactory, 0, - iid_unref(&IID_IOleObject), - (void **)browser) != S_OK) { - goto error; - } - pClassFactory->lpVtbl->Release(pClassFactory); - if ((*browser)->lpVtbl->SetClientSite( - *browser, (IOleClientSite *)_iOleClientSiteEx) != S_OK) { - goto error; - } - (*browser)->lpVtbl->SetHostNames(*browser, L"My Host Name", 0); - - if (OleSetContainedObject((struct IUnknown *)(*browser), TRUE) != S_OK) { - goto error; - } - GetClientRect(w->priv.hwnd, &rect); - if ((*browser)->lpVtbl->DoVerb((*browser), OLEIVERB_SHOW, NULL, - (IOleClientSite *)_iOleClientSiteEx, -1, - w->priv.hwnd, &rect) != S_OK) { - goto error; - } - if ((*browser)->lpVtbl->QueryInterface((*browser), - iid_unref(&IID_IWebBrowser2), - (void **)&webBrowser2) != S_OK) { - goto error; - } - - webBrowser2->lpVtbl->put_Left(webBrowser2, 0); - webBrowser2->lpVtbl->put_Top(webBrowser2, 0); - webBrowser2->lpVtbl->put_Width(webBrowser2, rect.right); - webBrowser2->lpVtbl->put_Height(webBrowser2, rect.bottom); - webBrowser2->lpVtbl->Release(webBrowser2); - - return 0; -error: - UnEmbedBrowserObject(w); - if (pClassFactory != NULL) { - pClassFactory->lpVtbl->Release(pClassFactory); - } - if (browser != NULL) { - GlobalFree(browser); - } - return -1; -} - -#define WEBVIEW_DATA_URL_PREFIX "data:text/html," -static int DisplayHTMLPage(struct webview *w) { - IWebBrowser2 *webBrowser2; - VARIANT myURL; - LPDISPATCH lpDispatch; - IHTMLDocument2 *htmlDoc2; - BSTR bstr; - IOleObject *browserObject; - SAFEARRAY *sfArray; - VARIANT *pVar; - browserObject = *w->priv.browser; - int isDataURL = 0; - const char *webview_url = webview_check_url(w->url); - if (!browserObject->lpVtbl->QueryInterface( - browserObject, iid_unref(&IID_IWebBrowser2), (void **)&webBrowser2)) { - LPCSTR webPageName; - isDataURL = (strncmp(webview_url, WEBVIEW_DATA_URL_PREFIX, - strlen(WEBVIEW_DATA_URL_PREFIX)) == 0); - if (isDataURL) { - webPageName = "about:blank"; - } else { - webPageName = (LPCSTR)webview_url; - } - VariantInit(&myURL); - myURL.vt = VT_BSTR; -#ifndef UNICODE - { - wchar_t *buffer = webview_to_utf16(webPageName); - if (buffer == NULL) { - goto badalloc; - } - myURL.bstrVal = SysAllocString(buffer); - GlobalFree(buffer); - } -#else - myURL.bstrVal = SysAllocString(webPageName); -#endif - if (!myURL.bstrVal) { - badalloc: - webBrowser2->lpVtbl->Release(webBrowser2); - return (-6); - } - webBrowser2->lpVtbl->Navigate2(webBrowser2, &myURL, 0, 0, 0, 0); - VariantClear(&myURL); - if (!isDataURL) { - return 0; - } - - char *url = (char *)calloc(1, strlen(webview_url) + 1); - char *q = url; - for (const char *p = webview_url + strlen(WEBVIEW_DATA_URL_PREFIX); *q = *p; - p++, q++) { - if (*q == '%' && *(p + 1) && *(p + 2)) { - sscanf(p + 1, "%02x", q); - p = p + 2; - } - } - - if (webBrowser2->lpVtbl->get_Document(webBrowser2, &lpDispatch) == S_OK) { - if (lpDispatch->lpVtbl->QueryInterface(lpDispatch, - iid_unref(&IID_IHTMLDocument2), - (void **)&htmlDoc2) == S_OK) { - if ((sfArray = SafeArrayCreate(VT_VARIANT, 1, - (SAFEARRAYBOUND *)&ArrayBound))) { - if (!SafeArrayAccessData(sfArray, (void **)&pVar)) { - pVar->vt = VT_BSTR; -#ifndef UNICODE - { - wchar_t *buffer = webview_to_utf16(url); - if (buffer == NULL) { - goto release; - } - bstr = SysAllocString(buffer); - GlobalFree(buffer); - } -#else - bstr = SysAllocString(string); -#endif - if ((pVar->bstrVal = bstr)) { - htmlDoc2->lpVtbl->write(htmlDoc2, sfArray); - htmlDoc2->lpVtbl->close(htmlDoc2); - } - } - SafeArrayDestroy(sfArray); - } - release: - free(url); - htmlDoc2->lpVtbl->Release(htmlDoc2); - } - lpDispatch->lpVtbl->Release(lpDispatch); - } - webBrowser2->lpVtbl->Release(webBrowser2); - return (0); - } - return (-5); -} - -static LRESULT CALLBACK wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, - LPARAM lParam) { - struct webview *w = (struct webview *)GetWindowLongPtr(hwnd, GWLP_USERDATA); - switch (uMsg) { - case WM_CREATE: - w = (struct webview *)((CREATESTRUCT *)lParam)->lpCreateParams; - w->priv.hwnd = hwnd; - return EmbedBrowserObject(w); - case WM_DESTROY: - UnEmbedBrowserObject(w); - PostQuitMessage(0); - return TRUE; - case WM_SIZE: { - IWebBrowser2 *webBrowser2; - IOleObject *browser = *w->priv.browser; - if (browser->lpVtbl->QueryInterface(browser, iid_unref(&IID_IWebBrowser2), - (void **)&webBrowser2) == S_OK) { - RECT rect; - GetClientRect(hwnd, &rect); - webBrowser2->lpVtbl->put_Width(webBrowser2, rect.right); - webBrowser2->lpVtbl->put_Height(webBrowser2, rect.bottom); - } - return TRUE; - } - case WM_WEBVIEW_DISPATCH: { - webview_dispatch_fn f = (webview_dispatch_fn)wParam; - void *arg = (void *)lParam; - (*f)(w, arg); - return TRUE; - } - } - return DefWindowProc(hwnd, uMsg, wParam, lParam); -} - -#define WEBVIEW_KEY_FEATURE_BROWSER_EMULATION \ - "Software\\Microsoft\\Internet " \ - "Explorer\\Main\\FeatureControl\\FEATURE_BROWSER_EMULATION" - -static int webview_fix_ie_compat_mode() { - HKEY hKey; - DWORD ie_version = 11000; - TCHAR appname[MAX_PATH + 1]; - TCHAR *p; - if (GetModuleFileName(NULL, appname, MAX_PATH + 1) == 0) { - return -1; - } - for (p = &appname[strlen(appname) - 1]; p != appname && *p != '\\'; p--) { - } - p++; - if (RegCreateKey(HKEY_CURRENT_USER, WEBVIEW_KEY_FEATURE_BROWSER_EMULATION, - &hKey) != ERROR_SUCCESS) { - return -1; - } - if (RegSetValueEx(hKey, p, 0, REG_DWORD, (BYTE *)&ie_version, - sizeof(ie_version)) != ERROR_SUCCESS) { - RegCloseKey(hKey); - return -1; - } - RegCloseKey(hKey); - return 0; -} - -WEBVIEW_API int webview_init(struct webview *w) { - WNDCLASSEX wc; - HINSTANCE hInstance; - DWORD style; - RECT clientRect; - RECT rect; - - if (webview_fix_ie_compat_mode() < 0) { - return -1; - } - - hInstance = GetModuleHandle(NULL); - if (hInstance == NULL) { - return -1; - } - if (OleInitialize(NULL) != S_OK) { - return -1; - } - ZeroMemory(&wc, sizeof(WNDCLASSEX)); - wc.cbSize = sizeof(WNDCLASSEX); - wc.hInstance = hInstance; - wc.lpfnWndProc = wndproc; - wc.lpszClassName = classname; - RegisterClassEx(&wc); - - style = WS_OVERLAPPEDWINDOW; - if (!w->resizable) { - style = WS_OVERLAPPED | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU; - } - - rect.left = 0; - rect.top = 0; - rect.right = w->width; - rect.bottom = w->height; - AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, 0); - - GetClientRect(GetDesktopWindow(), &clientRect); - int left = (clientRect.right / 2) - ((rect.right - rect.left) / 2); - int top = (clientRect.bottom / 2) - ((rect.bottom - rect.top) / 2); - rect.right = rect.right - rect.left + left; - rect.left = left; - rect.bottom = rect.bottom - rect.top + top; - rect.top = top; - - w->priv.hwnd = - CreateWindowEx(0, classname, w->title, style, rect.left, rect.top, - rect.right - rect.left, rect.bottom - rect.top, - HWND_DESKTOP, NULL, hInstance, (void *)w); - if (w->priv.hwnd == 0) { - OleUninitialize(); - return -1; - } - - SetWindowLongPtr(w->priv.hwnd, GWLP_USERDATA, (LONG_PTR)w); - - DisplayHTMLPage(w); - - SetWindowText(w->priv.hwnd, w->title); - ShowWindow(w->priv.hwnd, SW_SHOWDEFAULT); - UpdateWindow(w->priv.hwnd); - SetFocus(w->priv.hwnd); - - return 0; -} - -WEBVIEW_API int webview_loop(struct webview *w, int blocking) { - MSG msg; - if (blocking) { - GetMessage(&msg, 0, 0, 0); - } else { - PeekMessage(&msg, 0, 0, 0, PM_REMOVE); - } - switch (msg.message) { - case WM_QUIT: - return -1; - case WM_COMMAND: - case WM_KEYDOWN: - case WM_KEYUP: { - HRESULT r = S_OK; - IWebBrowser2 *webBrowser2; - IOleObject *browser = *w->priv.browser; - if (browser->lpVtbl->QueryInterface(browser, iid_unref(&IID_IWebBrowser2), - (void **)&webBrowser2) == S_OK) { - IOleInPlaceActiveObject *pIOIPAO; - if (browser->lpVtbl->QueryInterface( - browser, iid_unref(&IID_IOleInPlaceActiveObject), - (void **)&pIOIPAO) == S_OK) { - r = pIOIPAO->lpVtbl->TranslateAccelerator(pIOIPAO, &msg); - pIOIPAO->lpVtbl->Release(pIOIPAO); - } - webBrowser2->lpVtbl->Release(webBrowser2); - } - if (r != S_FALSE) { - break; - } - } - default: - TranslateMessage(&msg); - DispatchMessage(&msg); - } - return 0; -} - -WEBVIEW_API int webview_eval(struct webview *w, const char *js) { - IWebBrowser2 *webBrowser2; - IHTMLDocument2 *htmlDoc2; - IDispatch *docDispatch; - IDispatch *scriptDispatch; - if ((*w->priv.browser) - ->lpVtbl->QueryInterface((*w->priv.browser), - iid_unref(&IID_IWebBrowser2), - (void **)&webBrowser2) != S_OK) { - return -1; - } - - if (webBrowser2->lpVtbl->get_Document(webBrowser2, &docDispatch) != S_OK) { - return -1; - } - if (docDispatch->lpVtbl->QueryInterface(docDispatch, - iid_unref(&IID_IHTMLDocument2), - (void **)&htmlDoc2) != S_OK) { - return -1; - } - if (htmlDoc2->lpVtbl->get_Script(htmlDoc2, &scriptDispatch) != S_OK) { - return -1; - } - DISPID dispid; - BSTR evalStr = SysAllocString(L"eval"); - if (scriptDispatch->lpVtbl->GetIDsOfNames( - scriptDispatch, iid_unref(&IID_NULL), &evalStr, 1, - LOCALE_SYSTEM_DEFAULT, &dispid) != S_OK) { - SysFreeString(evalStr); - return -1; - } - SysFreeString(evalStr); - - DISPPARAMS params; - VARIANT arg; - VARIANT result; - EXCEPINFO excepInfo; - UINT nArgErr = (UINT)-1; - params.cArgs = 1; - params.cNamedArgs = 0; - params.rgvarg = &arg; - arg.vt = VT_BSTR; - static const char *prologue = "(function(){"; - static const char *epilogue = ";})();"; - int n = strlen(prologue) + strlen(epilogue) + strlen(js) + 1; - char *eval = (char *)malloc(n); - snprintf(eval, n, "%s%s%s", prologue, js, epilogue); - wchar_t *buf = webview_to_utf16(eval); - if (buf == NULL) { - return -1; - } - arg.bstrVal = SysAllocString(buf); - if (scriptDispatch->lpVtbl->Invoke( - scriptDispatch, dispid, iid_unref(&IID_NULL), 0, DISPATCH_METHOD, - ¶ms, &result, &excepInfo, &nArgErr) != S_OK) { - return -1; - } - SysFreeString(arg.bstrVal); - free(eval); - scriptDispatch->lpVtbl->Release(scriptDispatch); - htmlDoc2->lpVtbl->Release(htmlDoc2); - docDispatch->lpVtbl->Release(docDispatch); - return 0; -} - -WEBVIEW_API void webview_dispatch(struct webview *w, webview_dispatch_fn fn, - void *arg) { - PostMessageW(w->priv.hwnd, WM_WEBVIEW_DISPATCH, (WPARAM)fn, (LPARAM)arg); -} - -WEBVIEW_API void webview_set_title(struct webview *w, const char *title) { - SetWindowText(w->priv.hwnd, title); -} - -WEBVIEW_API void webview_set_fullscreen(struct webview *w, int fullscreen) { - if (w->priv.is_fullscreen == !!fullscreen) { - return; - } - if (w->priv.is_fullscreen == 0) { - w->priv.saved_style = GetWindowLong(w->priv.hwnd, GWL_STYLE); - w->priv.saved_ex_style = GetWindowLong(w->priv.hwnd, GWL_EXSTYLE); - GetWindowRect(w->priv.hwnd, &w->priv.saved_rect); - } - w->priv.is_fullscreen = !!fullscreen; - if (fullscreen) { - MONITORINFO monitor_info; - SetWindowLong(w->priv.hwnd, GWL_STYLE, - w->priv.saved_style & ~(WS_CAPTION | WS_THICKFRAME)); - SetWindowLong(w->priv.hwnd, GWL_EXSTYLE, - w->priv.saved_ex_style & - ~(WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE | - WS_EX_CLIENTEDGE | WS_EX_STATICEDGE)); - monitor_info.cbSize = sizeof(monitor_info); - GetMonitorInfo(MonitorFromWindow(w->priv.hwnd, MONITOR_DEFAULTTONEAREST), - &monitor_info); - RECT r; - r.left = monitor_info.rcMonitor.left; - r.top = monitor_info.rcMonitor.top; - r.right = monitor_info.rcMonitor.right; - r.bottom = monitor_info.rcMonitor.bottom; - SetWindowPos(w->priv.hwnd, NULL, r.left, r.top, r.right - r.left, - r.bottom - r.top, - SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED); - } else { - SetWindowLong(w->priv.hwnd, GWL_STYLE, w->priv.saved_style); - SetWindowLong(w->priv.hwnd, GWL_EXSTYLE, w->priv.saved_ex_style); - SetWindowPos(w->priv.hwnd, NULL, w->priv.saved_rect.left, - w->priv.saved_rect.top, - w->priv.saved_rect.right - w->priv.saved_rect.left, - w->priv.saved_rect.bottom - w->priv.saved_rect.top, - SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED); - } -} - -WEBVIEW_API void webview_set_color(struct webview *w, uint8_t r, uint8_t g, - uint8_t b, uint8_t a) { - HBRUSH brush = CreateSolidBrush(RGB(r, g, b)); - SetClassLongPtr(w->priv.hwnd, GCLP_HBRBACKGROUND, (LONG_PTR)brush); -} - -/* These are missing parts from MinGW */ -#ifndef __IFileDialog_INTERFACE_DEFINED__ -#define __IFileDialog_INTERFACE_DEFINED__ -enum _FILEOPENDIALOGOPTIONS { - FOS_OVERWRITEPROMPT = 0x2, - FOS_STRICTFILETYPES = 0x4, - FOS_NOCHANGEDIR = 0x8, - FOS_PICKFOLDERS = 0x20, - FOS_FORCEFILESYSTEM = 0x40, - FOS_ALLNONSTORAGEITEMS = 0x80, - FOS_NOVALIDATE = 0x100, - FOS_ALLOWMULTISELECT = 0x200, - FOS_PATHMUSTEXIST = 0x800, - FOS_FILEMUSTEXIST = 0x1000, - FOS_CREATEPROMPT = 0x2000, - FOS_SHAREAWARE = 0x4000, - FOS_NOREADONLYRETURN = 0x8000, - FOS_NOTESTFILECREATE = 0x10000, - FOS_HIDEMRUPLACES = 0x20000, - FOS_HIDEPINNEDPLACES = 0x40000, - FOS_NODEREFERENCELINKS = 0x100000, - FOS_DONTADDTORECENT = 0x2000000, - FOS_FORCESHOWHIDDEN = 0x10000000, - FOS_DEFAULTNOMINIMODE = 0x20000000, - FOS_FORCEPREVIEWPANEON = 0x40000000 -}; -typedef DWORD FILEOPENDIALOGOPTIONS; -typedef enum FDAP { FDAP_BOTTOM = 0, FDAP_TOP = 1 } FDAP; -DEFINE_GUID(IID_IFileDialog, 0x42f85136, 0xdb7e, 0x439c, 0x85, 0xf1, 0xe4, 0x07, - 0x5d, 0x13, 0x5f, 0xc8); -typedef struct IFileDialogVtbl { - BEGIN_INTERFACE - HRESULT(STDMETHODCALLTYPE *QueryInterface) - (IFileDialog *This, REFIID riid, void **ppvObject); - ULONG(STDMETHODCALLTYPE *AddRef)(IFileDialog *This); - ULONG(STDMETHODCALLTYPE *Release)(IFileDialog *This); - HRESULT(STDMETHODCALLTYPE *Show)(IFileDialog *This, HWND hwndOwner); - HRESULT(STDMETHODCALLTYPE *SetFileTypes) - (IFileDialog *This, UINT cFileTypes, const COMDLG_FILTERSPEC *rgFilterSpec); - HRESULT(STDMETHODCALLTYPE *SetFileTypeIndex) - (IFileDialog *This, UINT iFileType); - HRESULT(STDMETHODCALLTYPE *GetFileTypeIndex) - (IFileDialog *This, UINT *piFileType); - HRESULT(STDMETHODCALLTYPE *Advise) - (IFileDialog *This, IFileDialogEvents *pfde, DWORD *pdwCookie); - HRESULT(STDMETHODCALLTYPE *Unadvise)(IFileDialog *This, DWORD dwCookie); - HRESULT(STDMETHODCALLTYPE *SetOptions) - (IFileDialog *This, FILEOPENDIALOGOPTIONS fos); - HRESULT(STDMETHODCALLTYPE *GetOptions) - (IFileDialog *This, FILEOPENDIALOGOPTIONS *pfos); - HRESULT(STDMETHODCALLTYPE *SetDefaultFolder) - (IFileDialog *This, IShellItem *psi); - HRESULT(STDMETHODCALLTYPE *SetFolder)(IFileDialog *This, IShellItem *psi); - HRESULT(STDMETHODCALLTYPE *GetFolder)(IFileDialog *This, IShellItem **ppsi); - HRESULT(STDMETHODCALLTYPE *GetCurrentSelection) - (IFileDialog *This, IShellItem **ppsi); - HRESULT(STDMETHODCALLTYPE *SetFileName)(IFileDialog *This, LPCWSTR pszName); - HRESULT(STDMETHODCALLTYPE *GetFileName)(IFileDialog *This, LPWSTR *pszName); - HRESULT(STDMETHODCALLTYPE *SetTitle)(IFileDialog *This, LPCWSTR pszTitle); - HRESULT(STDMETHODCALLTYPE *SetOkButtonLabel) - (IFileDialog *This, LPCWSTR pszText); - HRESULT(STDMETHODCALLTYPE *SetFileNameLabel) - (IFileDialog *This, LPCWSTR pszLabel); - HRESULT(STDMETHODCALLTYPE *GetResult)(IFileDialog *This, IShellItem **ppsi); - HRESULT(STDMETHODCALLTYPE *AddPlace) - (IFileDialog *This, IShellItem *psi, FDAP fdap); - HRESULT(STDMETHODCALLTYPE *SetDefaultExtension) - (IFileDialog *This, LPCWSTR pszDefaultExtension); - HRESULT(STDMETHODCALLTYPE *Close)(IFileDialog *This, HRESULT hr); - HRESULT(STDMETHODCALLTYPE *SetClientGuid)(IFileDialog *This, REFGUID guid); - HRESULT(STDMETHODCALLTYPE *ClearClientData)(IFileDialog *This); - HRESULT(STDMETHODCALLTYPE *SetFilter) - (IFileDialog *This, IShellItemFilter *pFilter); - END_INTERFACE -} IFileDialogVtbl; -interface IFileDialog { - CONST_VTBL IFileDialogVtbl *lpVtbl; -}; -DEFINE_GUID(IID_IFileOpenDialog, 0xd57c7288, 0xd4ad, 0x4768, 0xbe, 0x02, 0x9d, - 0x96, 0x95, 0x32, 0xd9, 0x60); -DEFINE_GUID(IID_IFileSaveDialog, 0x84bccd23, 0x5fde, 0x4cdb, 0xae, 0xa4, 0xaf, - 0x64, 0xb8, 0x3d, 0x78, 0xab); -#endif - -WEBVIEW_API void webview_dialog(struct webview *w, - enum webview_dialog_type dlgtype, int flags, - const char *title, const char *arg, - char *result, size_t resultsz) { - if (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN || - dlgtype == WEBVIEW_DIALOG_TYPE_SAVE) { - IFileDialog *dlg = NULL; - IShellItem *res = NULL; - WCHAR *ws = NULL; - char *s = NULL; - FILEOPENDIALOGOPTIONS opts = 0, add_opts = 0; - if (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN) { - if (CoCreateInstance( - iid_unref(&CLSID_FileOpenDialog), NULL, CLSCTX_INPROC_SERVER, - iid_unref(&IID_IFileOpenDialog), (void **)&dlg) != S_OK) { - goto error_dlg; - } - if (flags & WEBVIEW_DIALOG_FLAG_DIRECTORY) { - add_opts |= FOS_PICKFOLDERS; - } - add_opts |= FOS_NOCHANGEDIR | FOS_ALLNONSTORAGEITEMS | FOS_NOVALIDATE | - FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST | FOS_SHAREAWARE | - FOS_NOTESTFILECREATE | FOS_NODEREFERENCELINKS | - FOS_FORCESHOWHIDDEN | FOS_DEFAULTNOMINIMODE; - } else { - if (CoCreateInstance( - iid_unref(&CLSID_FileSaveDialog), NULL, CLSCTX_INPROC_SERVER, - iid_unref(&IID_IFileSaveDialog), (void **)&dlg) != S_OK) { - goto error_dlg; - } - add_opts |= FOS_OVERWRITEPROMPT | FOS_NOCHANGEDIR | - FOS_ALLNONSTORAGEITEMS | FOS_NOVALIDATE | FOS_SHAREAWARE | - FOS_NOTESTFILECREATE | FOS_NODEREFERENCELINKS | - FOS_FORCESHOWHIDDEN | FOS_DEFAULTNOMINIMODE; - } - if (dlg->lpVtbl->GetOptions(dlg, &opts) != S_OK) { - goto error_dlg; - } - opts &= ~FOS_NOREADONLYRETURN; - opts |= add_opts; - if (dlg->lpVtbl->SetOptions(dlg, opts) != S_OK) { - goto error_dlg; - } - if (dlg->lpVtbl->Show(dlg, w->priv.hwnd) != S_OK) { - goto error_dlg; - } - if (dlg->lpVtbl->GetResult(dlg, &res) != S_OK) { - goto error_dlg; - } - if (res->lpVtbl->GetDisplayName(res, SIGDN_FILESYSPATH, &ws) != S_OK) { - goto error_result; - } - s = webview_from_utf16(ws); - strncpy(result, s, resultsz); - result[resultsz - 1] = '\0'; - CoTaskMemFree(ws); - error_result: - res->lpVtbl->Release(res); - error_dlg: - dlg->lpVtbl->Release(dlg); - return; - } else if (dlgtype == WEBVIEW_DIALOG_TYPE_ALERT) { -#if 0 - /* MinGW often doesn't contain TaskDialog, we'll use MessageBox for now */ - WCHAR *wtitle = webview_to_utf16(title); - WCHAR *warg = webview_to_utf16(arg); - TaskDialog(w->priv.hwnd, NULL, NULL, wtitle, warg, 0, NULL, NULL); - GlobalFree(warg); - GlobalFree(wtitle); -#else - UINT type = MB_OK; - switch (flags & WEBVIEW_DIALOG_FLAG_ALERT_MASK) { - case WEBVIEW_DIALOG_FLAG_INFO: - type |= MB_ICONINFORMATION; - break; - case WEBVIEW_DIALOG_FLAG_WARNING: - type |= MB_ICONWARNING; - break; - case WEBVIEW_DIALOG_FLAG_ERROR: - type |= MB_ICONERROR; - break; - } - MessageBox(w->priv.hwnd, arg, title, type); -#endif - } -} - -WEBVIEW_API void webview_terminate(struct webview *w) { PostQuitMessage(0); } - -WEBVIEW_API void webview_exit(struct webview *w) { - DestroyWindow(w->priv.hwnd); - OleUninitialize(); -} - -WEBVIEW_API void webview_print_log(const char *s) { OutputDebugString(s); } - -#endif /* WEBVIEW_WINAPI */ - -#if defined(WEBVIEW_COCOA) -#define NSAlertStyleWarning 0 -#define NSAlertStyleCritical 2 -#define NSWindowStyleMaskResizable 8 -#define NSWindowStyleMaskMiniaturizable 4 -#define NSWindowStyleMaskTitled 1 -#define NSWindowStyleMaskClosable 2 -#define NSWindowStyleMaskFullScreen (1 << 14) -#define NSViewWidthSizable 2 -#define NSViewHeightSizable 16 -#define NSBackingStoreBuffered 2 -#define NSEventMaskAny ULONG_MAX -#define NSEventModifierFlagCommand (1 << 20) -#define NSEventModifierFlagOption (1 << 19) -#define NSAlertStyleInformational 1 -#define NSAlertFirstButtonReturn 1000 -#define WKNavigationActionPolicyDownload 2 -#define NSModalResponseOK 1 -#define WKNavigationActionPolicyDownload 2 -#define WKNavigationResponsePolicyAllow 1 -#define WKUserScriptInjectionTimeAtDocumentStart 0 -#define NSApplicationActivationPolicyRegular 0 - -static id get_nsstring(const char *c_str) { - return objc_msgSend((id)objc_getClass("NSString"), - sel_registerName("stringWithUTF8String:"), c_str); -} - -static id create_menu_item(id title, const char *action, const char *key) { - id item = - objc_msgSend((id)objc_getClass("NSMenuItem"), sel_registerName("alloc")); - objc_msgSend(item, sel_registerName("initWithTitle:action:keyEquivalent:"), - title, sel_registerName(action), get_nsstring(key)); - objc_msgSend(item, sel_registerName("autorelease")); - - return item; -} - -static void webview_window_will_close(id self, SEL cmd, id notification) { - struct webview *w = - (struct webview *)objc_getAssociatedObject(self, "webview"); - webview_terminate(w); -} - -static void webview_external_invoke(id self, SEL cmd, id contentController, - id message) { - struct webview *w = - (struct webview *)objc_getAssociatedObject(contentController, "webview"); - if (w == NULL || w->external_invoke_cb == NULL) { - return; - } - - w->external_invoke_cb(w, (const char *)objc_msgSend( - objc_msgSend(message, sel_registerName("body")), - sel_registerName("UTF8String"))); -} - -static void run_open_panel(id self, SEL cmd, id webView, id parameters, - id frame, void (^completionHandler)(id)) { - - id openPanel = objc_msgSend((id)objc_getClass("NSOpenPanel"), - sel_registerName("openPanel")); - - objc_msgSend( - openPanel, sel_registerName("setAllowsMultipleSelection:"), - objc_msgSend(parameters, sel_registerName("allowsMultipleSelection"))); - - objc_msgSend(openPanel, sel_registerName("setCanChooseFiles:"), 1); - objc_msgSend( - openPanel, sel_registerName("beginWithCompletionHandler:"), ^(id result) { - if (result == (id)NSModalResponseOK) { - completionHandler(objc_msgSend(openPanel, sel_registerName("URLs"))); - } else { - completionHandler(nil); - } - }); -} - -static void run_save_panel(id self, SEL cmd, id download, id filename, - void (^completionHandler)(int allowOverwrite, - id destination)) { - id savePanel = objc_msgSend((id)objc_getClass("NSSavePanel"), - sel_registerName("savePanel")); - objc_msgSend(savePanel, sel_registerName("setCanCreateDirectories:"), 1); - objc_msgSend(savePanel, sel_registerName("setNameFieldStringValue:"), - filename); - objc_msgSend(savePanel, sel_registerName("beginWithCompletionHandler:"), - ^(id result) { - if (result == (id)NSModalResponseOK) { - id url = objc_msgSend(savePanel, sel_registerName("URL")); - id path = objc_msgSend(url, sel_registerName("path")); - completionHandler(1, path); - } else { - completionHandler(NO, nil); - } - }); -} - -static void run_confirmation_panel(id self, SEL cmd, id webView, id message, - id frame, void (^completionHandler)(bool)) { - - id alert = - objc_msgSend((id)objc_getClass("NSAlert"), sel_registerName("new")); - objc_msgSend(alert, sel_registerName("setIcon:"), - objc_msgSend((id)objc_getClass("NSImage"), - sel_registerName("imageNamed:"), - get_nsstring("NSCaution"))); - objc_msgSend(alert, sel_registerName("setShowsHelp:"), 0); - objc_msgSend(alert, sel_registerName("setInformativeText:"), message); - objc_msgSend(alert, sel_registerName("addButtonWithTitle:"), - get_nsstring("OK")); - objc_msgSend(alert, sel_registerName("addButtonWithTitle:"), - get_nsstring("Cancel")); - if (objc_msgSend(alert, sel_registerName("runModal")) == - (id)NSAlertFirstButtonReturn) { - completionHandler(true); - } else { - completionHandler(false); - } - objc_msgSend(alert, sel_registerName("release")); -} - -static void run_alert_panel(id self, SEL cmd, id webView, id message, id frame, - void (^completionHandler)(void)) { - id alert = - objc_msgSend((id)objc_getClass("NSAlert"), sel_registerName("new")); - objc_msgSend(alert, sel_registerName("setIcon:"), - objc_msgSend((id)objc_getClass("NSImage"), - sel_registerName("imageNamed:"), - get_nsstring("NSCaution"))); - objc_msgSend(alert, sel_registerName("setShowsHelp:"), 0); - objc_msgSend(alert, sel_registerName("setInformativeText:"), message); - objc_msgSend(alert, sel_registerName("addButtonWithTitle:"), - get_nsstring("OK")); - objc_msgSend(alert, sel_registerName("runModal")); - objc_msgSend(alert, sel_registerName("release")); - completionHandler(); -} - -static void download_failed(id self, SEL cmd, id download, id error) { - printf("%s", - (const char *)objc_msgSend( - objc_msgSend(error, sel_registerName("localizedDescription")), - sel_registerName("UTF8String"))); -} - -static void make_nav_policy_decision(id self, SEL cmd, id webView, id response, - void (^decisionHandler)(int)) { - if (objc_msgSend(response, sel_registerName("canShowMIMEType")) == 0) { - decisionHandler(WKNavigationActionPolicyDownload); - } else { - decisionHandler(WKNavigationResponsePolicyAllow); - } -} - -WEBVIEW_API int webview_init(struct webview *w) { - w->priv.pool = objc_msgSend((id)objc_getClass("NSAutoreleasePool"), - sel_registerName("new")); - objc_msgSend((id)objc_getClass("NSApplication"), - sel_registerName("sharedApplication")); - - Class __WKScriptMessageHandler = objc_allocateClassPair( - objc_getClass("NSObject"), "__WKScriptMessageHandler", 0); - class_addMethod( - __WKScriptMessageHandler, - sel_registerName("userContentController:didReceiveScriptMessage:"), - (IMP)webview_external_invoke, "v@:@@"); - objc_registerClassPair(__WKScriptMessageHandler); - - id scriptMessageHandler = - objc_msgSend((id)__WKScriptMessageHandler, sel_registerName("new")); - - /*** - _WKDownloadDelegate is an undocumented/private protocol with methods called - from WKNavigationDelegate - References: - https://github.com/WebKit/webkit/blob/master/Source/WebKit/UIProcess/API/Cocoa/_WKDownload.h - https://github.com/WebKit/webkit/blob/master/Source/WebKit/UIProcess/API/Cocoa/_WKDownloadDelegate.h - https://github.com/WebKit/webkit/blob/master/Tools/TestWebKitAPI/Tests/WebKitCocoa/Download.mm - ***/ - - Class __WKDownloadDelegate = objc_allocateClassPair( - objc_getClass("NSObject"), "__WKDownloadDelegate", 0); - class_addMethod( - __WKDownloadDelegate, - sel_registerName("_download:decideDestinationWithSuggestedFilename:" - "completionHandler:"), - (IMP)run_save_panel, "v@:@@?"); - class_addMethod(__WKDownloadDelegate, - sel_registerName("_download:didFailWithError:"), - (IMP)download_failed, "v@:@@"); - objc_registerClassPair(__WKDownloadDelegate); - id downloadDelegate = - objc_msgSend((id)__WKDownloadDelegate, sel_registerName("new")); - - Class __WKPreferences = objc_allocateClassPair(objc_getClass("WKPreferences"), - "__WKPreferences", 0); - objc_property_attribute_t type = {"T", "c"}; - objc_property_attribute_t ownership = {"N", ""}; - objc_property_attribute_t attrs[] = {type, ownership}; - class_replaceProperty(__WKPreferences, "developerExtrasEnabled", attrs, 2); - objc_registerClassPair(__WKPreferences); - id wkPref = objc_msgSend((id)__WKPreferences, sel_registerName("new")); - objc_msgSend(wkPref, sel_registerName("setValue:forKey:"), - objc_msgSend((id)objc_getClass("NSNumber"), - sel_registerName("numberWithBool:"), !!w->debug), - objc_msgSend((id)objc_getClass("NSString"), - sel_registerName("stringWithUTF8String:"), - "developerExtrasEnabled")); - - id userController = objc_msgSend((id)objc_getClass("WKUserContentController"), - sel_registerName("new")); - objc_setAssociatedObject(userController, "webview", (id)(w), - OBJC_ASSOCIATION_ASSIGN); - objc_msgSend( - userController, sel_registerName("addScriptMessageHandler:name:"), - scriptMessageHandler, - objc_msgSend((id)objc_getClass("NSString"), - sel_registerName("stringWithUTF8String:"), "invoke")); - - /*** - In order to maintain compatibility with the other 'webviews' we need to - override window.external.invoke to call - webkit.messageHandlers.invoke.postMessage - ***/ - - id windowExternalOverrideScript = objc_msgSend( - (id)objc_getClass("WKUserScript"), sel_registerName("alloc")); - objc_msgSend( - windowExternalOverrideScript, - sel_registerName("initWithSource:injectionTime:forMainFrameOnly:"), - get_nsstring("window.external = this; invoke = function(arg){ " - "webkit.messageHandlers.invoke.postMessage(arg); };"), - WKUserScriptInjectionTimeAtDocumentStart, 0); - - objc_msgSend(userController, sel_registerName("addUserScript:"), - windowExternalOverrideScript); - - id config = objc_msgSend((id)objc_getClass("WKWebViewConfiguration"), - sel_registerName("new")); - id processPool = objc_msgSend(config, sel_registerName("processPool")); - objc_msgSend(processPool, sel_registerName("_setDownloadDelegate:"), - downloadDelegate); - objc_msgSend(config, sel_registerName("setProcessPool:"), processPool); - objc_msgSend(config, sel_registerName("setUserContentController:"), - userController); - objc_msgSend(config, sel_registerName("setPreferences:"), wkPref); - - Class __NSWindowDelegate = objc_allocateClassPair(objc_getClass("NSObject"), - "__NSWindowDelegate", 0); - class_addProtocol(__NSWindowDelegate, objc_getProtocol("NSWindowDelegate")); - class_replaceMethod(__NSWindowDelegate, sel_registerName("windowWillClose:"), - (IMP)webview_window_will_close, "v@:@"); - objc_registerClassPair(__NSWindowDelegate); - - w->priv.windowDelegate = - objc_msgSend((id)__NSWindowDelegate, sel_registerName("new")); - - objc_setAssociatedObject(w->priv.windowDelegate, "webview", (id)(w), - OBJC_ASSOCIATION_ASSIGN); - - id nsTitle = - objc_msgSend((id)objc_getClass("NSString"), - sel_registerName("stringWithUTF8String:"), w->title); - - CGRect r = CGRectMake(0, 0, w->width, w->height); - - unsigned int style = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | - NSWindowStyleMaskMiniaturizable; - if (w->resizable) { - style = style | NSWindowStyleMaskResizable; - } - - w->priv.window = - objc_msgSend((id)objc_getClass("NSWindow"), sel_registerName("alloc")); - objc_msgSend(w->priv.window, - sel_registerName("initWithContentRect:styleMask:backing:defer:"), - r, style, NSBackingStoreBuffered, 0); - - objc_msgSend(w->priv.window, sel_registerName("autorelease")); - objc_msgSend(w->priv.window, sel_registerName("setTitle:"), nsTitle); - objc_msgSend(w->priv.window, sel_registerName("setDelegate:"), - w->priv.windowDelegate); - objc_msgSend(w->priv.window, sel_registerName("center")); - - Class __WKUIDelegate = - objc_allocateClassPair(objc_getClass("NSObject"), "__WKUIDelegate", 0); - class_addProtocol(__WKUIDelegate, objc_getProtocol("WKUIDelegate")); - class_addMethod(__WKUIDelegate, - sel_registerName("webView:runOpenPanelWithParameters:" - "initiatedByFrame:completionHandler:"), - (IMP)run_open_panel, "v@:@@@?"); - class_addMethod(__WKUIDelegate, - sel_registerName("webView:runJavaScriptAlertPanelWithMessage:" - "initiatedByFrame:completionHandler:"), - (IMP)run_alert_panel, "v@:@@@?"); - class_addMethod( - __WKUIDelegate, - sel_registerName("webView:runJavaScriptConfirmPanelWithMessage:" - "initiatedByFrame:completionHandler:"), - (IMP)run_confirmation_panel, "v@:@@@?"); - objc_registerClassPair(__WKUIDelegate); - id uiDel = objc_msgSend((id)__WKUIDelegate, sel_registerName("new")); - - Class __WKNavigationDelegate = objc_allocateClassPair( - objc_getClass("NSObject"), "__WKNavigationDelegate", 0); - class_addProtocol(__WKNavigationDelegate, - objc_getProtocol("WKNavigationDelegate")); - class_addMethod( - __WKNavigationDelegate, - sel_registerName( - "webView:decidePolicyForNavigationResponse:decisionHandler:"), - (IMP)make_nav_policy_decision, "v@:@@?"); - objc_registerClassPair(__WKNavigationDelegate); - id navDel = objc_msgSend((id)__WKNavigationDelegate, sel_registerName("new")); - - w->priv.webview = - objc_msgSend((id)objc_getClass("WKWebView"), sel_registerName("alloc")); - objc_msgSend(w->priv.webview, - sel_registerName("initWithFrame:configuration:"), r, config); - objc_msgSend(w->priv.webview, sel_registerName("setUIDelegate:"), uiDel); - objc_msgSend(w->priv.webview, sel_registerName("setNavigationDelegate:"), - navDel); - - id nsURL = objc_msgSend((id)objc_getClass("NSURL"), - sel_registerName("URLWithString:"), - get_nsstring(webview_check_url(w->url))); - - objc_msgSend(w->priv.webview, sel_registerName("loadRequest:"), - objc_msgSend((id)objc_getClass("NSURLRequest"), - sel_registerName("requestWithURL:"), nsURL)); - objc_msgSend(w->priv.webview, sel_registerName("setAutoresizesSubviews:"), 1); - objc_msgSend(w->priv.webview, sel_registerName("setAutoresizingMask:"), - (NSViewWidthSizable | NSViewHeightSizable)); - objc_msgSend(objc_msgSend(w->priv.window, sel_registerName("contentView")), - sel_registerName("addSubview:"), w->priv.webview); - objc_msgSend(w->priv.window, sel_registerName("orderFrontRegardless")); - - objc_msgSend(objc_msgSend((id)objc_getClass("NSApplication"), - sel_registerName("sharedApplication")), - sel_registerName("setActivationPolicy:"), - NSApplicationActivationPolicyRegular); - - objc_msgSend(objc_msgSend((id)objc_getClass("NSApplication"), - sel_registerName("sharedApplication")), - sel_registerName("finishLaunching")); - - objc_msgSend(objc_msgSend((id)objc_getClass("NSApplication"), - sel_registerName("sharedApplication")), - sel_registerName("activateIgnoringOtherApps:"), 1); - - id menubar = - objc_msgSend((id)objc_getClass("NSMenu"), sel_registerName("alloc")); - objc_msgSend(menubar, sel_registerName("initWithTitle:"), get_nsstring("")); - objc_msgSend(menubar, sel_registerName("autorelease")); - - id appName = objc_msgSend(objc_msgSend((id)objc_getClass("NSProcessInfo"), - sel_registerName("processInfo")), - sel_registerName("processName")); - - id appMenuItem = - objc_msgSend((id)objc_getClass("NSMenuItem"), sel_registerName("alloc")); - objc_msgSend(appMenuItem, - sel_registerName("initWithTitle:action:keyEquivalent:"), appName, - NULL, get_nsstring("")); - - id appMenu = - objc_msgSend((id)objc_getClass("NSMenu"), sel_registerName("alloc")); - objc_msgSend(appMenu, sel_registerName("initWithTitle:"), appName); - objc_msgSend(appMenu, sel_registerName("autorelease")); - - objc_msgSend(appMenuItem, sel_registerName("setSubmenu:"), appMenu); - objc_msgSend(menubar, sel_registerName("addItem:"), appMenuItem); - - id title = - objc_msgSend(get_nsstring("Hide "), - sel_registerName("stringByAppendingString:"), appName); - id item = create_menu_item(title, "hide:", "h"); - objc_msgSend(appMenu, sel_registerName("addItem:"), item); - - item = create_menu_item(get_nsstring("Hide Others"), - "hideOtherApplications:", "h"); - objc_msgSend(item, sel_registerName("setKeyEquivalentModifierMask:"), - (NSEventModifierFlagOption | NSEventModifierFlagCommand)); - objc_msgSend(appMenu, sel_registerName("addItem:"), item); - - item = - create_menu_item(get_nsstring("Show All"), "unhideAllApplications:", ""); - objc_msgSend(appMenu, sel_registerName("addItem:"), item); - - objc_msgSend(appMenu, sel_registerName("addItem:"), - objc_msgSend((id)objc_getClass("NSMenuItem"), - sel_registerName("separatorItem"))); - - title = objc_msgSend(get_nsstring("Quit "), - sel_registerName("stringByAppendingString:"), appName); - item = create_menu_item(title, "terminate:", "q"); - objc_msgSend(appMenu, sel_registerName("addItem:"), item); - -id editMenuItem = - objc_msgSend((id)objc_getClass("NSMenuItem"), sel_registerName("alloc")); - objc_msgSend(editMenuItem, - sel_registerName("initWithTitle:action:keyEquivalent:"), get_nsstring("Edit"), - NULL, get_nsstring("")); - - /*** - Edit menu - ***/ - -id editMenu = - objc_msgSend((id)objc_getClass("NSMenu"), sel_registerName("alloc")); - objc_msgSend(editMenu, sel_registerName("initWithTitle:"), get_nsstring("Edit")); - objc_msgSend(editMenu, sel_registerName("autorelease")); - - objc_msgSend(editMenuItem, sel_registerName("setSubmenu:"), editMenu); - objc_msgSend(menubar, sel_registerName("addItem:"), editMenuItem); - - item = create_menu_item(get_nsstring("Undo"), "undo:", "z"); - objc_msgSend(editMenu, sel_registerName("addItem:"), item); - - item = create_menu_item(get_nsstring("Redo"), "redo:", "y"); - objc_msgSend(editMenu, sel_registerName("addItem:"), item); - - item = objc_msgSend((id)objc_getClass("NSMenuItem"), sel_registerName("separatorItem")); - objc_msgSend(editMenu, sel_registerName("addItem:"), item); - - item = create_menu_item(get_nsstring("Cut"), "cut:", "x"); - objc_msgSend(editMenu, sel_registerName("addItem:"), item); - - item = create_menu_item(get_nsstring("Copy"), "copy:", "c"); - objc_msgSend(editMenu, sel_registerName("addItem:"), item); - - item = create_menu_item(get_nsstring("Paste"), "paste:", "v"); - objc_msgSend(editMenu, sel_registerName("addItem:"), item); - - item = create_menu_item(get_nsstring("Select All"), "selectAll:", "a"); - objc_msgSend(editMenu, sel_registerName("addItem:"), item); - - /*** - Finalize menubar - ***/ - - objc_msgSend(objc_msgSend((id)objc_getClass("NSApplication"), - sel_registerName("sharedApplication")), - sel_registerName("setMainMenu:"), menubar); - - w->priv.should_exit = 0; - return 0; -} - -WEBVIEW_API int webview_loop(struct webview *w, int blocking) { - id until = (blocking ? objc_msgSend((id)objc_getClass("NSDate"), - sel_registerName("distantFuture")) - : objc_msgSend((id)objc_getClass("NSDate"), - sel_registerName("distantPast"))); - - id event = objc_msgSend( - objc_msgSend((id)objc_getClass("NSApplication"), - sel_registerName("sharedApplication")), - sel_registerName("nextEventMatchingMask:untilDate:inMode:dequeue:"), - ULONG_MAX, until, - objc_msgSend((id)objc_getClass("NSString"), - sel_registerName("stringWithUTF8String:"), - "kCFRunLoopDefaultMode"), - true); - - if (event) { - objc_msgSend(objc_msgSend((id)objc_getClass("NSApplication"), - sel_registerName("sharedApplication")), - sel_registerName("sendEvent:"), event); - } - - return w->priv.should_exit; -} - -WEBVIEW_API int webview_eval(struct webview *w, const char *js) { - objc_msgSend(w->priv.webview, - sel_registerName("evaluateJavaScript:completionHandler:"), - get_nsstring(js), NULL); - - return 0; -} - -WEBVIEW_API void webview_set_title(struct webview *w, const char *title) { - objc_msgSend(w->priv.window, sel_registerName("setTitle"), - get_nsstring(title)); -} - -WEBVIEW_API void webview_set_fullscreen(struct webview *w, int fullscreen) { - unsigned long windowStyleMask = (unsigned long)objc_msgSend( - w->priv.window, sel_registerName("styleMask")); - int b = (((windowStyleMask & NSWindowStyleMaskFullScreen) == - NSWindowStyleMaskFullScreen) - ? 1 - : 0); - if (b != fullscreen) { - objc_msgSend(w->priv.window, sel_registerName("toggleFullScreen:"), NULL); - } -} - -WEBVIEW_API void webview_set_color(struct webview *w, uint8_t r, uint8_t g, - uint8_t b, uint8_t a) { - - id color = objc_msgSend((id)objc_getClass("NSColor"), - sel_registerName("colorWithRed:green:blue:alpha:"), - (float)r / 255.0, (float)g / 255.0, (float)b / 255.0, - (float)a / 255.0); - - objc_msgSend(w->priv.window, sel_registerName("setBackgroundColor:"), color); - - if (0.5 >= ((r / 255.0 * 299.0) + (g / 255.0 * 587.0) + (b / 255.0 * 114.0)) / - 1000.0) { - objc_msgSend(w->priv.window, sel_registerName("setAppearance:"), - objc_msgSend((id)objc_getClass("NSAppearance"), - sel_registerName("appearanceNamed:"), - get_nsstring("NSAppearanceNameVibrantDark"))); - } else { - objc_msgSend(w->priv.window, sel_registerName("setAppearance:"), - objc_msgSend((id)objc_getClass("NSAppearance"), - sel_registerName("appearanceNamed:"), - get_nsstring("NSAppearanceNameVibrantLight"))); - } - objc_msgSend(w->priv.window, sel_registerName("setOpaque:"), 0); - objc_msgSend(w->priv.window, - sel_registerName("setTitlebarAppearsTransparent:"), 1); - objc_msgSend(w->priv.webview, sel_registerName("_setDrawsBackground:"), 0); -} - -WEBVIEW_API void webview_dialog(struct webview *w, - enum webview_dialog_type dlgtype, int flags, - const char *title, const char *arg, - char *result, size_t resultsz) { - if (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN || - dlgtype == WEBVIEW_DIALOG_TYPE_SAVE) { - id panel = (id)objc_getClass("NSSavePanel"); - if (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN) { - id openPanel = objc_msgSend((id)objc_getClass("NSOpenPanel"), - sel_registerName("openPanel")); - if (flags & WEBVIEW_DIALOG_FLAG_DIRECTORY) { - objc_msgSend(openPanel, sel_registerName("setCanChooseFiles:"), 0); - objc_msgSend(openPanel, sel_registerName("setCanChooseDirectories:"), - 1); - } else { - objc_msgSend(openPanel, sel_registerName("setCanChooseFiles:"), 1); - objc_msgSend(openPanel, sel_registerName("setCanChooseDirectories:"), - 0); - } - objc_msgSend(openPanel, sel_registerName("setResolvesAliases:"), 0); - objc_msgSend(openPanel, sel_registerName("setAllowsMultipleSelection:"), - 0); - panel = openPanel; - } else { - panel = objc_msgSend((id)objc_getClass("NSSavePanel"), - sel_registerName("savePanel")); - } - - objc_msgSend(panel, sel_registerName("setCanCreateDirectories:"), 1); - objc_msgSend(panel, sel_registerName("setShowsHiddenFiles:"), 1); - objc_msgSend(panel, sel_registerName("setExtensionHidden:"), 0); - objc_msgSend(panel, sel_registerName("setCanSelectHiddenExtension:"), 0); - objc_msgSend(panel, sel_registerName("setTreatsFilePackagesAsDirectories:"), - 1); - objc_msgSend( - panel, sel_registerName("beginSheetModalForWindow:completionHandler:"), - w->priv.window, ^(id result) { - objc_msgSend(objc_msgSend((id)objc_getClass("NSApplication"), - sel_registerName("sharedApplication")), - sel_registerName("stopModalWithCode:"), result); - }); - - if (objc_msgSend(objc_msgSend((id)objc_getClass("NSApplication"), - sel_registerName("sharedApplication")), - sel_registerName("runModalForWindow:"), - panel) == (id)NSModalResponseOK) { - id url = objc_msgSend(panel, sel_registerName("URL")); - id path = objc_msgSend(url, sel_registerName("path")); - const char *filename = - (const char *)objc_msgSend(path, sel_registerName("UTF8String")); - strlcpy(result, filename, resultsz); - } - } else if (dlgtype == WEBVIEW_DIALOG_TYPE_ALERT) { - id a = objc_msgSend((id)objc_getClass("NSAlert"), sel_registerName("new")); - switch (flags & WEBVIEW_DIALOG_FLAG_ALERT_MASK) { - case WEBVIEW_DIALOG_FLAG_INFO: - objc_msgSend(a, sel_registerName("setAlertStyle:"), - NSAlertStyleInformational); - break; - case WEBVIEW_DIALOG_FLAG_WARNING: - printf("Warning\n"); - objc_msgSend(a, sel_registerName("setAlertStyle:"), NSAlertStyleWarning); - break; - case WEBVIEW_DIALOG_FLAG_ERROR: - printf("Error\n"); - objc_msgSend(a, sel_registerName("setAlertStyle:"), NSAlertStyleCritical); - break; - } - objc_msgSend(a, sel_registerName("setShowsHelp:"), 0); - objc_msgSend(a, sel_registerName("setShowsSuppressionButton:"), 0); - objc_msgSend(a, sel_registerName("setMessageText:"), get_nsstring(title)); - objc_msgSend(a, sel_registerName("setInformativeText:"), get_nsstring(arg)); - objc_msgSend(a, sel_registerName("addButtonWithTitle:"), - get_nsstring("OK")); - objc_msgSend(a, sel_registerName("runModal")); - objc_msgSend(a, sel_registerName("release")); - } -} - -static void webview_dispatch_cb(void *arg) { - struct webview_dispatch_arg *context = (struct webview_dispatch_arg *)arg; - (context->fn)(context->w, context->arg); - free(context); -} - -WEBVIEW_API void webview_dispatch(struct webview *w, webview_dispatch_fn fn, - void *arg) { - struct webview_dispatch_arg *context = (struct webview_dispatch_arg *)malloc( - sizeof(struct webview_dispatch_arg)); - context->w = w; - context->arg = arg; - context->fn = fn; - dispatch_async_f(dispatch_get_main_queue(), context, webview_dispatch_cb); -} - -WEBVIEW_API void webview_terminate(struct webview *w) { - w->priv.should_exit = 1; -} - -WEBVIEW_API void webview_exit(struct webview *w) { - id app = objc_msgSend((id)objc_getClass("NSApplication"), - sel_registerName("sharedApplication")); - objc_msgSend(app, sel_registerName("terminate:"), app); -} - -WEBVIEW_API void webview_print_log(const char *s) { printf("%s\n", s); } - -#endif /* WEBVIEW_COCOA */ - #endif /* WEBVIEW_IMPLEMENTATION */ #ifdef __cplusplus