├── .gitattributes ├── .gitignore ├── .gitmodules ├── .zpm ├── locals.zpm ├── pkgs.zig └── zpm.conf ├── README.md ├── Sdk.zig ├── build.zig ├── design ├── icon.svg ├── social-preview.png └── social-preview.xcf ├── example ├── app.htm ├── design.css ├── login.htm ├── main.zig └── minimal.zig ├── screenshots ├── i3-chat.png ├── i3-login.png ├── windows-chat.png └── windows-login.png ├── src ├── minimal.cpp ├── positron.zig └── wv │ ├── LICENCE │ ├── README.md │ ├── webview-cocoa.h │ ├── webview-gtk.h │ ├── webview-windows-chromium.h │ ├── webview-windows-edgehtml.h │ ├── webview-windows.h │ ├── webview.cpp │ └── webview.h └── vendor ├── Microsoft.Web.WebView2.1.0.902.49 ├── .signature.p7s ├── LICENSE.txt ├── Microsoft.Web.WebView2.1.0.902.49.nupkg ├── WebView2.idl ├── WebView2.tlb ├── build │ ├── Common.targets │ ├── Microsoft.Web.WebView2.targets │ └── native │ │ ├── Microsoft.Web.WebView2.targets │ │ ├── arm64 │ │ ├── WebView2Loader.dll │ │ ├── WebView2Loader.dll.lib │ │ └── WebView2LoaderStatic.lib │ │ ├── include │ │ ├── WebView2.h │ │ └── WebView2EnvironmentOptions.h │ │ ├── x64 │ │ ├── WebView2Loader.dll │ │ ├── WebView2Loader.dll.lib │ │ └── WebView2LoaderStatic.lib │ │ └── x86 │ │ ├── WebView2Loader.dll │ │ ├── WebView2Loader.dll.lib │ │ └── WebView2LoaderStatic.lib ├── lib │ ├── net45 │ │ ├── Microsoft.Web.WebView2.Core.dll │ │ ├── Microsoft.Web.WebView2.Core.xml │ │ ├── Microsoft.Web.WebView2.WinForms.dll │ │ ├── Microsoft.Web.WebView2.WinForms.xml │ │ ├── Microsoft.Web.WebView2.Wpf.dll │ │ └── Microsoft.Web.WebView2.Wpf.xml │ └── netcoreapp3.0 │ │ ├── Microsoft.Web.WebView2.Core.dll │ │ ├── Microsoft.Web.WebView2.Core.xml │ │ ├── Microsoft.Web.WebView2.WinForms.dll │ │ ├── Microsoft.Web.WebView2.WinForms.xml │ │ ├── Microsoft.Web.WebView2.Wpf.dll │ │ └── Microsoft.Web.WebView2.Wpf.xml ├── runtimes │ ├── win-arm64 │ │ └── native │ │ │ └── WebView2Loader.dll │ ├── win-x64 │ │ └── native │ │ │ └── WebView2Loader.dll │ └── win-x86 │ │ └── native │ │ └── WebView2Loader.dll └── tools │ └── VisualStudioToolsManifest.xml └── winsdk ├── EventToken.h ├── windows.devices.haptics.h ├── windows.devices.input.h ├── windows.ui.core.h ├── windows.ui.h └── windows.ui.input.h /.gitattributes: -------------------------------------------------------------------------------- 1 | *.zig text=auto eol=lf 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | zig-cache/ 2 | zig-out/ 3 | .vscode/ 4 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | 2 | [submodule "vendor/args"] 3 | path = vendor/args 4 | url = https://github.com/MasterQ32/zig-args 5 | [submodule "vendor/serve"] 6 | path = vendor/serve 7 | url = https://github.com/MasterQ32/zig-serve 8 | -------------------------------------------------------------------------------- /.zpm/locals.zpm: -------------------------------------------------------------------------------- 1 | [apple_pie] 2 | file = ../vendor/apple_pie/src/apple_pie.zig 3 | 4 | [positron] 5 | file = ../src/positron.zig 6 | deps = apple_pie -------------------------------------------------------------------------------- /.zpm/pkgs.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | fn pkgRoot() []const u8 { 4 | return std.fs.path.dirname(@src().file) orelse "."; 5 | } 6 | 7 | pub const pkgs = struct { 8 | pub const args = std.build.Pkg{ 9 | .name = "args", 10 | .path = .{ .path = pkgRoot() ++ "/../vendor/args/args.zig" }, 11 | .dependencies = &[_]std.build.Pkg{}, 12 | }; 13 | pub const apple_pie = std.build.Pkg{ 14 | .name = "apple_pie", 15 | .path = .{ .path = pkgRoot() ++ "/../.zpm/../vendor/apple_pie/src/apple_pie.zig" }, 16 | .dependencies = &[_]std.build.Pkg{}, 17 | }; 18 | pub const positron = std.build.Pkg{ 19 | .name = "positron", 20 | .path = .{ .path = pkgRoot() ++ "/../.zpm/../src/positron.zig" }, 21 | .dependencies = &[_]std.build.Pkg{apple_pie}, 22 | }; 23 | }; 24 | 25 | pub const imports = struct { 26 | }; 27 | -------------------------------------------------------------------------------- /.zpm/zpm.conf: -------------------------------------------------------------------------------- 1 | # configures the path where the build.zig import file is generated. 2 | # the path is relative to this file. 3 | # pkgs-file = ./pkgs.zig -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ⚛️ Positron 2 | 3 | ![project logo](design/icon.svg) 4 | 5 | A Zig binding to the [webview](https://github.com/webview/webview) library. Make Zig applications with a nice HTML5 frontend a reality! 6 | 7 | ![](screenshots/i3-login.png) ![](screenshots/windows-chat.png) 8 | 9 | ## Usage 10 | 11 | ```zig 12 | //! src/minimal.zig 13 | 14 | const std = @import("std"); 15 | const wv = @import("positron"); 16 | 17 | pub fn main() !void { 18 | const view = try wv.View.create(false, null); 19 | defer view.destroy(); 20 | 21 | view.setTitle("Webview Example"); 22 | view.setSize(480, 320, .none); 23 | 24 | view.navigate("https://ziglang.org"); 25 | view.run(); 26 | } 27 | ``` 28 | 29 | ```zig 30 | //! build.zig 31 | 32 | const std = @import("std"); 33 | const pkgs = @import(".zpm/pkgs.zig"); 34 | const Sdk = @import("Sdk.zig"); 35 | 36 | pub fn build(b: *std.build.Builder) void { 37 | const target = b.standardTargetOptions(.{}); 38 | const mode = b.standardReleaseOptions(); 39 | 40 | const exe = b.addExecutable("demo", "src/minimal.zig"); 41 | exe.setTarget(target); 42 | exe.setBuildMode(mode); 43 | 44 | // Add and link the package. 45 | exe.addPackage(Sdk.getPackage("positron")); 46 | Sdk.linkPositron(exe, null); 47 | 48 | exe.install(); 49 | } 50 | ``` 51 | 52 | ## Example 53 | 54 | The example is a small, two-view chat application that transfers data bidirectionally between backend and frontend. 55 | 56 | Log in with `ziggy`/`love` and you can send messages, no real server there though! 57 | 58 | You can build the example with `zig build` and run it with `zig build run`. 59 | 60 | ## Building 61 | 62 | ### Linux 63 | 64 | Install `gtk-3` and `webkit2gtk`, then invoke `zig build`. 65 | 66 | ### Windows 67 | 68 | Download [Edge Dev Channel](https://www.microsoftedgeinsider.com/download), then invoke `zig build`. 69 | 70 | ### MacOS 71 | 72 | No research was done for the support on MacOS. Try with `zig build`. 73 | 74 | ## Contributing 75 | 76 | This library is in a early state and is very WIP. Still, feel free to contribute with PRs, or use it. Just don't assume a stable API. -------------------------------------------------------------------------------- /Sdk.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | pub const Backend = enum { 4 | gtk, 5 | cocoa, 6 | edge, 7 | }; 8 | 9 | fn sdkRoot() []const u8 { 10 | return std.fs.path.dirname(@src().file) orelse "."; 11 | } 12 | 13 | pub fn getPackage(name: []const u8) std.build.Pkg { 14 | const sdkPath = comptime sdkRoot(); 15 | return std.build.Pkg{ 16 | .name = name, 17 | .source = .{ .path = sdkPath ++ "/src/positron.zig" }, 18 | .dependencies = &[_]std.build.Pkg{ 19 | std.build.Pkg{ 20 | .name = "serve", 21 | .source = .{ .path = sdkPath ++ "/vendor/serve/src/serve.zig" }, 22 | .dependencies = &[_]std.build.Pkg{ 23 | std.build.Pkg{ 24 | .name = "uri", 25 | .source = .{ .path = sdkPath ++ "/vendor/serve/vendor/uri/uri.zig" }, 26 | }, 27 | std.build.Pkg{ 28 | .name = "network", 29 | .source = .{ .path = sdkPath ++ "/vendor/serve/vendor/network/network.zig" }, 30 | }, 31 | }, 32 | }, 33 | }, 34 | }; 35 | } 36 | 37 | /// Links positron to `exe`. `exe` must have its final `target` already set! 38 | /// `backend` selects the backend to be used, use `null` for a good default. 39 | pub fn linkPositron(exe: *std.build.LibExeObjStep, backend: ?Backend) void { 40 | exe.linkLibC(); 41 | exe.linkSystemLibrary("c++"); 42 | 43 | // make webview library standalone 44 | const sdkPath = comptime sdkRoot(); 45 | exe.addCSourceFile(sdkPath ++ "/src/wv/webview.cpp", &[_][]const u8{ 46 | "-std=c++17", 47 | "-fno-sanitize=undefined", 48 | }); 49 | 50 | if (exe.target.isWindows()) { 51 | 52 | // Attempts to fix windows building: 53 | exe.addIncludePath("vendor/winsdk"); 54 | 55 | exe.addIncludePath(sdkPath ++ "/vendor/Microsoft.Web.WebView2.1.0.902.49/build/native/include"); 56 | exe.addLibraryPath(sdkPath ++ "/vendor/Microsoft.Web.WebView2.1.0.902.49/build/native/x64"); 57 | exe.linkSystemLibrary("user32"); 58 | exe.linkSystemLibrary("ole32"); 59 | exe.linkSystemLibrary("oleaut32"); 60 | exe.addObjectFile(sdkPath ++ "/vendor/Microsoft.Web.WebView2.1.0.902.49/build/native/x64/WebView2Loader.dll.lib"); 61 | //exe.linkSystemLibrary("windowsapp"); 62 | } 63 | 64 | if (backend) |b| { 65 | switch (b) { 66 | .gtk => exe.defineCMacro("WEBVIEW_GTK", null), 67 | .cocoa => exe.defineCMacro("WEBVIEW_COCOA", null), 68 | .edge => exe.defineCMacro("WEBVIEW_EDGE", null), 69 | } 70 | } 71 | 72 | switch (exe.target.getOsTag()) { 73 | //# Windows (x64) 74 | //$ c++ main.cc -mwindows -L./dll/x64 -lwebview -lWebView2Loader -o webview-example.exe 75 | .windows => { 76 | exe.addLibraryPath(sdkPath ++ "/vendor/webview/dll/x64"); 77 | }, 78 | //# MacOS 79 | //$ c++ main.cc -std=c++11 -framework WebKit -o webview-example 80 | .macos => { 81 | exe.linkFramework("WebKit"); 82 | }, 83 | //# Linux 84 | //$ c++ main.cc `pkg-config --cflags --libs gtk+-3.0 webkit2gtk-4.0` -o webview-example 85 | .linux => { 86 | exe.linkSystemLibrary("gtk+-3.0"); 87 | exe.linkSystemLibrary("webkit2gtk-4.0"); 88 | }, 89 | else => std.debug.panic("unsupported os: {s}", .{std.meta.tagName(exe.target.getOsTag())}), 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /build.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const pkgs = @import(".zpm/pkgs.zig"); 3 | const Sdk = @import("Sdk.zig"); 4 | const ZigServe = @import("vendor/serve/build.zig"); 5 | 6 | pub fn build(b: *std.build.Builder) void { 7 | const target = b.standardTargetOptions(.{}); 8 | const mode = b.standardReleaseOptions(); 9 | const backend = b.option(Sdk.Backend, "backend", "Configures the backend that should be used for webview."); 10 | 11 | const wolfssl = ZigServe.createWolfSSL(b, target); 12 | 13 | const minimal_exe = b.addExecutable("positron-minimal", "example/minimal.zig"); 14 | minimal_exe.setTarget(target); 15 | minimal_exe.setBuildMode(mode); 16 | minimal_exe.linkLibrary(wolfssl); 17 | minimal_exe.addIncludePath("vendor/serve/vendor/wolfssl"); 18 | 19 | minimal_exe.addPackage(Sdk.getPackage("positron")); 20 | Sdk.linkPositron(minimal_exe, null); 21 | 22 | minimal_exe.install(); 23 | 24 | const exe = b.addExecutable("positron-demo", "example/main.zig"); 25 | exe.linkLibrary(wolfssl); 26 | exe.addIncludePath("vendor/serve/vendor/wolfssl"); 27 | exe.setTarget(target); 28 | 29 | exe.setBuildMode(mode); 30 | 31 | Sdk.linkPositron(exe, backend); 32 | exe.addPackage(Sdk.getPackage("positron")); 33 | 34 | exe.install(); 35 | 36 | const positron_test = b.addTest("src/positron.zig"); 37 | 38 | Sdk.linkPositron(positron_test, null); 39 | positron_test.addPackage(Sdk.getPackage("positron")); 40 | 41 | const test_step = b.step("test", "Runs the test suite"); 42 | 43 | test_step.dependOn(&positron_test.step); 44 | 45 | const run_cmd = exe.run(); 46 | 47 | run_cmd.step.dependOn(b.getInstallStep()); 48 | 49 | if (b.args) |args| { 50 | run_cmd.addArgs(args); 51 | } 52 | 53 | const run_step = b.step("run", "Run the app"); 54 | 55 | run_step.dependOn(&run_cmd.step); 56 | 57 | // const demo = b.addExecutable("webview-demo", null); 58 | 59 | // // make webview library standalone 60 | // demo.addCSourceFile("src/minimal.cpp", &[_][]const u8{ 61 | // "-std=c++17", 62 | // "-fno-sanitize=undefined", 63 | // }); 64 | // demo.linkLibC(); 65 | // demo.linkSystemLibrary("c++"); 66 | // demo.install(); 67 | 68 | // demo.addIncludeDir("vendor/Microsoft.Web.WebView2.1.0.902.49/build/native/include"); 69 | // demo.addLibPath("vendor/Microsoft.Web.WebView2.1.0.902.49/build/native/x64"); 70 | // demo.linkSystemLibrary("user32"); 71 | // demo.linkSystemLibrary("ole32"); 72 | // demo.linkSystemLibrary("oleaut32"); 73 | // demo.addObjectFile("vendor/Microsoft.Web.WebView2.1.0.902.49/build/native/x64/WebView2Loader.dll.lib"); 74 | 75 | // const exec = demo.run(); 76 | // exec.step.dependOn(b.getInstallStep()); 77 | 78 | // const demo_run_step = b.step("run.demo", "Run the app"); 79 | // demo_run_step.dependOn(&exec.step); 80 | } 81 | -------------------------------------------------------------------------------- /design/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 35 | 37 | 45 | 50 | 51 | 52 | 56 | 63 | 66 | 70 | 72 | 76 | 81 | 84 | 85 | 86 | 89 | 93 | 98 | 101 | 102 | 103 | 104 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /design/social-preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ziglibs/positron/f144d53ba4ad9e88e5883212ead13e8c1f7ad234/design/social-preview.png -------------------------------------------------------------------------------- /design/social-preview.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ziglibs/positron/f144d53ba4ad9e88e5883212ead13e8c1f7ad234/design/social-preview.xcf -------------------------------------------------------------------------------- /example/app.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 55 | 144 | 145 | 146 | 147 |
Zig Chat
148 |
149 | 156 |
157 |
158 | 159 | 160 |
161 | 162 | 163 | 164 | 165 | 166 | 167 | -------------------------------------------------------------------------------- /example/design.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | } 4 | 5 | html { 6 | background-color: #444; 7 | color: #FFF; 8 | padding: 0; 9 | margin: 0; 10 | } 11 | 12 | body { 13 | position: fixed; 14 | left: 0; 15 | top: 0; 16 | width: 100%; 17 | height: 100%; 18 | 19 | display: flex; 20 | flex-direction: column; 21 | 22 | padding: 0; 23 | margin: 0; 24 | 25 | font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif; 26 | } 27 | 28 | .panel.top { 29 | display: block; 30 | width: 100%; 31 | height: 4rem; 32 | border-bottom: 1px solid white; 33 | } 34 | 35 | .panel.center { 36 | display: block; 37 | width: 100%; 38 | overflow-y: scroll; 39 | flex: 1; 40 | } 41 | 42 | .panel.bottom { 43 | width: 100%; 44 | height: 4rem; 45 | border-top: 1px solid white; 46 | } 47 | 48 | .panel.header { 49 | text-align: center; 50 | font-weight: bold; 51 | font-size: 2rem; 52 | line-height: 4rem; 53 | background-color: cornflowerblue; 54 | } 55 | 56 | .panel.padded { 57 | padding: 1rem; 58 | } -------------------------------------------------------------------------------- /example/login.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 51 | 84 | 85 | 86 | 87 |
Login
88 |
89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 |
User Name:
Password:
102 |
103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /example/main.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const wv = @import("positron"); 3 | 4 | const App = struct { 5 | arena: std.heap.ArenaAllocator, 6 | provider: *wv.Provider, 7 | view: *wv.View, 8 | 9 | user_name: ?[]const u8, 10 | 11 | shutdown_thread: u32, 12 | 13 | pub fn getWebView(app: *App) *wv.View { 14 | return app.view; 15 | } 16 | }; 17 | 18 | pub fn main() !void { 19 | var app = App{ 20 | .arena = std.heap.ArenaAllocator.init(std.heap.c_allocator), 21 | .provider = undefined, 22 | .view = undefined, 23 | 24 | .user_name = null, 25 | .shutdown_thread = 0, 26 | }; 27 | 28 | app.provider = try wv.Provider.create(std.heap.c_allocator); 29 | defer app.provider.destroy(); 30 | 31 | std.log.info("base uri: {s}", .{app.provider.base_url}); 32 | 33 | try app.provider.addContent("/login.htm", "text/html", @embedFile("login.htm")); 34 | try app.provider.addContent("/app.htm", "text/html", @embedFile("app.htm")); 35 | try app.provider.addContent("/design.css", "text/css", @embedFile("design.css")); 36 | 37 | const provide_thread = try std.Thread.spawn(.{}, wv.Provider.run, .{app.provider}); 38 | provide_thread.detach(); 39 | 40 | std.log.info("provider ready.", .{}); 41 | 42 | app.view = try wv.View.create((@import("builtin").mode == .Debug), null); 43 | defer app.view.destroy(); 44 | 45 | app.view.setTitle("Zig Chat"); 46 | app.view.setSize(400, 550, .fixed); 47 | 48 | app.view.bind("performLogin", performLogin, &app); 49 | app.view.bind("sendMessage", sendMessage, &app); 50 | 51 | app.view.navigate(app.provider.getUri("/login.htm") orelse unreachable); 52 | 53 | std.log.info("webview ready.", .{}); 54 | 55 | // must be started here, as we may have a race condition otherwise 56 | const fake_thread = try std.Thread.spawn(.{}, sendRandomMessagesInBackground, .{&app}); 57 | defer fake_thread.join(); 58 | 59 | std.log.info("start.", .{}); 60 | 61 | app.view.run(); 62 | 63 | @atomicStore(u32, &app.shutdown_thread, 1, .SeqCst); 64 | } 65 | 66 | fn performLogin(app: *App, user_name: []const u8, password: []const u8) !bool { 67 | if (!std.mem.eql(u8, user_name, "ziggy")) 68 | return false; 69 | if (!std.mem.eql(u8, password, "love")) 70 | return false; 71 | 72 | app.user_name = try app.arena.allocator().dupe(u8, user_name); 73 | 74 | app.view.navigate(app.provider.getUri("/app.htm") orelse unreachable); 75 | 76 | return true; 77 | } 78 | 79 | const Message = struct { 80 | sender: []const u8, 81 | timestamp: i64, 82 | content: []const u8, 83 | }; 84 | 85 | fn sendMessage(app: *App, message_text: []const u8) !void { 86 | std.debug.assert(app.user_name != null); 87 | 88 | const message = Message{ 89 | .sender = app.user_name.?, 90 | .timestamp = std.time.milliTimestamp(), 91 | .content = message_text, 92 | }; 93 | 94 | try appendMessage(app, message); 95 | } 96 | 97 | fn appendMessage(app: *App, message: Message) !void { 98 | var dynamic_buffer = std.ArrayList(u8).init(std.heap.c_allocator); 99 | defer dynamic_buffer.deinit(); 100 | 101 | const writer = dynamic_buffer.writer(); 102 | 103 | try writer.writeAll("appendMessage("); 104 | try std.json.stringify(message, .{}, writer); 105 | try writer.writeAll(");"); 106 | 107 | try dynamic_buffer.append(0); // nul terminator 108 | 109 | app.view.eval(dynamic_buffer.items[0 .. dynamic_buffer.items.len - 1 :0]); 110 | } 111 | 112 | fn sendRandomMessagesInBackground(app: *App) !void { 113 | var random = std.rand.DefaultPrng.init(@ptrToInt(&app)); 114 | const rng = random.random(); 115 | while (true) { 116 | const time_seconds = 1.5 + 5.5 * rng.float(f32); 117 | 118 | const ns = @floatToInt(u64, std.time.ns_per_s * time_seconds); 119 | 120 | std.time.sleep(ns); 121 | 122 | if (@atomicLoad(u32, &app.shutdown_thread, .SeqCst) != 0) { 123 | std.log.info("Disable Faker Thread", .{}); 124 | @atomicStore(u32, &app.shutdown_thread, 2, .SeqCst); 125 | return; 126 | } 127 | 128 | try appendMessage(app, Message{ 129 | .sender = senders[rng.intRangeLessThan(usize, 0, senders.len)], 130 | .timestamp = std.time.milliTimestamp(), 131 | .content = messages[rng.intRangeLessThan(usize, 0, messages.len)], 132 | }); 133 | } 134 | } 135 | 136 | const senders = [_][]const u8{ 137 | "Your mom", 138 | "xq", 139 | "mattnite", 140 | "Sobeston", 141 | "Aurame", 142 | "fengb", 143 | "Luuk", 144 | "MasterQ32", 145 | "Tater", 146 | }; 147 | 148 | const messages = [_][]const u8{ 149 | "hi", 150 | "what's up?", 151 | "I love zig!", 152 | "How do i exit vim?", 153 | "I finally finished my project", 154 | "I'm 90% through the code base!", 155 | "Where is the documentation for the Zig standard library?", 156 | "Why does Zig force me to use spaces instead of tabs?", 157 | "Why does zig fmt have no configuration options?", 158 | "Why is switching on []u8 (strings) not supported?", 159 | "vim is better than emacs!", 160 | "emacs is better than vim!", 161 | "btw, i use Arch Linux!", 162 | "… joined the channel!", 163 | "Как установить компилятор?", 164 | "Где я нахожу искусство Игуаны?", 165 | "私は安いリッピングを売っています。 禅に従ってください!", 166 | "How do I shot web?", 167 | "No, no, he got a point!", 168 | "I finally got my 5G shot. Now i can see the truth!", 169 | "Who do I contact when i want to donate 50k to the ZSF?", 170 | "Never Gonna Give You Up is actually nice", 171 | "All your base are belong to us", 172 | "Somebody set up us the bomb.", 173 | "Main screen turn on.", 174 | "You have no chance to survive make your time.", 175 | "Move 'ZIG'.", 176 | "For great justice.", 177 | "The cake is a lie.", 178 | "Rewrite it in Zig!", 179 | }; 180 | -------------------------------------------------------------------------------- /example/minimal.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const wv = @import("positron"); 3 | 4 | pub fn main() !void { 5 | const view = try wv.View.create(false, null); 6 | defer view.destroy(); 7 | 8 | view.setTitle("Webview Example"); 9 | view.setSize(480, 320, .none); 10 | 11 | view.navigate("https://ziglang.org"); 12 | view.run(); 13 | } 14 | -------------------------------------------------------------------------------- /screenshots/i3-chat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ziglibs/positron/f144d53ba4ad9e88e5883212ead13e8c1f7ad234/screenshots/i3-chat.png -------------------------------------------------------------------------------- /screenshots/i3-login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ziglibs/positron/f144d53ba4ad9e88e5883212ead13e8c1f7ad234/screenshots/i3-login.png -------------------------------------------------------------------------------- /screenshots/windows-chat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ziglibs/positron/f144d53ba4ad9e88e5883212ead13e8c1f7ad234/screenshots/windows-chat.png -------------------------------------------------------------------------------- /screenshots/windows-login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ziglibs/positron/f144d53ba4ad9e88e5883212ead13e8c1f7ad234/screenshots/windows-login.png -------------------------------------------------------------------------------- /src/minimal.cpp: -------------------------------------------------------------------------------- 1 | 2 | #define UNICODE 3 | #define WIN32_LEAN_AND_MEAN 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | #include 13 | 14 | extern "C" WINUSERAPI BOOL WINAPI SetProcessDpiAwarenessContext( 15 | _In_ DPI_AWARENESS_CONTEXT value); 16 | 17 | using msg_cb_t = std::function; 18 | 19 | static LRESULT wndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) 20 | { 21 | // auto w = (browser_engine *)GetWindowLongPtr(hwnd, GWLP_USERDATA); 22 | switch (msg) 23 | { 24 | case WM_SIZE: 25 | //if (w->m_browser) 26 | // w->m_browser->resize(hwnd); 27 | break; 28 | case WM_CLOSE: 29 | DestroyWindow(hwnd); 30 | break; 31 | case WM_DESTROY: 32 | // w->terminate(); 33 | break; 34 | //case WM_GETMINMAXINFO: 35 | //{ 36 | // auto lpmmi = (LPMINMAXINFO)lp; 37 | // if (w == nullptr) 38 | // { 39 | // return 0; 40 | // } 41 | // if (w->m_maxsz.x > 0 && w->m_maxsz.y > 0) 42 | // { 43 | // lpmmi->ptMaxSize = w->m_maxsz; 44 | // lpmmi->ptMaxTrackSize = w->m_maxsz; 45 | // } 46 | // if (w->m_minsz.x > 0 && w->m_minsz.y > 0) 47 | // { 48 | // lpmmi->ptMinTrackSize = w->m_minsz; 49 | // } 50 | //} 51 | //break; 52 | default: 53 | return DefWindowProc(hwnd, msg, wp, lp); 54 | } 55 | return 0; 56 | } 57 | 58 | class webview2_com_handler 59 | : public ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler, 60 | public ICoreWebView2CreateCoreWebView2ControllerCompletedHandler, 61 | public ICoreWebView2WebMessageReceivedEventHandler, 62 | public ICoreWebView2PermissionRequestedEventHandler 63 | { 64 | using webview2_com_handler_cb_t = 65 | std::function; 66 | 67 | public: 68 | webview2_com_handler(HWND hwnd, msg_cb_t msgCb, 69 | webview2_com_handler_cb_t cb) 70 | : m_window(hwnd), m_msgCb(msgCb), m_cb(cb) {} 71 | ULONG STDMETHODCALLTYPE AddRef() { return 1; } 72 | ULONG STDMETHODCALLTYPE Release() { return 1; } 73 | HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID *ppv) 74 | { 75 | return S_OK; 76 | } 77 | HRESULT STDMETHODCALLTYPE Invoke(HRESULT res, 78 | ICoreWebView2Environment *env) 79 | { 80 | printf("created env: 0x%X, env=%p, hwnd=%p\n", res, env, m_window); 81 | env->CreateCoreWebView2Controller(m_window, this); 82 | return S_OK; 83 | } 84 | HRESULT STDMETHODCALLTYPE Invoke(HRESULT res, 85 | ICoreWebView2Controller *controller) 86 | { 87 | printf("hresult: 0x%X, controller=%p\n", res, controller); 88 | if (controller != nullptr) 89 | { 90 | controller->AddRef(); 91 | 92 | ICoreWebView2 *webview; 93 | ::EventRegistrationToken token; 94 | controller->get_CoreWebView2(&webview); 95 | webview->add_WebMessageReceived(this, &token); 96 | webview->add_PermissionRequested(this, &token); 97 | 98 | ICoreWebView2Settings *Settings; 99 | webview->get_Settings(&Settings); 100 | Settings->put_IsScriptEnabled(TRUE); 101 | Settings->put_AreDefaultScriptDialogsEnabled(TRUE); 102 | Settings->put_IsWebMessageEnabled(TRUE); 103 | 104 | webview->Navigate(L"https://ziglang.org"); 105 | } 106 | m_cb(controller); 107 | return S_OK; 108 | } 109 | HRESULT STDMETHODCALLTYPE Invoke( 110 | ICoreWebView2 *sender, ICoreWebView2WebMessageReceivedEventArgs *args) 111 | { 112 | LPWSTR message; 113 | args->TryGetWebMessageAsString(&message); 114 | 115 | char long_buffer[4096]; 116 | sprintf_s(long_buffer, sizeof long_buffer, "%ls", message); 117 | 118 | m_msgCb(long_buffer); 119 | 120 | sender->PostWebMessageAsString(message); 121 | 122 | CoTaskMemFree(message); 123 | return S_OK; 124 | } 125 | HRESULT STDMETHODCALLTYPE 126 | Invoke(ICoreWebView2 *sender, 127 | ICoreWebView2PermissionRequestedEventArgs *args) 128 | { 129 | COREWEBVIEW2_PERMISSION_KIND kind; 130 | args->get_PermissionKind(&kind); 131 | if (kind == COREWEBVIEW2_PERMISSION_KIND_CLIPBOARD_READ) 132 | { 133 | args->put_State(COREWEBVIEW2_PERMISSION_STATE_ALLOW); 134 | } 135 | return S_OK; 136 | } 137 | 138 | private: 139 | HWND m_window; 140 | msg_cb_t m_msgCb; 141 | webview2_com_handler_cb_t m_cb; 142 | }; 143 | 144 | int main() 145 | { 146 | HINSTANCE hInstance = GetModuleHandle(nullptr); 147 | HICON icon = (HICON)LoadImage( 148 | hInstance, IDI_APPLICATION, IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), 149 | GetSystemMetrics(SM_CYSMICON), LR_DEFAULTCOLOR); 150 | 151 | WNDCLASSEX wc; 152 | ZeroMemory(&wc, sizeof(WNDCLASSEX)); 153 | wc.cbSize = sizeof(WNDCLASSEX); 154 | wc.hInstance = hInstance; 155 | wc.lpszClassName = L"webview"; 156 | wc.hIcon = icon; 157 | wc.hIconSm = icon; 158 | wc.lpfnWndProc = wndProc; 159 | RegisterClassEx(&wc); 160 | HWND window = CreateWindowExW(0L, L"webview", L"Title", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 161 | CW_USEDEFAULT, 640, 480, nullptr, nullptr, 162 | GetModuleHandle(nullptr), nullptr); 163 | // SetWindowLongPtr(m_window, GWLP_USERDATA, (LONG_PTR)this); 164 | 165 | SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE); 166 | ShowWindow(window, SW_SHOW); 167 | UpdateWindow(window); 168 | SetFocus(window); 169 | 170 | CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); 171 | std::atomic_flag flag = ATOMIC_FLAG_INIT; 172 | flag.test_and_set(); 173 | 174 | wchar_t currentExePath[MAX_PATH]; 175 | GetModuleFileNameW(NULL, currentExePath, MAX_PATH); 176 | // char *currentExeName = PathFindFileNameA(currentExePath); 177 | 178 | wchar_t userDataFolderBit[MAX_PATH]; 179 | GetEnvironmentVariableW(L"APPDATA", userDataFolderBit, MAX_PATH); 180 | 181 | wchar_t folder[MAX_PATH]; 182 | wcscpy(folder, userDataFolderBit); 183 | wcscat(folder, L"/"); 184 | wcscat(folder, L"ZigWebViewDemo"); 185 | 186 | wprintf(L"folder = %ls\n", folder); 187 | 188 | ICoreWebView2Controller *m_controller = nullptr; 189 | ICoreWebView2 *m_webview = nullptr; 190 | 191 | auto handler = new webview2_com_handler( 192 | window, [](std::string s) 193 | { printf("cb(%s)\n", s.c_str()); }, 194 | [&](ICoreWebView2Controller *controller) 195 | { 196 | printf("ready %p\n", controller); 197 | m_controller = controller; 198 | if (m_controller != nullptr) 199 | { 200 | m_controller->get_CoreWebView2(&m_webview); 201 | m_webview->AddRef(); 202 | } 203 | flag.clear(); 204 | }); 205 | wchar_t const *root = 206 | //L"Z:\\Temp\\positron\\vendor\\Microsoft.WebView2.FixedVersionRuntime.91.0.864.71.x64"; 207 | //L"Z:\\Temp\\positron\\vendor\\Microsoft.WebView2.FixedVersionRuntime.92.0.902.62.x64"; 208 | //L"C:\\Program Files (x86)\\Microsoft\\Edge Dev\\Application\\93.0.961.10"; 209 | nullptr; 210 | HRESULT res = CreateCoreWebView2EnvironmentWithOptions( 211 | root, folder, nullptr, handler); 212 | if (res != S_OK) 213 | { 214 | CoUninitialize(); 215 | if (res == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) 216 | { 217 | printf("File not found?\n"); 218 | } 219 | printf("failed to create webview: 0x%X!\n", res); 220 | return 1; 221 | } 222 | { 223 | MSG msg = {}; 224 | while (flag.test_and_set() && GetMessage(&msg, NULL, 0, 0)) 225 | { 226 | TranslateMessage(&msg); 227 | DispatchMessage(&msg); 228 | } 229 | } 230 | if (m_controller == nullptr) 231 | return 1; 232 | // init("window.external={invoke:s=>window.chrome.webview.postMessage(s)}"); 233 | 234 | printf("Navigate() => 0x%X\n", m_webview->Navigate(L"https://ziglang.org")); 235 | 236 | RECT bounds; 237 | GetClientRect(window, &bounds); 238 | m_controller->put_Bounds(bounds); 239 | 240 | m_controller->get_Bounds(&bounds); 241 | printf("%d %d %d %d\n", bounds.left, bounds.top, bounds.right, bounds.bottom); 242 | 243 | { 244 | MSG msg; 245 | BOOL res; 246 | while ((res = GetMessage(&msg, nullptr, 0, 0)) != -1) 247 | { 248 | if (msg.hwnd) 249 | { 250 | TranslateMessage(&msg); 251 | DispatchMessage(&msg); 252 | continue; 253 | } 254 | if (msg.message == WM_APP) 255 | { 256 | // auto f = (dispatch_fn_t *)(msg.lParam); 257 | // (*f)(); 258 | // delete f; 259 | } 260 | else if (msg.message == WM_QUIT) 261 | { 262 | return 0; 263 | } 264 | } 265 | } 266 | 267 | return 0; 268 | } -------------------------------------------------------------------------------- /src/positron.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | const zig_serve = @import("serve"); 4 | 5 | /// A web browser window that one can interact with. 6 | /// Uses a JSON RPC solution to talk to the browser window. 7 | pub const View = opaque { 8 | const Self = @This(); 9 | 10 | /// Creates a new webview instance. If `allow_debug` is set - developer tools will 11 | /// be enabled (if the platform supports them). `parent_window` parameter can be a 12 | /// pointer to the native window handle. If it's non-null - then child WebView 13 | /// is embedded into the given parent window. Otherwise a new window is created. 14 | /// Depending on the platform, a GtkWindow, NSWindow or HWND pointer can be 15 | /// passed here. 16 | pub fn create(allow_debug: bool, parent_window: ?*anyopaque) !*Self { 17 | return webview_create(@boolToInt(allow_debug), parent_window) orelse return error.WebviewError; 18 | } 19 | 20 | /// Destroys a webview and closes the native window. 21 | pub fn destroy(self: *Self) void { 22 | webview_destroy(self); 23 | } 24 | 25 | /// Runs the main loop until it's terminated. After this function exits - you 26 | /// must destroy the webview. 27 | pub fn run(self: *Self) void { 28 | webview_run(self); 29 | } 30 | 31 | /// Stops the main loop. It is safe to call this function from another other 32 | /// background thread. 33 | pub fn terminate(self: *Self) void { 34 | webview_terminate(self); 35 | } 36 | 37 | /// Posts a function to be executed on the main thread. You normally do not need 38 | /// to call this function, unless you want to tweak the native window. 39 | pub fn dispatch() void { 40 | // extern fn webview_dispatch(w: *WebView, func: ?fn (*WebView, ?*c_void) callconv(.C) void, arg: ?*c_void) void; 41 | } 42 | 43 | // Returns a native window handle pointer. When using GTK backend the pointer 44 | // is GtkWindow pointer, when using Cocoa backend the pointer is NSWindow 45 | // pointer, when using Win32 backend the pointer is HWND pointer. 46 | pub fn getWindow(self: *Self) *anyopaque { 47 | return webview_get_window(self) orelse @panic("missing native window!"); 48 | } 49 | 50 | /// Updates the title of the native window. Must be called from the UI thread. 51 | pub fn setTitle(self: *Self, title: [:0]const u8) void { 52 | webview_set_title(self, title.ptr); 53 | } 54 | 55 | /// Updates native window size. 56 | pub fn setSize(self: *Self, width: u16, height: u16, hint: SizeHint) void { 57 | webview_set_size(self, width, height, @enumToInt(hint)); 58 | } 59 | 60 | /// Navigates webview to the given URL. URL may be a data URI, i.e. 61 | /// `data:text/text,...`. It is often ok not to url-encode it 62 | /// properly, webview will re-encode it for you. 63 | pub fn navigate(self: *Self, url: [:0]const u8) void { 64 | webview_navigate(self, url.ptr); 65 | } 66 | 67 | /// Injects JavaScript code at the initialization of the new page. Every time 68 | /// the webview will open a the new page - this initialization code will be 69 | /// executed. It is guaranteed that code is executed before window.onload. 70 | pub fn init(self: *Self, js: [:0]const u8) void { 71 | webview_init(self, js.ptr); 72 | } 73 | 74 | /// Evaluates arbitrary JavaScript code. Evaluation happens asynchronously, also 75 | /// the result of the expression is ignored. Use RPC bindings if you want to 76 | /// receive notifications about the results of the evaluation. 77 | pub fn eval(self: *Self, js: [:0]const u8) void { 78 | webview_eval(self, js.ptr); 79 | } 80 | 81 | /// Binds a callback so that it will appear under the given name as a 82 | /// global JavaScript function. Internally it uses webview_init(). Callback 83 | /// receives a request string and a user-provided argument pointer. Request 84 | /// string is a JSON array of all the arguments passed to the JavaScript 85 | /// function. 86 | pub fn bindRaw(self: *Self, name: [:0]const u8, context: anytype, comptime callback: fn (ctx: @TypeOf(context), seq: [:0]const u8, req: [:0]const u8) void) void { 87 | const Context = @TypeOf(context); 88 | const Binder = struct { 89 | fn c_callback(seq: [*c]const u8, req: [*c]const u8, arg: ?*anyopaque) callconv(.C) void { 90 | callback( 91 | @ptrCast(Context, arg), 92 | std.mem.sliceTo(seq, 0), 93 | std.mem.sliceTo(req, 0), 94 | ); 95 | } 96 | }; 97 | 98 | webview_bind(self, name.ptr, Binder.c_callback, context); 99 | } 100 | 101 | /// Binds a callback so that it will appear under the given name as a 102 | /// global JavaScript function. The callback will be called with `context` as the first parameter, 103 | /// all other parameters must be deserializable to JSON. The return value might be a error union, 104 | /// in which case the error is returned to the JS promise. Otherwise, a normal result is serialized to 105 | /// JSON and then sent back to JS. 106 | pub fn bind(self: *Self, name: [:0]const u8, comptime callback: anytype, context: @typeInfo(@TypeOf(callback)).Fn.args[0].arg_type.?) void { 107 | const Fn = @TypeOf(callback); 108 | const function_info: std.builtin.TypeInfo.Fn = @typeInfo(Fn).Fn; 109 | 110 | if (function_info.args.len < 1) 111 | @compileError("Function must take at least the context argument!"); 112 | 113 | const ReturnType = function_info.return_type orelse @compileError("Function must be non-generic!"); 114 | const return_info: std.builtin.TypeInfo = @typeInfo(ReturnType); 115 | 116 | const Context = @TypeOf(context); 117 | 118 | const Binder = struct { 119 | fn getWebView(ctx: Context) *Self { 120 | if (Context == *Self) 121 | return ctx; 122 | return ctx.getWebView(); 123 | } 124 | 125 | fn expectArrayStart(stream: *std.json.TokenStream) !void { 126 | const tok = (try stream.next()) orelse return error.InvalidJson; 127 | if (tok != .ArrayBegin) 128 | return error.InvalidJson; 129 | } 130 | 131 | fn expectArrayEnd(stream: *std.json.TokenStream) !void { 132 | const tok = (try stream.next()) orelse return error.InvalidJson; 133 | if (tok != .ArrayEnd) 134 | return error.InvalidJson; 135 | } 136 | 137 | fn errorResponse(view: *Self, seq: [:0]const u8, err: anyerror) void { 138 | var buffer: [64]u8 = undefined; 139 | const err_str = std.fmt.bufPrint(&buffer, "\"{s}\"\x00", .{@errorName(err)}) catch @panic("error name too long!"); 140 | 141 | view.@"return"(seq, .{ .failure = err_str[0 .. err_str.len - 1 :0] }); 142 | } 143 | 144 | fn successResponse(view: *Self, seq: [:0]const u8, value: anytype) void { 145 | if (@TypeOf(value) != void) { 146 | var buf = std.ArrayList(u8).init(std.heap.c_allocator); 147 | defer buf.deinit(); 148 | 149 | std.json.stringify(value, .{}, buf.writer()) catch |err| { 150 | return errorResponse(view, seq, err); 151 | }; 152 | 153 | buf.append(0) catch |err| { 154 | return errorResponse(view, seq, err); 155 | }; 156 | 157 | const str = buf.items; 158 | 159 | view.@"return"(seq, .{ .success = str[0 .. str.len - 1 :0] }); 160 | } else { 161 | view.@"return"(seq, .{ .success = "" }); 162 | } 163 | } 164 | 165 | fn c_callback(seq0: [*c]const u8, req0: [*c]const u8, arg: ?*anyopaque) callconv(.C) void { 166 | const cb_context = @ptrCast(Context, @alignCast(@alignOf(std.meta.Child(Context)), arg)); 167 | 168 | const view = getWebView(cb_context); 169 | 170 | const seq = std.mem.sliceTo(seq0, 0); 171 | const req = std.mem.sliceTo(req0, 0); 172 | 173 | // std.log.info("invocation: {*} seq={s} req={s}", .{ 174 | // view, seq, req, 175 | // }); 176 | 177 | const ArgType = std.meta.ArgsTuple(Fn); 178 | 179 | var arena = std.heap.ArenaAllocator.init(std.heap.c_allocator); 180 | defer arena.deinit(); 181 | 182 | var parsed_args: ArgType = undefined; 183 | parsed_args[0] = cb_context; 184 | 185 | var json_parser = std.json.TokenStream.init(req); 186 | { 187 | expectArrayStart(&json_parser) catch |err| { 188 | std.log.err("parser start: {}", .{err}); 189 | return errorResponse(view, seq, err); 190 | }; 191 | 192 | comptime var i = 1; 193 | inline while (i < function_info.args.len) : (i += 1) { 194 | const Type = @TypeOf(parsed_args[i]); 195 | parsed_args[i] = std.json.parse(Type, &json_parser, .{ 196 | .allocator = arena.allocator(), 197 | .duplicate_field_behavior = .UseFirst, 198 | .ignore_unknown_fields = false, 199 | .allow_trailing_data = true, 200 | }) catch |err| { 201 | if (@errorReturnTrace()) |trace| 202 | std.debug.dumpStackTrace(trace.*); 203 | std.log.err("parsing argument {d}: {}", .{ i, err }); 204 | return errorResponse(view, seq, err); 205 | }; 206 | } 207 | 208 | expectArrayEnd(&json_parser) catch |err| { 209 | std.log.err("parser end: {}", .{err}); 210 | return errorResponse(view, seq, err); 211 | }; 212 | } 213 | 214 | const result = @call(.{}, callback, parsed_args); 215 | 216 | // std.debug.print("result: {}\n", .{result}); 217 | 218 | if (return_info == .ErrorUnion) { 219 | if (result) |value| { 220 | return successResponse(view, seq, value); 221 | } else |err| { 222 | return errorResponse(view, seq, err); 223 | } 224 | } else { 225 | successResponse(view, seq, result); 226 | } 227 | } 228 | }; 229 | 230 | webview_bind(self, name.ptr, Binder.c_callback, context); 231 | } 232 | 233 | /// Allows to return a value from the native binding. Original request pointer 234 | /// must be provided to help internal RPC engine match requests with responses. 235 | /// If status is zero - result is expected to be a valid JSON result value. 236 | /// If status is not zero - result is an error JSON object. 237 | pub fn @"return"(self: *Self, seq: [:0]const u8, result: ReturnValue) void { 238 | switch (result) { 239 | .success => |res_text| webview_return(self, seq.ptr, 0, res_text.ptr), 240 | .failure => |res_text| webview_return(self, seq.ptr, 1, res_text.ptr), 241 | } 242 | } 243 | 244 | // C Binding: 245 | 246 | extern fn webview_create(debug: c_int, window: ?*anyopaque) ?*Self; 247 | extern fn webview_destroy(w: *Self) void; 248 | extern fn webview_run(w: *Self) void; 249 | extern fn webview_terminate(w: *Self) void; 250 | extern fn webview_dispatch(w: *Self, func: ?fn (*Self, ?*anyopaque) callconv(.C) void, arg: ?*anyopaque) void; 251 | extern fn webview_get_window(w: *Self) ?*anyopaque; 252 | extern fn webview_set_title(w: *Self, title: [*:0]const u8) void; 253 | extern fn webview_set_size(w: *Self, width: c_int, height: c_int, hints: c_int) void; 254 | extern fn webview_navigate(w: *Self, url: [*:0]const u8) void; 255 | extern fn webview_init(w: *Self, js: [*:0]const u8) void; 256 | extern fn webview_eval(w: *Self, js: [*:0]const u8) void; 257 | extern fn webview_bind(w: *Self, name: [*:0]const u8, func: ?fn ([*c]const u8, [*c]const u8, ?*anyopaque) callconv(.C) void, arg: ?*anyopaque) void; 258 | extern fn webview_return(w: *Self, seq: [*:0]const u8, status: c_int, result: [*c]const u8) void; 259 | }; 260 | 261 | pub const SizeHint = enum(c_int) { 262 | /// Width and height are default size 263 | none = 0, 264 | /// Width and height are minimum bounds 265 | min = 1, 266 | /// Width and height are maximum bounds 267 | max = 2, 268 | /// Window size can not be changed by a user 269 | fixed = 3, 270 | }; 271 | 272 | pub const ReturnValue = union(enum) { 273 | success: [:0]const u8, 274 | failure: [:0]const u8, 275 | }; 276 | 277 | test { 278 | _ = View.create; 279 | _ = View.destroy; 280 | _ = View.run; 281 | _ = View.terminate; 282 | _ = View.dispatch; 283 | _ = View.getWindow; 284 | _ = View.setTitle; 285 | _ = View.setSize; 286 | _ = View.navigate; 287 | _ = View.init; 288 | _ = View.eval; 289 | _ = View.bind; 290 | _ = View.bindRaw; 291 | _ = View.@"return"; 292 | } 293 | 294 | pub const Provider = struct { 295 | const Self = @This(); 296 | 297 | const Route = struct { 298 | const Error = error{OutOfMemory} || zig_serve.HttpResponse.WriteError; 299 | 300 | const GenericPointer = opaque {}; 301 | const RouteHandler = fn (*Provider, *Route, *zig_serve.HttpContext) Error!void; 302 | 303 | arena: std.heap.ArenaAllocator, 304 | prefix: [:0]const u8, 305 | 306 | handler: RouteHandler, 307 | context: *GenericPointer, 308 | pub fn getContext(self: Route, comptime T: type) *T { 309 | return @ptrCast(*T, @alignCast(@alignOf(T), self.context)); 310 | } 311 | 312 | pub fn deinit(self: *Route) void { 313 | self.arena.deinit(); 314 | self.* = undefined; 315 | } 316 | }; 317 | 318 | allocator: std.mem.Allocator, 319 | server: zig_serve.HttpListener, 320 | base_url: []const u8, 321 | 322 | routes: std.ArrayList(Route), 323 | 324 | pub fn create(allocator: std.mem.Allocator) !*Self { 325 | const provider = try allocator.create(Self); 326 | errdefer allocator.destroy(provider); 327 | 328 | provider.* = Self{ 329 | .allocator = allocator, 330 | .server = undefined, 331 | .base_url = undefined, 332 | .routes = std.ArrayList(Route).init(allocator), 333 | }; 334 | errdefer provider.routes.deinit(); 335 | 336 | provider.server = try zig_serve.HttpListener.init(allocator); 337 | errdefer provider.server.deinit(); 338 | 339 | try provider.server.addEndpoint(zig_serve.IP.loopback_v4, 0); 340 | 341 | try provider.server.start(); 342 | 343 | const endpoint = try provider.server.bindings.items[0].socket.?.getLocalEndPoint(); 344 | 345 | provider.base_url = try std.fmt.allocPrint(provider.allocator, "http://127.0.0.1:{d}", .{endpoint.port}); 346 | errdefer provider.allocator.free(provider.base_url); 347 | 348 | return provider; 349 | } 350 | 351 | pub fn destroy(self: *Self) void { 352 | self.server.deinit(); 353 | 354 | for (self.routes.items) |*route| { 355 | route.deinit(); 356 | } 357 | self.routes.deinit(); 358 | self.allocator.free(self.base_url); 359 | self.* = undefined; 360 | std.heap.c_allocator.destroy(self); 361 | } 362 | 363 | fn compareRoute(_: void, lhs: Route, rhs: Route) bool { 364 | return std.ascii.lessThanIgnoreCase(lhs.prefix, rhs.prefix); 365 | } 366 | 367 | fn sortRoutes(self: *Self) void { 368 | std.sort.sort(Route, self.routes.items, {}, compareRoute); 369 | } 370 | 371 | fn defaultRoute(self: *Provider, route: *Route, context: *zig_serve.HttpContext) Route.Error!void { 372 | _ = self; 373 | _ = route; 374 | _ = context; 375 | 376 | try context.response.setHeader("Content-Type", "text/html"); 377 | try context.response.setStatusCode(.not_found); 378 | 379 | var writer = try context.response.writer(); 380 | try writer.writeAll( 381 | \\ 382 | \\ 383 | \\ 384 | \\ 385 | \\ 386 | \\ 387 | \\

The requested page was not found!

388 | \\ 389 | \\ 390 | ); 391 | } 392 | 393 | pub fn addRoute(self: *Self, abs_path: []const u8) !*Route { 394 | std.debug.assert(abs_path[0] == '/'); 395 | 396 | const route = try self.routes.addOne(); 397 | errdefer _ = self.routes.pop(); 398 | 399 | route.* = Route{ 400 | .arena = std.heap.ArenaAllocator.init(std.heap.c_allocator), 401 | .prefix = undefined, 402 | .handler = defaultRoute, 403 | .context = undefined, 404 | }; 405 | errdefer route.deinit(); 406 | 407 | route.prefix = try std.fmt.allocPrintZ(route.arena.allocator(), "{s}{s}", .{ self.base_url, abs_path }); 408 | 409 | return route; 410 | } 411 | 412 | pub fn addContent(self: *Self, abs_path: []const u8, mime_type: []const u8, contents: []const u8) !void { 413 | const route = try self.addRoute(abs_path); 414 | 415 | const Handler = struct { 416 | mime_type: []const u8, 417 | contents: []const u8, 418 | 419 | fn handle(_: *Provider, r: *Route, context: *zig_serve.HttpContext) Route.Error!void { 420 | const handler = r.getContext(@This()); 421 | 422 | try context.response.setHeader("Content-Type", handler.mime_type); 423 | 424 | var writer = try context.response.writer(); 425 | try writer.writeAll(handler.contents); 426 | } 427 | }; 428 | 429 | const handler = try route.arena.allocator().create(Handler); 430 | handler.* = Handler{ 431 | .mime_type = try route.arena.allocator().dupe(u8, mime_type), 432 | .contents = try route.arena.allocator().dupe(u8, contents), 433 | }; 434 | 435 | route.handler = Handler.handle; 436 | route.context = @ptrCast(*Route.GenericPointer, handler); 437 | } 438 | 439 | /// Returns the full URI for `abs_path` 440 | pub fn getUri(self: *Self, abs_path: []const u8) ?[:0]const u8 { 441 | std.debug.assert(abs_path[0] == '/'); 442 | for (self.routes.items) |route| { 443 | if (std.mem.eql(u8, route.prefix[self.base_url.len..], abs_path)) 444 | return route.prefix; 445 | } 446 | return null; 447 | } 448 | 449 | pub fn run(self: *Self) !void { 450 | while (true) { 451 | var ctx = try self.server.getContext(); 452 | defer ctx.deinit(); 453 | 454 | self.handleRequest(ctx) catch |err| { 455 | std.log.err("failed to handle request:{s}", .{@errorName(err)}); 456 | }; 457 | } 458 | } 459 | 460 | pub fn shutdown(self: *Self) void { 461 | self.server.shutdown(); 462 | } 463 | 464 | fn handleRequest(self: *Self, ctx: *zig_serve.HttpContext) !void { 465 | var path = ctx.request.url; 466 | if (std.mem.indexOfScalar(u8, path, '?')) |index| { 467 | path = path[0..index]; 468 | } 469 | 470 | std.log.info("positron request: {s}", .{path}); 471 | 472 | var best_match: ?*Route = null; 473 | for (self.routes.items) |*route| { 474 | if (std.mem.startsWith(u8, path, route.prefix[self.base_url.len..])) { 475 | if (best_match == null or best_match.?.prefix.len < route.prefix.len) { 476 | best_match = route; 477 | } 478 | } 479 | } 480 | 481 | if (best_match) |route| { 482 | try route.handler(self, route, ctx); 483 | } else { 484 | try defaultRoute(self, undefined, ctx); 485 | } 486 | } 487 | }; 488 | -------------------------------------------------------------------------------- /src/wv/LICENCE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017-2021 Serge Zaitsev, Felix Queißner 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/wv/README.md: -------------------------------------------------------------------------------- 1 | # WebView 2 | 3 | Fork of https://github.com/webview/webview, including bug fixes and restructurings for easier modification. -------------------------------------------------------------------------------- /src/wv/webview-cocoa.h: -------------------------------------------------------------------------------- 1 | #ifndef WEBVIEW_COCOA 2 | #error WEBVIEW_COCOA must be defined. This is a implementation file! 3 | #endif 4 | 5 | // 6 | // ==================================================================== 7 | // 8 | // This implementation uses Cocoa WKWebView backend on macOS. It is 9 | // written using ObjC runtime and uses WKWebView class as a browser runtime. 10 | // You should pass "-framework Webkit" flag to the compiler. 11 | // 12 | // ==================================================================== 13 | // 14 | 15 | #include 16 | #include 17 | 18 | #define NSBackingStoreBuffered 2 19 | 20 | #define NSWindowStyleMaskResizable 8 21 | #define NSWindowStyleMaskMiniaturizable 4 22 | #define NSWindowStyleMaskTitled 1 23 | #define NSWindowStyleMaskClosable 2 24 | 25 | #define NSApplicationActivationPolicyRegular 0 26 | 27 | #define WKUserScriptInjectionTimeAtDocumentStart 0 28 | 29 | namespace webview { 30 | 31 | // Helpers to avoid too much typing 32 | id operator"" _cls(const char *s, std::size_t) { return (id)objc_getClass(s); } 33 | SEL operator"" _sel(const char *s, std::size_t) { return sel_registerName(s); } 34 | id operator"" _str(const char *s, std::size_t) { 35 | return ((id(*)(id, SEL, const char *))objc_msgSend)( 36 | "NSString"_cls, "stringWithUTF8String:"_sel, s); 37 | } 38 | 39 | class cocoa_wkwebview_engine { 40 | public: 41 | cocoa_wkwebview_engine(bool debug, void *window) { 42 | // Application 43 | id app = ((id(*)(id, SEL))objc_msgSend)("NSApplication"_cls, 44 | "sharedApplication"_sel); 45 | ((void (*)(id, SEL, long))objc_msgSend)( 46 | app, "setActivationPolicy:"_sel, NSApplicationActivationPolicyRegular); 47 | 48 | // Delegate 49 | auto cls = 50 | objc_allocateClassPair((Class) "NSResponder"_cls, "AppDelegate", 0); 51 | class_addProtocol(cls, objc_getProtocol("NSTouchBarProvider")); 52 | class_addMethod(cls, "applicationShouldTerminateAfterLastWindowClosed:"_sel, 53 | (IMP)(+[](id, SEL, id) -> BOOL { return 1; }), "c@:@"); 54 | class_addMethod(cls, "userContentController:didReceiveScriptMessage:"_sel, 55 | (IMP)(+[](id self, SEL, id, id msg) { 56 | auto w = 57 | (cocoa_wkwebview_engine *)objc_getAssociatedObject( 58 | self, "webview"); 59 | assert(w); 60 | w->on_message(((const char *(*)(id, SEL))objc_msgSend)( 61 | ((id(*)(id, SEL))objc_msgSend)(msg, "body"_sel), 62 | "UTF8String"_sel)); 63 | }), 64 | "v@:@@"); 65 | objc_registerClassPair(cls); 66 | 67 | auto delegate = ((id(*)(id, SEL))objc_msgSend)((id)cls, "new"_sel); 68 | objc_setAssociatedObject(delegate, "webview", (id)this, 69 | OBJC_ASSOCIATION_ASSIGN); 70 | ((void (*)(id, SEL, id))objc_msgSend)(app, sel_registerName("setDelegate:"), 71 | delegate); 72 | 73 | // Main window 74 | if (window == nullptr) { 75 | m_window = ((id(*)(id, SEL))objc_msgSend)("NSWindow"_cls, "alloc"_sel); 76 | m_window = 77 | ((id(*)(id, SEL, CGRect, int, unsigned long, int))objc_msgSend)( 78 | m_window, "initWithContentRect:styleMask:backing:defer:"_sel, 79 | CGRectMake(0, 0, 0, 0), 0, NSBackingStoreBuffered, 0); 80 | } else { 81 | m_window = (id)window; 82 | } 83 | 84 | // Webview 85 | auto config = 86 | ((id(*)(id, SEL))objc_msgSend)("WKWebViewConfiguration"_cls, "new"_sel); 87 | m_manager = 88 | ((id(*)(id, SEL))objc_msgSend)(config, "userContentController"_sel); 89 | m_webview = ((id(*)(id, SEL))objc_msgSend)("WKWebView"_cls, "alloc"_sel); 90 | 91 | if (debug) { 92 | // Equivalent Obj-C: 93 | // [[config preferences] setValue:@YES forKey:@"developerExtrasEnabled"]; 94 | ((id(*)(id, SEL, id, id))objc_msgSend)( 95 | ((id(*)(id, SEL))objc_msgSend)(config, "preferences"_sel), 96 | "setValue:forKey:"_sel, 97 | ((id(*)(id, SEL, BOOL))objc_msgSend)("NSNumber"_cls, 98 | "numberWithBool:"_sel, 1), 99 | "developerExtrasEnabled"_str); 100 | } 101 | 102 | // Equivalent Obj-C: 103 | // [[config preferences] setValue:@YES forKey:@"fullScreenEnabled"]; 104 | ((id(*)(id, SEL, id, id))objc_msgSend)( 105 | ((id(*)(id, SEL))objc_msgSend)(config, "preferences"_sel), 106 | "setValue:forKey:"_sel, 107 | ((id(*)(id, SEL, BOOL))objc_msgSend)("NSNumber"_cls, 108 | "numberWithBool:"_sel, 1), 109 | "fullScreenEnabled"_str); 110 | 111 | // Equivalent Obj-C: 112 | // [[config preferences] setValue:@YES forKey:@"javaScriptCanAccessClipboard"]; 113 | ((id(*)(id, SEL, id, id))objc_msgSend)( 114 | ((id(*)(id, SEL))objc_msgSend)(config, "preferences"_sel), 115 | "setValue:forKey:"_sel, 116 | ((id(*)(id, SEL, BOOL))objc_msgSend)("NSNumber"_cls, 117 | "numberWithBool:"_sel, 1), 118 | "javaScriptCanAccessClipboard"_str); 119 | 120 | // Equivalent Obj-C: 121 | // [[config preferences] setValue:@YES forKey:@"DOMPasteAllowed"]; 122 | ((id(*)(id, SEL, id, id))objc_msgSend)( 123 | ((id(*)(id, SEL))objc_msgSend)(config, "preferences"_sel), 124 | "setValue:forKey:"_sel, 125 | ((id(*)(id, SEL, BOOL))objc_msgSend)("NSNumber"_cls, 126 | "numberWithBool:"_sel, 1), 127 | "DOMPasteAllowed"_str); 128 | 129 | ((void (*)(id, SEL, CGRect, id))objc_msgSend)( 130 | m_webview, "initWithFrame:configuration:"_sel, CGRectMake(0, 0, 0, 0), 131 | config); 132 | ((void (*)(id, SEL, id, id))objc_msgSend)( 133 | m_manager, "addScriptMessageHandler:name:"_sel, delegate, 134 | "external"_str); 135 | 136 | init(R"script( 137 | window.external = { 138 | invoke: function(s) { 139 | window.webkit.messageHandlers.external.postMessage(s); 140 | }, 141 | }; 142 | )script"); 143 | ((void (*)(id, SEL, id))objc_msgSend)(m_window, "setContentView:"_sel, 144 | m_webview); 145 | ((void (*)(id, SEL, id))objc_msgSend)(m_window, "makeKeyAndOrderFront:"_sel, 146 | nullptr); 147 | } 148 | ~cocoa_wkwebview_engine() { close(); } 149 | void *window() { return (void *)m_window; } 150 | void terminate() { 151 | close(); 152 | ((void (*)(id, SEL, id))objc_msgSend)("NSApp"_cls, "terminate:"_sel, 153 | nullptr); 154 | } 155 | void run() { 156 | id app = ((id(*)(id, SEL))objc_msgSend)("NSApplication"_cls, 157 | "sharedApplication"_sel); 158 | dispatch([&]() { 159 | ((void (*)(id, SEL, BOOL))objc_msgSend)( 160 | app, "activateIgnoringOtherApps:"_sel, 1); 161 | }); 162 | ((void (*)(id, SEL))objc_msgSend)(app, "run"_sel); 163 | } 164 | void dispatch(std::function f) { 165 | dispatch_async_f(dispatch_get_main_queue(), new dispatch_fn_t(f), 166 | (dispatch_function_t)([](void *arg) { 167 | auto f = static_cast(arg); 168 | (*f)(); 169 | delete f; 170 | })); 171 | } 172 | void set_title(const std::string title) { 173 | ((void (*)(id, SEL, id))objc_msgSend)( 174 | m_window, "setTitle:"_sel, 175 | ((id(*)(id, SEL, const char *))objc_msgSend)( 176 | "NSString"_cls, "stringWithUTF8String:"_sel, title.c_str())); 177 | } 178 | void set_size(int width, int height, int hints) { 179 | auto style = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | 180 | NSWindowStyleMaskMiniaturizable; 181 | if (hints != WEBVIEW_HINT_FIXED) { 182 | style = style | NSWindowStyleMaskResizable; 183 | } 184 | ((void (*)(id, SEL, unsigned long))objc_msgSend)( 185 | m_window, "setStyleMask:"_sel, style); 186 | 187 | if (hints == WEBVIEW_HINT_MIN) { 188 | ((void (*)(id, SEL, CGSize))objc_msgSend)( 189 | m_window, "setContentMinSize:"_sel, CGSizeMake(width, height)); 190 | } else if (hints == WEBVIEW_HINT_MAX) { 191 | ((void (*)(id, SEL, CGSize))objc_msgSend)( 192 | m_window, "setContentMaxSize:"_sel, CGSizeMake(width, height)); 193 | } else { 194 | ((void (*)(id, SEL, CGRect, BOOL, BOOL))objc_msgSend)( 195 | m_window, "setFrame:display:animate:"_sel, 196 | CGRectMake(0, 0, width, height), 1, 0); 197 | } 198 | ((void (*)(id, SEL))objc_msgSend)(m_window, "center"_sel); 199 | } 200 | void navigate(const std::string url) { 201 | auto nsurl = ((id(*)(id, SEL, id))objc_msgSend)( 202 | "NSURL"_cls, "URLWithString:"_sel, 203 | ((id(*)(id, SEL, const char *))objc_msgSend)( 204 | "NSString"_cls, "stringWithUTF8String:"_sel, url.c_str())); 205 | 206 | ((void (*)(id, SEL, id))objc_msgSend)( 207 | m_webview, "loadRequest:"_sel, 208 | ((id(*)(id, SEL, id))objc_msgSend)("NSURLRequest"_cls, 209 | "requestWithURL:"_sel, nsurl)); 210 | } 211 | void init(const std::string js) { 212 | // Equivalent Obj-C: 213 | // [m_manager addUserScript:[[WKUserScript alloc] initWithSource:[NSString stringWithUTF8String:js.c_str()] injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES]] 214 | ((void (*)(id, SEL, id))objc_msgSend)( 215 | m_manager, "addUserScript:"_sel, 216 | ((id(*)(id, SEL, id, long, BOOL))objc_msgSend)( 217 | ((id(*)(id, SEL))objc_msgSend)("WKUserScript"_cls, "alloc"_sel), 218 | "initWithSource:injectionTime:forMainFrameOnly:"_sel, 219 | ((id(*)(id, SEL, const char *))objc_msgSend)( 220 | "NSString"_cls, "stringWithUTF8String:"_sel, js.c_str()), 221 | WKUserScriptInjectionTimeAtDocumentStart, 1)); 222 | } 223 | void eval(const std::string js) { 224 | ((void (*)(id, SEL, id, id))objc_msgSend)( 225 | m_webview, "evaluateJavaScript:completionHandler:"_sel, 226 | ((id(*)(id, SEL, const char *))objc_msgSend)( 227 | "NSString"_cls, "stringWithUTF8String:"_sel, js.c_str()), 228 | nullptr); 229 | } 230 | 231 | private: 232 | virtual void on_message(const std::string msg) = 0; 233 | void close() { ((void (*)(id, SEL))objc_msgSend)(m_window, "close"_sel); } 234 | id m_window; 235 | id m_webview; 236 | id m_manager; 237 | }; 238 | 239 | using browser_engine = cocoa_wkwebview_engine; 240 | 241 | } // namespace webview -------------------------------------------------------------------------------- /src/wv/webview-gtk.h: -------------------------------------------------------------------------------- 1 | #ifndef WEBVIEW_GTK 2 | #error WEBVIEW_GTK must be defined. This is a implementation file! 3 | #endif 4 | 5 | // 6 | // ==================================================================== 7 | // 8 | // This implementation uses webkit2gtk backend. It requires gtk+3.0 and 9 | // webkit2gtk-4.0 libraries. Proper compiler flags can be retrieved via: 10 | // 11 | // pkg-config --cflags --libs gtk+-3.0 webkit2gtk-4.0 12 | // 13 | // ==================================================================== 14 | // 15 | #include 16 | #include 17 | #include 18 | 19 | class gtk_webkit_engine { 20 | public: 21 | gtk_webkit_engine(bool debug, void *window) 22 | : m_window(static_cast(window)) { 23 | gtk_init_check(0, NULL); 24 | m_window = static_cast(window); 25 | if (m_window == nullptr) { 26 | m_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); 27 | } 28 | g_signal_connect(G_OBJECT(m_window), "destroy", 29 | G_CALLBACK(+[](GtkWidget *, gpointer arg) { 30 | static_cast(arg)->terminate(); 31 | }), 32 | this); 33 | // Initialize webview widget 34 | m_webview = webkit_web_view_new(); 35 | WebKitUserContentManager *manager = 36 | webkit_web_view_get_user_content_manager(WEBKIT_WEB_VIEW(m_webview)); 37 | g_signal_connect(manager, "script-message-received::external", 38 | G_CALLBACK(+[](WebKitUserContentManager *, 39 | WebKitJavascriptResult *r, gpointer arg) { 40 | auto *w = static_cast(arg); 41 | #if WEBKIT_MAJOR_VERSION >= 2 && WEBKIT_MINOR_VERSION >= 22 42 | JSCValue *value = 43 | webkit_javascript_result_get_js_value(r); 44 | char *s = jsc_value_to_string(value); 45 | #else 46 | JSGlobalContextRef ctx = webkit_javascript_result_get_global_context(r); 47 | JSValueRef value = webkit_javascript_result_get_value(r); 48 | JSStringRef js = JSValueToStringCopy(ctx, value, NULL); 49 | size_t n = JSStringGetMaximumUTF8CStringSize(js); 50 | char *s = g_new(char, n); 51 | JSStringGetUTF8CString(js, s, n); 52 | JSStringRelease(js); 53 | #endif 54 | w->on_message(s); 55 | g_free(s); 56 | }), 57 | this); 58 | webkit_user_content_manager_register_script_message_handler(manager, 59 | "external"); 60 | init("window.external={invoke:function(s){window.webkit.messageHandlers." 61 | "external.postMessage(s);}}"); 62 | 63 | gtk_container_add(GTK_CONTAINER(m_window), GTK_WIDGET(m_webview)); 64 | gtk_widget_grab_focus(GTK_WIDGET(m_webview)); 65 | 66 | WebKitSettings *settings = 67 | webkit_web_view_get_settings(WEBKIT_WEB_VIEW(m_webview)); 68 | webkit_settings_set_javascript_can_access_clipboard(settings, true); 69 | if (debug) { 70 | webkit_settings_set_enable_write_console_messages_to_stdout(settings, 71 | true); 72 | webkit_settings_set_enable_developer_extras(settings, true); 73 | } 74 | 75 | gtk_widget_show_all(m_window); 76 | } 77 | void *window() { return (void *)m_window; } 78 | void run() { gtk_main(); } 79 | void terminate() { gtk_main_quit(); } 80 | void dispatch(std::function f) { 81 | g_idle_add_full(G_PRIORITY_HIGH_IDLE, (GSourceFunc)([](void *f) -> int { 82 | (*static_cast(f))(); 83 | return G_SOURCE_REMOVE; 84 | }), 85 | new std::function(f), 86 | [](void *f) { delete static_cast(f); }); 87 | } 88 | 89 | void set_title(const std::string title) { 90 | gtk_window_set_title(GTK_WINDOW(m_window), title.c_str()); 91 | } 92 | 93 | void set_size(int width, int height, int hints) { 94 | gtk_window_set_resizable(GTK_WINDOW(m_window), hints != WEBVIEW_HINT_FIXED); 95 | if (hints == WEBVIEW_HINT_NONE) { 96 | gtk_window_resize(GTK_WINDOW(m_window), width, height); 97 | } else if (hints == WEBVIEW_HINT_FIXED) { 98 | gtk_widget_set_size_request(m_window, width, height); 99 | } else { 100 | GdkGeometry g; 101 | g.min_width = g.max_width = width; 102 | g.min_height = g.max_height = height; 103 | GdkWindowHints h = 104 | (hints == WEBVIEW_HINT_MIN ? GDK_HINT_MIN_SIZE : GDK_HINT_MAX_SIZE); 105 | // This defines either MIN_SIZE, or MAX_SIZE, but not both: 106 | gtk_window_set_geometry_hints(GTK_WINDOW(m_window), nullptr, &g, h); 107 | } 108 | } 109 | 110 | void navigate(const std::string url) { 111 | webkit_web_view_load_uri(WEBKIT_WEB_VIEW(m_webview), url.c_str()); 112 | } 113 | 114 | void init(const std::string js) { 115 | WebKitUserContentManager *manager = 116 | webkit_web_view_get_user_content_manager(WEBKIT_WEB_VIEW(m_webview)); 117 | webkit_user_content_manager_add_script( 118 | manager, webkit_user_script_new( 119 | js.c_str(), WEBKIT_USER_CONTENT_INJECT_TOP_FRAME, 120 | WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_START, NULL, NULL)); 121 | } 122 | 123 | void eval(const std::string js) { 124 | webkit_web_view_run_javascript(WEBKIT_WEB_VIEW(m_webview), js.c_str(), NULL, 125 | NULL, NULL); 126 | } 127 | 128 | private: 129 | virtual void on_message(const std::string msg) = 0; 130 | GtkWidget *m_window; 131 | GtkWidget *m_webview; 132 | }; 133 | 134 | using browser_engine = gtk_webkit_engine; 135 | -------------------------------------------------------------------------------- /src/wv/webview-windows-chromium.h: -------------------------------------------------------------------------------- 1 | 2 | // Edge/Chromium headers and libs 3 | #include 4 | #pragma comment(lib, "ole32.lib") 5 | #pragma comment(lib, "oleaut32.lib") 6 | 7 | // 8 | // Edge/Chromium browser engine 9 | // 10 | class edge_chromium : public browser 11 | { 12 | public: 13 | bool embed(HWND wnd, bool debug, msg_cb_t cb) override 14 | { 15 | CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); 16 | std::atomic_flag flag = ATOMIC_FLAG_INIT; 17 | flag.test_and_set(); 18 | 19 | wchar_t currentExePath[MAX_PATH]; 20 | GetModuleFileNameW(NULL, currentExePath, MAX_PATH); 21 | // char *currentExeName = PathFindFileNameA(currentExePath); 22 | 23 | wchar_t userDataFolderBit[MAX_PATH]; 24 | GetEnvironmentVariableW(L"APPDATA", userDataFolderBit, MAX_PATH); 25 | 26 | wchar_t folder[MAX_PATH]; 27 | wcscpy(folder, userDataFolderBit); 28 | wcscat(folder, L"/"); 29 | wcscat(folder, L"ZigWebViewDemo"); 30 | 31 | wprintf(L"folder = %ls\n", folder); 32 | 33 | auto handler = new webview2_com_handler(wnd, cb, 34 | [&](ICoreWebView2Controller *controller) 35 | { 36 | printf("ready %p\n", controller); 37 | m_controller = controller; 38 | if (m_controller != nullptr) 39 | { 40 | m_controller->get_CoreWebView2(&m_webview); 41 | m_webview->AddRef(); 42 | } 43 | flag.clear(); 44 | }); 45 | HRESULT res = CreateCoreWebView2EnvironmentWithOptions( 46 | nullptr, folder, nullptr, handler); 47 | if (res != S_OK) 48 | { 49 | CoUninitialize(); 50 | if (res == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) 51 | { 52 | printf("File not found?\n"); 53 | } 54 | printf("failed to create webview: 0x%X!\n", res); 55 | return false; 56 | } 57 | MSG msg = {}; 58 | while (flag.test_and_set() && GetMessage(&msg, NULL, 0, 0)) 59 | { 60 | TranslateMessage(&msg); 61 | DispatchMessage(&msg); 62 | } 63 | if (this->m_controller == nullptr) 64 | return false; 65 | init("window.external={invoke:s=>window.chrome.webview.postMessage(s)}"); 66 | return true; 67 | } 68 | 69 | void resize(HWND wnd) override 70 | { 71 | RECT bounds; 72 | GetClientRect(wnd, &bounds); 73 | m_controller->put_IsVisible(true); 74 | m_controller->put_ParentWindow(wnd); 75 | m_controller->put_Bounds(bounds); 76 | } 77 | 78 | void navigate(const std::string url) override 79 | { 80 | auto wurl = to_lpwstr(url); 81 | m_webview->Navigate(wurl); 82 | delete[] wurl; 83 | } 84 | 85 | void init(const std::string js) override 86 | { 87 | LPCWSTR wjs = to_lpwstr(js); 88 | m_webview->AddScriptToExecuteOnDocumentCreated(wjs, nullptr); 89 | delete[] wjs; 90 | } 91 | 92 | void eval(const std::string js) override 93 | { 94 | LPCWSTR wjs = to_lpwstr(js); 95 | m_webview->ExecuteScript(wjs, nullptr); 96 | delete[] wjs; 97 | } 98 | 99 | private: 100 | LPWSTR to_lpwstr(const std::string s) 101 | { 102 | int n = MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, NULL, 0); 103 | wchar_t *ws = new wchar_t[n]; 104 | MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, ws, n); 105 | return ws; 106 | } 107 | 108 | ICoreWebView2 *m_webview = nullptr; 109 | ICoreWebView2Controller *m_controller = nullptr; 110 | 111 | class webview2_com_handler 112 | : public ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler, 113 | public ICoreWebView2CreateCoreWebView2ControllerCompletedHandler, 114 | public ICoreWebView2WebMessageReceivedEventHandler, 115 | public ICoreWebView2PermissionRequestedEventHandler 116 | { 117 | using webview2_com_handler_cb_t = 118 | std::function; 119 | 120 | public: 121 | webview2_com_handler(HWND hwnd, msg_cb_t msgCb, 122 | webview2_com_handler_cb_t cb) 123 | : m_window(hwnd), m_msgCb(msgCb), m_cb(cb) {} 124 | ULONG STDMETHODCALLTYPE AddRef() { return 1; } 125 | ULONG STDMETHODCALLTYPE Release() { return 1; } 126 | HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID *ppv) 127 | { 128 | return S_OK; 129 | } 130 | HRESULT STDMETHODCALLTYPE Invoke(HRESULT res, 131 | ICoreWebView2Environment *env) 132 | { 133 | printf("created env: 0x%X, env=%p, hwnd=%p\n", res, env, m_window); 134 | env->CreateCoreWebView2Controller(m_window, this); 135 | return S_OK; 136 | } 137 | HRESULT STDMETHODCALLTYPE Invoke(HRESULT res, 138 | ICoreWebView2Controller *controller) 139 | { 140 | printf("hresult: 0x%X, controller=%p\n", res, controller); 141 | if (controller != nullptr) 142 | { 143 | controller->AddRef(); 144 | 145 | ICoreWebView2 *webview; 146 | ::EventRegistrationToken token; 147 | controller->get_CoreWebView2(&webview); 148 | webview->add_WebMessageReceived(this, &token); 149 | webview->add_PermissionRequested(this, &token); 150 | 151 | ICoreWebView2Settings *Settings; 152 | webview->get_Settings(&Settings); 153 | Settings->put_IsScriptEnabled(TRUE); 154 | Settings->put_AreDefaultScriptDialogsEnabled(TRUE); 155 | Settings->put_IsWebMessageEnabled(TRUE); 156 | } 157 | m_cb(controller); 158 | return S_OK; 159 | } 160 | HRESULT STDMETHODCALLTYPE Invoke( 161 | ICoreWebView2 *sender, ICoreWebView2WebMessageReceivedEventArgs *args) 162 | { 163 | LPWSTR message; 164 | args->TryGetWebMessageAsString(&message); 165 | 166 | char long_buffer[4096]; 167 | sprintf_s(long_buffer, sizeof long_buffer, "%ls", message); 168 | 169 | m_msgCb(long_buffer); 170 | 171 | sender->PostWebMessageAsString(message); 172 | 173 | CoTaskMemFree(message); 174 | return S_OK; 175 | } 176 | HRESULT STDMETHODCALLTYPE 177 | Invoke(ICoreWebView2 *sender, 178 | ICoreWebView2PermissionRequestedEventArgs *args) 179 | { 180 | COREWEBVIEW2_PERMISSION_KIND kind; 181 | args->get_PermissionKind(&kind); 182 | if (kind == COREWEBVIEW2_PERMISSION_KIND_CLIPBOARD_READ) 183 | { 184 | args->put_State(COREWEBVIEW2_PERMISSION_STATE_ALLOW); 185 | } 186 | return S_OK; 187 | } 188 | 189 | private: 190 | HWND m_window; 191 | msg_cb_t m_msgCb; 192 | webview2_com_handler_cb_t m_cb; 193 | }; 194 | }; -------------------------------------------------------------------------------- /src/wv/webview-windows-edgehtml.h: -------------------------------------------------------------------------------- 1 | // 2 | // EdgeHTML browser engine 3 | // 4 | 5 | // EdgeHTML headers and libs 6 | #include 7 | #include 8 | #include 9 | #include 10 | #pragma comment(lib, "windowsapp") 11 | 12 | using namespace winrt; 13 | using namespace Windows::Foundation; 14 | using namespace Windows::Web::UI; 15 | using namespace Windows::Web::UI::Interop; 16 | 17 | class edge_html : public browser 18 | { 19 | public: 20 | bool embed(HWND wnd, bool debug, msg_cb_t cb) override 21 | { 22 | init_apartment(winrt::apartment_type::single_threaded); 23 | auto process = WebViewControlProcess(); 24 | auto op = process.CreateWebViewControlAsync(reinterpret_cast(wnd), 25 | Rect()); 26 | if (op.Status() != AsyncStatus::Completed) 27 | { 28 | handle h(CreateEvent(nullptr, false, false, nullptr)); 29 | op.Completed([h = h.get()](auto, auto) 30 | { SetEvent(h); }); 31 | HANDLE hs[] = {h.get()}; 32 | DWORD i; 33 | CoWaitForMultipleHandles(COWAIT_DISPATCH_WINDOW_MESSAGES | 34 | COWAIT_DISPATCH_CALLS | 35 | COWAIT_INPUTAVAILABLE, 36 | INFINITE, 1, hs, &i); 37 | } 38 | m_webview = op.GetResults(); 39 | m_webview.Settings().IsScriptNotifyAllowed(true); 40 | m_webview.IsVisible(true); 41 | m_webview.ScriptNotify([=](auto const &sender, auto const &args) 42 | { 43 | std::string s = winrt::to_string(args.Value()); 44 | cb(s.c_str()); 45 | }); 46 | m_webview.NavigationStarting([=](auto const &sender, auto const &args) 47 | { m_webview.AddInitializeScript(winrt::to_hstring(init_js)); }); 48 | init("window.external.invoke = s => window.external.notify(s)"); 49 | return true; 50 | } // 51 | 52 | void navigate(const std::string url) override 53 | { 54 | std::string html = html_from_uri(url); 55 | if (html != "") 56 | { 57 | m_webview.NavigateToString(winrt::to_hstring(html)); 58 | } 59 | else 60 | { 61 | Uri uri(winrt::to_hstring(url)); 62 | m_webview.Navigate(uri); 63 | } 64 | } // 65 | 66 | void init(const std::string js) override 67 | { 68 | init_js = init_js + "(function(){" + js + "})();"; 69 | } // 70 | 71 | void eval(const std::string js) override 72 | { 73 | m_webview.InvokeScriptAsync( 74 | L"eval", single_threaded_vector({winrt::to_hstring(js)})); 75 | } // 76 | 77 | void resize(HWND wnd) override 78 | { 79 | if (m_webview == nullptr) 80 | { 81 | return; 82 | } 83 | RECT r; 84 | GetClientRect(wnd, &r); 85 | Rect bounds(r.left, r.top, r.right - r.left, r.bottom - r.top); 86 | m_webview.Bounds(bounds); 87 | } // 88 | 89 | private: 90 | WebViewControl m_webview = nullptr; 91 | std::string init_js = ""; 92 | }; -------------------------------------------------------------------------------- /src/wv/webview-windows.h: -------------------------------------------------------------------------------- 1 | #ifndef WEBVIEW_EDGE 2 | #error WEBVIEW_EDGE must be defined. This is a implementation file! 3 | #endif 4 | 5 | #define WEBVIEW_WINDOWS_CHROMIUM 6 | // #define WEBVIEW_WINDOWS_EDGEHTML 7 | 8 | // 9 | // ==================================================================== 10 | // 11 | // This implementation uses Win32 API to create a native window. It can 12 | // use either EdgeHTML or Edge/Chromium backend as a browser engine. 13 | // 14 | // ==================================================================== 15 | // 16 | 17 | #define WIN32_LEAN_AND_MEAN 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | extern "C" WINUSERAPI BOOL WINAPI SetProcessDpiAwarenessContext( 25 | _In_ DPI_AWARENESS_CONTEXT value); 26 | 27 | // #define DPI_AWARENESS_CONTEXT_UNAWARE ((DPI_AWARENESS_CONTEXT)-1) 28 | // #define DPI_AWARENESS_CONTEXT_SYSTEM_AWARE ((DPI_AWARENESS_CONTEXT)-2) 29 | // #define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE ((DPI_AWARENESS_CONTEXT)-3) 30 | // #define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ((DPI_AWARENESS_CONTEXT)-4) 31 | // #define DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED ((DPI_AWARENESS_CONTEXT)-5) 32 | 33 | // #pragma comment(lib, "user32.lib") 34 | // #pragma comment(lib, "Shlwapi.lib") 35 | 36 | using msg_cb_t = std::function; 37 | 38 | // Common interface for EdgeHTML and Edge/Chromium 39 | class browser 40 | { 41 | public: 42 | virtual ~browser() = default; 43 | virtual bool embed(HWND, bool, msg_cb_t) = 0; 44 | virtual void navigate(const std::string url) = 0; 45 | virtual void eval(const std::string js) = 0; 46 | virtual void init(const std::string js) = 0; 47 | virtual void resize(HWND) = 0; 48 | }; 49 | 50 | #ifdef WEBVIEW_WINDOWS_CHROMIUM 51 | #include "webview-windows-chromium.h" 52 | #endif 53 | #ifdef WEBVIEW_WINDOWS_EDGEHTML 54 | #include "webview-windows-edgehtml.h" 55 | #endif 56 | 57 | class browser_engine 58 | { 59 | public: 60 | static LRESULT wndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) 61 | { 62 | auto w = (browser_engine *)GetWindowLongPtr(hwnd, GWLP_USERDATA); 63 | switch (msg) 64 | { 65 | case WM_SIZE: 66 | if (w->m_browser) 67 | w->m_browser->resize(hwnd); 68 | break; 69 | case WM_CLOSE: 70 | DestroyWindow(hwnd); 71 | break; 72 | case WM_DESTROY: 73 | w->terminate(); 74 | break; 75 | case WM_PAINT: 76 | 77 | break; 78 | case WM_GETMINMAXINFO: 79 | { 80 | auto lpmmi = (LPMINMAXINFO)lp; 81 | if (w == nullptr) 82 | { 83 | return 0; 84 | } 85 | if (w->m_maxsz.x > 0 && w->m_maxsz.y > 0) 86 | { 87 | lpmmi->ptMaxSize = w->m_maxsz; 88 | lpmmi->ptMaxTrackSize = w->m_maxsz; 89 | } 90 | if (w->m_minsz.x > 0 && w->m_minsz.y > 0) 91 | { 92 | lpmmi->ptMinTrackSize = w->m_minsz; 93 | } 94 | } 95 | break; 96 | default: 97 | return DefWindowProc(hwnd, msg, wp, lp); 98 | } 99 | return 0; 100 | } 101 | 102 | browser_engine(bool debug, void *window) 103 | { 104 | 105 | if (window == nullptr) 106 | { 107 | HINSTANCE hInstance = GetModuleHandle(nullptr); 108 | HICON icon = (HICON)LoadImage( 109 | hInstance, IDI_APPLICATION, IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), 110 | GetSystemMetrics(SM_CYSMICON), LR_DEFAULTCOLOR); 111 | 112 | WNDCLASSEX wc; 113 | ZeroMemory(&wc, sizeof(WNDCLASSEX)); 114 | wc.cbSize = sizeof(WNDCLASSEX); 115 | wc.hInstance = hInstance; 116 | wc.lpszClassName = "webview"; 117 | wc.hIcon = icon; 118 | wc.hIconSm = icon; 119 | wc.lpfnWndProc = wndProc; 120 | RegisterClassEx(&wc); 121 | m_window = CreateWindow("webview", "", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 122 | CW_USEDEFAULT, 640, 480, nullptr, nullptr, 123 | GetModuleHandle(nullptr), nullptr); 124 | SetWindowLongPtr(m_window, GWLP_USERDATA, (LONG_PTR)this); 125 | } 126 | else 127 | { 128 | m_window = *(static_cast(window)); 129 | } 130 | 131 | SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE); 132 | ShowWindow(m_window, SW_SHOW); 133 | UpdateWindow(m_window); 134 | SetFocus(m_window); 135 | 136 | if (this->startBrowserEngine(debug)) 137 | { 138 | this->m_browser->resize(m_window); 139 | } 140 | } 141 | 142 | bool 143 | startBrowserEngine(bool debug) 144 | { 145 | auto cb = 146 | std::bind(&browser_engine::on_message, this, std::placeholders::_1); 147 | #ifdef WEBVIEW_WINDOWS_CHROMIUM 148 | this->m_browser = std::make_unique(); 149 | if (this->m_browser->embed(m_window, debug, cb)) 150 | return true; 151 | #endif 152 | #ifdef WEBVIEW_WINDOWS_EDGEHTML 153 | this->m_browser = std::make_unique(); 154 | if (this->m_browser->embed(m_window, debug, cb)) 155 | return true; 156 | #endif 157 | this->m_browser.reset(); 158 | return false; 159 | } 160 | 161 | void run() 162 | { 163 | MSG msg; 164 | BOOL res; 165 | while ((res = GetMessage(&msg, nullptr, 0, 0)) != -1) 166 | { 167 | if (msg.hwnd) 168 | { 169 | TranslateMessage(&msg); 170 | DispatchMessage(&msg); 171 | continue; 172 | } 173 | if (msg.message == WM_APP) 174 | { 175 | auto f = (dispatch_fn_t *)(msg.lParam); 176 | (*f)(); 177 | delete f; 178 | } 179 | else if (msg.message == WM_QUIT) 180 | { 181 | return; 182 | } 183 | } 184 | } 185 | void *window() { return (void *)m_window; } 186 | void terminate() { PostQuitMessage(0); } 187 | void dispatch(dispatch_fn_t f) 188 | { 189 | PostThreadMessage(m_main_thread, WM_APP, 0, (LPARAM) new dispatch_fn_t(f)); 190 | } 191 | 192 | void set_title(const std::string title) 193 | { 194 | SetWindowText(m_window, title.c_str()); 195 | } 196 | 197 | void set_size(int width, int height, int hints) 198 | { 199 | auto style = GetWindowLong(m_window, GWL_STYLE); 200 | if (hints == WEBVIEW_HINT_FIXED) 201 | { 202 | style &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX); 203 | } 204 | else 205 | { 206 | style |= (WS_THICKFRAME | WS_MAXIMIZEBOX); 207 | } 208 | SetWindowLong(m_window, GWL_STYLE, style); 209 | 210 | if (hints == WEBVIEW_HINT_MAX) 211 | { 212 | m_maxsz.x = width; 213 | m_maxsz.y = height; 214 | } 215 | else if (hints == WEBVIEW_HINT_MIN) 216 | { 217 | m_minsz.x = width; 218 | m_minsz.y = height; 219 | } 220 | else 221 | { 222 | RECT r; 223 | r.left = r.top = 0; 224 | r.right = width; 225 | r.bottom = height; 226 | AdjustWindowRect(&r, WS_OVERLAPPEDWINDOW, 0); 227 | SetWindowPos(m_window, NULL, r.left, r.top, r.right - r.left, 228 | r.bottom - r.top, 229 | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE | SWP_FRAMECHANGED); 230 | m_browser->resize(m_window); 231 | } 232 | } 233 | 234 | void navigate(const std::string url) { m_browser->navigate(url); } 235 | void eval(const std::string js) { m_browser->eval(js); } 236 | void init(const std::string js) { m_browser->init(js); } 237 | 238 | public: 239 | virtual void on_message(const std::string msg) = 0; 240 | 241 | HWND m_window; 242 | POINT m_minsz = POINT{0, 0}; 243 | POINT m_maxsz = POINT{0, 0}; 244 | DWORD m_main_thread = GetCurrentThreadId(); 245 | std::unique_ptr m_browser; 246 | }; 247 | -------------------------------------------------------------------------------- /src/wv/webview.cpp: -------------------------------------------------------------------------------- 1 | #include "webview.h" 2 | 3 | #if !defined(WEBVIEW_GTK) && !defined(WEBVIEW_COCOA) && !defined(WEBVIEW_EDGE) 4 | #if defined(__linux__) 5 | #define WEBVIEW_GTK 6 | #elif defined(__APPLE__) 7 | #define WEBVIEW_COCOA 8 | #elif defined(_WIN32) 9 | #define WEBVIEW_EDGE 10 | #else 11 | #error "please, specify webview backend" 12 | #endif 13 | #endif 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | 25 | using dispatch_fn_t = std::function; 26 | 27 | // Convert ASCII hex digit to a nibble (four bits, 0 - 15). 28 | // 29 | // Use unsigned to avoid signed overflow UB. 30 | static inline unsigned char hex2nibble(unsigned char c) { 31 | if (c >= '0' && c <= '9') { 32 | return c - '0'; 33 | } else if (c >= 'a' && c <= 'f') { 34 | return 10 + (c - 'a'); 35 | } else if (c >= 'A' && c <= 'F') { 36 | return 10 + (c - 'A'); 37 | } 38 | return 0; 39 | } 40 | 41 | // Convert ASCII hex string (two characters) to byte. 42 | // 43 | // E.g., "0B" => 0x0B, "af" => 0xAF. 44 | static inline char hex2char(const char *p) { 45 | return hex2nibble(p[0]) * 16 + hex2nibble(p[1]); 46 | } 47 | 48 | inline std::string url_encode(const std::string s) { 49 | std::string encoded; 50 | for (unsigned int i = 0; i < s.length(); i++) { 51 | auto c = s[i]; 52 | if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') { 53 | encoded = encoded + c; 54 | } else { 55 | char hex[4]; 56 | snprintf(hex, sizeof(hex), "%%%02x", c); 57 | encoded = encoded + hex; 58 | } 59 | } 60 | return encoded; 61 | } 62 | 63 | inline std::string url_decode(const std::string st) { 64 | std::string decoded; 65 | const char *s = st.c_str(); 66 | size_t length = strlen(s); 67 | for (unsigned int i = 0; i < length; i++) { 68 | if (s[i] == '%') { 69 | decoded.push_back(hex2char(s + i + 1)); 70 | i = i + 2; 71 | } else if (s[i] == '+') { 72 | decoded.push_back(' '); 73 | } else { 74 | decoded.push_back(s[i]); 75 | } 76 | } 77 | return decoded; 78 | } 79 | 80 | inline std::string html_from_uri(const std::string s) { 81 | if (s.substr(0, 15) == "data:text/html,") { 82 | return url_decode(s.substr(15)); 83 | } 84 | return ""; 85 | } 86 | 87 | inline int json_parse_c(const char *s, size_t sz, const char *key, size_t keysz, 88 | const char **value, size_t *valuesz) { 89 | enum { 90 | JSON_STATE_VALUE, 91 | JSON_STATE_LITERAL, 92 | JSON_STATE_STRING, 93 | JSON_STATE_ESCAPE, 94 | JSON_STATE_UTF8 95 | } state = JSON_STATE_VALUE; 96 | const char *k = NULL; 97 | int index = 1; 98 | int depth = 0; 99 | int utf8_bytes = 0; 100 | 101 | if (key == NULL) { 102 | index = keysz; 103 | keysz = 0; 104 | } 105 | 106 | *value = NULL; 107 | *valuesz = 0; 108 | 109 | for (; sz > 0; s++, sz--) { 110 | enum { 111 | JSON_ACTION_NONE, 112 | JSON_ACTION_START, 113 | JSON_ACTION_END, 114 | JSON_ACTION_START_STRUCT, 115 | JSON_ACTION_END_STRUCT 116 | } action = JSON_ACTION_NONE; 117 | unsigned char c = *s; 118 | switch (state) { 119 | case JSON_STATE_VALUE: 120 | if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == ',' || 121 | c == ':') { 122 | continue; 123 | } else if (c == '"') { 124 | action = JSON_ACTION_START; 125 | state = JSON_STATE_STRING; 126 | } else if (c == '{' || c == '[') { 127 | action = JSON_ACTION_START_STRUCT; 128 | } else if (c == '}' || c == ']') { 129 | action = JSON_ACTION_END_STRUCT; 130 | } else if (c == 't' || c == 'f' || c == 'n' || c == '-' || 131 | (c >= '0' && c <= '9')) { 132 | action = JSON_ACTION_START; 133 | state = JSON_STATE_LITERAL; 134 | } else { 135 | return -1; 136 | } 137 | break; 138 | case JSON_STATE_LITERAL: 139 | if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == ',' || 140 | c == ']' || c == '}' || c == ':') { 141 | state = JSON_STATE_VALUE; 142 | s--; 143 | sz++; 144 | action = JSON_ACTION_END; 145 | } else if (c < 32 || c > 126) { 146 | return -1; 147 | } // fallthrough 148 | case JSON_STATE_STRING: 149 | if (c < 32 || (c > 126 && c < 192)) { 150 | return -1; 151 | } else if (c == '"') { 152 | action = JSON_ACTION_END; 153 | state = JSON_STATE_VALUE; 154 | } else if (c == '\\') { 155 | state = JSON_STATE_ESCAPE; 156 | } else if (c >= 192 && c < 224) { 157 | utf8_bytes = 1; 158 | state = JSON_STATE_UTF8; 159 | } else if (c >= 224 && c < 240) { 160 | utf8_bytes = 2; 161 | state = JSON_STATE_UTF8; 162 | } else if (c >= 240 && c < 247) { 163 | utf8_bytes = 3; 164 | state = JSON_STATE_UTF8; 165 | } else if (c >= 128 && c < 192) { 166 | return -1; 167 | } 168 | break; 169 | case JSON_STATE_ESCAPE: 170 | if (c == '"' || c == '\\' || c == '/' || c == 'b' || c == 'f' || 171 | c == 'n' || c == 'r' || c == 't' || c == 'u') { 172 | state = JSON_STATE_STRING; 173 | } else { 174 | return -1; 175 | } 176 | break; 177 | case JSON_STATE_UTF8: 178 | if (c < 128 || c > 191) { 179 | return -1; 180 | } 181 | utf8_bytes--; 182 | if (utf8_bytes == 0) { 183 | state = JSON_STATE_STRING; 184 | } 185 | break; 186 | default: 187 | return -1; 188 | } 189 | 190 | if (action == JSON_ACTION_END_STRUCT) { 191 | depth--; 192 | } 193 | 194 | if (depth == 1) { 195 | if (action == JSON_ACTION_START || action == JSON_ACTION_START_STRUCT) { 196 | if (index == 0) { 197 | *value = s; 198 | } else if (keysz > 0 && index == 1) { 199 | k = s; 200 | } else { 201 | index--; 202 | } 203 | } else if (action == JSON_ACTION_END || 204 | action == JSON_ACTION_END_STRUCT) { 205 | if (*value != NULL && index == 0) { 206 | *valuesz = (size_t)(s + 1 - *value); 207 | return 0; 208 | } else if (keysz > 0 && k != NULL) { 209 | if (keysz == (size_t)(s - k - 1) && memcmp(key, k + 1, keysz) == 0) { 210 | index = 0; 211 | } else { 212 | index = 2; 213 | } 214 | k = NULL; 215 | } 216 | } 217 | } 218 | 219 | if (action == JSON_ACTION_START_STRUCT) { 220 | depth++; 221 | } 222 | } 223 | return -1; 224 | } 225 | 226 | inline std::string json_escape(std::string s) { 227 | // TODO: implement 228 | return '"' + s + '"'; 229 | } 230 | 231 | inline int json_unescape(const char *s, size_t n, char *out) { 232 | int r = 0; 233 | if (*s++ != '"') { 234 | return -1; 235 | } 236 | while (n > 2) { 237 | char c = *s; 238 | if (c == '\\') { 239 | s++; 240 | n--; 241 | switch (*s) { 242 | case 'b': 243 | c = '\b'; 244 | break; 245 | case 'f': 246 | c = '\f'; 247 | break; 248 | case 'n': 249 | c = '\n'; 250 | break; 251 | case 'r': 252 | c = '\r'; 253 | break; 254 | case 't': 255 | c = '\t'; 256 | break; 257 | case '\\': 258 | c = '\\'; 259 | break; 260 | case '/': 261 | c = '/'; 262 | break; 263 | case '\"': 264 | c = '\"'; 265 | break; 266 | default: // TODO: support unicode decoding 267 | return -1; 268 | } 269 | } 270 | if (out != NULL) { 271 | *out++ = c; 272 | } 273 | s++; 274 | n--; 275 | r++; 276 | } 277 | if (*s != '"') { 278 | return -1; 279 | } 280 | if (out != NULL) { 281 | *out = '\0'; 282 | } 283 | return r; 284 | } 285 | 286 | inline std::string json_parse(const std::string s, const std::string key, 287 | const int index) { 288 | const char *value; 289 | size_t value_sz; 290 | if (key == "") { 291 | json_parse_c(s.c_str(), s.length(), nullptr, index, &value, &value_sz); 292 | } else { 293 | json_parse_c(s.c_str(), s.length(), key.c_str(), key.length(), &value, 294 | &value_sz); 295 | } 296 | if (value != nullptr) { 297 | if (value[0] != '"') { 298 | return std::string(value, value_sz); 299 | } 300 | int n = json_unescape(value, value_sz, nullptr); 301 | if (n > 0) { 302 | char *decoded = new char[n + 1]; 303 | json_unescape(value, value_sz, decoded); 304 | std::string result(decoded, n); 305 | delete[] decoded; 306 | return result; 307 | } 308 | } 309 | return ""; 310 | } 311 | 312 | #if defined(WEBVIEW_GTK) 313 | #include "webview-gtk.h" 314 | #elif defined(WEBVIEW_COCOA) 315 | #include "webview-cocoa.h" 316 | #elif defined(WEBVIEW_EDGE) 317 | #include "webview-windows.h" 318 | #endif /* WEBVIEW_GTK, WEBVIEW_COCOA, WEBVIEW_EDGE */ 319 | 320 | class webview : public browser_engine { 321 | public: 322 | webview(bool debug = false, void *wnd = nullptr) 323 | : browser_engine(debug, wnd) {} 324 | 325 | void navigate(const std::string url) { 326 | if (url == "") { 327 | browser_engine::navigate("data:text/html," + 328 | url_encode("Hello")); 329 | return; 330 | } 331 | std::string html = html_from_uri(url); 332 | if (html != "") { 333 | browser_engine::navigate("data:text/html," + url_encode(html)); 334 | } else { 335 | browser_engine::navigate(url); 336 | } 337 | } 338 | 339 | using binding_t = std::function; 340 | using binding_ctx_t = std::pair; 341 | 342 | using sync_binding_t = std::function; 343 | using sync_binding_ctx_t = std::pair; 344 | 345 | void bind(const std::string name, sync_binding_t fn) { 346 | bind( 347 | name, 348 | [](std::string seq, std::string req, void *arg) { 349 | auto pair = static_cast(arg); 350 | pair->first->resolve(seq, 0, pair->second(req)); 351 | }, 352 | new sync_binding_ctx_t(this, fn)); 353 | } 354 | 355 | void bind(const std::string name, binding_t f, void *arg) { 356 | auto js = "(function() { var name = '" + name + "';" + R"( 357 | var RPC = window._rpc = (window._rpc || {nextSeq: 1}); 358 | window[name] = function() { 359 | var seq = RPC.nextSeq++; 360 | var promise = new Promise(function(resolve, reject) { 361 | RPC[seq] = { 362 | resolve: resolve, 363 | reject: reject, 364 | }; 365 | }); 366 | window.external.invoke(JSON.stringify({ 367 | id: seq, 368 | method: name, 369 | params: Array.prototype.slice.call(arguments), 370 | })); 371 | return promise; 372 | } 373 | })())"; 374 | init(js); 375 | bindings[name] = new binding_ctx_t(new binding_t(f), arg); 376 | } 377 | 378 | void resolve(const std::string seq, int status, const std::string result) { 379 | dispatch([=]() { 380 | if (status == 0) { 381 | eval("window._rpc[" + seq + "].resolve(" + result + "); window._rpc[" + 382 | seq + "] = undefined"); 383 | } else { 384 | eval("window._rpc[" + seq + "].reject(" + result + "); window._rpc[" + 385 | seq + "] = undefined"); 386 | } 387 | }); 388 | } 389 | 390 | private: 391 | void on_message(const std::string msg) { 392 | auto seq = json_parse(msg, "id", 0); 393 | auto name = json_parse(msg, "method", 0); 394 | auto args = json_parse(msg, "params", 0); 395 | if (bindings.find(name) == bindings.end()) { 396 | return; 397 | } 398 | auto fn = bindings[name]; 399 | (*fn->first)(seq, args, fn->second); 400 | } 401 | std::map bindings; 402 | }; 403 | 404 | WEBVIEW_API webview_t webview_create(int debug, void *wnd) { 405 | try { 406 | auto wv = new webview(debug, wnd); 407 | #ifdef defined(WEBVIEW_EDGE) 408 | if (wv->m_browser == nullptr) { 409 | delete wv; 410 | return nullptr; 411 | } 412 | #endif 413 | return wv; 414 | } catch (...) { 415 | return nullptr; 416 | } 417 | } 418 | 419 | WEBVIEW_API void webview_destroy(webview_t w) { 420 | delete static_cast(w); 421 | } 422 | 423 | WEBVIEW_API void webview_run(webview_t w) { static_cast(w)->run(); } 424 | 425 | WEBVIEW_API void webview_terminate(webview_t w) { 426 | static_cast(w)->terminate(); 427 | } 428 | 429 | WEBVIEW_API void webview_dispatch(webview_t w, void (*fn)(webview_t, void *), 430 | void *arg) { 431 | static_cast(w)->dispatch([=]() { fn(w, arg); }); 432 | } 433 | 434 | WEBVIEW_API void *webview_get_window(webview_t w) { 435 | return static_cast(w)->window(); 436 | } 437 | 438 | WEBVIEW_API void webview_set_title(webview_t w, const char *title) { 439 | static_cast(w)->set_title(title); 440 | } 441 | 442 | WEBVIEW_API void webview_set_size(webview_t w, int width, int height, 443 | int hints) { 444 | static_cast(w)->set_size(width, height, hints); 445 | } 446 | 447 | WEBVIEW_API void webview_navigate(webview_t w, const char *url) { 448 | static_cast(w)->navigate(url); 449 | } 450 | 451 | WEBVIEW_API void webview_init(webview_t w, const char *js) { 452 | static_cast(w)->init(js); 453 | } 454 | 455 | WEBVIEW_API void webview_eval(webview_t w, const char *js) { 456 | static_cast(w)->eval(js); 457 | } 458 | 459 | WEBVIEW_API void webview_bind(webview_t w, const char *name, 460 | void (*fn)(const char *seq, const char *req, 461 | void *arg), 462 | void *arg) { 463 | static_cast(w)->bind( 464 | name, 465 | [=](std::string seq, std::string req, void *arg) { 466 | fn(seq.c_str(), req.c_str(), arg); 467 | }, 468 | arg); 469 | } 470 | 471 | WEBVIEW_API void webview_return(webview_t w, const char *seq, int status, 472 | const char *result) { 473 | static_cast(w)->resolve(seq, status, result); 474 | } 475 | -------------------------------------------------------------------------------- /src/wv/webview.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2017 Serge Zaitsev 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | #ifndef WEBVIEW_H 25 | #define WEBVIEW_H 26 | 27 | #ifndef WEBVIEW_API 28 | #define WEBVIEW_API extern 29 | #endif 30 | 31 | #ifdef __cplusplus 32 | extern "C" { 33 | #endif 34 | 35 | typedef void *webview_t; 36 | 37 | // Creates a new webview instance. If debug is non-zero - developer tools will 38 | // be enabled (if the platform supports them). Window parameter can be a 39 | // pointer to the native window handle. If it's non-null - then child WebView 40 | // is embedded into the given parent window. Otherwise a new window is created. 41 | // Depending on the platform, a GtkWindow, NSWindow or HWND pointer can be 42 | // passed here. 43 | WEBVIEW_API webview_t webview_create(int debug, void *window); 44 | 45 | // Destroys a webview and closes the native window. 46 | WEBVIEW_API void webview_destroy(webview_t w); 47 | 48 | // Runs the main loop until it's terminated. After this function exits - you 49 | // must destroy the webview. 50 | WEBVIEW_API void webview_run(webview_t w); 51 | 52 | // Stops the main loop. It is safe to call this function from another other 53 | // background thread. 54 | WEBVIEW_API void webview_terminate(webview_t w); 55 | 56 | // Posts a function to be executed on the main thread. You normally do not need 57 | // to call this function, unless you want to tweak the native window. 58 | WEBVIEW_API void 59 | webview_dispatch(webview_t w, void (*fn)(webview_t w, void *arg), void *arg); 60 | 61 | // Returns a native window handle pointer. When using GTK backend the pointer 62 | // is GtkWindow pointer, when using Cocoa backend the pointer is NSWindow 63 | // pointer, when using Win32 backend the pointer is HWND pointer. 64 | WEBVIEW_API void *webview_get_window(webview_t w); 65 | 66 | // Updates the title of the native window. Must be called from the UI thread. 67 | WEBVIEW_API void webview_set_title(webview_t w, const char *title); 68 | 69 | // Window size hints 70 | #define WEBVIEW_HINT_NONE 0 // Width and height are default size 71 | #define WEBVIEW_HINT_MIN 1 // Width and height are minimum bounds 72 | #define WEBVIEW_HINT_MAX 2 // Width and height are maximum bounds 73 | #define WEBVIEW_HINT_FIXED 3 // Window size can not be changed by a user 74 | // Updates native window size. See WEBVIEW_HINT constants. 75 | WEBVIEW_API void webview_set_size(webview_t w, int width, int height, 76 | int hints); 77 | 78 | // Navigates webview to the given URL. URL may be a data URI, i.e. 79 | // "data:text/text,...". It is often ok not to url-encode it 80 | // properly, webview will re-encode it for you. 81 | WEBVIEW_API void webview_navigate(webview_t w, const char *url); 82 | 83 | // Injects JavaScript code at the initialization of the new page. Every time 84 | // the webview will open a the new page - this initialization code will be 85 | // executed. It is guaranteed that code is executed before window.onload. 86 | WEBVIEW_API void webview_init(webview_t w, const char *js); 87 | 88 | // Evaluates arbitrary JavaScript code. Evaluation happens asynchronously, also 89 | // the result of the expression is ignored. Use RPC bindings if you want to 90 | // receive notifications about the results of the evaluation. 91 | WEBVIEW_API void webview_eval(webview_t w, const char *js); 92 | 93 | // Binds a native C callback so that it will appear under the given name as a 94 | // global JavaScript function. Internally it uses webview_init(). Callback 95 | // receives a request string and a user-provided argument pointer. Request 96 | // string is a JSON array of all the arguments passed to the JavaScript 97 | // function. 98 | WEBVIEW_API void webview_bind(webview_t w, const char *name, 99 | void (*fn)(const char *seq, const char *req, 100 | void *arg), 101 | void *arg); 102 | 103 | // Allows to return a value from the native binding. Original request pointer 104 | // must be provided to help internal RPC engine match requests with responses. 105 | // If status is zero - result is expected to be a valid JSON result value. 106 | // If status is not zero - result is an error JSON object. 107 | WEBVIEW_API void webview_return(webview_t w, const char *seq, int status, 108 | const char *result); 109 | 110 | #ifdef __cplusplus 111 | } 112 | #endif 113 | 114 | #endif -------------------------------------------------------------------------------- /vendor/Microsoft.Web.WebView2.1.0.902.49/.signature.p7s: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ziglibs/positron/f144d53ba4ad9e88e5883212ead13e8c1f7ad234/vendor/Microsoft.Web.WebView2.1.0.902.49/.signature.p7s -------------------------------------------------------------------------------- /vendor/Microsoft.Web.WebView2.1.0.902.49/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (C) Microsoft Corporation. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * The name of Microsoft Corporation, or the names of its contributors 14 | may not be used to endorse or promote products derived from this 15 | software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /vendor/Microsoft.Web.WebView2.1.0.902.49/Microsoft.Web.WebView2.1.0.902.49.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ziglibs/positron/f144d53ba4ad9e88e5883212ead13e8c1f7ad234/vendor/Microsoft.Web.WebView2.1.0.902.49/Microsoft.Web.WebView2.1.0.902.49.nupkg -------------------------------------------------------------------------------- /vendor/Microsoft.Web.WebView2.1.0.902.49/WebView2.tlb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ziglibs/positron/f144d53ba4ad9e88e5883212ead13e8c1f7ad234/vendor/Microsoft.Web.WebView2.1.0.902.49/WebView2.tlb -------------------------------------------------------------------------------- /vendor/Microsoft.Web.WebView2.1.0.902.49/build/Common.targets: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ziglibs/positron/f144d53ba4ad9e88e5883212ead13e8c1f7ad234/vendor/Microsoft.Web.WebView2.1.0.902.49/build/Common.targets -------------------------------------------------------------------------------- /vendor/Microsoft.Web.WebView2.1.0.902.49/build/Microsoft.Web.WebView2.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | managed 8 | 9 | 10 | 11 | 13 | $(MSBuildThisFileDirectory)..\ 14 | 15 | 16 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /vendor/Microsoft.Web.WebView2.1.0.902.49/build/native/Microsoft.Web.WebView2.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | native 7 | 8 | 9 | 10 | 12 | $(MSBuildThisFileDirectory)..\..\ 13 | 14 | 15 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /vendor/Microsoft.Web.WebView2.1.0.902.49/build/native/arm64/WebView2Loader.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ziglibs/positron/f144d53ba4ad9e88e5883212ead13e8c1f7ad234/vendor/Microsoft.Web.WebView2.1.0.902.49/build/native/arm64/WebView2Loader.dll -------------------------------------------------------------------------------- /vendor/Microsoft.Web.WebView2.1.0.902.49/build/native/arm64/WebView2Loader.dll.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ziglibs/positron/f144d53ba4ad9e88e5883212ead13e8c1f7ad234/vendor/Microsoft.Web.WebView2.1.0.902.49/build/native/arm64/WebView2Loader.dll.lib -------------------------------------------------------------------------------- /vendor/Microsoft.Web.WebView2.1.0.902.49/build/native/arm64/WebView2LoaderStatic.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ziglibs/positron/f144d53ba4ad9e88e5883212ead13e8c1f7ad234/vendor/Microsoft.Web.WebView2.1.0.902.49/build/native/arm64/WebView2LoaderStatic.lib -------------------------------------------------------------------------------- /vendor/Microsoft.Web.WebView2.1.0.902.49/build/native/include/WebView2EnvironmentOptions.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) Microsoft Corporation. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #ifndef __core_webview2_environment_options_h__ 6 | #define __core_webview2_environment_options_h__ 7 | 8 | #include 9 | #include 10 | 11 | #include "webview2.h" 12 | #define CORE_WEBVIEW_TARGET_PRODUCT_VERSION L"92.0.902.49" 13 | 14 | #define COREWEBVIEW2ENVIRONMENTOPTIONS_STRING_PROPERTY(p) \ 15 | public: \ 16 | HRESULT STDMETHODCALLTYPE get_##p(LPWSTR* value) override { \ 17 | if (!value) \ 18 | return E_POINTER; \ 19 | *value = m_##p.Copy(); \ 20 | if ((*value == nullptr) && (m_##p.Get() != nullptr)) \ 21 | return HRESULT_FROM_WIN32(GetLastError()); \ 22 | return S_OK; \ 23 | } \ 24 | HRESULT STDMETHODCALLTYPE put_##p(LPCWSTR value) override { \ 25 | LPCWSTR result = m_##p.Set(value); \ 26 | if ((result == nullptr) && (value != nullptr)) \ 27 | return HRESULT_FROM_WIN32(GetLastError()); \ 28 | return S_OK; \ 29 | } \ 30 | \ 31 | protected: \ 32 | AutoCoMemString m_##p; 33 | 34 | #define COREWEBVIEW2ENVIRONMENTOPTIONS_BOOL_PROPERTY(p) \ 35 | public: \ 36 | HRESULT STDMETHODCALLTYPE get_##p(BOOL* value) override { \ 37 | if (!value) \ 38 | return E_POINTER; \ 39 | *value = m_##p; \ 40 | return S_OK; \ 41 | } \ 42 | HRESULT STDMETHODCALLTYPE put_##p(BOOL value) override { \ 43 | m_##p = value; \ 44 | return S_OK; \ 45 | } \ 46 | \ 47 | protected: \ 48 | BOOL m_##p = FALSE; 49 | 50 | // This is a base COM class that implements ICoreWebView2EnvironmentOptions. 51 | template 55 | class CoreWebView2EnvironmentOptionsBase 56 | : public Microsoft::WRL::Implements< 57 | Microsoft::WRL::RuntimeClassFlags, 58 | ICoreWebView2EnvironmentOptions> { 59 | public: 60 | CoreWebView2EnvironmentOptionsBase() { 61 | // Initialize the target compatible browser version value to the version of 62 | // the browser binaries corresponding to this version of the SDK. 63 | m_TargetCompatibleBrowserVersion.Set(CORE_WEBVIEW_TARGET_PRODUCT_VERSION); 64 | } 65 | 66 | protected: 67 | ~CoreWebView2EnvironmentOptionsBase(){}; 68 | 69 | class AutoCoMemString { 70 | public: 71 | AutoCoMemString() {} 72 | ~AutoCoMemString() { Release(); } 73 | void Release() { 74 | if (m_string) { 75 | deallocate_fn(m_string); 76 | m_string = nullptr; 77 | } 78 | } 79 | 80 | LPCWSTR Set(LPCWSTR str) { 81 | Release(); 82 | if (str) { 83 | m_string = MakeCoMemString(str); 84 | } 85 | return m_string; 86 | } 87 | LPCWSTR Get() { return m_string; } 88 | LPWSTR Copy() { 89 | if (m_string) 90 | return MakeCoMemString(m_string); 91 | return nullptr; 92 | } 93 | 94 | protected: 95 | LPWSTR MakeCoMemString(LPCWSTR source) { 96 | const size_t length = wcslen(source); 97 | const size_t bytes = (length + 1) * sizeof(*source); 98 | // Ensure we didn't overflow during our size calculation. 99 | if (bytes <= length) { 100 | return nullptr; 101 | } 102 | 103 | wchar_t* result = reinterpret_cast(allocate_fn(bytes)); 104 | if (result) 105 | memcpy(result, source, bytes); 106 | 107 | return result; 108 | } 109 | 110 | LPWSTR m_string = nullptr; 111 | }; 112 | 113 | COREWEBVIEW2ENVIRONMENTOPTIONS_STRING_PROPERTY(AdditionalBrowserArguments) 114 | COREWEBVIEW2ENVIRONMENTOPTIONS_STRING_PROPERTY(Language) 115 | COREWEBVIEW2ENVIRONMENTOPTIONS_STRING_PROPERTY(TargetCompatibleBrowserVersion) 116 | COREWEBVIEW2ENVIRONMENTOPTIONS_BOOL_PROPERTY( 117 | AllowSingleSignOnUsingOSPrimaryAccount) 118 | }; 119 | 120 | template 124 | class CoreWebView2EnvironmentOptionsBaseClass 125 | : public Microsoft::WRL::RuntimeClass< 126 | Microsoft::WRL::RuntimeClassFlags, 127 | CoreWebView2EnvironmentOptionsBase> { 131 | public: 132 | CoreWebView2EnvironmentOptionsBaseClass() {} 133 | 134 | protected: 135 | ~CoreWebView2EnvironmentOptionsBaseClass() override{}; 136 | }; 137 | 138 | typedef CoreWebView2EnvironmentOptionsBaseClass 142 | CoreWebView2EnvironmentOptions; 143 | 144 | #endif // __core_webview2_environment_options_h__ 145 | -------------------------------------------------------------------------------- /vendor/Microsoft.Web.WebView2.1.0.902.49/build/native/x64/WebView2Loader.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ziglibs/positron/f144d53ba4ad9e88e5883212ead13e8c1f7ad234/vendor/Microsoft.Web.WebView2.1.0.902.49/build/native/x64/WebView2Loader.dll -------------------------------------------------------------------------------- /vendor/Microsoft.Web.WebView2.1.0.902.49/build/native/x64/WebView2Loader.dll.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ziglibs/positron/f144d53ba4ad9e88e5883212ead13e8c1f7ad234/vendor/Microsoft.Web.WebView2.1.0.902.49/build/native/x64/WebView2Loader.dll.lib -------------------------------------------------------------------------------- /vendor/Microsoft.Web.WebView2.1.0.902.49/build/native/x64/WebView2LoaderStatic.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ziglibs/positron/f144d53ba4ad9e88e5883212ead13e8c1f7ad234/vendor/Microsoft.Web.WebView2.1.0.902.49/build/native/x64/WebView2LoaderStatic.lib -------------------------------------------------------------------------------- /vendor/Microsoft.Web.WebView2.1.0.902.49/build/native/x86/WebView2Loader.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ziglibs/positron/f144d53ba4ad9e88e5883212ead13e8c1f7ad234/vendor/Microsoft.Web.WebView2.1.0.902.49/build/native/x86/WebView2Loader.dll -------------------------------------------------------------------------------- /vendor/Microsoft.Web.WebView2.1.0.902.49/build/native/x86/WebView2Loader.dll.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ziglibs/positron/f144d53ba4ad9e88e5883212ead13e8c1f7ad234/vendor/Microsoft.Web.WebView2.1.0.902.49/build/native/x86/WebView2Loader.dll.lib -------------------------------------------------------------------------------- /vendor/Microsoft.Web.WebView2.1.0.902.49/build/native/x86/WebView2LoaderStatic.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ziglibs/positron/f144d53ba4ad9e88e5883212ead13e8c1f7ad234/vendor/Microsoft.Web.WebView2.1.0.902.49/build/native/x86/WebView2LoaderStatic.lib -------------------------------------------------------------------------------- /vendor/Microsoft.Web.WebView2.1.0.902.49/lib/net45/Microsoft.Web.WebView2.Core.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ziglibs/positron/f144d53ba4ad9e88e5883212ead13e8c1f7ad234/vendor/Microsoft.Web.WebView2.1.0.902.49/lib/net45/Microsoft.Web.WebView2.Core.dll -------------------------------------------------------------------------------- /vendor/Microsoft.Web.WebView2.1.0.902.49/lib/net45/Microsoft.Web.WebView2.WinForms.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ziglibs/positron/f144d53ba4ad9e88e5883212ead13e8c1f7ad234/vendor/Microsoft.Web.WebView2.1.0.902.49/lib/net45/Microsoft.Web.WebView2.WinForms.dll -------------------------------------------------------------------------------- /vendor/Microsoft.Web.WebView2.1.0.902.49/lib/net45/Microsoft.Web.WebView2.WinForms.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Microsoft.Web.WebView2.WinForms 5 | 6 | 7 | 8 | 9 | This class is a bundle of the most common parameters used to create a . 10 | Its main purpose is to be set to in order to customize the environment used by a during implicit initialization. 11 | 12 | 13 | This class isn't intended to contain all possible environment customization options. 14 | If you need complete control over the environment used by a WebView2 control then you'll need to initialize the control explicitly by 15 | creating your own environment with and passing it to 16 | *before* you set the property to anything. 17 | See the class documentation for an initialization overview. 18 | 19 | 20 | 21 | 22 | Creates a new instance of with default data for all properties. 23 | 24 | 25 | 26 | 27 | Gets or sets the value to pass as the browserExecutableFolder parameter of when creating an environment with this instance. 28 | 29 | 30 | 31 | 32 | Gets or sets the value to pass as the userDataFolder parameter of when creating an environment with this instance. 33 | 34 | 35 | 36 | 37 | Gets or sets the value to use for the Language property of the CoreWebView2EnvironmentOptions parameter passed to when creating an environment with this instance. 38 | 39 | 40 | 41 | 42 | Create a using the current values of this instance's properties. 43 | 44 | A task which will provide the created environment on completion. 45 | 46 | As long as no other properties on this instance are changed, repeated calls to this method will return the same task/environment as earlier calls. 47 | If some other property is changed then the next call to this method will return a different task/environment. 48 | 49 | 50 | 51 | 52 | Control to embed WebView2 in WinForms. 53 | 54 | 55 | 56 | 57 | Create a new WebView2 WinForms control. 58 | After construction the property is null. 59 | Call to initialize the underlying . 60 | 61 | 62 | This control is effectively a wrapper around the WebView2 COM API, which you can find documentation for here: https://aka.ms/webview2 63 | You can directly access the underlying ICoreWebView2 interface and all of its functionality by accessing the property. 64 | Some of the most common COM functionality is also accessible directly through wrapper methods/properties/events on the control. 65 | 66 | Upon creation, the control's CoreWebView2 property will be null. 67 | This is because creating the CoreWebView2 is an expensive operation which involves things like launching Edge browser processes. 68 | There are two ways to cause the CoreWebView2 to be created: 69 | 1) Call the method. This is referred to as explicit initialization. 70 | 2) Set the property. This is referred to as implicit initialization. 71 | Either option will start initialization in the background and return back to the caller without waiting for it to finish. 72 | To specify options regarding the initialization process, either pass your own to EnsureCoreWebView2Async or set the control's property prior to initialization. 73 | 74 | When initialization has finished (regardless of how it was triggered) then the following things will occur, in this order: 75 | 1) The control's event will be invoked. If you need to perform one time setup operations on the CoreWebView2 prior to its use then you should do so in a handler for that event. 76 | 2) If a Uri has been set to the property then the control will start navigating to it in the background (i.e. these steps will continue without waiting for the navigation to finish). 77 | 3) The Task returned from will complete. 78 | 79 | For more details about any of the methods/properties/events involved in the initialization process, see its specific documentation. 80 | 81 | Accelerator key presses (e.g. Ctrl+P) that occur within the control will 82 | fire standard key press events such as OnKeyDown. You can suppress the 83 | control's default implementation of an accelerator key press (e.g. 84 | printing, in the case of Ctrl+P) by setting the Handled property of its 85 | EventArgs to true. Also note that the underlying browser process is 86 | blocked while these handlers execute, so: 87 | 88 | 89 | You should avoid doing a lot of work in these handlers. 90 | 91 | 92 | Some of the WebView2 and CoreWebView2 APIs may throw errors if 93 | invoked within these handlers due to being unable to communicate with 94 | the browser process. 95 | 96 | 97 | If you need to do a lot of work and/or invoke WebView2 APIs in response to 98 | accelerator keys then consider kicking off a background task or queuing 99 | the work for later execution on the UI thread. 100 | 101 | 102 | 103 | 104 | Cleans up any resources being used. 105 | 106 | true if managed resources should be disposed; otherwise, false. 107 | 108 | 109 | 110 | Overrides the base OnPaint event to have custom actions 111 | in designer mode 112 | 113 | The graphics devices which is the source 114 | 115 | 116 | 117 | Overrides the base WndProc events to handle specific window messages. 118 | 119 | The Message object containing the HWND window message and parameters 120 | 121 | 122 | 123 | Gets or sets a bag of options which are used during initialization of the control's . 124 | This property cannot be modified (an exception will be thrown) after initialization of the control's CoreWebView2 has started. 125 | 126 | Thrown if initialization of the control's CoreWebView2 has already started. 127 | 128 | 129 | 130 | Explicitly trigger initialization of the control's . 131 | 132 | 133 | A pre-created that should be used to create the . 134 | Creating your own environment gives you control over several options that affect how the is initialized. 135 | If you pass null (the default value) then a default environment will be created and used automatically. 136 | 137 | 138 | A Task that represents the background initialization process. 139 | When the task completes then the property will be available for use (i.e. non-null). 140 | Note that the control's event will be invoked before the task completes 141 | or on exceptions. 142 | 143 | 144 | Calling this method additional times will have no effect (any specified environment is ignored) and return the same Task as the first call. 145 | Calling this method after initialization has been implicitly triggered by setting the property will have no effect (any specified environment is ignored) and simply return a Task representing that initialization already in progress. 146 | 147 | Thrown when invoked from non-UI thread. 148 | Thrown when this instance of is already disposed. 149 | Thrown when browser process has unexpectedly and left this control in an invalid state. We are considering throwing a different type of exception for this case in the future. 150 | 151 | 152 | 153 | This is the private function which implements the actual background initialization task. 154 | Cannot be called if the control is already initialized or has been disposed. 155 | 156 | 157 | The environment to use to create the . 158 | If that is null then a default environment is created with and its default parameters. 159 | 160 | A task representing the background initialization process. 161 | All the event handlers added here need to be removed in . 162 | 163 | 164 | 165 | Protected CreateParams property. Used to set custom window styles to the forms HWND. 166 | 167 | 168 | 169 | 170 | Protected VisibilityChanged handler. 171 | 172 | 173 | 174 | 175 | Protected SizeChanged handler. 176 | 177 | 178 | 179 | 180 | Protected Select method: override this to capture tab direction when WebView control is activated 181 | 182 | 183 | 184 | 185 | Protected OnGotFocus handler. 186 | 187 | 188 | 189 | 190 | True if initialization finished successfully and the control is not disposed yet. 191 | 192 | 193 | 194 | 195 | Recursive retrieval of the parent control 196 | 197 | The control to get the parent for 198 | The root parent control 199 | 200 | 201 | 202 | The underlying CoreWebView2. Use this property to perform more operations on the WebView2 content than is exposed 203 | on the WebView2. This value is null until it is initialized and the object itself has undefined behaviour once the control is disposed. 204 | You can force the underlying CoreWebView2 to 205 | initialize via the method. 206 | 207 | 208 | 209 | 210 | The zoom factor for the WebView. 211 | 212 | 213 | 214 | 215 | The Source property is the URI of the top level document of the 216 | WebView2. Setting the Source is equivalent to calling . 217 | Setting the Source will trigger initialization of the , if not already initialized. 218 | The default value of Source is null, indicating that the is not yet initialized. 219 | 220 | Specified value is not an absolute . 221 | Specified value is null and the control is initialized. 222 | 223 | 224 | 225 | 226 | Returns true if the webview can navigate to a next page in the 227 | navigation history via the method. 228 | This is equivalent to the . 229 | If the underlying is not yet initialized, this property is false. 230 | 231 | 232 | 233 | 234 | 235 | Returns true if the webview can navigate to a previous page in the 236 | navigation history via the method. 237 | This is equivalent to the . 238 | If the underlying is not yet initialized, this property is false. 239 | 240 | 241 | 242 | 243 | 244 | The default background color for the WebView. 245 | 246 | 247 | 248 | 249 | Executes the provided script in the top level document of the . 250 | This is equivalent to . 251 | 252 | The underlying is not yet initialized. 253 | Thrown when browser process has unexpectedly and left this control in an invalid state. We are considering throwing a different type of exception for this case in the future. 254 | 255 | 256 | 257 | 258 | Reloads the top level document of the . 259 | This is equivalent to . 260 | 261 | The underlying is not yet initialized. 262 | Thrown when browser process has unexpectedly and left this control in an invalid state. We are considering throwing a different type of exception for this case in the future. 263 | 264 | 265 | 266 | 267 | Navigates to the next page in navigation history. 268 | This is equivalent to . 269 | If the underlying is not yet initialized, this method does nothing. 270 | 271 | 272 | 273 | 274 | 275 | Navigates to the previous page in navigation history. 276 | This is equivalent to . 277 | If the underlying is not yet initialized, this method does nothing. 278 | 279 | 280 | 281 | 282 | 283 | Renders the provided HTML as the top level document of the . 284 | This is equivalent to . 285 | 286 | The underlying is not yet initialized. 287 | Thrown when browser process has unexpectedly and left this control in an invalid state. We are considering throwing a different type of exception for this case in the future. 288 | 289 | 290 | 291 | 292 | Stops any in progress navigation in the . 293 | This is equivalent to . 294 | If the underlying is not yet initialized, this method does nothing. 295 | 296 | 297 | 298 | 299 | 300 | This event is triggered either 1) when the control's has finished being initialized (regardless of how it was triggered or whether it succeeded) but before it is used for anything 301 | OR 2) the initialization failed. 302 | You should handle this event if you need to perform one time setup operations on the CoreWebView2 which you want to affect all of its usages 303 | (e.g. adding event handlers, configuring settings, installing document creation scripts, adding host objects). 304 | 305 | 306 | This sender will be the WebView2 control, whose CoreWebView2 property will now be valid (i.e. non-null) for the first time 307 | if is true. 308 | Unlikely this event can fire second time (after reporting initialization success first) 309 | if the initialization is followed by navigation which fails. 310 | 311 | 312 | 313 | 314 | NavigationStarting dispatches before a new navigate starts for the top 315 | level document of the . 316 | This is equivalent to the event. 317 | 318 | 319 | 320 | 321 | 322 | NavigationCompleted dispatches after a navigate of the top level 323 | document completes rendering either successfully or not. 324 | This is equivalent to the event. 325 | 326 | 327 | 328 | 329 | 330 | WebMessageReceived dispatches after web content sends a message to the 331 | app host via chrome.webview.postMessage. 332 | This is equivalent to the event. 333 | 334 | 335 | 336 | 337 | 338 | SourceChanged dispatches after the property changes. This may happen 339 | during a navigation or if otherwise the script in the page changes the 340 | URI of the document. 341 | This is equivalent to the event. 342 | 343 | 344 | 345 | 346 | 347 | ContentLoading dispatches after a navigation begins to a new URI and the 348 | content of that URI begins to render. 349 | This is equivalent to the event. 350 | 351 | 352 | 353 | 354 | 355 | ZoomFactorChanged dispatches when the property changes. 356 | This is equivalent to the event. 357 | 358 | 359 | 360 | 361 | 362 | Required designer variable. 363 | 364 | 365 | 366 | 367 | Required method for Designer support - do not modify 368 | the contents of this method with the code editor. 369 | 370 | 371 | 372 | 373 | -------------------------------------------------------------------------------- /vendor/Microsoft.Web.WebView2.1.0.902.49/lib/net45/Microsoft.Web.WebView2.Wpf.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ziglibs/positron/f144d53ba4ad9e88e5883212ead13e8c1f7ad234/vendor/Microsoft.Web.WebView2.1.0.902.49/lib/net45/Microsoft.Web.WebView2.Wpf.dll -------------------------------------------------------------------------------- /vendor/Microsoft.Web.WebView2.1.0.902.49/lib/netcoreapp3.0/Microsoft.Web.WebView2.Core.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ziglibs/positron/f144d53ba4ad9e88e5883212ead13e8c1f7ad234/vendor/Microsoft.Web.WebView2.1.0.902.49/lib/netcoreapp3.0/Microsoft.Web.WebView2.Core.dll -------------------------------------------------------------------------------- /vendor/Microsoft.Web.WebView2.1.0.902.49/lib/netcoreapp3.0/Microsoft.Web.WebView2.WinForms.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ziglibs/positron/f144d53ba4ad9e88e5883212ead13e8c1f7ad234/vendor/Microsoft.Web.WebView2.1.0.902.49/lib/netcoreapp3.0/Microsoft.Web.WebView2.WinForms.dll -------------------------------------------------------------------------------- /vendor/Microsoft.Web.WebView2.1.0.902.49/lib/netcoreapp3.0/Microsoft.Web.WebView2.WinForms.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Microsoft.Web.WebView2.WinForms 5 | 6 | 7 | 8 | 9 | This class is a bundle of the most common parameters used to create a . 10 | Its main purpose is to be set to in order to customize the environment used by a during implicit initialization. 11 | 12 | 13 | This class isn't intended to contain all possible environment customization options. 14 | If you need complete control over the environment used by a WebView2 control then you'll need to initialize the control explicitly by 15 | creating your own environment with and passing it to 16 | *before* you set the property to anything. 17 | See the class documentation for an initialization overview. 18 | 19 | 20 | 21 | 22 | Creates a new instance of with default data for all properties. 23 | 24 | 25 | 26 | 27 | Gets or sets the value to pass as the browserExecutableFolder parameter of when creating an environment with this instance. 28 | 29 | 30 | 31 | 32 | Gets or sets the value to pass as the userDataFolder parameter of when creating an environment with this instance. 33 | 34 | 35 | 36 | 37 | Gets or sets the value to use for the Language property of the CoreWebView2EnvironmentOptions parameter passed to when creating an environment with this instance. 38 | 39 | 40 | 41 | 42 | Create a using the current values of this instance's properties. 43 | 44 | A task which will provide the created environment on completion. 45 | 46 | As long as no other properties on this instance are changed, repeated calls to this method will return the same task/environment as earlier calls. 47 | If some other property is changed then the next call to this method will return a different task/environment. 48 | 49 | 50 | 51 | 52 | Control to embed WebView2 in WinForms. 53 | 54 | 55 | 56 | 57 | Create a new WebView2 WinForms control. 58 | After construction the property is null. 59 | Call to initialize the underlying . 60 | 61 | 62 | This control is effectively a wrapper around the WebView2 COM API, which you can find documentation for here: https://aka.ms/webview2 63 | You can directly access the underlying ICoreWebView2 interface and all of its functionality by accessing the property. 64 | Some of the most common COM functionality is also accessible directly through wrapper methods/properties/events on the control. 65 | 66 | Upon creation, the control's CoreWebView2 property will be null. 67 | This is because creating the CoreWebView2 is an expensive operation which involves things like launching Edge browser processes. 68 | There are two ways to cause the CoreWebView2 to be created: 69 | 1) Call the method. This is referred to as explicit initialization. 70 | 2) Set the property. This is referred to as implicit initialization. 71 | Either option will start initialization in the background and return back to the caller without waiting for it to finish. 72 | To specify options regarding the initialization process, either pass your own to EnsureCoreWebView2Async or set the control's property prior to initialization. 73 | 74 | When initialization has finished (regardless of how it was triggered) then the following things will occur, in this order: 75 | 1) The control's event will be invoked. If you need to perform one time setup operations on the CoreWebView2 prior to its use then you should do so in a handler for that event. 76 | 2) If a Uri has been set to the property then the control will start navigating to it in the background (i.e. these steps will continue without waiting for the navigation to finish). 77 | 3) The Task returned from will complete. 78 | 79 | For more details about any of the methods/properties/events involved in the initialization process, see its specific documentation. 80 | 81 | Accelerator key presses (e.g. Ctrl+P) that occur within the control will 82 | fire standard key press events such as OnKeyDown. You can suppress the 83 | control's default implementation of an accelerator key press (e.g. 84 | printing, in the case of Ctrl+P) by setting the Handled property of its 85 | EventArgs to true. Also note that the underlying browser process is 86 | blocked while these handlers execute, so: 87 | 88 | 89 | You should avoid doing a lot of work in these handlers. 90 | 91 | 92 | Some of the WebView2 and CoreWebView2 APIs may throw errors if 93 | invoked within these handlers due to being unable to communicate with 94 | the browser process. 95 | 96 | 97 | If you need to do a lot of work and/or invoke WebView2 APIs in response to 98 | accelerator keys then consider kicking off a background task or queuing 99 | the work for later execution on the UI thread. 100 | 101 | 102 | 103 | 104 | Cleans up any resources being used. 105 | 106 | true if managed resources should be disposed; otherwise, false. 107 | 108 | 109 | 110 | Overrides the base OnPaint event to have custom actions 111 | in designer mode 112 | 113 | The graphics devices which is the source 114 | 115 | 116 | 117 | Overrides the base WndProc events to handle specific window messages. 118 | 119 | The Message object containing the HWND window message and parameters 120 | 121 | 122 | 123 | Gets or sets a bag of options which are used during initialization of the control's . 124 | This property cannot be modified (an exception will be thrown) after initialization of the control's CoreWebView2 has started. 125 | 126 | Thrown if initialization of the control's CoreWebView2 has already started. 127 | 128 | 129 | 130 | Explicitly trigger initialization of the control's . 131 | 132 | 133 | A pre-created that should be used to create the . 134 | Creating your own environment gives you control over several options that affect how the is initialized. 135 | If you pass null (the default value) then a default environment will be created and used automatically. 136 | 137 | 138 | A Task that represents the background initialization process. 139 | When the task completes then the property will be available for use (i.e. non-null). 140 | Note that the control's event will be invoked before the task completes 141 | or on exceptions. 142 | 143 | 144 | Calling this method additional times will have no effect (any specified environment is ignored) and return the same Task as the first call. 145 | Calling this method after initialization has been implicitly triggered by setting the property will have no effect (any specified environment is ignored) and simply return a Task representing that initialization already in progress. 146 | 147 | Thrown when invoked from non-UI thread. 148 | Thrown when this instance of is already disposed. 149 | Thrown when browser process has unexpectedly and left this control in an invalid state. We are considering throwing a different type of exception for this case in the future. 150 | 151 | 152 | 153 | This is the private function which implements the actual background initialization task. 154 | Cannot be called if the control is already initialized or has been disposed. 155 | 156 | 157 | The environment to use to create the . 158 | If that is null then a default environment is created with and its default parameters. 159 | 160 | A task representing the background initialization process. 161 | All the event handlers added here need to be removed in . 162 | 163 | 164 | 165 | Protected CreateParams property. Used to set custom window styles to the forms HWND. 166 | 167 | 168 | 169 | 170 | Protected VisibilityChanged handler. 171 | 172 | 173 | 174 | 175 | Protected SizeChanged handler. 176 | 177 | 178 | 179 | 180 | Protected Select method: override this to capture tab direction when WebView control is activated 181 | 182 | 183 | 184 | 185 | Protected OnGotFocus handler. 186 | 187 | 188 | 189 | 190 | True if initialization finished successfully and the control is not disposed yet. 191 | 192 | 193 | 194 | 195 | Recursive retrieval of the parent control 196 | 197 | The control to get the parent for 198 | The root parent control 199 | 200 | 201 | 202 | The underlying CoreWebView2. Use this property to perform more operations on the WebView2 content than is exposed 203 | on the WebView2. This value is null until it is initialized and the object itself has undefined behaviour once the control is disposed. 204 | You can force the underlying CoreWebView2 to 205 | initialize via the method. 206 | 207 | 208 | 209 | 210 | The zoom factor for the WebView. 211 | 212 | 213 | 214 | 215 | The Source property is the URI of the top level document of the 216 | WebView2. Setting the Source is equivalent to calling . 217 | Setting the Source will trigger initialization of the , if not already initialized. 218 | The default value of Source is null, indicating that the is not yet initialized. 219 | 220 | Specified value is not an absolute . 221 | Specified value is null and the control is initialized. 222 | 223 | 224 | 225 | 226 | Returns true if the webview can navigate to a next page in the 227 | navigation history via the method. 228 | This is equivalent to the . 229 | If the underlying is not yet initialized, this property is false. 230 | 231 | 232 | 233 | 234 | 235 | Returns true if the webview can navigate to a previous page in the 236 | navigation history via the method. 237 | This is equivalent to the . 238 | If the underlying is not yet initialized, this property is false. 239 | 240 | 241 | 242 | 243 | 244 | The default background color for the WebView. 245 | 246 | 247 | 248 | 249 | Executes the provided script in the top level document of the . 250 | This is equivalent to . 251 | 252 | The underlying is not yet initialized. 253 | Thrown when browser process has unexpectedly and left this control in an invalid state. We are considering throwing a different type of exception for this case in the future. 254 | 255 | 256 | 257 | 258 | Reloads the top level document of the . 259 | This is equivalent to . 260 | 261 | The underlying is not yet initialized. 262 | Thrown when browser process has unexpectedly and left this control in an invalid state. We are considering throwing a different type of exception for this case in the future. 263 | 264 | 265 | 266 | 267 | Navigates to the next page in navigation history. 268 | This is equivalent to . 269 | If the underlying is not yet initialized, this method does nothing. 270 | 271 | 272 | 273 | 274 | 275 | Navigates to the previous page in navigation history. 276 | This is equivalent to . 277 | If the underlying is not yet initialized, this method does nothing. 278 | 279 | 280 | 281 | 282 | 283 | Renders the provided HTML as the top level document of the . 284 | This is equivalent to . 285 | 286 | The underlying is not yet initialized. 287 | Thrown when browser process has unexpectedly and left this control in an invalid state. We are considering throwing a different type of exception for this case in the future. 288 | 289 | 290 | 291 | 292 | Stops any in progress navigation in the . 293 | This is equivalent to . 294 | If the underlying is not yet initialized, this method does nothing. 295 | 296 | 297 | 298 | 299 | 300 | This event is triggered either 1) when the control's has finished being initialized (regardless of how it was triggered or whether it succeeded) but before it is used for anything 301 | OR 2) the initialization failed. 302 | You should handle this event if you need to perform one time setup operations on the CoreWebView2 which you want to affect all of its usages 303 | (e.g. adding event handlers, configuring settings, installing document creation scripts, adding host objects). 304 | 305 | 306 | This sender will be the WebView2 control, whose CoreWebView2 property will now be valid (i.e. non-null) for the first time 307 | if is true. 308 | Unlikely this event can fire second time (after reporting initialization success first) 309 | if the initialization is followed by navigation which fails. 310 | 311 | 312 | 313 | 314 | NavigationStarting dispatches before a new navigate starts for the top 315 | level document of the . 316 | This is equivalent to the event. 317 | 318 | 319 | 320 | 321 | 322 | NavigationCompleted dispatches after a navigate of the top level 323 | document completes rendering either successfully or not. 324 | This is equivalent to the event. 325 | 326 | 327 | 328 | 329 | 330 | WebMessageReceived dispatches after web content sends a message to the 331 | app host via chrome.webview.postMessage. 332 | This is equivalent to the event. 333 | 334 | 335 | 336 | 337 | 338 | SourceChanged dispatches after the property changes. This may happen 339 | during a navigation or if otherwise the script in the page changes the 340 | URI of the document. 341 | This is equivalent to the event. 342 | 343 | 344 | 345 | 346 | 347 | ContentLoading dispatches after a navigation begins to a new URI and the 348 | content of that URI begins to render. 349 | This is equivalent to the event. 350 | 351 | 352 | 353 | 354 | 355 | ZoomFactorChanged dispatches when the property changes. 356 | This is equivalent to the event. 357 | 358 | 359 | 360 | 361 | 362 | Required designer variable. 363 | 364 | 365 | 366 | 367 | Required method for Designer support - do not modify 368 | the contents of this method with the code editor. 369 | 370 | 371 | 372 | 373 | -------------------------------------------------------------------------------- /vendor/Microsoft.Web.WebView2.1.0.902.49/lib/netcoreapp3.0/Microsoft.Web.WebView2.Wpf.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ziglibs/positron/f144d53ba4ad9e88e5883212ead13e8c1f7ad234/vendor/Microsoft.Web.WebView2.1.0.902.49/lib/netcoreapp3.0/Microsoft.Web.WebView2.Wpf.dll -------------------------------------------------------------------------------- /vendor/Microsoft.Web.WebView2.1.0.902.49/runtimes/win-arm64/native/WebView2Loader.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ziglibs/positron/f144d53ba4ad9e88e5883212ead13e8c1f7ad234/vendor/Microsoft.Web.WebView2.1.0.902.49/runtimes/win-arm64/native/WebView2Loader.dll -------------------------------------------------------------------------------- /vendor/Microsoft.Web.WebView2.1.0.902.49/runtimes/win-x64/native/WebView2Loader.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ziglibs/positron/f144d53ba4ad9e88e5883212ead13e8c1f7ad234/vendor/Microsoft.Web.WebView2.1.0.902.49/runtimes/win-x64/native/WebView2Loader.dll -------------------------------------------------------------------------------- /vendor/Microsoft.Web.WebView2.1.0.902.49/runtimes/win-x86/native/WebView2Loader.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ziglibs/positron/f144d53ba4ad9e88e5883212ead13e8c1f7ad234/vendor/Microsoft.Web.WebView2.1.0.902.49/runtimes/win-x86/native/WebView2Loader.dll -------------------------------------------------------------------------------- /vendor/Microsoft.Web.WebView2.1.0.902.49/tools/VisualStudioToolsManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /vendor/winsdk/EventToken.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | /* this ALWAYS GENERATED file contains the definitions for the interfaces */ 4 | 5 | 6 | /* File created by MIDL compiler version 8.01.0622 */ 7 | /* @@MIDL_FILE_HEADING( ) */ 8 | 9 | 10 | 11 | /* verify that the version is high enough to compile this file*/ 12 | #ifndef __REQUIRED_RPCNDR_H_VERSION__ 13 | #define __REQUIRED_RPCNDR_H_VERSION__ 500 14 | #endif 15 | 16 | /* verify that the version is high enough to compile this file*/ 17 | #ifndef __REQUIRED_RPCSAL_H_VERSION__ 18 | #define __REQUIRED_RPCSAL_H_VERSION__ 100 19 | #endif 20 | 21 | #include "rpc.h" 22 | #include "rpcndr.h" 23 | 24 | #ifndef __RPCNDR_H_VERSION__ 25 | #error this stub requires an updated version of 26 | #endif /* __RPCNDR_H_VERSION__ */ 27 | 28 | 29 | #ifndef __eventtoken_h__ 30 | #define __eventtoken_h__ 31 | 32 | #if defined(_MSC_VER) && (_MSC_VER >= 1020) 33 | #pragma once 34 | #endif 35 | 36 | /* Forward Declarations */ 37 | 38 | #ifdef __cplusplus 39 | extern "C"{ 40 | #endif 41 | 42 | 43 | /* interface __MIDL_itf_eventtoken_0000_0000 */ 44 | /* [local] */ 45 | 46 | // Microsoft Windows 47 | // Copyright (c) Microsoft Corporation. All rights reserved. 48 | #pragma once 49 | typedef struct EventRegistrationToken 50 | { 51 | __int64 value; 52 | } EventRegistrationToken; 53 | 54 | 55 | 56 | extern RPC_IF_HANDLE __MIDL_itf_eventtoken_0000_0000_v0_0_c_ifspec; 57 | extern RPC_IF_HANDLE __MIDL_itf_eventtoken_0000_0000_v0_0_s_ifspec; 58 | 59 | /* Additional Prototypes for ALL interfaces */ 60 | 61 | /* end of Additional Prototypes */ 62 | 63 | #ifdef __cplusplus 64 | } 65 | #endif 66 | 67 | #endif 68 | 69 | 70 | --------------------------------------------------------------------------------