From 13734c338b9daa9c20aacb8fed7d60400a3d2c00 Mon Sep 17 00:00:00 2001 From: nothingismagick Date: Sun, 14 Jul 2019 14:50:49 +0200 Subject: [PATCH] chore(merge) dev to master (#4) * feat(readme) clarifications and styling * fix(readme) logo position * feat(scaffolding) folders, templates, rust, c, node WOW * feat(proton) initial packages for webview and binding-rust * feat(proton) new folder structure * chore(compliance) readmes and licenses * chore(npm) create package.json * chore(proton) rename packages and create lib/rust * chore(proton) create templates directory * feat(rust) rustfmt tab_spaces = 2 * feat(rust) run fmt and fix template structure * chore(npm) update package - package name (@quasar/proton) - node 10, npm 6.6, yarn 1.17.3 (security) - .gitignore - .npmignore - add docs and spec dirs Signed-off-by: Daniel Thompson-Yvetot --- .gitignore | 61 + .npmignore | 11 + LICENSE | 2 +- README.md | 113 +- bindings/go/LICENSE | 21 + bindings/go/README.md | 1 + bindings/go/proton.go | 572 ++++++ bindings/go/proton_test.go | 97 + bindings/rust/.gitignore | 4 + bindings/rust/Cargo.toml | 24 + bindings/rust/LICENSE | 45 + bindings/rust/README.md | 1 + bindings/rust/proton-sys/Cargo.toml | 22 + bindings/rust/proton-sys/build.rs | 56 + bindings/rust/proton-sys/lib.rs | 45 + bindings/rust/proton-sys/proton.c | 28 + bindings/rust/rustfmt.toml | 13 + bindings/rust/src/color.rs | 52 + bindings/rust/src/dialog.rs | 141 ++ bindings/rust/src/error.rs | 79 + bindings/rust/src/escape.rs | 81 + bindings/rust/src/lib.rs | 526 ++++++ docs/README.md | 1 + lib/rust/Cargo.toml | 27 + lib/rust/rustfmt.toml | 13 + lib/rust/src/api/cmd.rs | 40 + lib/rust/src/api/mod.rs | 63 + lib/rust/src/command.rs | 78 + lib/rust/src/dir/mod.rs | 77 + lib/rust/src/dir/utils.rs | 4 + lib/rust/src/file/error.rs | 46 + lib/rust/src/file/extract.rs | 189 ++ lib/rust/src/file/file_move.rs | 62 + lib/rust/src/file/mod.rs | 21 + lib/rust/src/file_system.rs | 100 ++ lib/rust/src/http/error.rs | 57 + lib/rust/src/http/mod.rs | 80 + lib/rust/src/lib.rs | 43 + lib/rust/src/macros.rs | 46 + lib/rust/src/platform/error.rs | 29 + lib/rust/src/platform/mod.rs | 54 + lib/rust/src/process.rs | 18 + lib/rust/src/rpc.rs | 15 + lib/rust/src/tcp.rs | 25 + lib/rust/src/updater/error.rs | 78 + lib/rust/src/updater/github/mod.rs | 43 + lib/rust/src/updater/github/release.rs | 215 +++ lib/rust/src/updater/mod.rs | 270 +++ lib/rust/src/version/error.rs | 35 + lib/rust/src/version/mod.rs | 53 + package.json | 27 + spec/README.md | 1 + templates/rust/Cargo.toml | 39 + templates/rust/_gitignore | 10 + templates/rust/build.rs | 10 + templates/rust/icons/128x128.png | Bin 0 -> 12360 bytes templates/rust/icons/128x128@2x.png | Bin 0 -> 28226 bytes templates/rust/icons/32x32.png | Bin 0 -> 2285 bytes templates/rust/icons/icon.icns | Bin 0 -> 242236 bytes templates/rust/icons/icon.ico | Bin 0 -> 44401 bytes templates/rust/rustfmt.toml | 13 + templates/rust/src/cmd.rs | 8 + templates/rust/src/main.rs | 112 ++ templates/rust/src/server.rs | 26 + templates/rust/src/updater.rs | 73 + ui/.gitattributes | 1 + ui/.gitignore | 2 + ui/CMakeLists.txt | 32 + ui/LICENSE | 21 + ui/README.md | 3 + ui/proton.h | 2265 ++++++++++++++++++++++++ ui/proton_test.cc | 176 ++ 72 files changed, 6567 insertions(+), 29 deletions(-) create mode 100644 .gitignore create mode 100644 .npmignore create mode 100644 bindings/go/LICENSE create mode 100644 bindings/go/README.md create mode 100755 bindings/go/proton.go create mode 100644 bindings/go/proton_test.go create mode 100644 bindings/rust/.gitignore create mode 100644 bindings/rust/Cargo.toml create mode 100644 bindings/rust/LICENSE create mode 100644 bindings/rust/README.md create mode 100644 bindings/rust/proton-sys/Cargo.toml create mode 100644 bindings/rust/proton-sys/build.rs create mode 100644 bindings/rust/proton-sys/lib.rs create mode 100644 bindings/rust/proton-sys/proton.c create mode 100644 bindings/rust/rustfmt.toml create mode 100644 bindings/rust/src/color.rs create mode 100644 bindings/rust/src/dialog.rs create mode 100644 bindings/rust/src/error.rs create mode 100644 bindings/rust/src/escape.rs create mode 100644 bindings/rust/src/lib.rs create mode 100644 docs/README.md create mode 100644 lib/rust/Cargo.toml create mode 100644 lib/rust/rustfmt.toml create mode 100644 lib/rust/src/api/cmd.rs create mode 100644 lib/rust/src/api/mod.rs create mode 100755 lib/rust/src/command.rs create mode 100755 lib/rust/src/dir/mod.rs create mode 100755 lib/rust/src/dir/utils.rs create mode 100644 lib/rust/src/file/error.rs create mode 100644 lib/rust/src/file/extract.rs create mode 100644 lib/rust/src/file/file_move.rs create mode 100644 lib/rust/src/file/mod.rs create mode 100755 lib/rust/src/file_system.rs create mode 100644 lib/rust/src/http/error.rs create mode 100644 lib/rust/src/http/mod.rs create mode 100644 lib/rust/src/lib.rs create mode 100644 lib/rust/src/macros.rs create mode 100644 lib/rust/src/platform/error.rs create mode 100644 lib/rust/src/platform/mod.rs create mode 100644 lib/rust/src/process.rs create mode 100755 lib/rust/src/rpc.rs create mode 100644 lib/rust/src/tcp.rs create mode 100644 lib/rust/src/updater/error.rs create mode 100644 lib/rust/src/updater/github/mod.rs create mode 100644 lib/rust/src/updater/github/release.rs create mode 100644 lib/rust/src/updater/mod.rs create mode 100644 lib/rust/src/version/error.rs create mode 100644 lib/rust/src/version/mod.rs create mode 100644 package.json create mode 100644 spec/README.md create mode 100755 templates/rust/Cargo.toml create mode 100755 templates/rust/_gitignore create mode 100755 templates/rust/build.rs create mode 100644 templates/rust/icons/128x128.png create mode 100644 templates/rust/icons/128x128@2x.png create mode 100644 templates/rust/icons/32x32.png create mode 100644 templates/rust/icons/icon.icns create mode 100644 templates/rust/icons/icon.ico create mode 100644 templates/rust/rustfmt.toml create mode 100755 templates/rust/src/cmd.rs create mode 100755 templates/rust/src/main.rs create mode 100644 templates/rust/src/server.rs create mode 100644 templates/rust/src/updater.rs create mode 100644 ui/.gitattributes create mode 100644 ui/.gitignore create mode 100755 ui/CMakeLists.txt create mode 100644 ui/LICENSE create mode 100755 ui/README.md create mode 100644 ui/proton.h create mode 100644 ui/proton_test.cc diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..aee53c804 --- /dev/null +++ b/.gitignore @@ -0,0 +1,61 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Typescript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +/.vs +.DS_Store +.Thumbs.db +*.sublime* +.idea/ +debug.log +package-lock.json +.vscode/settings.json diff --git a/.npmignore b/.npmignore new file mode 100644 index 000000000..36ad2b169 --- /dev/null +++ b/.npmignore @@ -0,0 +1,11 @@ +test +bindings +docs +lib +node_modules +spec +ui +.git +.github +.idea +SECURITY.md diff --git a/LICENSE b/LICENSE index c123e6407..76b8255d3 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2019 Quasar Framework +Copyright (c) 2017 - Present Quasar Framework Contributors, Boscop, Serge Zaitsev Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index c451024ee..f9ed9448f 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,14 @@ # quasar-proton [WIP] ## A fresh take on creating cross-platform apps. -[![official icon](https://img.shields.io/badge/Quasar%201.0-Official-blue.svg)](https://quasar.dev) +[![official icon](https://img.shields.io/badge/Quasar%201.0-Official-blue.svg)](https://quasar.dev) [![status](https://img.shields.io/badge/Status-Internal%20Review-yellow.svg)](https://github.com/quasarframework/quasar/tree/proton) -[![version](https://img.shields.io/badge/Version-unreleased-yellow.svg)](https://github.com/quasarframework/quasar/tree/proton) +[![version](https://img.shields.io/badge/Version-unreleased-yellow.svg)](https://github.com/quasarframework/quasar/tree/proton) [![Join the chat at https://chat.quasar.dev](https://img.shields.io/badge/chat-on%20discord-7289da.svg)](https://chat.quasar.dev) [![https://good-labs.github.io/greater-good-affirmation/assets/images/badge.svg](https://good-labs.github.io/greater-good-affirmation/assets/images/badge.svg)](https://good-labs.github.io/greater-good-affirmation) -**Proton** brings a mode to build Quasar Apps that creates tiny, blazing +**Proton** brings a mode to build Quasar Apps that creates tiny, blazing fast binaries for all major desktop platforms. In Quasar's [neverending quest](https://quasar.dev/introduction-to-quasar#Why-Quasar%3F) for performance and security, the core team is proud to offer an @@ -18,25 +18,77 @@ Whether you are just starting out making apps for your meetup or regularly crunch terabyte datasets, we are absolutely confident that you will love using Proton as much as we love making and maintaining it. -It leverages Cocoa/WebKit on macOS, gtk-webkit2 on Linux and MSHTML -(IE10/11) or Webkit via Edge on Windows. **Proton** is based on the -MIT licensed prior work known as [webview](https://github.com/zserge/webview). -The default binding to the underlying webview library uses Rust, but -other languages are possible (and only a PR away). +## Who Proton is For +Anyone who can create a Quasar app can use Proton, as it is *merely* a new +build target. All components and plugins (suitable for Native Desktop) can +be used. For the User Interface, nothing has changed, except you will +probably notice that everything seems much faster. + +Because of the way Proton has been built and can be extended, developers +are able to interface not only with the entire Rust ecosystem, but also +with many other programming languages. Being freed of the heaviest thing +in the universe and the many shortcomings of server-side Javascript +suddenly opens up whole new avenues for high-performance, security-focused +applications that need the purebred power, agility and community +acceptance of a low-level language. + +We expect to witness an entire new class of applications being built with +Quasar Proton. From a simple calender to locally crunching massive realtime +feeds at particle colliders or even mesh-network based distributed message- +passing ecosystems - the bar has been raised and gauntlet thrown. + +What will you make? + +## Technical Details +The user interface in Proton apps currently leverages Cocoa/WebKit on macOS, +gtk-webkit2 on Linux and MSHTML (IE10/11) or Webkit via Edge on Windows. +**Proton** is based on the MIT licensed prior work known as +[webview](https://github.com/zserge/webview). + +The default binding to the underlying webview library currently uses Rust, +but other languages like Golang or Python (and many others) are possible +(and only a PR away). + +> Rust is blazingly fast and memory-efficient: with no runtime or garbage +collector, it can power performance-critical services, run on embedded +devices, and easily integrate with other languages. Rust’s rich type system +and ownership model guarantee memory-safety and thread-safety — and enable +you to eliminate many classes of bugs at compile-time. Rust has great +documentation, a friendly compiler with useful error messages, and top-notch +tooling — an integrated package manager and build tool, smart multi-editor +support with auto-completion and type inspections, an auto-formatter, and +more. - [https://www.rust-lang.org/](https://www.rust-lang.org/) + +This combination of power, safety and usability are why we chose Rust to be +the default binding for Proton. It is our intention to provide the most safe +and performant native app experience (for devs and app consumers), out of +the box. ### Current Status We are in the process of vetting this new mode. It is not yet available to use without jumping through some development hurdles. If you don't care, please reach out to the team at https://chat.quasar.dev and we'll guide -you through the process. +you through the process. Here is a bit of a status report. -### Comparison between Proton and Electron +- [x] Promise based File System Access +- [x] App Icons and integration with Icon-Genie +- [ ] Frameless Mode +- [x] Build on MacOS +- [x] Build on Linux +- [ ] Build on Windows +- [x] STDOUT Passthrough with Command Invocation +- [x] Self Updater +- [x] Inter Process Communication (IPC) +- [x] Documentation + + +### Comparison between Proton 1 and Electron 5 | | Proton | Electron | |--|--------|----------| | Binary Size MacOS | 3.6 MB | 148.7 MB | | Memory Consumption MacOS | 13 MB | 34.1 MB | -| Benchmark FPS † | TODO | TODO | +| Benchmark FPS | TODO | TODO | | Interface Service Provider | Varies | Chromium | | Quasar UI | VueJS | VueJS | | Backend Binding | Rust | Node.js (ECMAScript) | @@ -44,8 +96,9 @@ you through the process. | FLOSS | Yes | No | | Multithreading | Yes | No | | Bytecode Delivery | Yes | No | -| Multiple Windows | Yes | Yes | | Can Render PDF | Yes | No | +| Multiple Windows | Yes | Yes | +| GPU Access | Yes | Yes | | Updater | Yes | Yes | | Inter Process Communication (IPC) | Yes | Yes | | Cross Platform | Yes | Yes | @@ -64,38 +117,42 @@ This has been done with our best attempt at due diligence and in respect of the original authors. Thankyou - this project would never have been possible without your amazing contribution to open-source and we are honoured to carry the torch further. Of special note: -- [zserge](https://github.com/zserge) for the original webview approach +- [zserge](https://github.com/zserge) for the original webview approach and +go bindings - [Boscop](https://github.com/Boscop) for the Rust Bindings ## Documentation -Head over to the Quasar Framework official website: [https://quasar.dev](https://quasar.dev) +Head over to the Quasar Framework official website: +[https://quasar.dev](https://quasar.dev) ## Stay in Touch -For latest releases and announcements, follow on Twitter: [@quasarframework](https://twitter.com/quasarframework) +For latest releases and announcements, follow on Twitter: +[@quasarframework](https://twitter.com/quasarframework) ## Chat Support -Get realtime help at the official community Discord server: [https://chat.quasar.dev](https://chat.quasar.dev) +Get realtime help at the official community Discord server: +[https://chat.quasar.dev](https://chat.quasar.dev) ## Community Forum -Ask complicated questions at the official community forum: [https://forum.quasar.dev](https://forum.quasar.dev) +Ask complicated questions at the official community forum: +[https://forum.quasar.dev](https://forum.quasar.dev) ## Contributing -Please make sure to read the [Contributing Guide](./.github/CONTRIBUTING.md) before making a pull request. If you have a Quasar-related project/component/tool, add it with a pull request to [this curated list](https://github.com/quasarframework/quasar-awesome)! +Please make sure to read the [Contributing Guide](./.github/CONTRIBUTING.md) +before making a pull request. If you have a Quasar-related +project/component/tool, add it with a pull request to +[this curated list](https://github.com/quasarframework/quasar-awesome)! -Thank you to all the people who already contributed to Proton! +Thank you to all the people who already contributed to Proton! ## Semver -Quasar is following [Semantic Versioning 2.0](https://semver.org/). +quasarframework/proton is following [Semantic Versioning 2.0](https://semver.org/). ## Licenses -Code: (c) 2019 - Daniel Thompson-Yvetot, Razvan Stoenescu, Lucas Nogueira. -MIT and where applicable Apache +Code: (c) 2019 - Daniel Thompson-Yvetot, Razvan Stoenescu, Lucas Nogueira, Boscop, Serge Zaitsev. +MIT Logo: CC-BY-NC-ND -Original Proton Logo Design by [Daniel Thompson-Yvetot](https://github.com/nothingismagick) -Based on the prior work by [Emanuele Bertoldi](https://github.com/zuck) - -Note: This license notice will not be complete until we have performed an -upstream audit. If you feel that your name should be listed here, please -create a PR to this file and provide references so we can fact-check. Thanks! +- Original Proton Logo Design by [Daniel Thompson-Yvetot](https://github.com/nothingismagick) +- Based on the prior work by [Emanuele Bertoldi](https://github.com/zuck) diff --git a/bindings/go/LICENSE b/bindings/go/LICENSE new file mode 100644 index 000000000..b18604bf4 --- /dev/null +++ b/bindings/go/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Serge Zaitsev + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/bindings/go/README.md b/bindings/go/README.md new file mode 100644 index 000000000..e0ee100b5 --- /dev/null +++ b/bindings/go/README.md @@ -0,0 +1 @@ +# go-bindings diff --git a/bindings/go/proton.go b/bindings/go/proton.go new file mode 100755 index 000000000..0b065f70e --- /dev/null +++ b/bindings/go/proton.go @@ -0,0 +1,572 @@ +// +// Package proton implements Go bindings to Proton UI. +// +// Bindings closely repeat the C APIs and include both, a simplified +// single-function API to just open a full-screen webview window, and a more +// advanced and featureful set of APIs, including Go-to-JavaScript bindings. +// +// The library uses gtk-webkit, Cocoa/Webkit and MSHTML (IE8..11) as a browser +// engine and supports Linux, MacOS and Windows 7..10 respectively. +// +package proton + +/* +#cgo linux openbsd freebsd CFLAGS: -DWEBVIEW_GTK=1 +#cgo linux openbsd freebsd pkg-config: gtk+-3.0 webkit2gtk-4.0 + +#cgo windows CFLAGS: -DWEBVIEW_WINAPI=1 +#cgo windows LDFLAGS: -lole32 -lcomctl32 -loleaut32 -luuid -lgdi32 + +#cgo darwin CFLAGS: -DWEBVIEW_COCOA=1 +#cgo darwin LDFLAGS: -framework WebKit + +#include +#include +#define WEBVIEW_STATIC +#define WEBVIEW_IMPLEMENTATION +#include "webview.h" + +extern void _webviewExternalInvokeCallback(void *, void *); + +static inline void CgoWebViewFree(void *w) { + free((void *)((struct webview *)w)->title); + free((void *)((struct webview *)w)->url); + free(w); +} + +static inline void *CgoWebViewCreate(int width, int height, char *title, char *url, int resizable, int debug) { + struct webview *w = (struct webview *) calloc(1, sizeof(*w)); + w->width = width; + w->height = height; + w->title = title; + w->url = url; + w->resizable = resizable; + w->debug = debug; + w->external_invoke_cb = (webview_external_invoke_cb_t) _webviewExternalInvokeCallback; + if (webview_init(w) != 0) { + CgoWebViewFree(w); + return NULL; + } + return (void *)w; +} + +static inline int CgoWebViewLoop(void *w, int blocking) { + return webview_loop((struct webview *)w, blocking); +} + +static inline void CgoWebViewTerminate(void *w) { + webview_terminate((struct webview *)w); +} + +static inline void CgoWebViewExit(void *w) { + webview_exit((struct webview *)w); +} + +static inline void CgoWebViewSetTitle(void *w, char *title) { + webview_set_title((struct webview *)w, title); +} + +static inline void CgoWebViewSetFullscreen(void *w, int fullscreen) { + webview_set_fullscreen((struct webview *)w, fullscreen); +} + +static inline void CgoWebViewSetColor(void *w, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + webview_set_color((struct webview *)w, r, g, b, a); +} + +static inline void CgoDialog(void *w, int dlgtype, int flags, + char *title, char *arg, char *res, size_t ressz) { + webview_dialog(w, dlgtype, flags, + (const char*)title, (const char*) arg, res, ressz); +} + +static inline int CgoWebViewEval(void *w, char *js) { + return webview_eval((struct webview *)w, js); +} + +static inline void CgoWebViewInjectCSS(void *w, char *css) { + webview_inject_css((struct webview *)w, css); +} + +extern void _webviewDispatchGoCallback(void *); +static inline void _webview_dispatch_cb(struct webview *w, void *arg) { + _webviewDispatchGoCallback(arg); +} +static inline void CgoWebViewDispatch(void *w, uintptr_t arg) { + webview_dispatch((struct webview *)w, _webview_dispatch_cb, (void *)arg); +} +*/ +import "C" +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "html/template" + "log" + "reflect" + "runtime" + "sync" + "unicode" + "unsafe" +) + +func init() { + // Ensure that main.main is called from the main thread + runtime.LockOSThread() +} + +// Open is a simplified API to open a single native window with a full-size webview in +// it. It can be helpful if you want to communicate with the core app using XHR +// or WebSockets (as opposed to using JavaScript bindings). +// +// Window appearance can be customized using title, width, height and resizable parameters. +// URL must be provided and can user either a http or https protocol, or be a +// local file:// URL. On some platforms "data:" URLs are also supported +// (Linux/MacOS). +func Open(title, url string, w, h int, resizable bool) error { + titleStr := C.CString(title) + defer C.free(unsafe.Pointer(titleStr)) + urlStr := C.CString(url) + defer C.free(unsafe.Pointer(urlStr)) + resize := C.int(0) + if resizable { + resize = C.int(1) + } + + r := C.webview(titleStr, urlStr, C.int(w), C.int(h), resize) + if r != 0 { + return errors.New("failed to create webview") + } + return nil +} + +// Debug prints a debug string using stderr on Linux/BSD, NSLog on MacOS and +// OutputDebugString on Windows. +func Debug(a ...interface{}) { + s := C.CString(fmt.Sprint(a...)) + defer C.free(unsafe.Pointer(s)) + C.webview_print_log(s) +} + +// Debugf prints a formatted debug string using stderr on Linux/BSD, NSLog on +// MacOS and OutputDebugString on Windows. +func Debugf(format string, a ...interface{}) { + s := C.CString(fmt.Sprintf(format, a...)) + defer C.free(unsafe.Pointer(s)) + C.webview_print_log(s) +} + +// ExternalInvokeCallbackFunc is a function type that is called every time +// "window.external.invoke()" is called from JavaScript. Data is the only +// obligatory string parameter passed into the "invoke(data)" function from +// JavaScript. To pass more complex data serialized JSON or base64 encoded +// string can be used. +type ExternalInvokeCallbackFunc func(w WebView, data string) + +// Settings is a set of parameters to customize the initial WebView appearance +// and behavior. It is passed into the webview.New() constructor. +type Settings struct { + // WebView main window title + Title string + // URL to open in a webview + URL string + // Window width in pixels + Width int + // Window height in pixels + Height int + // Allows/disallows window resizing + Resizable bool + // Enable debugging tools (Linux/BSD/MacOS, on Windows use Firebug) + Debug bool + // A callback that is executed when JavaScript calls "window.external.invoke()" + ExternalInvokeCallback ExternalInvokeCallbackFunc +} + +// WebView is an interface that wraps the basic methods for controlling the UI +// loop, handling multithreading and providing JavaScript bindings. +type WebView interface { + // Run() starts the main UI loop until the user closes the webview window or + // Terminate() is called. + Run() + // Loop() runs a single iteration of the main UI. + Loop(blocking bool) bool + // SetTitle() changes window title. This method must be called from the main + // thread only. See Dispatch() for more details. + SetTitle(title string) + // SetFullscreen() controls window full-screen mode. This method must be + // called from the main thread only. See Dispatch() for more details. + SetFullscreen(fullscreen bool) + // SetColor() changes window background color. This method must be called from + // the main thread only. See Dispatch() for more details. + SetColor(r, g, b, a uint8) + // Eval() evaluates an arbitrary JS code inside the webview. This method must + // be called from the main thread only. See Dispatch() for more details. + Eval(js string) error + // InjectJS() injects an arbitrary block of CSS code using the JS API. This + // method must be called from the main thread only. See Dispatch() for more + // details. + InjectCSS(css string) + // Dialog() opens a system dialog of the given type and title. String + // argument can be provided for certain dialogs, such as alert boxes. For + // alert boxes argument is a message inside the dialog box. + Dialog(dlgType DialogType, flags int, title string, arg string) string + // Terminate() breaks the main UI loop. This method must be called from the main thread + // only. See Dispatch() for more details. + Terminate() + // Dispatch() schedules some arbitrary function to be executed on the main UI + // thread. This may be helpful if you want to run some JavaScript from + // background threads/goroutines, or to terminate the app. + Dispatch(func()) + // Exit() closes the window and cleans up the resources. Use Terminate() to + // forcefully break out of the main UI loop. + Exit() + // Bind() registers a binding between a given value and a JavaScript object with the + // given name. A value must be a struct or a struct pointer. All methods are + // available under their camel-case names, starting with a lower-case letter, + // e.g. "FooBar" becomes "fooBar" in JavaScript. + // Bind() returns a function that updates JavaScript object with the current + // Go value. You only need to call it if you change Go value asynchronously. + Bind(name string, v interface{}) (sync func(), err error) +} + +// DialogType is an enumeration of all supported system dialog types +type DialogType int + +const ( + // DialogTypeOpen is a system file open dialog + DialogTypeOpen DialogType = iota + // DialogTypeSave is a system file save dialog + DialogTypeSave + // DialogTypeAlert is a system alert dialog (message box) + DialogTypeAlert +) + +const ( + // DialogFlagFile is a normal file picker dialog + DialogFlagFile = C.WEBVIEW_DIALOG_FLAG_FILE + // DialogFlagDirectory is an open directory dialog + DialogFlagDirectory = C.WEBVIEW_DIALOG_FLAG_DIRECTORY + // DialogFlagInfo is an info alert dialog + DialogFlagInfo = C.WEBVIEW_DIALOG_FLAG_INFO + // DialogFlagWarning is a warning alert dialog + DialogFlagWarning = C.WEBVIEW_DIALOG_FLAG_WARNING + // DialogFlagError is an error dialog + DialogFlagError = C.WEBVIEW_DIALOG_FLAG_ERROR +) + +var ( + m sync.Mutex + index uintptr + fns = map[uintptr]func(){} + cbs = map[WebView]ExternalInvokeCallbackFunc{} +) + +type webview struct { + w unsafe.Pointer +} + +var _ WebView = &webview{} + +func boolToInt(b bool) int { + if b { + return 1 + } + return 0 +} + +// New creates and opens a new webview window using the given settings. The +// returned object implements the WebView interface. This function returns nil +// if a window can not be created. +func New(settings Settings) WebView { + if settings.Width == 0 { + settings.Width = 640 + } + if settings.Height == 0 { + settings.Height = 480 + } + if settings.Title == "" { + settings.Title = "WebView" + } + w := &webview{} + w.w = C.CgoWebViewCreate(C.int(settings.Width), C.int(settings.Height), + C.CString(settings.Title), C.CString(settings.URL), + C.int(boolToInt(settings.Resizable)), C.int(boolToInt(settings.Debug))) + m.Lock() + if settings.ExternalInvokeCallback != nil { + cbs[w] = settings.ExternalInvokeCallback + } else { + cbs[w] = func(w WebView, data string) {} + } + m.Unlock() + return w +} + +func (w *webview) Loop(blocking bool) bool { + block := C.int(0) + if blocking { + block = 1 + } + return C.CgoWebViewLoop(w.w, block) == 0 +} + +func (w *webview) Run() { + for w.Loop(true) { + } +} + +func (w *webview) Exit() { + C.CgoWebViewExit(w.w) +} + +func (w *webview) Dispatch(f func()) { + m.Lock() + for ; fns[index] != nil; index++ { + } + fns[index] = f + m.Unlock() + C.CgoWebViewDispatch(w.w, C.uintptr_t(index)) +} + +func (w *webview) SetTitle(title string) { + p := C.CString(title) + defer C.free(unsafe.Pointer(p)) + C.CgoWebViewSetTitle(w.w, p) +} + +func (w *webview) SetColor(r, g, b, a uint8) { + C.CgoWebViewSetColor(w.w, C.uint8_t(r), C.uint8_t(g), C.uint8_t(b), C.uint8_t(a)) +} + +func (w *webview) SetFullscreen(fullscreen bool) { + C.CgoWebViewSetFullscreen(w.w, C.int(boolToInt(fullscreen))) +} + +func (w *webview) Dialog(dlgType DialogType, flags int, title string, arg string) string { + const maxPath = 4096 + titlePtr := C.CString(title) + defer C.free(unsafe.Pointer(titlePtr)) + argPtr := C.CString(arg) + defer C.free(unsafe.Pointer(argPtr)) + resultPtr := (*C.char)(C.calloc((C.size_t)(unsafe.Sizeof((*C.char)(nil))), (C.size_t)(maxPath))) + defer C.free(unsafe.Pointer(resultPtr)) + C.CgoDialog(w.w, C.int(dlgType), C.int(flags), titlePtr, + argPtr, resultPtr, C.size_t(maxPath)) + return C.GoString(resultPtr) +} + +func (w *webview) Eval(js string) error { + p := C.CString(js) + defer C.free(unsafe.Pointer(p)) + switch C.CgoWebViewEval(w.w, p) { + case -1: + return errors.New("evaluation failed") + } + return nil +} + +func (w *webview) InjectCSS(css string) { + p := C.CString(css) + defer C.free(unsafe.Pointer(p)) + C.CgoWebViewInjectCSS(w.w, p) +} + +func (w *webview) Terminate() { + C.CgoWebViewTerminate(w.w) +} + +//export _webviewDispatchGoCallback +func _webviewDispatchGoCallback(index unsafe.Pointer) { + var f func() + m.Lock() + f = fns[uintptr(index)] + delete(fns, uintptr(index)) + m.Unlock() + f() +} + +//export _webviewExternalInvokeCallback +func _webviewExternalInvokeCallback(w unsafe.Pointer, data unsafe.Pointer) { + m.Lock() + var ( + cb ExternalInvokeCallbackFunc + wv WebView + ) + for wv, cb = range cbs { + if wv.(*webview).w == w { + break + } + } + m.Unlock() + cb(wv, C.GoString((*C.char)(data))) +} + +var bindTmpl = template.Must(template.New("").Parse(` +if (typeof {{.Name}} === 'undefined') { + {{.Name}} = {}; +} +{{ range .Methods }} +{{$.Name}}.{{.JSName}} = function({{.JSArgs}}) { + window.external.invoke(JSON.stringify({scope: "{{$.Name}}", method: "{{.Name}}", params: [{{.JSArgs}}]})); +}; +{{ end }} +`)) + +type binding struct { + Value interface{} + Name string + Methods []methodInfo +} + +func newBinding(name string, v interface{}) (*binding, error) { + methods, err := getMethods(v) + if err != nil { + return nil, err + } + return &binding{Name: name, Value: v, Methods: methods}, nil +} + +func (b *binding) JS() (string, error) { + js := &bytes.Buffer{} + err := bindTmpl.Execute(js, b) + return js.String(), err +} + +func (b *binding) Sync() (string, error) { + js, err := json.Marshal(b.Value) + if err == nil { + return fmt.Sprintf("%[1]s.data=%[2]s;if(%[1]s.render){%[1]s.render(%[2]s);}", b.Name, string(js)), nil + } + return "", err +} + +func (b *binding) Call(js string) bool { + type rpcCall struct { + Scope string `json:"scope"` + Method string `json:"method"` + Params []interface{} `json:"params"` + } + + rpc := rpcCall{} + if err := json.Unmarshal([]byte(js), &rpc); err != nil { + return false + } + if rpc.Scope != b.Name { + return false + } + var mi *methodInfo + for i := 0; i < len(b.Methods); i++ { + if b.Methods[i].Name == rpc.Method { + mi = &b.Methods[i] + break + } + } + if mi == nil { + return false + } + args := make([]reflect.Value, mi.Arity(), mi.Arity()) + for i := 0; i < mi.Arity(); i++ { + val := reflect.ValueOf(rpc.Params[i]) + arg := mi.Value.Type().In(i) + u := reflect.New(arg) + if b, err := json.Marshal(val.Interface()); err == nil { + if err = json.Unmarshal(b, u.Interface()); err == nil { + args[i] = reflect.Indirect(u) + } + } + if !args[i].IsValid() { + return false + } + } + mi.Value.Call(args) + return true +} + +type methodInfo struct { + Name string + Value reflect.Value +} + +func (mi methodInfo) Arity() int { return mi.Value.Type().NumIn() } + +func (mi methodInfo) JSName() string { + r := []rune(mi.Name) + if len(r) > 0 { + r[0] = unicode.ToLower(r[0]) + } + return string(r) +} + +func (mi methodInfo) JSArgs() (js string) { + for i := 0; i < mi.Arity(); i++ { + if i > 0 { + js = js + "," + } + js = js + fmt.Sprintf("a%d", i) + } + return js +} + +func getMethods(obj interface{}) ([]methodInfo, error) { + p := reflect.ValueOf(obj) + v := reflect.Indirect(p) + t := reflect.TypeOf(obj) + if t == nil { + return nil, errors.New("object can not be nil") + } + k := t.Kind() + if k == reflect.Ptr { + k = v.Type().Kind() + } + if k != reflect.Struct { + return nil, errors.New("must be a struct or a pointer to a struct") + } + + methods := []methodInfo{} + for i := 0; i < t.NumMethod(); i++ { + method := t.Method(i) + if !unicode.IsUpper([]rune(method.Name)[0]) { + continue + } + mi := methodInfo{ + Name: method.Name, + Value: p.MethodByName(method.Name), + } + methods = append(methods, mi) + } + + return methods, nil +} + +func (w *webview) Bind(name string, v interface{}) (sync func(), err error) { + b, err := newBinding(name, v) + if err != nil { + return nil, err + } + js, err := b.JS() + if err != nil { + return nil, err + } + sync = func() { + if js, err := b.Sync(); err != nil { + log.Println(err) + } else { + w.Eval(js) + } + } + + m.Lock() + cb := cbs[w] + cbs[w] = func(w WebView, data string) { + if ok := b.Call(data); ok { + sync() + } else { + cb(w, data) + } + } + m.Unlock() + + w.Eval(js) + sync() + return sync, nil +} diff --git a/bindings/go/proton_test.go b/bindings/go/proton_test.go new file mode 100644 index 000000000..49fc951e8 --- /dev/null +++ b/bindings/go/proton_test.go @@ -0,0 +1,97 @@ +package proton + +import ( + "image" + "testing" +) + +type foo struct { + Result interface{} +} + +func (f *foo) Foo1(a int, b float32) { + f.Result = float64(a) + float64(b) +} +func (f *foo) Foo2(a []int, b [3]float32, c map[int]int) { + f.Result = map[string]interface{}{"a": a, "b": b, "c": c} +} +func (f *foo) Foo3(a []image.Point, b struct{ Z int }) { + f.Result = map[string]interface{}{"a": a, "b": b} +} + +func TestBadBinding(t *testing.T) { + x := 123 + for _, v := range []interface{}{ + nil, + true, + 123, + 123.4, + "hello", + 'a', + make(chan struct{}, 0), + func() {}, + map[string]string{}, + []int{}, + [3]int{0, 0, 0}, + &x, + } { + if _, err := newBinding("test", v); err == nil { + t.Errorf("should return an error: %#v", v) + } + } +} + +func TestBindingCall(t *testing.T) { + foo := &foo{} + b, err := newBinding("test", foo) + if err != nil { + t.Fatal(err) + } + t.Run("Primitives", func(t *testing.T) { + if !b.Call(`{"scope":"test","method":"Foo1","params":[3,4.5]}`) { + t.Fatal() + } + if foo.Result.(float64) != 7.5 { + t.Fatal(foo) + } + }) + + t.Run("Collections", func(t *testing.T) { + // Call with slices, arrays and maps + if !b.Call(`{"scope":"test","method":"Foo2","params":[[1,2,3],[4.5,4.6,4.7],{"1":2,"3":4}]}`) { + t.Fatal() + } + m := foo.Result.(map[string]interface{}) + if ints := m["a"].([]int); ints[0] != 1 || ints[1] != 2 || ints[2] != 3 { + t.Fatal(foo) + } + if floats := m["b"].([3]float32); floats[0] != 4.5 || floats[1] != 4.6 || floats[2] != 4.7 { + t.Fatal(foo) + } + if dict := m["c"].(map[int]int); len(dict) != 2 || dict[1] != 2 || dict[3] != 4 { + t.Fatal(foo) + } + }) + + t.Run("Structs", func(t *testing.T) { + if !b.Call(`{"scope":"test","method":"Foo3","params":[[{"X":1,"Y":2},{"X":3,"Y":4}],{"Z":42}]}`) { + t.Fatal() + } + m := foo.Result.(map[string]interface{}) + if p := m["a"].([]image.Point); p[0].X != 1 || p[0].Y != 2 || p[1].X != 3 || p[1].Y != 4 { + t.Fatal(foo) + } + if z := m["b"].(struct{ Z int }); z.Z != 42 { + t.Fatal(foo) + } + }) + + t.Run("Errors", func(t *testing.T) { + if b.Call(`{"scope":"foo"}`) || b.Call(`{"scope":"test", "method":"Bar"}`) { + t.Fatal() + } + if b.Call(`{"scope":"test","method":"Foo1","params":["3",4.5]}`) { + t.Fatal() + } + }) +} diff --git a/bindings/rust/.gitignore b/bindings/rust/.gitignore new file mode 100644 index 000000000..ace562d6e --- /dev/null +++ b/bindings/rust/.gitignore @@ -0,0 +1,4 @@ +target/ +**/*.rs.bk +Cargo.lock +.idea diff --git a/bindings/rust/Cargo.toml b/bindings/rust/Cargo.toml new file mode 100644 index 000000000..93d6250f0 --- /dev/null +++ b/bindings/rust/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "proton-ui" +version = "0.1.0" +authors = ["Boscop", "rstoenescu", "nothingismagick", "lucasfernog"] +readme = "README.md" +license = "MIT" +repository = "https://github.com/quasarframework/proton" +description = "Rust bindings for proton, a toolchain for building more secure native apps that have tiny binaries and are very fast." +keywords = ["quasar", "web", "gui", "desktop", "webkit"] +categories = ["quasar", "gui", "web-programming", "api-bindings", "rendering", "visualization"] + +[dependencies] +urlencoding = "1.0" +proton-sys = { path = "proton-sys", version = "0.1.0" } +boxfnonce = "0.1" + +[features] +default = ["V1_30"] +V1_30 = [] + +[dev-dependencies] +serde = "1.0" +serde_derive = "1.0" +serde_json = "1.0" \ No newline at end of file diff --git a/bindings/rust/LICENSE b/bindings/rust/LICENSE new file mode 100644 index 000000000..c592e15be --- /dev/null +++ b/bindings/rust/LICENSE @@ -0,0 +1,45 @@ +MIT License + +Copyright (c) 2018 Boscop + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +---- + +MIT License + +Copyright (c) 2019 Quasar Framework + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/bindings/rust/README.md b/bindings/rust/README.md new file mode 100644 index 000000000..795f7eb8e --- /dev/null +++ b/bindings/rust/README.md @@ -0,0 +1 @@ +# rust bindings diff --git a/bindings/rust/proton-sys/Cargo.toml b/bindings/rust/proton-sys/Cargo.toml new file mode 100644 index 000000000..f63a7fb7e --- /dev/null +++ b/bindings/rust/proton-sys/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "proton-sys" +version = "0.1.0" +authors = ["Boscop", "rstoenescu", "nothingismagick", "lucasfernog"] +license = "MIT" +repository = "https://github.com/quasarframework/proton" +description = "Rust native ffi bindings for proton UI" +keywords = ["quasar", "web", "gui", "desktop", "webkit"] +categories = ["quasar", "gui", "web-programming", "api-bindings", "rendering", "visualization"] +build = "build.rs" +links = "proton" + +[lib] +name = "proton_sys" +path = "lib.rs" + +[dependencies] +bitflags = "1.0" + +[build-dependencies] +cc = "1" +pkg-config = "0.3" \ No newline at end of file diff --git a/bindings/rust/proton-sys/build.rs b/bindings/rust/proton-sys/build.rs new file mode 100644 index 000000000..f76754355 --- /dev/null +++ b/bindings/rust/proton-sys/build.rs @@ -0,0 +1,56 @@ +extern crate cc; +extern crate pkg_config; + +use std::{ + env, + path::{Path, PathBuf}, + process::Command, +}; + +fn main() { + let proton_path = PathBuf::from("../../../ui"); + + let mut build = cc::Build::new(); + + build + .include(&proton_path) + .file("proton.c") + .flag_if_supported("-std=c11") + .flag_if_supported("-w"); + + if env::var("DEBUG").is_err() { + build.define("NDEBUG", None); + } else { + build.define("DEBUG", None); + } + + let target = env::var("TARGET").unwrap(); + + if target.contains("windows") { + 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") { + let webkit = pkg_config::Config::new() + .atleast_version("2.8") + .probe("webkit2gtk-4.0") + .unwrap(); + + for path in webkit.include_paths { + build.include(path); + } + build.define("WEBVIEW_GTK", None); + } else if target.contains("apple") { + build + .define("WEBVIEW_COCOA", None) + .flag("-x") + .flag("objective-c"); + println!("cargo:rustc-link-lib=framework=Cocoa"); + println!("cargo:rustc-link-lib=framework=WebKit"); + } else { + panic!("unsupported target"); + } + + build.compile("proton"); +} diff --git a/bindings/rust/proton-sys/lib.rs b/bindings/rust/proton-sys/lib.rs new file mode 100644 index 000000000..559040b84 --- /dev/null +++ b/bindings/rust/proton-sys/lib.rs @@ -0,0 +1,45 @@ +//! Raw FFI bindings to proton UI. + +#[macro_use] +extern crate bitflags; + +use std::os::raw::*; + +pub enum CWebView {} // opaque type, only used in ffi pointers + +type ErasedExternalInvokeFn = extern "C" fn(webview: *mut CWebView, arg: *const c_char); +type ErasedDispatchFn = extern "C" fn(webview: *mut CWebView, arg: *mut c_void); + +#[repr(C)] +pub enum DialogType { + Open = 0, + Save = 1, + Alert = 2, +} + +bitflags! { + #[repr(C)] + pub struct DialogFlags: u32 { + const FILE = 0b0000; + const DIRECTORY = 0b0001; + const INFO = 0b0010; + const WARNING = 0b0100; + const ERROR = 0b0110; + } +} + +extern { + pub fn wrapper_webview_free(this: *mut CWebView); + pub fn wrapper_webview_new(title: *const c_char, url: *const c_char, width: c_int, height: c_int, resizable: c_int, debug: c_int, external_invoke_cb: Option, userdata: *mut c_void) -> *mut CWebView; + pub fn webview_loop(this: *mut CWebView, blocking: c_int) -> c_int; + pub fn webview_terminate(this: *mut CWebView); + pub fn webview_exit(this: *mut CWebView); + pub fn wrapper_webview_get_userdata(this: *mut CWebView) -> *mut c_void; + pub fn webview_dispatch(this: *mut CWebView, f: Option, arg: *mut c_void); + pub fn webview_eval(this: *mut CWebView, js: *const c_char) -> c_int; + pub fn webview_inject_css(this: *mut CWebView, css: *const c_char) -> c_int; + pub fn webview_set_title(this: *mut CWebView, title: *const c_char); + pub fn webview_set_fullscreen(this: *mut CWebView, fullscreen: c_int); + pub fn webview_set_color(this: *mut CWebView, red: u8, green: u8, blue: u8, alpha: u8); + pub fn webview_dialog(this: *mut CWebView, dialog_type: DialogType, flags: DialogFlags, title: *const c_char, arg: *const c_char, result: *mut c_char, result_size: usize); +} diff --git a/bindings/rust/proton-sys/proton.c b/bindings/rust/proton-sys/proton.c new file mode 100644 index 000000000..a4bb108e5 --- /dev/null +++ b/bindings/rust/proton-sys/proton.c @@ -0,0 +1,28 @@ +#define WEBVIEW_IMPLEMENTATION +#include "proton.h" + +void wrapper_webview_free(struct webview* w) { + free(w); +} + +struct webview* wrapper_webview_new(const char* title, const char* url, int width, int height, int resizable, int debug, webview_external_invoke_cb_t external_invoke_cb, void* userdata) { + struct webview* w = (struct webview*)calloc(1, sizeof(*w)); + w->width = width; + w->height = height; + w->title = title; + w->url = url; + w->resizable = resizable; + w->debug = debug; + w->external_invoke_cb = external_invoke_cb; + w->userdata = userdata; + if (webview_init(w) != 0) { + wrapper_webview_free(w); + return NULL; + } + return w; +} + +void* wrapper_webview_get_userdata(struct webview* w) { + return w->userdata; +} + diff --git a/bindings/rust/rustfmt.toml b/bindings/rust/rustfmt.toml new file mode 100644 index 000000000..9da25275e --- /dev/null +++ b/bindings/rust/rustfmt.toml @@ -0,0 +1,13 @@ +max_width = 100 +hard_tabs = false +tab_spaces = 2 +newline_style = "Auto" +use_small_heuristics = "Default" +reorder_imports = true +reorder_modules = true +remove_nested_parens = true +edition = "2015" +merge_derives = true +use_try_shorthand = false +use_field_init_shorthand = false +force_explicit_abi = true diff --git a/bindings/rust/src/color.rs b/bindings/rust/src/color.rs new file mode 100644 index 000000000..9bddcd257 --- /dev/null +++ b/bindings/rust/src/color.rs @@ -0,0 +1,52 @@ +/// An RGBA color. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub struct Color { + pub r: u8, + pub g: u8, + pub b: u8, + pub a: u8, +} + +impl From<(u8, u8, u8, u8)> for Color { + fn from(tuple: (u8, u8, u8, u8)) -> Color { + Color { + r: tuple.0, + g: tuple.1, + b: tuple.2, + a: tuple.3, + } + } +} + +impl From<(u8, u8, u8)> for Color { + fn from(tuple: (u8, u8, u8)) -> Color { + Color { + r: tuple.0, + g: tuple.1, + b: tuple.2, + a: 255, + } + } +} + +impl From<[u8; 4]> for Color { + fn from(array: [u8; 4]) -> Color { + Color { + r: array[0], + g: array[1], + b: array[2], + a: array[3], + } + } +} + +impl From<[u8; 3]> for Color { + fn from(array: [u8; 3]) -> Color { + Color { + r: array[0], + g: array[1], + b: array[2], + a: 255, + } + } +} diff --git a/bindings/rust/src/dialog.rs b/bindings/rust/src/dialog.rs new file mode 100644 index 000000000..2a6b8e9ed --- /dev/null +++ b/bindings/rust/src/dialog.rs @@ -0,0 +1,141 @@ +use ffi::{self, DialogFlags, DialogType}; +use std::{ffi::CString, path::PathBuf}; +use {read_str, WVResult, WebView}; + +const STR_BUF_SIZE: usize = 4096; + +/// A builder for opening a new dialog window. +#[derive(Debug)] +pub struct DialogBuilder<'a: 'b, 'b, T: 'a> { + webview: &'b mut WebView<'a, T>, +} + +impl<'a: 'b, 'b, T: 'a> DialogBuilder<'a, 'b, T> { + /// Creates a new dialog builder for a WebView. + pub fn new(webview: &'b mut WebView<'a, T>) -> DialogBuilder<'a, 'b, T> { + DialogBuilder { webview } + } + + fn dialog( + &mut self, + title: String, + arg: String, + dialog_type: DialogType, + dialog_flags: DialogFlags, + ) -> WVResult { + let mut s = [0u8; STR_BUF_SIZE]; + + let title_cstr = CString::new(title)?; + let arg_cstr = CString::new(arg)?; + + unsafe { + ffi::webview_dialog( + self.webview.inner, + dialog_type, + dialog_flags, + title_cstr.as_ptr(), + arg_cstr.as_ptr(), + s.as_mut_ptr() as _, + s.len(), + ); + } + + Ok(read_str(&s)) + } + + /// Opens a new open file dialog and returns the chosen file path. + pub fn open_file(&mut self, title: S, default_file: P) -> WVResult> + where + S: Into, + P: Into, + { + self + .dialog( + title.into(), + default_file.into().to_string_lossy().into_owned(), + DialogType::Open, + DialogFlags::FILE, + ) + .map(|path| { + if path.is_empty() { + None + } else { + Some(PathBuf::from(path)) + } + }) + } + + /// Opens a new choose directory dialog as returns the chosen directory path. + pub fn choose_directory( + &mut self, + title: S, + default_directory: P, + ) -> WVResult> + where + S: Into, + P: Into, + { + self + .dialog( + title.into(), + default_directory.into().to_string_lossy().into_owned(), + DialogType::Open, + DialogFlags::DIRECTORY, + ) + .map(|path| { + if path.is_empty() { + None + } else { + Some(PathBuf::from(path)) + } + }) + } + + /// Opens an info alert dialog. + pub fn info(&mut self, title: TS, message: MS) -> WVResult + where + TS: Into, + MS: Into, + { + self + .dialog( + title.into(), + message.into(), + DialogType::Alert, + DialogFlags::INFO, + ) + .map(|_| ()) + } + + /// Opens a warning alert dialog. + pub fn warning(&mut self, title: TS, message: MS) -> WVResult + where + TS: Into, + MS: Into, + { + self + .dialog( + title.into(), + message.into(), + DialogType::Alert, + DialogFlags::WARNING, + ) + .map(|_| ()) + } + + /// Opens an error alert dialog. + pub fn error(&mut self, title: TS, message: MS) -> WVResult + where + TS: Into, + MS: Into, + { + self + .dialog( + title.into(), + message.into(), + DialogType::Alert, + DialogFlags::ERROR, + ) + .map(|_| ()) + } +} diff --git a/bindings/rust/src/error.rs b/bindings/rust/src/error.rs new file mode 100644 index 000000000..07ed11a67 --- /dev/null +++ b/bindings/rust/src/error.rs @@ -0,0 +1,79 @@ +use std::{ + error, + ffi::NulError, + fmt::{self, Debug, Display}, +}; + +pub trait CustomError: Display + Debug + Send + Sync + 'static {} + +impl CustomError for T {} + +/// A WebView error. +#[derive(Debug)] +pub enum Error { + /// While attempting to build a WebView instance, a required field was not initialized. + UninitializedField(&'static str), + /// An error occurred while initializing a WebView instance. + Initialization, + /// A nul-byte was found in a provided string. + NulByte(NulError), + /// An error occurred while evaluating JavaScript in a WebView instance. + JsEvaluation, + /// An error occurred while injecting CSS into a WebView instance. + CssInjection, + /// Failure to dispatch a closure to a WebView instance via a handle, likely because the + /// WebView was dropped. + Dispatch, + /// An user-specified error occurred. For use inside invoke and dispatch closures. + Custom(Box), +} + +impl Error { + /// Creates a custom error from a `T: Display + Debug + Send + Sync + 'static`. + pub fn custom(error: E) -> Error { + Error::Custom(Box::new(error)) + } +} + +impl error::Error for Error { + fn cause(&self) -> Option<&error::Error> { + match self { + Error::NulByte(cause) => Some(cause), + _ => None, + } + } + + #[cfg(feature = "V1_30")] + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + match self { + Error::NulByte(ref cause) => Some(cause), + _ => None, + } + } +} + +impl Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Error::UninitializedField(field) => write!(f, "Required field uninitialized: {}.", field), + Error::Initialization => write!(f, "Webview failed to initialize."), + Error::NulByte(cause) => write!(f, "{}", cause), + Error::JsEvaluation => write!(f, "Failed to evaluate JavaScript."), + Error::CssInjection => write!(f, "Failed to inject CSS."), + Error::Dispatch => write!( + f, + "Closure could not be dispatched. WebView was likely dropped." + ), + Error::Custom(e) => write!(f, "Error: {}", e), + } + } +} + +/// A WebView result. +pub type WVResult = Result; + +impl From for Error { + fn from(e: NulError) -> Error { + Error::NulByte(e) + } +} diff --git a/bindings/rust/src/escape.rs b/bindings/rust/src/escape.rs new file mode 100644 index 000000000..e949cd9f4 --- /dev/null +++ b/bindings/rust/src/escape.rs @@ -0,0 +1,81 @@ +use std::fmt::{self, Write}; + +/// Escape a string to pass it into JavaScript. +/// +/// # Example +/// +/// ```rust,no_run +/// # use web_view::WebView; +/// # use std::mem; +/// # +/// # let mut view: WebView<()> = unsafe { mem::uninitialized() }; +/// # +/// let string = "Hello, world!"; +/// +/// // Calls the function callback with "Hello, world!" as its parameter. +/// +/// view.eval(&format!("callback({});", web_view::escape(string))); +/// ``` +pub fn escape(string: &str) -> Escaper { + Escaper(string) +} + +// "All code points may appear literally in a string literal except for the +// closing quote code points, U+005C (REVERSE SOLIDUS), U+000D (CARRIAGE +// RETURN), U+2028 (LINE SEPARATOR), U+2029 (PARAGRAPH SEPARATOR), and U+000A +// (LINE FEED)." - ES6 Specification + +pub struct Escaper<'a>(&'a str); + +const SPECIAL: &[char] = &[ + '\n', // U+000A (LINE FEED) + '\r', // U+000D (CARRIAGE RETURN) + '\'', // U+0027 (APOSTROPHE) + '\\', // U+005C (REVERSE SOLIDUS) + '\u{2028}', // U+2028 (LINE SEPARATOR) + '\u{2029}', // U+2029 (PARAGRAPH SEPARATOR) +]; + +impl<'a> fmt::Display for Escaper<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let &Escaper(mut string) = self; + + f.write_char('\'')?; + + while !string.is_empty() { + if let Some(i) = string.find(SPECIAL) { + if i > 0 { + f.write_str(&string[..i])?; + } + + let mut chars = string[i..].chars(); + + f.write_str(match chars.next().unwrap() { + '\n' => "\\n", + '\r' => "\\r", + '\'' => "\\'", + '\\' => "\\\\", + '\u{2028}' => "\\u2028", + '\u{2029}' => "\\u2029", + _ => unreachable!(), + })?; + + string = chars.as_str(); + } else { + f.write_str(string)?; + break; + } + } + + f.write_char('\'')?; + + Ok(()) + } +} + +#[test] +fn test() { + let plain = "ABC \n\r' abc \\ \u{2028} \u{2029}123"; + let escaped = escape(plain).to_string(); + assert!(escaped == "'ABC \\n\\r\\' abc \\\\ \\u2028 \\u2029123'"); +} diff --git a/bindings/rust/src/lib.rs b/bindings/rust/src/lib.rs new file mode 100644 index 000000000..d0b146948 --- /dev/null +++ b/bindings/rust/src/lib.rs @@ -0,0 +1,526 @@ +extern crate boxfnonce; +extern crate proton_sys as ffi; +extern crate urlencoding; + +mod color; +mod dialog; +mod error; +mod escape; +pub use color::Color; +pub use dialog::DialogBuilder; +pub use error::{CustomError, Error, WVResult}; +pub use escape::escape; + +use boxfnonce::SendBoxFnOnce; +use ffi::*; +use std::{ + ffi::{CStr, CString}, + marker::PhantomData, + mem, + os::raw::*, + sync::{Arc, RwLock, Weak}, +}; +use urlencoding::encode; + +/// Content displayable inside a [`WebView`]. +/// +/// # Variants +/// +/// - `Url` - Content to be fetched from a URL. +/// - `Html` - A string containing literal HTML. +/// +/// [`WebView`]: struct.WebView.html +#[derive(Debug)] +pub enum Content { + Url(T), + Html(T), +} + +/// Builder for constructing a [`WebView`] instance. +/// +/// # Example +/// +/// ```no_run +/// extern crate web_view; +/// +/// use web_view::*; +/// +/// fn main() { +/// WebViewBuilder::new() +/// .title("Minimal webview example") +/// .content(Content::Url("https://en.m.wikipedia.org/wiki/Main_Page")) +/// .size(800, 600) +/// .resizable(true) +/// .debug(true) +/// .user_data(()) +/// .invoke_handler(|_webview, _arg| Ok(())) +/// .build() +/// .unwrap() +/// .run() +/// .unwrap(); +/// } +/// ``` +/// +/// [`WebView`]: struct.WebView.html +pub struct WebViewBuilder<'a, T: 'a, I, C> { + pub title: &'a str, + pub content: Option>, + pub width: i32, + pub height: i32, + pub resizable: bool, + pub debug: bool, + pub invoke_handler: Option, + pub user_data: Option, +} + +impl<'a, T: 'a, I, C> Default for WebViewBuilder<'a, T, I, C> +where + I: FnMut(&mut WebView, &str) -> WVResult + 'a, + C: AsRef, +{ + fn default() -> Self { + #[cfg(debug_assertions)] + let debug = true; + #[cfg(not(debug_assertions))] + let debug = false; + + WebViewBuilder { + title: "Application", + content: None, + width: 800, + height: 600, + resizable: true, + debug, + invoke_handler: None, + user_data: None, + } + } +} + +impl<'a, T: 'a, I, C> WebViewBuilder<'a, T, I, C> +where + I: FnMut(&mut WebView, &str) -> WVResult + 'a, + C: AsRef, +{ + /// Alias for [`WebViewBuilder::default()`]. + /// + /// [`WebViewBuilder::default()`]: struct.WebviewBuilder.html#impl-Default + pub fn new() -> Self { + WebViewBuilder::default() + } + + /// Sets the title of the WebView window. + /// + /// Defaults to `"Application"`. + pub fn title(mut self, title: &'a str) -> Self { + self.title = title; + self + } + + /// Sets the content of the WebView. Either a URL or a HTML string. + pub fn content(mut self, content: Content) -> Self { + self.content = Some(content); + self + } + + /// Sets the size of the WebView window. + /// + /// Defaults to 800 x 600. + pub fn size(mut self, width: i32, height: i32) -> Self { + self.width = width; + self.height = height; + self + } + + /// Sets the resizability of the WebView window. If set to false, the window cannot be resized. + /// + /// Defaults to `true`. + pub fn resizable(mut self, resizable: bool) -> Self { + self.resizable = resizable; + self + } + + /// Enables or disables debug mode. + /// + /// Defaults to `true` for debug builds, `false` for release builds. + pub fn debug(mut self, debug: bool) -> Self { + self.debug = debug; + self + } + + /// Sets the invoke handler callback. This will be called when a message is received from + /// JavaScript. + /// + /// # Errors + /// + /// If the closure returns an `Err`, it will be returned on the next call to [`step()`]. + /// + /// [`step()`]: struct.WebView.html#method.step + pub fn invoke_handler(mut self, invoke_handler: I) -> Self { + self.invoke_handler = Some(invoke_handler); + self + } + + /// Sets the initial state of the user data. This is an arbitrary value stored on the WebView + /// thread, accessible from dispatched closures without synchronization overhead. + pub fn user_data(mut self, user_data: T) -> Self { + self.user_data = Some(user_data); + self + } + + /// Validates provided arguments and returns a new WebView if successful. + pub fn build(self) -> WVResult> { + macro_rules! require_field { + ($name:ident) => { + self + .$name + .ok_or_else(|| Error::UninitializedField(stringify!($name)))? + }; + } + + let title = CString::new(self.title)?; + let content = require_field!(content); + let url = match content { + Content::Url(url) => CString::new(url.as_ref())?, + Content::Html(html) => CString::new(format!("data:text/html,{}", encode(html.as_ref())))?, + }; + let user_data = require_field!(user_data); + let invoke_handler = require_field!(invoke_handler); + + WebView::new( + &title, + &url, + self.width, + self.height, + self.resizable, + self.debug, + user_data, + invoke_handler, + ) + } + + /// Validates provided arguments and runs a new WebView to completion, returning the user data. + /// + /// Equivalent to `build()?.run()`. + pub fn run(self) -> WVResult { + self.build()?.run() + } +} + +/// Constructs a new builder for a [`WebView`]. +/// +/// Alias for [`WebViewBuilder::default()`]. +/// +/// [`WebView`]: struct.Webview.html +/// [`WebViewBuilder::default()`]: struct.WebviewBuilder.html#impl-Default +pub fn builder<'a, T, I, C>() -> WebViewBuilder<'a, T, I, C> +where + I: FnMut(&mut WebView, &str) -> WVResult + 'a, + C: AsRef, +{ + WebViewBuilder::new() +} + +struct UserData<'a, T> { + inner: T, + live: Arc>, + invoke_handler: Box, &str) -> WVResult + 'a>, + result: WVResult, +} + +/// An owned webview instance. +/// +/// Construct via a [`WebViewBuilder`]. +/// +/// [`WebViewBuilder`]: struct.WebViewBuilder.html +#[derive(Debug)] +pub struct WebView<'a, T: 'a> { + inner: *mut CWebView, + _phantom: PhantomData<&'a mut T>, +} + +impl<'a, T> WebView<'a, T> { + #![cfg_attr(feature = "cargo-clippy", allow(clippy::too_many_arguments))] + fn new( + title: &CStr, + url: &CStr, + width: i32, + height: i32, + resizable: bool, + debug: bool, + user_data: T, + invoke_handler: I, + ) -> WVResult> + where + I: FnMut(&mut WebView, &str) -> WVResult + 'a, + { + let user_data = Box::new(UserData { + inner: user_data, + live: Arc::new(RwLock::new(())), + invoke_handler: Box::new(invoke_handler), + result: Ok(()), + }); + let user_data_ptr = Box::into_raw(user_data); + + unsafe { + let inner = wrapper_webview_new( + title.as_ptr(), + url.as_ptr(), + width, + height, + resizable as _, + debug as _, + Some(ffi_invoke_handler::), + user_data_ptr as _, + ); + + if inner.is_null() { + Box::>::from_raw(user_data_ptr); + Err(Error::Initialization) + } else { + Ok(WebView::from_ptr(inner)) + } + } + } + + unsafe fn from_ptr(inner: *mut CWebView) -> WebView<'a, T> { + WebView { + inner, + _phantom: PhantomData, + } + } + + /// Creates a thread-safe [`Handle`] to the `WebView`, from which closures can be dispatched. + /// + /// [`Handle`]: struct.Handle.html + pub fn handle(&self) -> Handle { + Handle { + inner: self.inner, + live: Arc::downgrade(&self.user_data_wrapper().live), + _phantom: PhantomData, + } + } + + fn user_data_wrapper_ptr(&self) -> *mut UserData<'a, T> { + unsafe { wrapper_webview_get_userdata(self.inner) as _ } + } + + fn user_data_wrapper(&self) -> &UserData<'a, T> { + unsafe { &(*self.user_data_wrapper_ptr()) } + } + + fn user_data_wrapper_mut(&mut self) -> &mut UserData<'a, T> { + unsafe { &mut (*self.user_data_wrapper_ptr()) } + } + + /// Borrows the user data immutably. + pub fn user_data(&self) -> &T { + &self.user_data_wrapper().inner + } + + /// Borrows the user data mutably. + pub fn user_data_mut(&mut self) -> &mut T { + &mut self.user_data_wrapper_mut().inner + } + + /// Forces the `WebView` instance to end, without dropping. + pub fn terminate(&mut self) { + unsafe { webview_terminate(self.inner) } + } + + /// Executes the provided string as JavaScript code within the `WebView` instance. + pub fn eval(&mut self, js: &str) -> WVResult { + let js = CString::new(js)?; + let ret = unsafe { webview_eval(self.inner, js.as_ptr()) }; + if ret != 0 { + Err(Error::JsEvaluation) + } else { + Ok(()) + } + } + + /// Injects the provided string as CSS within the `WebView` instance. + pub fn inject_css(&mut self, css: &str) -> WVResult { + let css = CString::new(css)?; + let ret = unsafe { webview_inject_css(self.inner, css.as_ptr()) }; + if ret != 0 { + Err(Error::CssInjection) + } else { + Ok(()) + } + } + + /// Sets the color of the title bar. + /// + /// # Examples + /// + /// Without specifying alpha (defaults to 255): + /// ```ignore + /// webview.set_color((123, 321, 213)); + /// ``` + /// + /// Specifying alpha: + /// ```ignore + /// webview.set_color((123, 321, 213, 127)); + /// ``` + pub fn set_color>(&mut self, color: C) { + let color = color.into(); + unsafe { webview_set_color(self.inner, color.r, color.g, color.b, color.a) } + } + + /// Sets the title displayed at the top of the window. + /// + /// # Errors + /// + /// If `title` contain a nul byte, returns [`Error::NulByte`]. + /// + /// [`Error::NulByte`]: enum.Error.html#variant.NulByte + pub fn set_title(&mut self, title: &str) -> WVResult { + let title = CString::new(title)?; + unsafe { webview_set_title(self.inner, title.as_ptr()) } + Ok(()) + } + + /// Enables or disables fullscreen. + pub fn set_fullscreen(&mut self, fullscreen: bool) { + unsafe { webview_set_fullscreen(self.inner, fullscreen as _) }; + } + + /// Returns a builder for opening a new dialog window. + pub fn dialog<'b>(&'b mut self) -> DialogBuilder<'a, 'b, T> { + DialogBuilder::new(self) + } + + /// Iterates the event loop. Returns `None` if the view has been closed or terminated. + pub fn step(&mut self) -> Option { + unsafe { + match webview_loop(self.inner, 1) { + 0 => { + let closure_result = &mut self.user_data_wrapper_mut().result; + match closure_result { + Ok(_) => Some(Ok(())), + e => Some(mem::replace(e, Ok(()))), + } + } + _ => None, + } + } + } + + /// Runs the event loop to completion and returns the user data. + pub fn run(mut self) -> WVResult { + loop { + match self.step() { + Some(Ok(_)) => (), + Some(e) => e?, + None => return Ok(self.into_inner()), + } + } + } + + /// Consumes the `WebView` and returns ownership of the user data. + pub fn into_inner(mut self) -> T { + unsafe { + let user_data = self._into_inner(); + mem::forget(self); + user_data + } + } + + unsafe fn _into_inner(&mut self) -> T { + let _lock = self + .user_data_wrapper() + .live + .write() + .expect("A dispatch channel thread panicked while holding mutex to WebView."); + + let user_data_ptr = self.user_data_wrapper_ptr(); + webview_exit(self.inner); + wrapper_webview_free(self.inner); + let user_data = *Box::from_raw(user_data_ptr); + user_data.inner + } +} + +impl<'a, T> Drop for WebView<'a, T> { + fn drop(&mut self) { + unsafe { + self._into_inner(); + } + } +} + +/// A thread-safe handle to a [`WebView`] instance. Used to dispatch closures onto its task queue. +/// +/// [`WebView`]: struct.WebView.html +pub struct Handle { + inner: *mut CWebView, + live: Weak>, + _phantom: PhantomData, +} + +impl Handle { + /// Schedules a closure to be run on the [`WebView`] thread. + /// + /// # Errors + /// + /// Returns [`Error::Dispatch`] if the [`WebView`] has been dropped. + /// + /// If the closure returns an `Err`, it will be returned on the next call to [`step()`]. + /// + /// [`WebView`]: struct.WebView.html + /// [`Error::Dispatch`]: enum.Error.html#variant.Dispatch + /// [`step()`]: struct.WebView.html#method.step + pub fn dispatch(&self, f: F) -> WVResult + where + F: FnOnce(&mut WebView) -> WVResult + Send + 'static, + { + // Abort if WebView has been dropped. Otherwise, keep it alive until closure has been + // dispatched. + let mutex = self.live.upgrade().ok_or(Error::Dispatch)?; + let closure = Box::new(SendBoxFnOnce::new(f)); + let _lock = mutex.read().map_err(|_| Error::Dispatch)?; + + // Send closure to webview. + unsafe { + webview_dispatch( + self.inner, + Some(ffi_dispatch_handler:: as _), + Box::into_raw(closure) as _, + ) + } + Ok(()) + } +} + +unsafe impl Send for Handle {} +unsafe impl Sync for Handle {} + +fn read_str(s: &[u8]) -> String { + let end = s.iter().position(|&b| b == 0).map_or(0, |i| i + 1); + match CStr::from_bytes_with_nul(&s[..end]) { + Ok(s) => s.to_string_lossy().into_owned(), + Err(_) => "".to_string(), + } +} + +extern "C" fn ffi_dispatch_handler(webview: *mut CWebView, arg: *mut c_void) { + unsafe { + let mut handle = mem::ManuallyDrop::new(WebView::::from_ptr(webview)); + let result = { + let callback = + Box::,), WVResult>>::from_raw(arg as _); + callback.call(&mut handle) + }; + handle.user_data_wrapper_mut().result = result; + } +} + +extern "C" fn ffi_invoke_handler(webview: *mut CWebView, arg: *const c_char) { + unsafe { + let arg = CStr::from_ptr(arg).to_string_lossy().to_string(); + let mut handle = mem::ManuallyDrop::new(WebView::::from_ptr(webview)); + let result = ((*handle.user_data_wrapper_ptr()).invoke_handler)(&mut *handle, &arg); + handle.user_data_wrapper_mut().result = result; + } +} diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 000000000..6ddf4a4e4 --- /dev/null +++ b/docs/README.md @@ -0,0 +1 @@ +[WIP] diff --git a/lib/rust/Cargo.toml b/lib/rust/Cargo.toml new file mode 100644 index 000000000..b819a01f7 --- /dev/null +++ b/lib/rust/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "proton" +version = "0.1.0" +authors = ["Lucas Fernandes Gonçalves Nogueira "] +edition = "2018" + +[dependencies] +proton-ui = { path = "../../bindings/rust" } +serde_json = "1.0.39" +serde = "1.0" +serde_derive = "1.0" +dirs = "1.0" +ignore = "0.4.7" +phf = "0.7.21" +threadpool = "1.7" +rand = "0.7" +reqwest = "0.9" +pbr = "1" +zip = "0.5.0" +tempdir = "0.3" +semver = "0.9" +tempfile = "3" +either = "1.5.0" +tar = "0.4" +flate2 = "1" +hyper-old-types = "0.11.0" +sysinfo = "0.9" \ No newline at end of file diff --git a/lib/rust/rustfmt.toml b/lib/rust/rustfmt.toml new file mode 100644 index 000000000..9da25275e --- /dev/null +++ b/lib/rust/rustfmt.toml @@ -0,0 +1,13 @@ +max_width = 100 +hard_tabs = false +tab_spaces = 2 +newline_style = "Auto" +use_small_heuristics = "Default" +reorder_imports = true +reorder_modules = true +remove_nested_parens = true +edition = "2015" +merge_derives = true +use_try_shorthand = false +use_field_init_shorthand = false +force_explicit_abi = true diff --git a/lib/rust/src/api/cmd.rs b/lib/rust/src/api/cmd.rs new file mode 100644 index 000000000..1891a1e77 --- /dev/null +++ b/lib/rust/src/api/cmd.rs @@ -0,0 +1,40 @@ +#[derive(Deserialize)] +#[serde(tag = "cmd", rename_all = "camelCase")] +pub enum Cmd { + Init, + ReadAsString { + path: String, + callback: String, + error: String, + }, + ReadAsBinary { + path: String, + callback: String, + error: String, + }, + Write { + file: String, + contents: String, + callback: String, + error: String, + }, + List { + path: String, + callback: String, + error: String, + }, + ListDirs { + path: String, + callback: String, + error: String, + }, + SetTitle { + title: String, + }, + Call { + command: String, + args: Vec, + callback: String, + error: String, + }, +} diff --git a/lib/rust/src/api/mod.rs b/lib/rust/src/api/mod.rs new file mode 100644 index 000000000..a7dbf6d11 --- /dev/null +++ b/lib/rust/src/api/mod.rs @@ -0,0 +1,63 @@ +mod cmd; + +use proton_ui::WebView; + +pub fn handler(webview: &mut WebView, arg: &str) -> bool { + use cmd::Cmd::*; + match serde_json::from_str(arg) { + Err(_) => false, + Ok(command) => { + match command { + Init => (), + ReadAsString { + path, + callback, + error, + } => { + super::file_system::read_text_file(webview, path, callback, error); + } + ReadAsBinary { + path, + callback, + error, + } => { + super::file_system::read_binary_file(webview, path, callback, error); + } + Write { + file, + contents, + callback, + error, + } => { + super::file_system::write_file(webview, file, contents, callback, error); + } + ListDirs { + path, + callback, + error, + } => { + super::file_system::list_dirs(webview, path, callback, error); + } + List { + path, + callback, + error, + } => { + super::file_system::list(webview, path, callback, error); + } + SetTitle { title } => { + webview.set_title(&title).unwrap(); + } + Call { + command, + args, + callback, + error, + } => { + super::command::call(webview, command, args, callback, error); + } + } + true + } + } +} diff --git a/lib/rust/src/command.rs b/lib/rust/src/command.rs new file mode 100755 index 000000000..d4ad38a7a --- /dev/null +++ b/lib/rust/src/command.rs @@ -0,0 +1,78 @@ +use proton_ui::WebView; + +use std::process::{Child, Command, Stdio}; + +use super::run_async; + +pub fn get_output(cmd: String, args: Vec, stdout: Stdio) -> Result { + Command::new(cmd) + .args(args) + .stdout(stdout) + .output() + .map_err(|err| err.to_string()) + .and_then(|output| { + if output.status.success() { + return Result::Ok(String::from_utf8_lossy(&output.stdout).to_string()); + } else { + return Result::Err(String::from_utf8_lossy(&output.stderr).to_string()); + } + }) +} + +// TODO use .exe for windows builds +pub fn format_command(path: String, command: String) -> String { + return format!("{}/./{}", path, command); +} + +pub fn relative_command(command: String) -> Result { + match std::env::current_exe()?.parent() { + Some(exe_dir) => return Ok(format_command(exe_dir.display().to_string(), command)), + None => { + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + "Could not evaluate executable dir".to_string(), + )) + } + } +} + +// TODO append .exe for windows builds +pub fn command_path(command: String) -> Result { + match std::env::current_exe()?.parent() { + Some(exe_dir) => return Ok(format!("{}/{}", exe_dir.display().to_string(), command)), + None => { + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + "Could not evaluate executable dir".to_string(), + )) + } + } +} + +pub fn spawn_relative_command( + command: String, + args: Vec, + stdout: Stdio, +) -> Result { + let cmd = relative_command(command)?; + Ok(Command::new(cmd).args(args).stdout(stdout).spawn()?) +} + +pub fn call( + webview: &mut WebView, + command: String, + args: Vec, + callback: String, + error: String, +) { + run_async( + webview, + || { + get_output(command, args, Stdio::piped()) + .map_err(|err| format!("`{}`", err)) + .map(|output| format!("`{}`", output)) + }, + callback, + error, + ); +} diff --git a/lib/rust/src/dir/mod.rs b/lib/rust/src/dir/mod.rs new file mode 100755 index 000000000..079e2675a --- /dev/null +++ b/lib/rust/src/dir/mod.rs @@ -0,0 +1,77 @@ +extern crate dirs; +extern crate tempfile; + +mod utils; +use ignore::Walk; +use std::fs; +use std::fs::metadata; +use utils::get_dir_name_from_path; + +use tempfile::tempdir; + +#[derive(Serialize)] +pub struct DiskEntry { + pub path: String, + pub is_dir: bool, + pub name: String, +} + +fn is_dir(file_name: String) -> Result { + match metadata(file_name.to_string()) { + Ok(md) => return Result::Ok(md.is_dir()), + Err(err) => return Result::Err(err.to_string()), + }; +} + +pub fn walk_dir(path_copy: String) -> Result, String> { + println!("Trying to walk: {}", path_copy.as_str()); + let mut files_and_dirs: Vec = vec![]; + for result in Walk::new(path_copy) { + match result { + Ok(entry) => { + let display_value = entry.path().display(); + let _dir_name = display_value.to_string(); + + match is_dir(display_value.to_string()) { + Ok(flag) => { + files_and_dirs.push(DiskEntry { + path: display_value.to_string(), + is_dir: flag, + name: display_value.to_string(), + }); + } + Err(_) => {} + } + } + Err(_) => {} + } + } + return Result::Ok(files_and_dirs); +} + +pub fn list_dir_contents(dir_path: &String) -> Result, String> { + fs::read_dir(dir_path) + .map_err(|err| err.to_string()) + .and_then(|paths| { + let mut dirs: Vec = vec![]; + for path in paths { + let dir_path = path.expect("dirpath error").path(); + let _dir_name = dir_path.display(); + dirs.push(DiskEntry { + path: format!("{}", _dir_name), + is_dir: true, + name: get_dir_name_from_path(_dir_name.to_string()), + }); + } + Ok(dirs) + }) +} + +pub fn with_temp_dir ()>( + callback: F, +) -> Result<(), std::io::Error> { + let dir = tempdir()?; + callback(&dir); + dir.close()?; + Ok(()) +} diff --git a/lib/rust/src/dir/utils.rs b/lib/rust/src/dir/utils.rs new file mode 100755 index 000000000..bd0f6c993 --- /dev/null +++ b/lib/rust/src/dir/utils.rs @@ -0,0 +1,4 @@ +pub fn get_dir_name_from_path(path: String) -> String { + let path_collect: Vec<&str> = path.split("/").collect(); + return path_collect[path_collect.len() - 1].to_string(); +} diff --git a/lib/rust/src/file/error.rs b/lib/rust/src/file/error.rs new file mode 100644 index 000000000..d9a198516 --- /dev/null +++ b/lib/rust/src/file/error.rs @@ -0,0 +1,46 @@ +use std; +use zip::result::ZipError; + +#[derive(Debug)] +pub enum Error { + Extract(String), + Io(std::io::Error), + Zip(ZipError), +} + +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + use Error::*; + match *self { + Extract(ref s) => write!(f, "ExtractError: {}", s), + Io(ref e) => write!(f, "IoError: {}", e), + Zip(ref e) => write!(f, "ZipError: {}", e), + } + } +} + +impl std::error::Error for Error { + fn description(&self) -> &str { + "File Error" + } + + fn cause(&self) -> Option<&std::error::Error> { + use Error::*; + Some(match *self { + Io(ref e) => e, + _ => return None, + }) + } +} + +impl From for Error { + fn from(e: std::io::Error) -> Self { + Error::Io(e) + } +} + +impl From for Error { + fn from(e: ZipError) -> Self { + Error::Zip(e) + } +} diff --git a/lib/rust/src/file/extract.rs b/lib/rust/src/file/extract.rs new file mode 100644 index 000000000..1e80bf759 --- /dev/null +++ b/lib/rust/src/file/extract.rs @@ -0,0 +1,189 @@ +extern crate either; +extern crate flate2; +extern crate tar; +extern crate zip; + +use super::error::*; +use either::Either; +use std::fs; +use std::io; +use std::path; + +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum ArchiveFormat { + Tar(Option), + Plain(Option), + Zip, +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum Compression { + Gz, +} + +#[derive(Debug)] +pub struct Extract<'a> { + source: &'a path::Path, + archive_format: Option, +} + +fn detect_archive_type(path: &path::Path) -> ArchiveFormat { + match path.extension() { + Some(extension) if extension == std::ffi::OsStr::new("zip") => ArchiveFormat::Zip, + Some(extension) if extension == std::ffi::OsStr::new("tar") => ArchiveFormat::Tar(None), + Some(extension) if extension == std::ffi::OsStr::new("gz") => match path + .file_stem() + .map(|e| path::Path::new(e)) + .and_then(|f| f.extension()) + { + Some(extension) if extension == std::ffi::OsStr::new("tar") => { + ArchiveFormat::Tar(Some(Compression::Gz)) + } + _ => ArchiveFormat::Plain(Some(Compression::Gz)), + }, + _ => ArchiveFormat::Plain(None), + } +} + +impl<'a> Extract<'a> { + /// Create an `Extractor from a source path + pub fn from_source(source: &'a path::Path) -> Extract<'a> { + Self { + source, + archive_format: None, + } + } + + /// Specify an archive format of the source being extracted. If not specified, the + /// archive format will determined from the file extension. + pub fn archive_format(&mut self, format: ArchiveFormat) -> &mut Self { + self.archive_format = Some(format); + self + } + + fn get_archive_reader( + source: fs::File, + compression: Option, + ) -> Either> { + match compression { + Some(Compression::Gz) => Either::Right(flate2::read::GzDecoder::new(source)), + None => Either::Left(source), + } + } + + /// Extract an entire source archive into a specified path. If the source is a single compressed + /// file and not an archive, it will be extracted into a file with the same name inside of + /// `into_dir`. + pub fn extract_into(&self, into_dir: &path::Path) -> Result<(), Error> { + let source = fs::File::open(self.source)?; + let archive = self + .archive_format + .unwrap_or_else(|| detect_archive_type(&self.source)); + + match archive { + ArchiveFormat::Plain(compression) | ArchiveFormat::Tar(compression) => { + let mut reader = Self::get_archive_reader(source, compression); + + match archive { + ArchiveFormat::Plain(_) => { + match fs::create_dir_all(into_dir) { + Ok(_) => (), + Err(e) => { + if e.kind() != io::ErrorKind::AlreadyExists { + return Err(Error::Io(e)); + } + } + } + let file_name = self + .source + .file_name() + .ok_or_else(|| Error::Extract("Extractor source has no file-name".into()))?; + let mut out_path = into_dir.join(file_name); + out_path.set_extension(""); + let mut out_file = fs::File::create(&out_path)?; + io::copy(&mut reader, &mut out_file)?; + } + ArchiveFormat::Tar(_) => { + let mut archive = tar::Archive::new(reader); + archive.unpack(into_dir)?; + } + _ => unreachable!(), + }; + } + ArchiveFormat::Zip => { + let mut archive = zip::ZipArchive::new(source)?; + for i in 0..archive.len() { + let mut file = archive.by_index(i)?; + let path = into_dir.join(file.name()); + let mut output = fs::File::create(path)?; + io::copy(&mut file, &mut output)?; + } + } + }; + Ok(()) + } + + /// Extract a single file from a source and save to a file of the same name in `into_dir`. + /// If the source is a single compressed file, it will be saved with the name `file_to_extract` + /// in the specified `into_dir`. + pub fn extract_file>( + &self, + into_dir: &path::Path, + file_to_extract: T, + ) -> Result<(), Error> { + let file_to_extract = file_to_extract.as_ref(); + let source = fs::File::open(self.source)?; + let archive = self + .archive_format + .unwrap_or_else(|| detect_archive_type(&self.source)); + + match archive { + ArchiveFormat::Plain(compression) | ArchiveFormat::Tar(compression) => { + let mut reader = Self::get_archive_reader(source, compression); + + match archive { + ArchiveFormat::Plain(_) => { + match fs::create_dir_all(into_dir) { + Ok(_) => (), + Err(e) => { + if e.kind() != io::ErrorKind::AlreadyExists { + return Err(Error::Io(e)); + } + } + } + let file_name = file_to_extract + .file_name() + .ok_or_else(|| Error::Extract("Extractor source has no file-name".into()))?; + let out_path = into_dir.join(file_name); + let mut out_file = fs::File::create(&out_path)?; + io::copy(&mut reader, &mut out_file)?; + } + ArchiveFormat::Tar(_) => { + let mut archive = tar::Archive::new(reader); + let mut entry = archive + .entries()? + .filter_map(|e| e.ok()) + .find(|e| e.path().ok().filter(|p| p == file_to_extract).is_some()) + .ok_or_else(|| { + Error::Extract(format!( + "Could not find the required path in the archive: {:?}", + file_to_extract + )) + })?; + entry.unpack_in(into_dir)?; + } + _ => { + panic!("Unreasonable code"); + } + }; + } + ArchiveFormat::Zip => { + let mut archive = zip::ZipArchive::new(source)?; + let mut file = archive.by_name(file_to_extract.to_str().unwrap())?; + let mut output = fs::File::create(into_dir.join(file.name()))?; + io::copy(&mut file, &mut output)?; + } + }; + Ok(()) + } +} diff --git a/lib/rust/src/file/file_move.rs b/lib/rust/src/file/file_move.rs new file mode 100644 index 000000000..c11c622d1 --- /dev/null +++ b/lib/rust/src/file/file_move.rs @@ -0,0 +1,62 @@ +use std::fs; +use std::path; + +use super::error::*; + +/// Moves a file from the given path to the specified destination. +/// +/// `source` and `dest` must be on the same filesystem. +/// If `replace_using_temp` is specified, the destination file will be +/// replaced using the given temporary path. +/// +/// * Errors: +/// * Io - copying / renaming +#[derive(Debug)] +pub struct Move<'a> { + source: &'a path::Path, + temp: Option<&'a path::Path>, +} +impl<'a> Move<'a> { + /// Specify source file + pub fn from_source(source: &'a path::Path) -> Move<'a> { + Self { source, temp: None } + } + + /// If specified and the destination file already exists, the "destination" + /// file will be moved to the given temporary location before the "source" + /// file is moved to the "destination" file. + /// + /// In the event of an `io` error while renaming "source" to "destination", + /// the temporary file will be moved back to "destination". + /// + /// The `temp` dir must be explicitly provided since `rename` operations require + /// files to live on the same filesystem. + pub fn replace_using_temp(&mut self, temp: &'a path::Path) -> &mut Self { + self.temp = Some(temp); + self + } + + /// Move source file to specified destination + pub fn to_dest(&self, dest: &path::Path) -> Result<(), Error> { + match self.temp { + None => { + fs::rename(self.source, dest)?; + } + Some(temp) => { + println!("dest {}", dest.to_str().unwrap()); + println!("temp {}", temp.to_str().unwrap()); + println!("source {}", self.source.to_str().unwrap()); + if dest.exists() { + fs::rename(dest, temp)?; + if let Err(e) = fs::rename(self.source, dest) { + fs::rename(temp, dest)?; + return Err(Error::from(e)); + } + } else { + fs::rename(self.source, dest)?; + } + } + }; + Ok(()) + } +} diff --git a/lib/rust/src/file/mod.rs b/lib/rust/src/file/mod.rs new file mode 100644 index 000000000..c81d4040d --- /dev/null +++ b/lib/rust/src/file/mod.rs @@ -0,0 +1,21 @@ +use std::fs; + +extern crate serde_json; + +mod error; +mod extract; +mod file_move; + +pub use error::Error; +pub use extract::*; +pub use file_move::*; + +pub fn read_string(file: String) -> Result { + fs::read_to_string(file) + .map_err(|err| err.to_string()) + .map(|c| c) +} + +pub fn read_binary(file: String) -> Result, String> { + fs::read(file).map_err(|err| err.to_string()).map(|b| b) +} diff --git a/lib/rust/src/file_system.rs b/lib/rust/src/file_system.rs new file mode 100755 index 000000000..11450d58c --- /dev/null +++ b/lib/rust/src/file_system.rs @@ -0,0 +1,100 @@ +use proton_ui::WebView; + +use super::dir; +use super::file; +use super::run_async; + +use std::fs::File; +use std::io::Write; + +pub fn list(webview: &mut WebView, path: String, callback: String, error: String) { + run_async( + webview, + move || { + dir::walk_dir(path.to_string()) + .and_then(|f| serde_json::to_string(&f).map_err(|err| err.to_string())) + }, + callback, + error, + ); +} + +pub fn list_dirs( + webview: &mut WebView, + path: String, + callback: String, + error: String, +) { + run_async( + webview, + move || { + dir::list_dir_contents(&path) + .and_then(|f| serde_json::to_string(&f).map_err(|err| err.to_string())) + }, + callback, + error, + ); +} + +pub fn write_file( + webview: &mut WebView, + file: String, + contents: String, + callback: String, + error: String, +) { + run_async( + webview, + move || { + File::create(file) + .map_err(|err| err.to_string()) + .and_then(|mut f| { + f.write_all(contents.as_bytes()) + .map_err(|err| err.to_string()) + .map(|_| "".to_string()) + }) + }, + callback, + error, + ); +} + +pub fn read_text_file( + webview: &mut WebView, + path: String, + callback: String, + error: String, +) { + run_async( + webview, + move || { + file::read_string(path).and_then(|f| { + serde_json::to_string(&f) + .map_err(|err| err.to_string()) + .map(|s| s.to_string()) + }) + }, + callback, + error, + ); +} + +pub fn read_binary_file( + webview: &mut WebView, + path: String, + callback: String, + error: String, +) { + run_async( + webview, + move || { + file::read_binary(path).and_then(|f| { + serde_json::to_string(&f) + .map_err(|err| err.to_string()) + .map(|s| s.to_string()) + }) + }, + callback, + error, + ); +} diff --git a/lib/rust/src/http/error.rs b/lib/rust/src/http/error.rs new file mode 100644 index 000000000..4ac9ffe91 --- /dev/null +++ b/lib/rust/src/http/error.rs @@ -0,0 +1,57 @@ +use reqwest; +use serde_json; +use std; + +#[derive(Debug)] +pub enum Error { + Download(String), + Json(serde_json::Error), + Reqwest(reqwest::Error), + Io(std::io::Error), +} + +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + use Error::*; + match *self { + Download(ref s) => write!(f, "DownloadError: {}", s), + Json(ref e) => write!(f, "JsonError: {}", e), + Reqwest(ref e) => write!(f, "ReqwestError: {}", e), + Io(ref e) => write!(f, "IoError: {}", e), + } + } +} + +impl std::error::Error for Error { + fn description(&self) -> &str { + "Http Error" + } + + fn cause(&self) -> Option<&std::error::Error> { + use Error::*; + Some(match *self { + Json(ref e) => e, + Reqwest(ref e) => e, + Io(ref e) => e, + _ => return None, + }) + } +} + +impl From for Error { + fn from(e: serde_json::Error) -> Self { + Error::Json(e) + } +} + +impl From for Error { + fn from(e: reqwest::Error) -> Self { + Error::Reqwest(e) + } +} + +impl From for Error { + fn from(e: std::io::Error) -> Self { + Error::Io(e) + } +} diff --git a/lib/rust/src/http/mod.rs b/lib/rust/src/http/mod.rs new file mode 100644 index 000000000..0097e64ee --- /dev/null +++ b/lib/rust/src/http/mod.rs @@ -0,0 +1,80 @@ +extern crate pbr; +extern crate reqwest; + +use serde::Serialize; +use std::io; +mod error; +pub use self::error::Error; + +pub fn get(url: &String) -> Result { + let response = reqwest::Client::new().get(url).send()?; + Ok(response) +} + +pub fn post_as_json( + url: &String, + payload: &T, +) -> Result { + let response = reqwest::Client::new().post(url).json(payload).send()?; + Ok(response) +} + +pub fn download( + url: &String, + mut dest: T, + display_progress: bool, +) -> Result<(), Error> { + use io::BufRead; + + set_ssl_vars!(); + + let resp = get(url)?; + let size = resp + .headers() + .get(reqwest::header::CONTENT_LENGTH) + .map(|val| { + val + .to_str() + .map(|s| s.parse::().unwrap_or(0)) + .unwrap_or(0) + }) + .unwrap_or(0); + + if !resp.status().is_success() { + bail!( + Error::Download, + "Download request failed with status: {:?}", + resp.status() + ) + } + + let show_progress = if size == 0 { false } else { display_progress }; + + let mut src = io::BufReader::new(resp); + let mut bar = if show_progress { + let mut bar = pbr::ProgressBar::new(size); + bar.set_units(pbr::Units::Bytes); + bar.format("[=> ]"); + Some(bar) + } else { + None + }; + loop { + let n = { + let buf = src.fill_buf()?; + dest.write_all(&buf)?; + buf.len() + }; + if n == 0 { + break; + } + src.consume(n); + if let Some(ref mut bar) = bar { + bar.add(n as u64); + } + } + if show_progress { + println!(" ... Done"); + } + Ok(()) +} diff --git a/lib/rust/src/lib.rs b/lib/rust/src/lib.rs new file mode 100644 index 000000000..2ad400f4b --- /dev/null +++ b/lib/rust/src/lib.rs @@ -0,0 +1,43 @@ +extern crate threadpool; +#[macro_use] +extern crate serde_derive; + +#[macro_use] +mod macros; + +pub mod api; +pub mod command; +pub mod dir; +pub mod file; +pub mod file_system; +pub mod http; +pub mod platform; +pub mod process; +pub mod rpc; +pub mod tcp; +pub mod updater; +pub mod version; + +extern crate proton_ui; +use proton_ui::WebView; + +use threadpool::ThreadPool; + +thread_local!(static POOL: ThreadPool = ThreadPool::new(4)); + +pub fn run_async Result + Send + 'static>( + webview: &mut WebView, + what: F, + callback: String, + error: String, +) { + let handle = webview.handle(); + POOL.with(|thread| { + thread.execute(move || { + let callback_string = rpc::format_callback_result(what(), callback, error); + handle + .dispatch(move |_webview| _webview.eval(callback_string.as_str())) + .unwrap() + }); + }); +} diff --git a/lib/rust/src/macros.rs b/lib/rust/src/macros.rs new file mode 100644 index 000000000..4012fd566 --- /dev/null +++ b/lib/rust/src/macros.rs @@ -0,0 +1,46 @@ +/// Helper for formatting `errors::Error`s +macro_rules! format_err { + ($e_type:expr, $literal:expr) => { + $e_type(format!($literal)) + }; + ($e_type:expr, $literal:expr, $($arg:expr),*) => { + $e_type(format!($literal, $($arg),*)) + }; +} + +/// Helper for formatting `errors::Error`s and returning early +macro_rules! bail { + ($e_type:expr, $literal:expr) => { + return Err(format_err!($e_type, $literal)) + }; + ($e_type:expr, $literal:expr, $($arg:expr),*) => { + return Err(format_err!($e_type, $literal, $($arg),*)) + }; +} + +/// Helper to `print!` and immediately `flush` `stdout` +macro_rules! print_flush { + ($literal:expr) => { + print!($literal); + ::std::io::Write::flush(&mut ::std::io::stdout())?; + }; + ($literal:expr, $($arg:expr),*) => { + print!($literal, $($arg),*); + ::std::io::Write::flush(&mut ::std::io::stdout())?; + } +} + +/// Set ssl cert env. vars to make sure openssl can find required files +macro_rules! set_ssl_vars { + () => { + #[cfg(target_os = "linux")] + { + if ::std::env::var_os("SSL_CERT_FILE").is_none() { + ::std::env::set_var("SSL_CERT_FILE", "/etc/ssl/certs/ca-certificates.crt"); + } + if ::std::env::var_os("SSL_CERT_DIR").is_none() { + ::std::env::set_var("SSL_CERT_DIR", "/etc/ssl/certs"); + } + } + }; +} diff --git a/lib/rust/src/platform/error.rs b/lib/rust/src/platform/error.rs new file mode 100644 index 000000000..e8413d5e0 --- /dev/null +++ b/lib/rust/src/platform/error.rs @@ -0,0 +1,29 @@ +use std; + +#[derive(Debug)] +pub enum Error { + Arch(String), + Target(String), + Abi(String), +} + +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + use Error::*; + match *self { + Arch(ref s) => write!(f, "ArchError: {}", s), + Target(ref e) => write!(f, "TargetError: {}", e), + Abi(ref e) => write!(f, "AbiError: {}", e), + } + } +} + +impl std::error::Error for Error { + fn description(&self) -> &str { + "Platform Error" + } + + fn cause(&self) -> Option<&std::error::Error> { + return None; + } +} diff --git a/lib/rust/src/platform/mod.rs b/lib/rust/src/platform/mod.rs new file mode 100644 index 000000000..bbc17d7f9 --- /dev/null +++ b/lib/rust/src/platform/mod.rs @@ -0,0 +1,54 @@ +pub mod error; +use error::*; + +/// Try to determine the current target triple. +/// +/// Returns a target triple (e.g. `x86_64-unknown-linux-gnu` or `i686-pc-windows-msvc`) or an +/// `Error::Config` if the current config cannot be determined or is not some combination of the +/// following values: +/// `linux, mac, windows` -- `i686, x86, armv7` -- `gnu, musl, msvc` +/// +/// * Errors: +/// * Unexpected system config +pub fn target_triple() -> Result { + let arch = if cfg!(target_arch = "x86") { + "i686" + } else if cfg!(target_arch = "x86_64") { + "x86_64" + } else if cfg!(target_arch = "arm") { + "armv7" + } else { + bail!(Error::Arch, "Unable to determine target-architecture") + }; + + let os = if cfg!(target_os = "linux") { + "unknown-linux" + } else if cfg!(target_os = "macos") { + "apple-darwin" + } else if cfg!(target_os = "windows") { + "pc-windows" + } else if cfg!(target_os = "freebsd") { + "unknown-freebsd" + } else { + bail!(Error::Target, "Unable to determine target-os"); + }; + + let s; + let os = if cfg!(target_os = "macos") || cfg!(target_os = "freebsd") { + os + } else { + let env = if cfg!(target_env = "gnu") { + "gnu" + } else if cfg!(target_env = "gnu") { + "musl" + } else if cfg!(target_env = "msvc") { + "msvc" + } else { + bail!(Error::Abi, "Unable to determine target-environment") + }; + s = format!("{}-{}", os, env); + &s + }; + + Ok(format!("{}-{}", arch, os)) +} diff --git a/lib/rust/src/process.rs b/lib/rust/src/process.rs new file mode 100644 index 000000000..dcaa7dca1 --- /dev/null +++ b/lib/rust/src/process.rs @@ -0,0 +1,18 @@ +extern crate sysinfo; + +pub use sysinfo::{Process, ProcessExt, Signal, System, SystemExt}; + +pub fn get_parent_process(system: &mut sysinfo::System) -> Result<&Process, String> { + let pid = sysinfo::get_current_pid().unwrap(); + system.refresh_process(pid); + let current_process = system + .get_process(pid) + .ok_or("Could not get current process")?; + let parent_pid = current_process.parent().ok_or("Could not get parent PID")?; + let parent_process = system + .get_process(parent_pid) + .ok_or("Could not get parent process")?; + + println!("{}", pid); + Ok(parent_process) +} diff --git a/lib/rust/src/rpc.rs b/lib/rust/src/rpc.rs new file mode 100755 index 000000000..f44220338 --- /dev/null +++ b/lib/rust/src/rpc.rs @@ -0,0 +1,15 @@ +pub fn format_callback(function_name: String, arg: String) -> String { + let formatted_string = &format!("window[\"{}\"]({})", function_name, arg); + return formatted_string.to_string(); +} + +pub fn format_callback_result( + result: Result, + callback: String, + error_callback: String, +) -> String { + match result { + Ok(res) => return format_callback(callback, res), + Err(err) => return format_callback(error_callback, format!("\"{}\"", err)), + } +} diff --git a/lib/rust/src/tcp.rs b/lib/rust/src/tcp.rs new file mode 100644 index 000000000..433413004 --- /dev/null +++ b/lib/rust/src/tcp.rs @@ -0,0 +1,25 @@ +use std::net::TcpListener; + +extern crate rand; + +use rand::distributions::{Distribution, Uniform}; + +pub fn get_available_port() -> Option { + let mut rng = rand::thread_rng(); + let die = Uniform::from(8000..9000); + + for _i in 0..100 { + let port = die.sample(&mut rng); + if port_is_available(port) { + return Some(port); + } + } + None +} + +pub fn port_is_available(port: u16) -> bool { + match TcpListener::bind(("127.0.0.1", port)) { + Ok(_) => true, + Err(_) => false, + } +} diff --git a/lib/rust/src/updater/error.rs b/lib/rust/src/updater/error.rs new file mode 100644 index 000000000..5476ecccd --- /dev/null +++ b/lib/rust/src/updater/error.rs @@ -0,0 +1,78 @@ +use super::super::file; +use super::super::http; +use super::super::version; +use reqwest; +use std; +use zip::result::ZipError; + +#[derive(Debug)] +pub enum Error { + Updater(String), + Release(String), + Network(String), + Config(String), + Io(std::io::Error), + Zip(ZipError), + File(file::Error), + Version(version::Error), +} + +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + use Error::*; + match *self { + Updater(ref s) => write!(f, "UpdaterError: {}", s), + Release(ref s) => write!(f, "ReleaseError: {}", s), + Network(ref s) => write!(f, "NetworkError: {}", s), + Config(ref s) => write!(f, "ConfigError: {}", s), + Io(ref e) => write!(f, "IoError: {}", e), + Zip(ref e) => write!(f, "ZipError: {}", e), + File(ref e) => write!(f, "FileError: {}", e), + Version(ref e) => write!(f, "VersionError: {}", e), + } + } +} + +impl std::error::Error for Error { + fn description(&self) -> &str { + "Updater Error" + } + + fn cause(&self) -> Option<&std::error::Error> { + use Error::*; + Some(match *self { + Io(ref e) => e, + _ => return None, + }) + } +} + +impl From for Error { + fn from(e: std::io::Error) -> Self { + Error::Io(e) + } +} + +impl From for Error { + fn from(e: file::Error) -> Self { + Error::File(e) + } +} + +impl From for Error { + fn from(e: http::Error) -> Self { + Error::Network(e.to_string()) + } +} + +impl From for Error { + fn from(e: reqwest::Error) -> Self { + Error::Network(e.to_string()) + } +} + +impl From for Error { + fn from(e: version::Error) -> Self { + Error::Version(e) + } +} diff --git a/lib/rust/src/updater/github/mod.rs b/lib/rust/src/updater/github/mod.rs new file mode 100644 index 000000000..ba41cf1c0 --- /dev/null +++ b/lib/rust/src/updater/github/mod.rs @@ -0,0 +1,43 @@ +mod release; +pub use super::error::Error; +pub use release::*; + +use super::super::http; + +pub fn get_latest_release(repo_owner: &str, repo_name: &str) -> Result { + set_ssl_vars!(); + let api_url = format!( + "https://api.github.com/repos/{}/{}/releases/latest", + repo_owner, repo_name + ); + let mut resp = http::get(&api_url)?; + if !resp.status().is_success() { + bail!( + Error::Network, + "api request failed with status: {:?} - for: {:?}", + resp.status(), + api_url + ) + } + let json = resp.json::()?; + Ok(Release::parse(&json)?) +} + +pub fn get_release_version(repo_owner: &str, repo_name: &str, ver: &str) -> Result { + set_ssl_vars!(); + let api_url = format!( + "https://api.github.com/repos/{}/{}/releases/tags/{}", + repo_owner, repo_name, ver + ); + let mut resp = http::get(&api_url)?; + if !resp.status().is_success() { + bail!( + Error::Network, + "api request failed with status: {:?} - for: {:?}", + resp.status(), + api_url + ) + } + let json = resp.json::()?; + Ok(Release::parse(&json)?) +} diff --git a/lib/rust/src/updater/github/release.rs b/lib/rust/src/updater/github/release.rs new file mode 100644 index 000000000..79c98469e --- /dev/null +++ b/lib/rust/src/updater/github/release.rs @@ -0,0 +1,215 @@ +use super::super::error::*; +use hyper_old_types::header::{LinkValue, RelationType}; +use serde_json; + +/// GitHub release-asset information +#[derive(Clone, Debug)] +pub struct ReleaseAsset { + pub download_url: String, + pub name: String, +} +impl ReleaseAsset { + /// Parse a release-asset json object + /// + /// Errors: + /// * Missing required name & browser_download_url keys + fn from_asset(asset: &serde_json::Value) -> Result { + let download_url = asset["browser_download_url"] + .as_str() + .ok_or_else(|| format_err!(Error::Release, "Asset missing `browser_download_url`"))?; + let name = asset["name"] + .as_str() + .ok_or_else(|| format_err!(Error::Release, "Asset missing `name`"))?; + Ok(ReleaseAsset { + download_url: download_url.to_owned(), + name: name.to_owned(), + }) + } +} + +#[derive(Clone, Debug)] +pub struct Release { + pub name: String, + pub body: String, + pub tag: String, + pub date_created: String, + pub assets: Vec, +} +impl Release { + pub fn parse(release: &serde_json::Value) -> Result { + let tag = release["tag_name"] + .as_str() + .ok_or_else(|| format_err!(Error::Release, "Release missing `tag_name`"))?; + let date_created = release["created_at"] + .as_str() + .ok_or_else(|| format_err!(Error::Release, "Release missing `created_at`"))?; + let name = release["name"].as_str().unwrap_or(tag); + let body = release["body"].as_str().unwrap_or(""); + let assets = release["assets"] + .as_array() + .ok_or_else(|| format_err!(Error::Release, "No assets found"))?; + let assets = assets + .iter() + .map(ReleaseAsset::from_asset) + .collect::, Error>>()?; + Ok(Release { + name: name.to_owned(), + body: body.to_owned(), + tag: tag.to_owned(), + date_created: date_created.to_owned(), + assets, + }) + } + + /// Check if release has an asset who's name contains the specified `target` + pub fn has_target_asset(&self, target: &str) -> bool { + self.assets.iter().any(|asset| asset.name.contains(target)) + } + + /// Return the first `ReleaseAsset` for the current release who's name + /// contains the specified `target` + pub fn asset_for(&self, target: &str) -> Option { + self + .assets + .iter() + .filter(|asset| asset.name.contains(target)) + .cloned() + .nth(0) + } + + pub fn version(&self) -> &str { + self.tag.trim_start_matches('v') + } +} + +/// `ReleaseList` Builder +#[derive(Clone, Debug)] +pub struct ReleaseListBuilder { + repo_owner: Option, + repo_name: Option, + target: Option, +} +impl ReleaseListBuilder { + /// Set the repo owner, used to build a github api url + pub fn repo_owner(&mut self, owner: &str) -> &mut Self { + self.repo_owner = Some(owner.to_owned()); + self + } + + /// Set the repo name, used to build a github api url + pub fn repo_name(&mut self, name: &str) -> &mut Self { + self.repo_name = Some(name.to_owned()); + self + } + + /// Set the optional arch `target` name, used to filter available releases + pub fn target(&mut self, target: &str) -> &mut Self { + self.target = Some(target.to_owned()); + self + } + + /// Verify builder args, returning a `ReleaseList` + pub fn build(&self) -> Result { + Ok(ReleaseList { + repo_owner: if let Some(ref owner) = self.repo_owner { + owner.to_owned() + } else { + bail!(Error::Config, "`repo_owner` required") + }, + repo_name: if let Some(ref name) = self.repo_name { + name.to_owned() + } else { + bail!(Error::Config, "`repo_name` required") + }, + target: self.target.clone(), + }) + } +} + +/// `ReleaseList` provides a builder api for querying a GitHub repo, +/// returning a `Vec` of available `Release`s +#[derive(Clone, Debug)] +pub struct ReleaseList { + repo_owner: String, + repo_name: String, + target: Option, +} +impl ReleaseList { + /// Initialize a ReleaseListBuilder + pub fn configure() -> ReleaseListBuilder { + ReleaseListBuilder { + repo_owner: None, + repo_name: None, + target: None, + } + } + + /// Retrieve a list of `Release`s. + /// If specified, filter for those containing a specified `target` + pub fn fetch(self) -> Result, Error> { + set_ssl_vars!(); + let api_url = format!( + "https://api.github.com/repos/{}/{}/releases", + self.repo_owner, self.repo_name + ); + let releases = Self::fetch_releases(&api_url)?; + let releases = match self.target { + None => releases, + Some(ref target) => releases + .into_iter() + .filter(|r| r.has_target_asset(target)) + .collect::>(), + }; + Ok(releases) + } + + fn fetch_releases(url: &str) -> Result, Error> { + let mut resp = reqwest::get(url)?; + if !resp.status().is_success() { + bail!( + Error::Network, + "api request failed with status: {:?} - for: {:?}", + resp.status(), + url + ) + } + let releases = resp.json::()?; + let releases = releases + .as_array() + .ok_or_else(|| format_err!(Error::Release, "No releases found"))?; + let mut releases = releases + .iter() + .map(Release::parse) + .collect::, Error>>()?; + + // handle paged responses containing `Link` header: + // `Link: ; rel="next"` + let headers = resp.headers(); + let links = headers.get_all(reqwest::header::LINK); + + let next_link = links + .iter() + .filter_map(|link| { + if let Ok(link) = link.to_str() { + let lv = LinkValue::new(link.to_owned()); + if let Some(rels) = lv.rel() { + if rels.contains(&RelationType::Next) { + return Some(link); + } + } + None + } else { + None + } + }) + .nth(0); + + Ok(match next_link { + None => releases, + Some(link) => { + releases.extend(Self::fetch_releases(link)?); + releases + } + }) + } +} diff --git a/lib/rust/src/updater/mod.rs b/lib/rust/src/updater/mod.rs new file mode 100644 index 000000000..bbac888a4 --- /dev/null +++ b/lib/rust/src/updater/mod.rs @@ -0,0 +1,270 @@ +extern crate hyper_old_types; + +use std::env; +use std::fs; +use std::path::PathBuf; + +use super::file::{Extract, Move}; +use super::http; + +pub mod github; + +mod error; +pub use error::Error; + +/// Status returned after updating +/// +/// Wrapped `String`s are version tags +#[derive(Debug, Clone)] +pub enum Status { + UpToDate(String), + Updated(String), +} +impl Status { + /// Return the version tag + pub fn version(&self) -> &str { + use Status::*; + match *self { + UpToDate(ref s) => s, + Updated(ref s) => s, + } + } + + /// Returns `true` if `Status::UpToDate` + pub fn uptodate(&self) -> bool { + match *self { + Status::UpToDate(_) => true, + _ => false, + } + } + + /// Returns `true` if `Status::Updated` + pub fn updated(&self) -> bool { + match *self { + Status::Updated(_) => true, + _ => false, + } + } +} + +#[derive(Clone, Debug)] +pub struct Release { + pub version: String, + pub asset_name: String, + pub download_url: String, +} + +#[derive(Debug)] +pub struct UpdateBuilder { + release: Option, + bin_name: Option, + bin_install_path: Option, + bin_path_in_archive: Option, + show_download_progress: bool, + show_output: bool, + current_version: Option, +} +impl UpdateBuilder { + /// Initialize a new builder, defaulting the `bin_install_path` to the current + /// executable's path + /// + /// * Errors: + /// * Io - Determining current exe path + pub fn new() -> Result { + Ok(Self { + release: None, + bin_name: None, + bin_install_path: Some(env::current_exe()?), + bin_path_in_archive: None, + show_download_progress: false, + show_output: true, + current_version: None, + }) + } + + pub fn release(&mut self, release: Release) -> &mut Self { + self.release = Some(release); + self + } + + /// Set the current app version, used to compare against the latest available version. + /// The `cargo_crate_version!` macro can be used to pull the version from your `Cargo.toml` + pub fn current_version(&mut self, ver: &str) -> &mut Self { + self.current_version = Some(ver.to_owned()); + self + } + + /// Set the exe's name. Also sets `bin_path_in_archive` if it hasn't already been set. + pub fn bin_name(&mut self, name: &str) -> &mut Self { + self.bin_name = Some(name.to_owned()); + if self.bin_path_in_archive.is_none() { + self.bin_path_in_archive = Some(PathBuf::from(name)); + } + self + } + + /// Set the installation path for the new exe, defaults to the current + /// executable's path + pub fn bin_install_path(&mut self, bin_install_path: &str) -> &mut Self { + self.bin_install_path = Some(PathBuf::from(bin_install_path)); + self + } + + /// Set the path of the exe inside the release tarball. This is the location + /// of the executable relative to the base of the tar'd directory and is the + /// path that will be copied to the `bin_install_path`. If not specified, this + /// will default to the value of `bin_name`. This only needs to be specified if + /// the path to the binary (from the root of the tarball) is not equal to just + /// the `bin_name`. + /// + /// # Example + /// + /// For a tarball `myapp.tar.gz` with the contents: + /// + /// ```shell + /// myapp.tar/ + /// |------- bin/ + /// | |--- myapp # <-- executable + /// ``` + /// + /// The path provided should be: + /// + /// ``` + /// # use proton::updater::Update; + /// # fn run() -> Result<(), Box<::std::error::Error>> { + /// Update::configure()? + /// .bin_path_in_archive("bin/myapp") + /// # .build()?; + /// # Ok(()) + /// # } + /// ``` + pub fn bin_path_in_archive(&mut self, bin_path: &str) -> &mut Self { + self.bin_path_in_archive = Some(PathBuf::from(bin_path)); + self + } + + /// Toggle download progress bar, defaults to `off`. + pub fn show_download_progress(&mut self, show: bool) -> &mut Self { + self.show_download_progress = show; + self + } + + /// Toggle update output information, defaults to `true`. + pub fn show_output(&mut self, show: bool) -> &mut Self { + self.show_output = show; + self + } + + /// Confirm config and create a ready-to-use `Update` + /// + /// * Errors: + /// * Config - Invalid `Update` configuration + pub fn build(&self) -> Result { + Ok(Update { + release: if let Some(ref release) = self.release { + release.to_owned() + } else { + bail!(Error::Config, "`release` required") + }, + bin_name: if let Some(ref name) = self.bin_name { + name.to_owned() + } else { + bail!(Error::Config, "`bin_name` required") + }, + bin_install_path: if let Some(ref path) = self.bin_install_path { + path.to_owned() + } else { + bail!(Error::Config, "`bin_install_path` required") + }, + bin_path_in_archive: if let Some(ref path) = self.bin_path_in_archive { + path.to_owned() + } else { + bail!(Error::Config, "`bin_path_in_archive` required") + }, + current_version: if let Some(ref ver) = self.current_version { + ver.to_owned() + } else { + bail!(Error::Config, "`current_version` required") + }, + show_download_progress: self.show_download_progress, + show_output: self.show_output, + }) + } +} + +/// Updates to a specified or latest release distributed +#[derive(Debug)] +pub struct Update { + release: Release, + current_version: String, + bin_name: String, + bin_install_path: PathBuf, + bin_path_in_archive: PathBuf, + show_download_progress: bool, + show_output: bool, +} +impl Update { + /// Initialize a new `Update` builder + pub fn configure() -> Result { + UpdateBuilder::new() + } + + fn print_flush(&self, msg: &str) -> Result<(), Error> { + if self.show_output { + print_flush!("{}", msg); + } + Ok(()) + } + + fn println(&self, msg: &str) { + if self.show_output { + println!("{}", msg); + } + } + + pub fn update(self) -> Result { + self.println(&format!( + "Checking current version... v{}", + self.current_version + )); + + if self.show_output { + println!("\n{} release status:", self.bin_name); + println!(" * Current exe: {:?}", self.bin_install_path); + println!(" * New exe download url: {:?}", self.release.download_url); + println!( + "\nThe new release will be downloaded/extracted and the existing binary will be replaced." + ); + } + + let tmp_dir_parent = self + .bin_install_path + .parent() + .ok_or_else(|| Error::Updater("Failed to determine parent dir".into()))?; + let tmp_dir = + tempdir::TempDir::new_in(&tmp_dir_parent, &format!("{}_download", self.bin_name))?; + let tmp_archive_path = tmp_dir.path().join(&self.release.asset_name); + let mut tmp_archive = fs::File::create(&tmp_archive_path)?; + + self.println("Downloading..."); + http::download( + &self.release.download_url, + &mut tmp_archive, + self.show_download_progress, + )?; + + self.print_flush("Extracting archive... ")?; + Extract::from_source(&tmp_archive_path) + .extract_file(&tmp_dir.path(), &self.bin_path_in_archive)?; + let new_exe = tmp_dir.path().join(&self.bin_path_in_archive); + self.println("Done"); + + self.print_flush("Replacing binary file... ")?; + let tmp_file = tmp_dir.path().join(&format!("__{}_backup", self.bin_name)); + Move::from_source(&new_exe) + .replace_using_temp(&tmp_file) + .to_dest(&self.bin_install_path)?; + self.println("Done"); + Ok(Status::Updated(self.release.version)) + } +} diff --git a/lib/rust/src/version/error.rs b/lib/rust/src/version/error.rs new file mode 100644 index 000000000..1b084e60c --- /dev/null +++ b/lib/rust/src/version/error.rs @@ -0,0 +1,35 @@ +use semver; +use std; + +#[derive(Debug)] +pub enum Error { + SemVer(semver::SemVerError), +} + +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + use Error::*; + match *self { + SemVer(ref e) => write!(f, "SemVerError: {}", e), + } + } +} + +impl std::error::Error for Error { + fn description(&self) -> &str { + "Version Error" + } + + fn cause(&self) -> Option<&std::error::Error> { + use Error::*; + Some(match *self { + SemVer(ref e) => e, + }) + } +} + +impl From for Error { + fn from(e: semver::SemVerError) -> Self { + Error::SemVer(e) + } +} diff --git a/lib/rust/src/version/mod.rs b/lib/rust/src/version/mod.rs new file mode 100644 index 000000000..2082c23e8 --- /dev/null +++ b/lib/rust/src/version/mod.rs @@ -0,0 +1,53 @@ +use semver::Version; + +mod error; +pub use self::error::Error; + +/// Compare two semver versions +pub fn compare(first: &str, second: &str) -> Result { + let v1 = Version::parse(first)?; + let v2 = Version::parse(second)?; + if v1 > v2 { + Ok(-1) + } else if v1 == v2 { + Ok(0) + } else { + Ok(1) + } +} + +/// Check if the "second" semver is compatible with the "first" +pub fn is_compatible(first: &str, second: &str) -> Result { + let first = Version::parse(first)?; + let second = Version::parse(second)?; + Ok(if second.major == 0 && first.major == 0 { + first.minor == second.minor && second.patch > first.patch + } else if second.major > 0 { + first.major == second.major + && ((second.minor > first.minor) + || (first.minor == second.minor && second.patch > first.patch)) + } else { + false + }) +} + +/// Check if a the "other" version is a major bump from the "current" +pub fn is_major(current: &str, other: &str) -> Result { + let current = Version::parse(current)?; + let other = Version::parse(other)?; + Ok(other.major > current.major) +} + +/// Check if a the "other" version is a minor bump from the "current" +pub fn is_minor(current: &str, other: &str) -> Result { + let current = Version::parse(current)?; + let other = Version::parse(other)?; + Ok(current.major == other.major && other.minor > current.minor) +} + +/// Check if a the "other" version is a patch bump from the "current" +pub fn is_patch(current: &str, other: &str) -> Result { + let current = Version::parse(current)?; + let other = Version::parse(other)?; + Ok(current.major == other.major && current.minor == other.minor && other.patch > current.patch) +} diff --git a/package.json b/package.json new file mode 100644 index 000000000..ead99ac9f --- /dev/null +++ b/package.json @@ -0,0 +1,27 @@ +{ + "name": "@quasar/proton", + "version": "1.0.0-alpha.1", + "description": "Multi-binding collection of libraries and templates for building Proton", + "main": "templates/index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/quasarframework/proton.git" + }, + "author": "Quasar Framework", + "license": "MIT", + "bugs": { + "url": "https://github.com/quasarframework/proton/issues" + }, + "homepage": "https://github.com/quasarframework/proton#readme", + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">= 10.16.0", + "npm": ">= 6.6.0", + "yarn": ">= 1.17.3" + } +} diff --git a/spec/README.md b/spec/README.md new file mode 100644 index 000000000..6ddf4a4e4 --- /dev/null +++ b/spec/README.md @@ -0,0 +1 @@ +[WIP] diff --git a/templates/rust/Cargo.toml b/templates/rust/Cargo.toml new file mode 100755 index 000000000..78f8b2f21 --- /dev/null +++ b/templates/rust/Cargo.toml @@ -0,0 +1,39 @@ +[package] +name = "app" +version = "0.1.0" +description = "A Quasar app" +authors = ["Lucas Fernandes Nogueira "] +edition = "2018" +build = "build.rs" +include = ["data"] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +proton-ui = { path = "../../proton/bindings/rust" } +serde_json = "1.0.39" +serde = "1.0" +serde_derive = "1.0" +tiny_http = "0.6" +clap = {version = "2.33", features = ["yaml"]} +phf = "0.7.21" +includedir = "0.5.0" +proton = { path = "../../proton/lib/rust" } + +[build-dependencies] +includedir_codegen = "0.5.0" + +[features] +dev = [] # has no explicit dependencies + +[package.metadata.bundle] +identifier = "com.quasar.dev" +icon = ["icons/32x32.png", "icons/128x128.png", "icons/128x128@2x.png", "icons/icon.icns", "icons/icon.ico"] + +[[bin]] +name = "updater" +path = "src/updater.rs" + +[[bin]] +name = "app" +path = "src/main.rs" diff --git a/templates/rust/_gitignore b/templates/rust/_gitignore new file mode 100755 index 000000000..50c83018e --- /dev/null +++ b/templates/rust/_gitignore @@ -0,0 +1,10 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk \ No newline at end of file diff --git a/templates/rust/build.rs b/templates/rust/build.rs new file mode 100755 index 000000000..07829cd86 --- /dev/null +++ b/templates/rust/build.rs @@ -0,0 +1,10 @@ +extern crate includedir_codegen; + +use includedir_codegen::Compression; + +fn main() { + includedir_codegen::start("ASSETS") + .dir("./target/compiled-web", Compression::Gzip) + .build("data.rs") + .unwrap(); +} diff --git a/templates/rust/icons/128x128.png b/templates/rust/icons/128x128.png new file mode 100644 index 0000000000000000000000000000000000000000..4ab8a828e47051dcef9c06d6679f9ef990243b64 GIT binary patch literal 12360 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�B#L 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 FV1m_(T_pej literal 0 HcmV?d00001 diff --git a/templates/rust/icons/32x32.png b/templates/rust/icons/32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..a21c3721df67fe20e247279a9fc30a3f6c8d539e GIT binary patch literal 2285 zcmV+P)h3d=bI1R7w6o2 zpL*IqSotMZt@+AJpo#;)KLr1Af!C@y{#^Bi7aVyd1nNn6;={b$tzH5}0QeVhy88b8 ze~n%7#hgE;%p7}k?4(am61-k6_xA1ka~yE02HzDZB0GDNmq0xZkA2X-Tc?r&Zt}kq zMLPz~oI4@Ab(ao^ubBp)h&a4mDF`o>@-}!&u6ZxMMjBdo?tmF{Cgc7#eMr4GhMiA<){!61%Vc0bV_=LzJ2pJfFfA-Tp9w&yWk|M)~>B33YzZB#(pwUM}Tr`A5g=u6?@0FEUQcruyxQf$)up#e}J&aPM{|fD@4LWA4H!ki!sF zxf(tmGZFzY33#hQ0Qn+DN9%5Vdmu12QNQ$lC9CJa-f(e3h`!lN;MBRmuzkJ|u5>^+ z-l`br5CQ?XtatAEP5Va1p{*!D)WA@ike@oa3yoU0fNYEk!I3z2bRB>TD9GQ2wp}}- zX}i`8y9O@mMayO|I*w8jojD6hOgt-)v-3(QcafpgYBz0)7v8IAZ{NK$bF6_vef9Do z_(#QK&z?U3oEH=zCucLFt5l<*hNR#BxCForL80KBCp|RQAtaUmb(h}V%-$J|8`H?4 z0`=P;E4{dwA~SQ|#O(KcLz!>u$dR>LLQDh1>z#O`yg!D2Jj8f-;oN>CrKV9C5b2Q< z)IFp)Vf}{hP*||tWyl$0geU|wz;=9t7E)U8`h;Nmobj~o-yRjP!nNzx3D3P<-s;i6 zr>6JzJ6X)LF@R+9i;BaU(|gc=Sbrg?kIDP%<@{(I^DWT`FeDW2pk=3a_~ECexPLF( z7~K(0Eg{5~YKddaMCV!E{N1pECtQF#TFtU%8GXfv5tr$*g%3@%%d# zKs__>Qec=SdgrPSSHa3jveKEZeY+b2?%c8=yj!j0K<4`w={^LDwrNa(CT&T8lD8l zCE?7e-Npb)+Q@MEOcGpVn2z0hexE2%iVMf6M;D?qEGDMZw&>87WTOJec*0`<2}zZ( zk@3&~XEdn-794l;(tQT^(a*9FbzvbYN)sr>g{64L+zC0Cp=%2}8;dpv?-6ZyW0hD=2`duER**CRh22J*;VmU(z)=0-z6azwt(Bl!i&Os@jP z8Ln27CZ=I@?AZl0fYT0B!4MK`G~AGl=jY#+lkrRkCCHlKX+~0685PbAyl0#tL0^=; z(;GMDgfv{Be*Sg2mvWEJ93baPt7Ji9>s+{GI)F>sX4{}!b!w0H-8(^yG#+M&>`2ZR zr4`q0*2H*9z0+9si%#MIOZLv4ziLByX1Y*VHaUK5D{8h?7D?LQa=7*zY=<7CAF@);Q+h$dE&fG%($BKrepB z9^mNSzZZ?HFHqu`mq77?@QltJAUVTvj1r=mk&NS2B>^iW89(f!AuLAZh9^Co8`!i+ zQYqP*V;sMISN4#-?Dbl-km9v@qCh35rI!>ag3CkT0NJorOG=BCvDi==qAO?A;+$YY z&ViI;8UMa}D-%`Z*@Hu=?BS@_qB(i7OrRbF7Y~Y$%y|+ChKvJ*M1o^EtE77@*CJ0X z3Ly+xsT`RL+3grDi{Fs(U1cWB$=*C!popi9NIU)kXSR-l7ZF1XMBh6rZ#A32$AQZe!O@x1t3VY8I~m(4!RrSj$RhK1exCmy_*jW?8OHJi00000NkvXX Hu0mjfGLcGK literal 0 HcmV?d00001 diff --git a/templates/rust/icons/icon.icns b/templates/rust/icons/icon.icns new file mode 100644 index 0000000000000000000000000000000000000000..cf57cfb49d0a21b455bfe16724888de768311055 GIT binary patch literal 242236 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

N;v^8SB{$*{P-{?nz|K;S$LW! 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 literal 0 HcmV?d00001 diff --git a/templates/rust/rustfmt.toml b/templates/rust/rustfmt.toml new file mode 100644 index 000000000..9da25275e --- /dev/null +++ b/templates/rust/rustfmt.toml @@ -0,0 +1,13 @@ +max_width = 100 +hard_tabs = false +tab_spaces = 2 +newline_style = "Auto" +use_small_heuristics = "Default" +reorder_imports = true +reorder_modules = true +remove_nested_parens = true +edition = "2015" +merge_derives = true +use_try_shorthand = false +use_field_init_shorthand = false +force_explicit_abi = true diff --git a/templates/rust/src/cmd.rs b/templates/rust/src/cmd.rs new file mode 100755 index 000000000..53b696461 --- /dev/null +++ b/templates/rust/src/cmd.rs @@ -0,0 +1,8 @@ +#[derive(Deserialize)] +#[serde(tag = "cmd", rename_all = "camelCase")] +pub enum Cmd { + // your custom commands + // multiple arguments are allowed + // note that rename_all = "camelCase": you need to use "myCustomCommand" on JS + MyCustomCommand { argument: String }, +} diff --git a/templates/rust/src/main.rs b/templates/rust/src/main.rs new file mode 100755 index 000000000..41c5f4fa5 --- /dev/null +++ b/templates/rust/src/main.rs @@ -0,0 +1,112 @@ +#[macro_use] +extern crate serde_derive; +extern crate clap; +extern crate proton; +extern crate proton_ui; +extern crate serde_json; + +#[cfg(not(feature = "dev"))] +extern crate tiny_http; + +#[cfg(feature = "dev")] +use clap::{App, Arg}; + +#[cfg(not(feature = "dev"))] +use std::thread; + +mod cmd; + +#[cfg(not(feature = "dev"))] +mod server; + +fn main() { + let debug; + let content; + let _matches: clap::ArgMatches; + + #[cfg(not(feature = "dev"))] + { + thread::spawn(|| { + proton::command::spawn_relative_command( + "updater".to_string(), + Vec::new(), + std::process::Stdio::inherit(), + ) + .unwrap(); + }); + } + + #[cfg(feature = "dev")] + { + let app = App::new("app") + .version("1.0.0") + .author("Author") + .about("About") + .arg( + Arg::with_name("url") + .short("u") + .long("url") + .value_name("URL") + .help("Loads the specified URL into webview") + .required(true) + .takes_value(true), + ); + + _matches = app.get_matches(); + content = proton_ui::Content::Url(_matches.value_of("url").unwrap()); + debug = true; + } + #[cfg(not(feature = "dev"))] + { + if let Some(available_port) = proton::tcp::get_available_port() { + let server_url = format!("{}:{}", "127.0.0.1", available_port); + content = proton_ui::Content::Url(format!("http://{}", server_url)); + debug = cfg!(debug_assertions); + + thread::spawn(move || { + let server = tiny_http::Server::http(server_url).unwrap(); + for request in server.incoming_requests() { + let mut url = request.url().to_string(); + if url == "/" { + url = "/index.html".to_string(); + } + request.respond(server::asset_response(&url)).unwrap(); + } + }); + } else { + panic!("Could not find an open port"); + } + } + + let webview = proton_ui::builder() + .title("MyAppTitle") + .content(content) + .size(800, 600) // TODO:Resolution is fixed right now, change this later to be dynamic + .resizable(true) + .debug(debug) + .user_data(()) + .invoke_handler(|_webview, arg| { + // leave this as is to use the proton API from your JS code + if !proton::api::handler(_webview, arg) { + use cmd::Cmd::*; + match serde_json::from_str(arg) { + Err(_) => {} + Ok(command) => { + match command { + // definitions for your custom commands from Cmd here + MyCustomCommand { argument } => { + // your command code + println!("{}", argument); + } + } + } + } + } + + Ok(()) + }) + .build() + .unwrap(); + + webview.run().unwrap(); +} diff --git a/templates/rust/src/server.rs b/templates/rust/src/server.rs new file mode 100644 index 000000000..5a7534d15 --- /dev/null +++ b/templates/rust/src/server.rs @@ -0,0 +1,26 @@ +use tiny_http::{Header, Response}; + +include!(concat!(env!("OUT_DIR"), "/data.rs")); + +pub fn asset_response(path: &str) -> Response>> { + let asset = ASSETS + .get(&format!("./target/compiled-web{}", path)) + .unwrap() + .into_owned(); + let mut response = Response::from_data(asset); + let header; + + if path.ends_with(".svg") { + header = Header::from_bytes(&b"Content-Type"[..], &b"image/svg+xml"[..]).unwrap(); + } else if path.ends_with(".css") { + header = Header::from_bytes(&b"Content-Type"[..], &b"text/css"[..]).unwrap(); + } else if path.ends_with(".html") { + header = Header::from_bytes(&b"Content-Type"[..], &b"text/html"[..]).unwrap(); + } else { + header = Header::from_bytes(&b"Content-Type"[..], &b"appication/octet-stream"[..]).unwrap(); + } + + response.add_header(header); + + response +} diff --git a/templates/rust/src/updater.rs b/templates/rust/src/updater.rs new file mode 100644 index 000000000..d8e66ae80 --- /dev/null +++ b/templates/rust/src/updater.rs @@ -0,0 +1,73 @@ +extern crate proton; +extern crate serde_derive; +extern crate serde_json; + +use crate::proton::process::{ProcessExt, Signal, SystemExt}; + +fn update() -> Result<(), String> { + let target = proton::platform::target_triple().map_err(|_| "Could not determine target")?; + let github_release = proton::updater::github::get_latest_release("jaemk", "self_update") + .map_err(|_| "Could not fetch latest release")?; + match github_release.asset_for(&target) { + Some(github_release_asset) => { + let release = proton::updater::Release { + version: github_release.tag.trim_start_matches('v').to_string(), + download_url: github_release_asset.download_url, + asset_name: github_release_asset.name, + }; + + let status = proton::updater::Update::configure() + .unwrap() + .release(release) + .bin_path_in_archive("github") + .bin_name("app") + .bin_install_path(&proton::command::command_path("app".to_string()).unwrap()) + .show_download_progress(true) + .current_version(env!("CARGO_PKG_VERSION")) + .build() + .unwrap() + .update() + .unwrap(); + + println!("found release: {}", status.version()); + + /*let tmp_dir = proton::dir::with_temp_dir(|dir| { + let file_path = dir.path().join("my-temporary-note.pdf"); + let mut tmp_archive = std::fs::File::create(file_path).unwrap(); + proton::http::download(&"https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf".to_string(), &mut tmp_archive, true).unwrap(); + });*/ + + Ok(()) + } + None => Err(format!("Could not find release for target {}", target)), + } +} + +fn restart_app(app_command: String) -> Result<(), String> { + let mut system = proton::process::System::new(); + let parent_process = proton::process::get_parent_process(&mut system) + .map_err(|_| "Could not determine parent process")?; + if parent_process.name() == "app" { + parent_process.kill(Signal::Kill); + std::thread::sleep(std::time::Duration::from_secs(1)); + std::process::Command::new(app_command) + .spawn() + .map_err(|_| "Could not start app")?; + } + Ok(()) +} + +fn run_updater() -> Result<(), String> { + let app_command = proton::command::relative_command("app".to_string()) + .map_err(|_| "Could not determine app path")?; + update()?; + restart_app(app_command)?; + Ok(()) +} + +fn main() { + match run_updater() { + Ok(_) => {} + Err(err) => panic!(err), + }; +} diff --git a/ui/.gitattributes b/ui/.gitattributes new file mode 100644 index 000000000..5170675f3 --- /dev/null +++ b/ui/.gitattributes @@ -0,0 +1 @@ +*.h linguist-language=c diff --git a/ui/.gitignore b/ui/.gitignore new file mode 100644 index 000000000..27a766120 --- /dev/null +++ b/ui/.gitignore @@ -0,0 +1,2 @@ +# Build atrifacts +/build diff --git a/ui/CMakeLists.txt b/ui/CMakeLists.txt new file mode 100755 index 000000000..a616430bb --- /dev/null +++ b/ui/CMakeLists.txt @@ -0,0 +1,32 @@ +cmake_minimum_required(VERSION 2.8) + +project(webview) + +if(APPLE) + set(WEBVIEW_COMPILE_DEFS "-DWEBVIEW_COCOA=1") + set(WEBVIEW_LIBS "-framework WebKit") +elseif(WIN32) + set(WEBVIEW_COMPILE_DEFS "-DWEBVIEW_WINAPI=1") + set(WEBVIEW_LIBS "ole32 comctl32 oleaut32 uuid") +else() + set(WEBVIEW_COMPILE_DEFS "-DWEBVIEW_GTK=1") + find_package(PkgConfig REQUIRED) + pkg_check_modules(GTK3 REQUIRED gtk+-3.0) + pkg_check_modules(WEBKIT2 REQUIRED webkit2gtk-4.0) + set(WEBVIEW_COMPILE_INCS ${GTK3_INCLUDE_DIRS} ${WEBKIT2_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}) + set(WEBVIEW_LIBS ${GTK3_LIBRARIES} ${WEBKIT2_LIBRARIES}) +endif() + +add_library(proton ${CMAKE_CURRENT_BINARY_DIR}/proton.c) +file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/proton.c "#define WEBVIEW_IMPLEMENTATION\n#include ") +target_include_directories(proton PUBLIC ${PROJECT_SOURCE_DIR} ${WEBVIEW_COMPILE_INCS}) +target_compile_definitions(proton PUBLIC ${WEBVIEW_COMPILE_DEFS}) +target_compile_options(proton PRIVATE ${WEBVIEW_COMPILE_OPTS}) +target_link_libraries(proton ${WEBVIEW_LIBS}) + +add_executable(proton_test WIN32 MACOSX_BUNDLE proton_test.cc) +set_target_properties(proton_test PROPERTIES CXX_STANDARD 11 CXX_STANDARD_REQUIRED YES CXX_EXTENSIONS NO) +find_package(Threads) +target_link_libraries(proton_test PRIVATE proton ${CMAKE_THREAD_LIBS_INIT}) +enable_testing () +add_test(NAME proton_test COMMAND proton_test) diff --git a/ui/LICENSE b/ui/LICENSE new file mode 100644 index 000000000..a12fe0483 --- /dev/null +++ b/ui/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 - present Serge Zaitsev & Quasar Framework Contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/ui/README.md b/ui/README.md new file mode 100755 index 000000000..95d9ca04e --- /dev/null +++ b/ui/README.md @@ -0,0 +1,3 @@ +# PROTON WEBVIEW + +Documentation forthcoming. diff --git a/ui/proton.h b/ui/proton.h new file mode 100644 index 000000000..b2a6f83d5 --- /dev/null +++ b/ui/proton.h @@ -0,0 +1,2265 @@ +/* + * MIT License + * + * Copyright (c) 2017 Serge Zaitsev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef WEBVIEW_H +#define WEBVIEW_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef WEBVIEW_STATIC +#define WEBVIEW_API static +#else +#define WEBVIEW_API extern +#endif + +#include +#include +#include + +#if defined(WEBVIEW_GTK) +#include +#include +#include + +struct webview_priv { + GtkWidget *window; + GtkWidget *scroller; + GtkWidget *webview; + GtkWidget *inspector_window; + GAsyncQueue *queue; + int ready; + int js_busy; + int should_exit; +}; +#elif defined(WEBVIEW_WINAPI) +#define CINTERFACE +#include + +#include +#include +#include +#include +#include + +#include + +struct webview_priv { + HWND hwnd; + IOleObject **browser; + BOOL is_fullscreen; + DWORD saved_style; + DWORD saved_ex_style; + RECT saved_rect; +}; +#elif defined(WEBVIEW_COCOA) +#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 + +struct webview; + +typedef void (*webview_external_invoke_cb_t)(struct webview *w, + const char *arg); + +struct webview { + const char *url; + const char *title; + int width; + int height; + int resizable; + int debug; + webview_external_invoke_cb_t external_invoke_cb; + struct webview_priv priv; + void *userdata; +}; + +enum webview_dialog_type { + WEBVIEW_DIALOG_TYPE_OPEN = 0, + WEBVIEW_DIALOG_TYPE_SAVE = 1, + WEBVIEW_DIALOG_TYPE_ALERT = 2 +}; + +#define WEBVIEW_DIALOG_FLAG_FILE (0 << 0) +#define WEBVIEW_DIALOG_FLAG_DIRECTORY (1 << 0) + +#define WEBVIEW_DIALOG_FLAG_INFO (1 << 1) +#define WEBVIEW_DIALOG_FLAG_WARNING (2 << 1) +#define WEBVIEW_DIALOG_FLAG_ERROR (3 << 1) +#define WEBVIEW_DIALOG_FLAG_ALERT_MASK (3 << 1) + +typedef void (*webview_dispatch_fn)(struct webview *w, void *arg); + +struct webview_dispatch_arg { + webview_dispatch_fn fn; + struct webview *w; + void *arg; +}; + +#define DEFAULT_URL \ + "data:text/" \ + "html,%3C%21DOCTYPE%20html%3E%0A%3Chtml%20lang=%22en%22%3E%0A%3Chead%3E%" \ + "3Cmeta%20charset=%22utf-8%22%3E%3Cmeta%20http-equiv=%22X-UA-Compatible%22%" \ + "20content=%22IE=edge%22%3E%3C%2Fhead%3E%0A%3Cbody%3E%3Cdiv%20id=%22app%22%" \ + "3E%3C%2Fdiv%3E%3Cscript%20type=%22text%2Fjavascript%22%3E%3C%2Fscript%3E%" \ + "3C%2Fbody%3E%0A%3C%2Fhtml%3E" + +#define CSS_INJECT_FUNCTION \ + "(function(e){var " \ + "t=document.createElement('style'),d=document.head||document." \ + "getElementsByTagName('head')[0];t.setAttribute('type','text/" \ + "css'),t.styleSheet?t.styleSheet.cssText=e:t.appendChild(document." \ + "createTextNode(e)),d.appendChild(t)})" + +static const char *webview_check_url(const char *url) { + if (url == NULL || strlen(url) == 0) { + return DEFAULT_URL; + } + return url; +} + +WEBVIEW_API int webview(const char *title, const char *url, int width, + int height, int resizable); + +WEBVIEW_API int webview_init(struct webview *w); +WEBVIEW_API int webview_loop(struct webview *w, int blocking); +WEBVIEW_API int webview_eval(struct webview *w, const char *js); +WEBVIEW_API int webview_inject_css(struct webview *w, const char *css); +WEBVIEW_API void webview_set_title(struct webview *w, const char *title); +WEBVIEW_API void webview_set_fullscreen(struct webview *w, int fullscreen); +WEBVIEW_API void webview_set_color(struct webview *w, uint8_t r, uint8_t g, + uint8_t b, uint8_t a); +WEBVIEW_API void webview_dialog(struct webview *w, + enum webview_dialog_type dlgtype, int flags, + const char *title, const char *arg, + char *result, size_t resultsz); +WEBVIEW_API void webview_dispatch(struct webview *w, webview_dispatch_fn fn, + void *arg); +WEBVIEW_API void webview_terminate(struct webview *w); +WEBVIEW_API void webview_exit(struct webview *w); +WEBVIEW_API void webview_debug(const char *format, ...); +WEBVIEW_API void webview_print_log(const char *s); + +#ifdef WEBVIEW_IMPLEMENTATION +#undef WEBVIEW_IMPLEMENTATION + +WEBVIEW_API int webview(const char *title, const char *url, int width, + int height, int resizable) { + struct webview webview; + memset(&webview, 0, sizeof(webview)); + webview.title = title; + webview.url = url; + webview.width = width; + webview.height = height; + webview.resizable = resizable; + int r = webview_init(&webview); + if (r != 0) { + return r; + } + while (webview_loop(&webview, 1) == 0) { + } + webview_exit(&webview); + return 0; +} + +WEBVIEW_API void webview_debug(const char *format, ...) { + char buf[4096]; + va_list ap; + va_start(ap, format); + vsnprintf(buf, sizeof(buf), format, ap); + webview_print_log(buf); + va_end(ap); +} + +static int webview_js_encode(const char *s, char *esc, size_t n) { + int r = 1; /* At least one byte for trailing zero */ + for (; *s; s++) { + const unsigned char c = *s; + if (c >= 0x20 && c < 0x80 && strchr("<>\\'\"", c) == NULL) { + if (n > 0) { + *esc++ = c; + n--; + } + r++; + } else { + if (n > 0) { + snprintf(esc, n, "\\x%02x", (int)c); + esc += 4; + n -= 4; + } + r += 4; + } + } + return r; +} + +WEBVIEW_API int webview_inject_css(struct webview *w, const char *css) { + int n = webview_js_encode(css, NULL, 0); + char *esc = (char *)calloc(1, sizeof(CSS_INJECT_FUNCTION) + n + 4); + if (esc == NULL) { + return -1; + } + char *js = (char *)calloc(1, n); + webview_js_encode(css, js, n); + snprintf(esc, sizeof(CSS_INJECT_FUNCTION) + n + 4, "%s(\"%s\")", + CSS_INJECT_FUNCTION, js); + int r = webview_eval(w, esc); + free(js); + free(esc); + return r; +} + +#if defined(WEBVIEW_GTK) +static void external_message_received_cb(WebKitUserContentManager *m, + WebKitJavascriptResult *r, + gpointer arg) { + (void)m; + struct webview *w = (struct webview *)arg; + if (w->external_invoke_cb == NULL) { + return; + } + JSGlobalContextRef context = webkit_javascript_result_get_global_context(r); + JSValueRef value = webkit_javascript_result_get_value(r); + JSStringRef js = JSValueToStringCopy(context, value, NULL); + size_t n = JSStringGetMaximumUTF8CStringSize(js); + char *s = g_new(char, n); + JSStringGetUTF8CString(js, s, n); + w->external_invoke_cb(w, s); + JSStringRelease(js); + g_free(s); +} + +static void webview_load_changed_cb(WebKitWebView *webview, + WebKitLoadEvent event, gpointer arg) { + (void)webview; + struct webview *w = (struct webview *)arg; + if (event == WEBKIT_LOAD_FINISHED) { + w->priv.ready = 1; + } +} + +static void webview_destroy_cb(GtkWidget *widget, gpointer arg) { + (void)widget; + struct webview *w = (struct webview *)arg; + webview_terminate(w); +} + +static gboolean webview_context_menu_cb(WebKitWebView *webview, + GtkWidget *default_menu, + WebKitHitTestResult *hit_test_result, + gboolean triggered_with_keyboard, + gpointer userdata) { + (void)webview; + (void)default_menu; + (void)hit_test_result; + (void)triggered_with_keyboard; + (void)userdata; + return TRUE; +} + +WEBVIEW_API int webview_init(struct webview *w) { + if (gtk_init_check(0, NULL) == FALSE) { + return -1; + } + + w->priv.ready = 0; + w->priv.should_exit = 0; + w->priv.queue = g_async_queue_new(); + w->priv.window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(w->priv.window), w->title); + + if (w->resizable) { + gtk_window_set_default_size(GTK_WINDOW(w->priv.window), w->width, + w->height); + } else { + gtk_widget_set_size_request(w->priv.window, w->width, w->height); + } + gtk_window_set_resizable(GTK_WINDOW(w->priv.window), !!w->resizable); + gtk_window_set_position(GTK_WINDOW(w->priv.window), GTK_WIN_POS_CENTER); + + w->priv.scroller = gtk_scrolled_window_new(NULL, NULL); + gtk_container_add(GTK_CONTAINER(w->priv.window), w->priv.scroller); + + WebKitUserContentManager *m = webkit_user_content_manager_new(); + webkit_user_content_manager_register_script_message_handler(m, "external"); + g_signal_connect(m, "script-message-received::external", + G_CALLBACK(external_message_received_cb), w); + + w->priv.webview = webkit_web_view_new_with_user_content_manager(m); + webkit_web_view_load_uri(WEBKIT_WEB_VIEW(w->priv.webview), + webview_check_url(w->url)); + g_signal_connect(G_OBJECT(w->priv.webview), "load-changed", + G_CALLBACK(webview_load_changed_cb), w); + gtk_container_add(GTK_CONTAINER(w->priv.scroller), w->priv.webview); + + if (w->debug) { + WebKitSettings *settings = + webkit_web_view_get_settings(WEBKIT_WEB_VIEW(w->priv.webview)); + webkit_settings_set_enable_write_console_messages_to_stdout(settings, true); + webkit_settings_set_enable_developer_extras(settings, true); + } else { + g_signal_connect(G_OBJECT(w->priv.webview), "context-menu", + G_CALLBACK(webview_context_menu_cb), w); + } + + gtk_widget_show_all(w->priv.window); + + webkit_web_view_run_javascript( + WEBKIT_WEB_VIEW(w->priv.webview), + "window.external={invoke:function(x){" + "window.webkit.messageHandlers.external.postMessage(x);}}", + NULL, NULL, NULL); + + g_signal_connect(G_OBJECT(w->priv.window), "destroy", + G_CALLBACK(webview_destroy_cb), w); + return 0; +} + +WEBVIEW_API int webview_loop(struct webview *w, int blocking) { + gtk_main_iteration_do(blocking); + return w->priv.should_exit; +} + +WEBVIEW_API void webview_set_title(struct webview *w, const char *title) { + gtk_window_set_title(GTK_WINDOW(w->priv.window), title); +} + +WEBVIEW_API void webview_set_fullscreen(struct webview *w, int fullscreen) { + if (fullscreen) { + gtk_window_fullscreen(GTK_WINDOW(w->priv.window)); + } else { + gtk_window_unfullscreen(GTK_WINDOW(w->priv.window)); + } +} + +WEBVIEW_API void webview_set_color(struct webview *w, uint8_t r, uint8_t g, + uint8_t b, uint8_t a) { + GdkRGBA color = {r / 255.0, g / 255.0, b / 255.0, a / 255.0}; + webkit_web_view_set_background_color(WEBKIT_WEB_VIEW(w->priv.webview), + &color); +} + +WEBVIEW_API void webview_dialog(struct webview *w, + enum webview_dialog_type dlgtype, int flags, + const char *title, const char *arg, + char *result, size_t resultsz) { + GtkWidget *dlg; + if (result != NULL) { + result[0] = '\0'; + } + if (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN || + dlgtype == WEBVIEW_DIALOG_TYPE_SAVE) { + dlg = gtk_file_chooser_dialog_new( + title, GTK_WINDOW(w->priv.window), + (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN + ? (flags & WEBVIEW_DIALOG_FLAG_DIRECTORY + ? GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER + : GTK_FILE_CHOOSER_ACTION_OPEN) + : GTK_FILE_CHOOSER_ACTION_SAVE), + "_Cancel", GTK_RESPONSE_CANCEL, + (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN ? "_Open" : "_Save"), + GTK_RESPONSE_ACCEPT, NULL); + gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(dlg), FALSE); + gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dlg), FALSE); + gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(dlg), TRUE); + gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dlg), TRUE); + gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER(dlg), TRUE); + gint response = gtk_dialog_run(GTK_DIALOG(dlg)); + if (response == GTK_RESPONSE_ACCEPT) { + gchar *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dlg)); + g_strlcpy(result, filename, resultsz); + g_free(filename); + } + gtk_widget_destroy(dlg); + } else if (dlgtype == WEBVIEW_DIALOG_TYPE_ALERT) { + GtkMessageType type = GTK_MESSAGE_OTHER; + switch (flags & WEBVIEW_DIALOG_FLAG_ALERT_MASK) { + case WEBVIEW_DIALOG_FLAG_INFO: + type = GTK_MESSAGE_INFO; + break; + case WEBVIEW_DIALOG_FLAG_WARNING: + type = GTK_MESSAGE_WARNING; + break; + case WEBVIEW_DIALOG_FLAG_ERROR: + type = GTK_MESSAGE_ERROR; + break; + } + dlg = gtk_message_dialog_new(GTK_WINDOW(w->priv.window), GTK_DIALOG_MODAL, + type, GTK_BUTTONS_OK, "%s", title); + gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dlg), "%s", + arg); + gtk_dialog_run(GTK_DIALOG(dlg)); + gtk_widget_destroy(dlg); + } +} + +static void webview_eval_finished(GObject *object, GAsyncResult *result, + gpointer userdata) { + (void)object; + (void)result; + struct webview *w = (struct webview *)userdata; + w->priv.js_busy = 0; +} + +WEBVIEW_API int webview_eval(struct webview *w, const char *js) { + while (w->priv.ready == 0) { + g_main_context_iteration(NULL, TRUE); + } + w->priv.js_busy = 1; + webkit_web_view_run_javascript(WEBKIT_WEB_VIEW(w->priv.webview), js, NULL, + webview_eval_finished, w); + while (w->priv.js_busy) { + g_main_context_iteration(NULL, TRUE); + } + return 0; +} + +static gboolean webview_dispatch_wrapper(gpointer userdata) { + struct webview *w = (struct webview *)userdata; + for (;;) { + struct webview_dispatch_arg *arg = + (struct webview_dispatch_arg *)g_async_queue_try_pop(w->priv.queue); + if (arg == NULL) { + break; + } + (arg->fn)(w, arg->arg); + g_free(arg); + } + return FALSE; +} + +WEBVIEW_API void webview_dispatch(struct webview *w, webview_dispatch_fn fn, + void *arg) { + struct webview_dispatch_arg *context = + (struct webview_dispatch_arg *)g_new(struct webview_dispatch_arg, 1); + context->w = w; + context->arg = arg; + context->fn = fn; + g_async_queue_lock(w->priv.queue); + g_async_queue_push_unlocked(w->priv.queue, context); + if (g_async_queue_length_unlocked(w->priv.queue) == 1) { + gdk_threads_add_idle(webview_dispatch_wrapper, w); + } + g_async_queue_unlock(w->priv.queue); +} + +WEBVIEW_API void webview_terminate(struct webview *w) { + w->priv.should_exit = 1; +} + +WEBVIEW_API void webview_exit(struct webview *w) { (void)w; } +WEBVIEW_API void webview_print_log(const char *s) { + fprintf(stderr, "%s\n", s); +} + +#endif /* WEBVIEW_GTK */ + +#if defined(WEBVIEW_WINAPI) + +#pragma comment(lib, "user32.lib") +#pragma comment(lib, "ole32.lib") +#pragma comment(lib, "oleaut32.lib") + +#define WM_WEBVIEW_DISPATCH (WM_APP + 1) + +typedef struct { + IOleInPlaceFrame frame; + HWND window; +} _IOleInPlaceFrameEx; + +typedef struct { + IOleInPlaceSite inplace; + _IOleInPlaceFrameEx frame; +} _IOleInPlaceSiteEx; + +typedef struct { + IDocHostUIHandler ui; +} _IDocHostUIHandlerEx; + +typedef struct { + 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, add_opts; + if (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN) { + if (CoCreateInstance( + iid_unref(&CLSID_FileOpenDialog), NULL, CLSCTX_INPROC_SERVER, + iid_unref(&IID_IFileOpenDialog), (void **)&dlg) != S_OK) { + goto error_dlg; + } + if (flags & WEBVIEW_DIALOG_FLAG_DIRECTORY) { + add_opts |= FOS_PICKFOLDERS; + } + add_opts |= FOS_NOCHANGEDIR | FOS_ALLNONSTORAGEITEMS | FOS_NOVALIDATE | + FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST | FOS_SHAREAWARE | + FOS_NOTESTFILECREATE | FOS_NODEREFERENCELINKS | + FOS_FORCESHOWHIDDEN | FOS_DEFAULTNOMINIMODE; + } else { + if (CoCreateInstance( + iid_unref(&CLSID_FileSaveDialog), NULL, CLSCTX_INPROC_SERVER, + iid_unref(&IID_IFileSaveDialog), (void **)&dlg) != S_OK) { + goto error_dlg; + } + add_opts |= FOS_OVERWRITEPROMPT | FOS_NOCHANGEDIR | + FOS_ALLNONSTORAGEITEMS | FOS_NOVALIDATE | FOS_SHAREAWARE | + FOS_NOTESTFILECREATE | FOS_NODEREFERENCELINKS | + FOS_FORCESHOWHIDDEN | FOS_DEFAULTNOMINIMODE; + } + if (dlg->lpVtbl->GetOptions(dlg, &opts) != S_OK) { + goto error_dlg; + } + opts &= ~FOS_NOREADONLYRETURN; + opts |= add_opts; + if (dlg->lpVtbl->SetOptions(dlg, opts) != S_OK) { + goto error_dlg; + } + if (dlg->lpVtbl->Show(dlg, w->priv.hwnd) != S_OK) { + goto error_dlg; + } + if (dlg->lpVtbl->GetResult(dlg, &res) != S_OK) { + goto error_dlg; + } + if (res->lpVtbl->GetDisplayName(res, SIGDN_FILESYSPATH, &ws) != S_OK) { + goto error_result; + } + s = webview_from_utf16(ws); + strncpy(result, s, resultsz); + result[resultsz - 1] = '\0'; + CoTaskMemFree(ws); + error_result: + res->lpVtbl->Release(res); + error_dlg: + dlg->lpVtbl->Release(dlg); + return; + } else if (dlgtype == WEBVIEW_DIALOG_TYPE_ALERT) { +#if 0 + /* MinGW often doesn't contain TaskDialog, we'll use MessageBox for now */ + WCHAR *wtitle = webview_to_utf16(title); + WCHAR *warg = webview_to_utf16(arg); + TaskDialog(w->priv.hwnd, NULL, NULL, wtitle, warg, 0, NULL, NULL); + GlobalFree(warg); + GlobalFree(wtitle); +#else + UINT type = MB_OK; + switch (flags & WEBVIEW_DIALOG_FLAG_ALERT_MASK) { + case WEBVIEW_DIALOG_FLAG_INFO: + type |= MB_ICONINFORMATION; + break; + case WEBVIEW_DIALOG_FLAG_WARNING: + type |= MB_ICONWARNING; + break; + case WEBVIEW_DIALOG_FLAG_ERROR: + type |= MB_ICONERROR; + break; + } + MessageBox(w->priv.hwnd, arg, title, type); +#endif + } +} + +WEBVIEW_API void webview_terminate(struct webview *w) { PostQuitMessage(0); } + +WEBVIEW_API void webview_exit(struct webview *w) { + 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); + + 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); +} + +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 +} +#endif + +#endif /* WEBVIEW_H */ diff --git a/ui/proton_test.cc b/ui/proton_test.cc new file mode 100644 index 000000000..2074cc84a --- /dev/null +++ b/ui/proton_test.cc @@ -0,0 +1,176 @@ +// +build ignore + +#include +#include +#include +#include +#include +#include +#include +#include + +#define WEBVIEW_IMPLEMENTATION +#include "proton.h" + +extern "C" void webview_dispatch_proxy(struct webview *w, void *arg) { + (*static_cast *>(arg))(w); +} + +class runner { +public: + runner(struct webview *w) : w(w) { webview_init(this->w); } + ~runner() { webview_exit(this->w); } + runner &then(std::function fn) { + auto arg = new std::pair, void *>( + fn, nullptr); + this->queue.push_back([=](struct webview *w) { + webview_dispatch( + w, + [](struct webview *w, void *arg) { + auto dispatch_arg = reinterpret_cast< + std::pair, void *> *>( + arg); + dispatch_arg->first(w); + delete dispatch_arg; + }, + reinterpret_cast(arg)); + }); + return *this; + } + runner &sleep(const int millis) { + this->queue.push_back([=](struct webview *w) { + (void)w; + std::this_thread::sleep_for(std::chrono::milliseconds(millis)); + }); + return *this; + } + void wait() { + this->then([](struct webview *w) { webview_terminate(w); }); + auto q = this->queue; + auto w = this->w; + std::thread bg_thread([w, q]() { + for (auto f : q) { + f(w); + } + }); + while (webview_loop(w, 1) == 0) { + } + bg_thread.join(); + } + +private: + struct webview *w; + std::vector> queue; +}; + +static void test_minimal() { + struct webview w = {}; + std::cout << "TEST: minimal" << std::endl; + w.title = "Minimal test"; + w.width = 480; + w.height = 320; + webview_init(&w); + webview_dispatch(&w, + [](struct webview *w, void *arg) { + (void)arg; + webview_terminate(w); + }, + nullptr); + while (webview_loop(&w, 1) == 0) { + } + webview_exit(&w); +} + +static void test_window_size() { + struct webview w = {}; + std::vector results; + std::cout << "TEST: window size" << std::endl; + w.width = 480; + w.height = 320; + w.resizable = 1; + w.userdata = static_cast(&results); + w.external_invoke_cb = [](struct webview *w, const char *arg) { + auto *v = static_cast *>(w->userdata); + v->push_back(std::string(arg)); + }; + runner(&w) + .then([](struct webview *w) { + webview_eval(w, "window.external.invoke(''+window.screen.width+' ' + " + "window.screen.height)"); + webview_eval(w, "window.external.invoke(''+window.innerWidth+' ' + " + "window.innerHeight)"); + }) + .sleep(200) + .then([](struct webview *w) { webview_set_fullscreen(w, 1); }) + .sleep(500) + .then([](struct webview *w) { + webview_eval(w, "window.external.invoke(''+window.innerWidth+' ' + " + "window.innerHeight)"); + }) + .sleep(200) + .then([](struct webview *w) { webview_set_fullscreen(w, 0); }) + .sleep(500) + .then([](struct webview *w) { + webview_eval(w, "window.external.invoke(''+window.innerWidth+' ' + " + "window.innerHeight)"); + }) + .wait(); + assert(results.size() == 4); + assert(results[1] == "480 320"); + assert(results[0] == results[2]); + assert(results[1] == results[3]); +} + +static void test_inject_js() { + struct webview w = {}; + std::vector results; + std::cout << "TEST: inject JS" << std::endl; + w.width = 480; + w.height = 320; + w.userdata = static_cast(&results); + w.external_invoke_cb = [](struct webview *w, const char *arg) { + auto *v = static_cast *>(w->userdata); + v->push_back(std::string(arg)); + }; + runner(&w) + .then([](struct webview *w) { + webview_eval(w, + R"(document.body.innerHTML = '

';)"); + webview_eval( + w, + "window.external.invoke(document.getElementById('foo').innerText)"); + }) + .wait(); + assert(results.size() == 1); + assert(results[0] == "Foo"); +} + +static void test_inject_css() { + struct webview w = {}; + std::vector results; + std::cout << "TEST: inject CSS" << std::endl; + w.width = 480; + w.height = 320; + w.userdata = static_cast(&results); + w.external_invoke_cb = [](struct webview *w, const char *arg) { + auto *v = static_cast *>(w->userdata); + v->push_back(std::string(arg)); + }; + runner(&w) + .then([](struct webview *w) { + webview_inject_css(w, "#app { margin-left: 4px; }"); + webview_eval(w, "window.external.invoke(getComputedStyle(document." + "getElementById('app')).marginLeft)"); + }) + .wait(); + assert(results.size() == 1); + assert(results[0] == "4px"); +} + +int main() { + test_minimal(); + test_window_size(); + test_inject_js(); + test_inject_css(); + return 0; +}