From 54ca96cf2ad6df92fbd923e833c1c35169ca2ba7 Mon Sep 17 00:00:00 2001 From: DarknessFX Date: Sun, 5 Jan 2025 13:52:52 -0500 Subject: [PATCH] . --- Base/Base.ico | Bin 0 -> 4286 bytes Base/Base.rc | 32 + Base/build.zig | 4 + BaseEx/build.zig | 1 - .../.vscode/BaseWebview.code-workspace | 200 ++++ BaseWebview/.vscode/launch.json | 36 + BaseWebview/.vscode/tasks.json | 106 +++ BaseWebview/BaseWebview.ico | Bin 0 -> 90022 bytes BaseWebview/BaseWebview.rc | 32 + BaseWebview/asset/webview.ico | Bin 0 -> 90022 bytes BaseWebview/asset/webview.png | Bin 0 -> 1547 bytes BaseWebview/build.zig | 57 ++ BaseWebview/lib/webview/include/api.h | 244 +++++ BaseWebview/lib/webview/include/backends.hh | 43 + BaseWebview/lib/webview/include/c_api_impl.hh | 255 +++++ .../include/detail/backends/cocoa_webkit.hh | 688 ++++++++++++++ .../include/detail/backends/gtk_webkitgtk.hh | 334 +++++++ .../include/detail/backends/win32_edge.hh | 892 ++++++++++++++++++ .../webview/include/detail/basic_result.hh | 116 +++ .../lib/webview/include/detail/engine_base.hh | 343 +++++++ .../lib/webview/include/detail/exceptions.hh | 39 + .../lib/webview/include/detail/json.hh | 331 +++++++ .../webview/include/detail/native_library.hh | 167 ++++ .../lib/webview/include/detail/optional.hh | 115 +++ .../include/detail/platform/darwin/cocoa.hh | 62 ++ .../include/detail/platform/darwin/objc.hh | 112 +++ .../include/detail/platform/darwin/webkit.hh | 42 + .../detail/platform/linux/gtk/compat.hh | 128 +++ .../detail/platform/linux/webkitgtk/compat.hh | 135 +++ .../detail/platform/linux/webkitgtk/dmabuf.hh | 160 ++++ .../platform/windows/com_init_wrapper.hh | 115 +++ .../include/detail/platform/windows/dpi.hh | 156 +++ .../include/detail/platform/windows/dwmapi.hh | 60 ++ .../include/detail/platform/windows/iid.hh | 71 ++ .../include/detail/platform/windows/ntdll.hh | 51 + .../detail/platform/windows/reg_key.hh | 128 +++ .../include/detail/platform/windows/shcore.hh | 52 + .../include/detail/platform/windows/theme.hh | 69 ++ .../include/detail/platform/windows/user32.hh | 83 ++ .../detail/platform/windows/version.hh | 143 +++ .../platform/windows/webview2/loader.hh | 375 ++++++++ .../lib/webview/include/detail/user_script.hh | 73 ++ .../webview/include/detail/utility/string.hh | 96 ++ BaseWebview/lib/webview/include/errors.h | 67 ++ BaseWebview/lib/webview/include/errors.hh | 82 ++ .../lib/webview/include/json_deprecated.hh | 58 ++ BaseWebview/lib/webview/include/macros.h | 123 +++ BaseWebview/lib/webview/include/types.h | 81 ++ BaseWebview/lib/webview/include/types.hh | 45 + BaseWebview/lib/webview/include/version.h | 67 ++ BaseWebview/lib/webview/include/webview.h | 37 + BaseWebview/lib/webview/webview.dll | Bin 0 -> 94208 bytes BaseWebview/lib/webview/webview.exp | Bin 0 -> 2701 bytes BaseWebview/lib/webview/webview.lib | Bin 0 -> 4972 bytes BaseWebview/lib/webview/webview_static.lib | Bin 0 -> 779998 bytes BaseWebview/main.zig | 115 +++ BaseWebview/tools/buildReleaseStrip.bat | 75 ++ README.md | 3 + 58 files changed, 6898 insertions(+), 1 deletion(-) create mode 100644 Base/Base.ico create mode 100644 Base/Base.rc create mode 100644 BaseWebview/.vscode/BaseWebview.code-workspace create mode 100644 BaseWebview/.vscode/launch.json create mode 100644 BaseWebview/.vscode/tasks.json create mode 100644 BaseWebview/BaseWebview.ico create mode 100644 BaseWebview/BaseWebview.rc create mode 100644 BaseWebview/asset/webview.ico create mode 100644 BaseWebview/asset/webview.png create mode 100644 BaseWebview/build.zig create mode 100644 BaseWebview/lib/webview/include/api.h create mode 100644 BaseWebview/lib/webview/include/backends.hh create mode 100644 BaseWebview/lib/webview/include/c_api_impl.hh create mode 100644 BaseWebview/lib/webview/include/detail/backends/cocoa_webkit.hh create mode 100644 BaseWebview/lib/webview/include/detail/backends/gtk_webkitgtk.hh create mode 100644 BaseWebview/lib/webview/include/detail/backends/win32_edge.hh create mode 100644 BaseWebview/lib/webview/include/detail/basic_result.hh create mode 100644 BaseWebview/lib/webview/include/detail/engine_base.hh create mode 100644 BaseWebview/lib/webview/include/detail/exceptions.hh create mode 100644 BaseWebview/lib/webview/include/detail/json.hh create mode 100644 BaseWebview/lib/webview/include/detail/native_library.hh create mode 100644 BaseWebview/lib/webview/include/detail/optional.hh create mode 100644 BaseWebview/lib/webview/include/detail/platform/darwin/cocoa.hh create mode 100644 BaseWebview/lib/webview/include/detail/platform/darwin/objc.hh create mode 100644 BaseWebview/lib/webview/include/detail/platform/darwin/webkit.hh create mode 100644 BaseWebview/lib/webview/include/detail/platform/linux/gtk/compat.hh create mode 100644 BaseWebview/lib/webview/include/detail/platform/linux/webkitgtk/compat.hh create mode 100644 BaseWebview/lib/webview/include/detail/platform/linux/webkitgtk/dmabuf.hh create mode 100644 BaseWebview/lib/webview/include/detail/platform/windows/com_init_wrapper.hh create mode 100644 BaseWebview/lib/webview/include/detail/platform/windows/dpi.hh create mode 100644 BaseWebview/lib/webview/include/detail/platform/windows/dwmapi.hh create mode 100644 BaseWebview/lib/webview/include/detail/platform/windows/iid.hh create mode 100644 BaseWebview/lib/webview/include/detail/platform/windows/ntdll.hh create mode 100644 BaseWebview/lib/webview/include/detail/platform/windows/reg_key.hh create mode 100644 BaseWebview/lib/webview/include/detail/platform/windows/shcore.hh create mode 100644 BaseWebview/lib/webview/include/detail/platform/windows/theme.hh create mode 100644 BaseWebview/lib/webview/include/detail/platform/windows/user32.hh create mode 100644 BaseWebview/lib/webview/include/detail/platform/windows/version.hh create mode 100644 BaseWebview/lib/webview/include/detail/platform/windows/webview2/loader.hh create mode 100644 BaseWebview/lib/webview/include/detail/user_script.hh create mode 100644 BaseWebview/lib/webview/include/detail/utility/string.hh create mode 100644 BaseWebview/lib/webview/include/errors.h create mode 100644 BaseWebview/lib/webview/include/errors.hh create mode 100644 BaseWebview/lib/webview/include/json_deprecated.hh create mode 100644 BaseWebview/lib/webview/include/macros.h create mode 100644 BaseWebview/lib/webview/include/types.h create mode 100644 BaseWebview/lib/webview/include/types.hh create mode 100644 BaseWebview/lib/webview/include/version.h create mode 100644 BaseWebview/lib/webview/include/webview.h create mode 100644 BaseWebview/lib/webview/webview.dll create mode 100644 BaseWebview/lib/webview/webview.exp create mode 100644 BaseWebview/lib/webview/webview.lib create mode 100644 BaseWebview/lib/webview/webview_static.lib create mode 100644 BaseWebview/main.zig create mode 100644 BaseWebview/tools/buildReleaseStrip.bat diff --git a/Base/Base.ico b/Base/Base.ico new file mode 100644 index 0000000000000000000000000000000000000000..e376252bee28dcde9ef6006bbea55f00a122feca GIT binary patch literal 4286 zcmd^C-HR4g7$4Ihh*D}R+nuj>S48k85b0Jdf`a~lKq|V4g6KlKDU!XA=t3w_L<`Z~ zIWxPyQm%ciMj~S6A{Is@f`}L-m{7K?bLMs3IsJam%(%0|IBT`r2F>!kXXc#e<9D9( ze4MqcMfmf53y*INFSD$N@wQ67ENdM`tm4qUA!ecGu>T5%6z7%g2fm~KZV~)L8GqCmdoX_7-)ru1 z0{yG_Wq(RN@aJvzknME>>t`7U8Ge1Tgg@IuG)X<=I|4ch8k?qg%vJhZe4oeO4^v0? zQ0&-2@;zhO#U8O%Oa7y-<+Clt=^yb>$A+~4A z=YC+HklGOH7pVt*aA$^TT1I@%I$j~3so+)mCCV}`1w&EyEBrI$VK?936zAz9d+3Jb z1TdT+hJqh_omvWXB7F{bpq_BzF~P6&D9@*szodsek{4OOPbEHaM$6mgp2?Q_ NC;7i`6VLx)?N4B*ijx2U literal 0 HcmV?d00001 diff --git a/Base/Base.rc b/Base/Base.rc new file mode 100644 index 0000000..79fc6b6 --- /dev/null +++ b/Base/Base.rc @@ -0,0 +1,32 @@ +#include "windows.h" + +IDI_ICON1 ICON "Base.ico" + +1 VERSIONINFO + FILEVERSION 1,0,0,0 + PRODUCTVERSION 1,0,0,0 + FILEFLAGSMASK 0x3fL + FILEFLAGS 0x1L + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "Base" + VALUE "FileDescription", "Base" + VALUE "FileVersion", "1.0.0.0" + VALUE "InternalName", "Base.exe" + VALUE "LegalCopyright", "Copyleft(c) Created by DarknessFX, http://dfx.lv/ , @DrkFX." + VALUE "OriginalFilename", "Base.exe" + VALUE "ProductName", "Base" + VALUE "ProductVersion", "1.0.0.0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END diff --git a/Base/build.zig b/Base/build.zig index 6e1d017..d648ae5 100644 --- a/Base/build.zig +++ b/Base/build.zig @@ -14,6 +14,10 @@ pub fn build(b: *std.Build) void { .target = target, .optimize = optimize }); + exe.addWin32ResourceFile(.{ + .file = b.path(projectname ++ ".rc"), + .flags = &.{"/c65001"}, // UTF-8 codepage + }); switch (optimize) { .Debug => b.exe_dir = "bin/Debug", diff --git a/BaseEx/build.zig b/BaseEx/build.zig index 2c6f75b..5ddcb13 100644 --- a/BaseEx/build.zig +++ b/BaseEx/build.zig @@ -18,7 +18,6 @@ pub fn build(b: *std.Build) void { .file = b.path(projectname ++ ".rc"), .flags = &.{"/c65001"}, // UTF-8 codepage }); - exe.linkLibC(); switch (optimize) { .Debug => b.exe_dir = "bin/Debug", diff --git a/BaseWebview/.vscode/BaseWebview.code-workspace b/BaseWebview/.vscode/BaseWebview.code-workspace new file mode 100644 index 0000000..4755d19 --- /dev/null +++ b/BaseWebview/.vscode/BaseWebview.code-workspace @@ -0,0 +1,200 @@ +{ + "folders": [{ "path": ".." }], + "settings": { + // Zig Workspace + "files.exclude": { + "*.lnk": true, + "**/.git": true, + ".gitignore": true, + "**/.svn": true, + "**/.hg": true, + "**/CVS": true, + "**/.DS_Store": true, + "**/Thumbs.db": true, + // VSCODE FOLDERS -------- + "**/.vscode": false, + "**/.code-workspace": true, + // ZIG FOLDERS -------- + "**/zig-cache": true, + "**/.zig-cache": true + }, + "search.exclude": { + "**/node_modules": true, + "**/bower_components": true, + "**/*.code-search": true, + // ZIG FOLDERS -------- + "**/zig-out": true + }, + "files.watcherExclude": { + // ZIG FOLDERS -------- + "**/zig-cache": true + }, + + // DarknessFX settings + "telemetry.telemetryLevel": "off", + "workbench.colorTheme": "Default Dark+", + "workbench.startupEditor": "none", + "workbench.colorCustomizations": { + "statusBar.background" : "#1A1A1A", + "statusBar.noFolderBackground" : "#212121", + "statusBar.debuggingBackground": "#263238", + "editorBracketHighlight.foreground1": "#888888", + "editorBracketHighlight.foreground2": "#686888", + "editorBracketHighlight.foreground3": "#688868", + "editorBracketHighlight.foreground4": "#886868", + "editorBracketHighlight.foreground5": "#686868", + }, + "editor.tokenColorCustomizations": { + "comments": "#546E7A", + "functions": "#DFAB4B", + "keywords": "#367CB6", + "numbers": "#CE9178", + "strings": "#CE9178", + "types": "#2EA990", + "variables": "#7CBCDE", + + "[Default Dark+]": { + "textMateRules": [ + { + "scope": [ + "other", + "source", + "keyword.operator", + "keyword.operator.noexcept", + "punctuation", + "punctuation.dot", + "punctuation.comma", + "punctuation.definitions", + "punctuation.definition.comment", + "punctuation.definition.tag", + "punctuation.definition.tag.html", + "punctuation.definition.tag.begin.html", + "punctuation.definition.tag.end.html", + "punctuation.section.embedded", + "punctuation.separator", + "punctuation.separator.inheritance.php", + ], + "settings": { + "foreground": "#758E68", + "fontStyle": "", + } + }, + { + "scope": [ + "comment", + ], + "settings": { + "fontStyle": "italic", + } + }, + { + "scope": [ + "keyword", + "keyword.control", + "keyword.other", + "keyword.other.template", + "keyword.other.substitution", + "keyword.other.unit", + "storage", + "storage.type", + "storage.modifier", + "keywordLiteral", + "keyword.literal", + "keyword.constant.default", + ], + "settings": { + "fontStyle": "italic", + "foreground": "#367CB6" + } + }, + { + "scope": [ + "support.class", + "support.type", + "entity.name.type", + "entity.name.namespace", + "entity.other.attribute", + "entity.name.scope-resolution", + "entity.name.class", + ], + "settings": { + "foreground": "#2EA990" + } + }, + ], + }, + }, + "editor.tabSize": 2, + "editor.fontSize": 16, + "editor.unicodeHighlight.invisibleCharacters": false, + "editor.autoClosingBrackets": "never", + "editor.autoSurround": "never", + "editor.autoClosingQuotes": "never", + "editor.detectIndentation": false, + "editor.lightbulb.enabled": "off", + "workbench.editor.focusRecentEditorAfterClose": false, + "workbench.panel.defaultLocation": "left", + "workbench.panel.opensMaximized": "never", + "workbench.editor.tabSizing": "fixed", + "workbench.editor.tabSizingFixedMaxWidth": 140, + "workbench.sideBar.location": "right", + "debug.allowBreakpointsEverywhere": true, + "debug.onTaskErrors": "abort", + "explorer.confirmDragAndDrop": false, + "explorer.compactFolders": false, + "explorer.confirmDelete": false, + "terminal.integrated.profiles.windows": { + "Windows Terminal": { + "path": "wt", + "args": [ + "-p", "cmd", "cmd" + ], + "icon": "terminal-bash", + }, + "Command Prompt": { + "path": [ + "${env:windir}\\Sysnative\\cmd.exe", + "${env:windir}\\System32\\cmd.exe" + ], + "args": [], + "icon": "terminal-cmd" + }, + "PowerShell": { + "source": "PowerShell", + "icon": "terminal-powershell" + }, + "Git Bash": { + "source": "Git Bash" + }, + }, + "terminal.integrated.tabs.location": "left", + "terminal.integrated.defaultProfile.windows": "Command Prompt", + "C_Cpp.vcFormat.space.groupSquareBrackets": false, + "C_Cpp.loggingLevel": "Debug", + "C_Cpp.codeAnalysis.clangTidy.codeAction.formatFixes": false, + "git.openRepositoryInParentFolders": "never", + "html.autoCreateQuotes": false, + "zig.buildFilePath": "${workspaceFolder}", + "zig.formattingProvider": "off", + "zig.checkForUpdate": false, + "zig.zls.enabled": true, + "zig.zls.highlightGlobalVarDeclarations": true, + "zig.zls.semanticTokens": "full", + "zig.zls.enableArgumentPlaceholders": false, + "zig.zls.inlayHintsExcludeSingleArgument": false, + "zig.zls.enableAutofix": false, + "zig.zls.checkForUpdate": false, + "zig.zls.enableInlayHints": false, + "zig.zls.preferAstCheckAsChildProcess": false, + "files.associations": { + "raylib.h": "c", + "rcamera.h": "c", + "raymath.h": "c" + }, + }, + "extensions": { + "recommendations": [ + "pkief.material-icon-theme" + ] + } +} diff --git a/BaseWebview/.vscode/launch.json b/BaseWebview/.vscode/launch.json new file mode 100644 index 0000000..d4ce92d --- /dev/null +++ b/BaseWebview/.vscode/launch.json @@ -0,0 +1,36 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Debug", + "type": "cppvsdbg", + "request": "launch", + "presentation": { + "hidden": false, + "group": "", + "order": 1 + }, + "program": "${workspaceFolder}/bin/Debug/${workspaceFolderBasename}.exe", + "args": [ ], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "preLaunchTask": "${defaultBuildTask}", + "console": "integratedTerminal", + "symbolSearchPath": "${workspaceFolder}/bin/Debug/", + }, + { + "name": "Debug Attach", + "type": "cppvsdbg", + "request": "attach", + "presentation": { + "hidden": true, + "group": "", + "order": 2 + }, + "preLaunchTask": "Zig: Run main", + "internalConsoleOptions": "openOnFirstSessionStart", + "processId": "${command:pickProcess}" + } + ] +} \ No newline at end of file diff --git a/BaseWebview/.vscode/tasks.json b/BaseWebview/.vscode/tasks.json new file mode 100644 index 0000000..6a0d691 --- /dev/null +++ b/BaseWebview/.vscode/tasks.json @@ -0,0 +1,106 @@ +{ + "tasks": [{ + "label": "Zig: Build", + "args": [ "build", "--summary", "all" ], + "detail": "zig build --summary all", + "command": "zig", "type": "shell", "options": { "cwd": "${workspaceRoot}" }, "group": { "kind": "build", "isDefault": true }, + "presentation": { "group": "build", "echo": true, "reveal": "always", "focus": false, "panel": "shared", "showReuseMessage": false, "clear": true, "close": false }, "problemMatcher": [] + }, + { + "label": "Zig: Build Release Safe.", + "args": [ "build", "-Doptimize=ReleaseSafe", "--summary", "all" ], + "detail": "zig build -Doptimize=ReleaseSafe --summary all", + "command": "zig", "type": "shell", "options": { "cwd": "${workspaceRoot}" }, "group": { "kind": "build", "isDefault": false }, + "presentation": { "group": "build", "echo": true, "reveal": "always", "focus": false, "panel": "shared", "showReuseMessage": false, "clear": true, "close": false }, "problemMatcher": [] + }, + { + "label": "Zig: Build Release Fast.", + "args": [ "build", "-Doptimize=ReleaseFast", "--summary", "all" ], + "detail": "zig build -Doptimize=ReleaseFast --summary all", + "command": "zig","type": "shell", "options": { "cwd": "${workspaceRoot}" }, "group": { "kind": "build", "isDefault": false }, + "presentation": { "group": "build", "echo": true, "reveal": "always", "focus": false, "panel": "shared", "showReuseMessage": false, "clear": true, "close": false }, "problemMatcher": [] + }, + { + "label": "Zig: Build Release Small.", + "args": [ "build", "-Doptimize=ReleaseSmall", "--summary", "all" ], + "detail": "zig build -Doptimize=ReleaseSmall --summary all", + "command": "zig", "type": "shell", "options": { "cwd": "${workspaceRoot}" }, "group": { "kind": "build", "isDefault": false }, + "presentation": { "group": "build", "echo": true, "reveal": "always", "focus": false, "panel": "shared", "showReuseMessage": false, "clear": true, "close": false }, "problemMatcher": [] + }, + { + "label": "Zig: Build Release Strip.", + "args": [ ], + "detail": "zig build-exe -O ReleaseSmall -fstrip -fsingle-threaded (+ lots of options) main.zig", + "command": "buildReleaseStrip.bat", "type": "shell", "options": { "cwd": "${workspaceRoot}\\tools" }, "group": { "kind": "build", "isDefault": false }, + "presentation": { "group": "build", "echo": true, "reveal": "always", "focus": false, "panel": "shared", "showReuseMessage": false, "clear": true, "close": false }, "problemMatcher": [] + }, + { + "label": "Zig: Test", + "args": [ "test", "${file}" ], + "detail": "'zig test' in the current workspace.", + "command": "zig", "type": "shell", "options": { "cwd": "${workspaceRoot}" }, "group": { "kind": "test", "isDefault": true }, + "presentation": { "group": "test", "echo": true, "reveal": "always", "focus": false, "panel": "shared", "showReuseMessage": false, "clear": true, "close": false }, "problemMatcher": [] + }, + { + "label": "Zig: Run main", + "args": [ "run", "-lc", + "-I.", "-Ilib/webview/include", + "-Llib/webview", + "-lwebview", + "main.zig", + ], + "detail": "zig run main.zig", + "command": "zig", "type": "shell", "options": { "cwd": "${workspaceRoot}" }, "group": { "kind": "none", "isDefault": true }, + "presentation": { "group": "launch", "echo": true, "reveal": "always", "focus": false, "panel": "shared", "showReuseMessage": false, "clear": true, "close": false }, "problemMatcher": [] + }, + { + "label": "Zig: Run main (With Args)", + "args": [ "run", "-lc", "main.zig", "--", "ArgsForYourProgram" ], + "detail": "zig run main.zig -- ArgsForYourProgram", + "command": "zig", "type": "shell", "options": { "cwd": "${workspaceRoot}" }, "group": { "kind": "none", "isDefault": false }, + "presentation": { "group": "launch", "echo": true, "reveal": "always", "focus": false, "panel": "shared", "showReuseMessage": false, "clear": true, "close": false }, "problemMatcher": [] + }, + { + "label": "Zig: Run main (Fast)", + "args": [ "run", "-lc", "main.zig", "-O", "ReleaseFast" ], + "detail": "zig run main.zig -O ReleaseFast", + "command": "zig", "type": "shell", "options": { "cwd": "${workspaceRoot}" }, "group": { "kind": "none", "isDefault": false }, + "presentation": { "group": "launch", "echo": true, "reveal": "always", "focus": false, "panel": "shared", "showReuseMessage": false, "clear": true, "close": false }, "problemMatcher": [] + }, + { + "label": "Zig: Run main (Safe)", + "args": [ "run", "-lc", "main.zig", "-O", "ReleaseSafe" ], + "detail": "zig run main.zig -O ReleaseSafe", + "command": "zig", "type": "shell", "options": { "cwd": "${workspaceRoot}" }, "group": { "kind": "none", "isDefault": false }, + "presentation": { "group": "launch", "echo": true, "reveal": "always", "focus": false, "panel": "shared", "showReuseMessage": false, "clear": true, "close": false }, "problemMatcher": [] + }, + { + "label": "Zig: Run main (Small)", + "args": [ "run", "-lc", "main.zig", "-O", "ReleaseSmall" ], + "detail": "zig run main.zig -O ReleaseSmall", + "command": "zig", "type": "shell", "options": { "cwd": "${workspaceRoot}" }, "group": { "kind": "none", "isDefault": false }, + "presentation": { "group": "launch", "echo": true, "reveal": "always", "focus": false, "panel": "shared", "showReuseMessage": false, "clear": true, "close": false }, "problemMatcher": [] + }, + { + "label": "Zig: Run current file", + "args": [ "run", "${file}" ], + "detail": "'zig run' active file in the current workspace.", + "command": "zig", "type": "shell", "options": { "cwd": "${workspaceRoot}" }, "group": { "kind": "none", "isDefault": false }, + "presentation": { "group": "launch", "echo": true, "reveal": "always", "focus": false, "panel": "shared", "showReuseMessage": false, "clear": true, "close": false }, "problemMatcher": [] + }, + { + "label": "zTime Zig: Run current file", + "args": [ "zig", "run", "${file}" ], + "detail": "'zTime zig run' active file in the current workspace.", + "command": "zig", "type": "shell", "options": { "cwd": "${workspaceRoot}" }, "group": { "kind": "none", "isDefault": false }, + "presentation": { "group": "zTime", "echo": true, "reveal": "always", "focus": false, "panel": "shared", "showReuseMessage": false, "clear": true, "close": false }, "problemMatcher": [] + }, + { + "label": "Zig: Docs", + "args": [ "run", "-femit-docs", "main.zig" ], + "detail": "Generate docs from source comments.", + "command": "zig", "type": "shell", "options": { "cwd": "${workspaceRoot}" }, "group": { "kind": "none", "isDefault": false }, + "presentation": { "group": "docs", "echo": true, "reveal": "always", "focus": false, "panel": "shared", "showReuseMessage": false, "clear": true, "close": false }, "problemMatcher": [] + }], + "version": "2.0.0" +} \ No newline at end of file diff --git a/BaseWebview/BaseWebview.ico b/BaseWebview/BaseWebview.ico new file mode 100644 index 0000000000000000000000000000000000000000..143e30ff84dfe1c4b2bcf03cdb5fc6711d252800 GIT binary patch literal 90022 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 WNU7z)2rvSS03*N%Fal+Y!2bXX!e*8L literal 0 HcmV?d00001 diff --git a/BaseWebview/BaseWebview.rc b/BaseWebview/BaseWebview.rc new file mode 100644 index 0000000..1b70f92 --- /dev/null +++ b/BaseWebview/BaseWebview.rc @@ -0,0 +1,32 @@ +#include "windows.h" + +IDI_ICON1 ICON "BaseWebview.ico" + +1 VERSIONINFO + FILEVERSION 1,0,0,0 + PRODUCTVERSION 1,0,0,0 + FILEFLAGSMASK 0x3fL + FILEFLAGS 0x0L + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "BaseWebview" + VALUE "FileDescription", "BaseWebview" + VALUE "FileVersion", "1.0.0.0" + VALUE "InternalName", "BaseWebview.exe" + VALUE "LegalCopyright", "Copyleft(c) Created by DarknessFX, http://dfx.lv/ , @DrkFX." + VALUE "OriginalFilename", "BaseWebview.exe" + VALUE "ProductName", "BaseWebview" + VALUE "ProductVersion", "1.0.0.0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END diff --git a/BaseWebview/asset/webview.ico b/BaseWebview/asset/webview.ico new file mode 100644 index 0000000000000000000000000000000000000000..143e30ff84dfe1c4b2bcf03cdb5fc6711d252800 GIT binary patch literal 90022 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 WNU7z)2rvSS03*N%Fal+Y!2bXX!e*8L literal 0 HcmV?d00001 diff --git a/BaseWebview/asset/webview.png b/BaseWebview/asset/webview.png new file mode 100644 index 0000000000000000000000000000000000000000..707f55eb498924d5ddbf0a339e58f5c6862330c0 GIT binary patch literal 1547 zcmV+m2K4!fP)EX>4Tx04R}tkvmAkP!xv$rWQpif_4yb$WS}k#TSmHibb$c+6t{YnB4RQO&XFE z7e~Rh;NWAi>fqw6tAnc`2tGjE1t&!pDe>H-&?3eIm;3SG^Z(p?E+90kOf`FAfT~$W zIuRFh`Bky^6+Q$oga~FNX6nhzViunDbx++?cTt|@-S=k=Ce3JS*_Mt`=0!T!GgAu;X17`B(Q`eQV=1djtZ)<5T#us#YCFU;~xG|$DbmXOs)zT zITlcZ3d!+<|H1Fsn#HL}Hz^ng0x!1xF%IjdyW16NwdUuyz$pQJZB zTKEVU+y*YLJDR))TzpLZQ$eU}R*3wY4?g zzkkoOXU`rduO*3OGD#wlXdmEkIPiEp`2BvIPUoYgkk{+Q;c(bf#Fk}YS=OW1fNe#5 z8>VSeE|;m*YM7>Jd)mmdjL+wzr>Do>QwTr^L9JF}Z*Pyo!$T^Simd=eQJ9#RU}Wa{P{CiS64`qWLvt(vdqViA6Z;nw7(DB?to=k7>2>GU%xmzJF`9S zmzS6SP9wXE5JI$d8YzlGG#X`md>og{MIw=4XJ-chUDr7~J3~>FzXPx<*I&JQ#p2>3 z;c%Fxr6n#eFUe-J=(^6<))tjY1zp!M45RY^x2M^_zyMQIQ^>MRy8^t0WQ$`uqDS7K_Zx%rH7S%EZJ3V`F2qS}lsjBD=f0B$G)>rBY`B zTCEnx$Hz3AO|sc6sZ@%|$w{KoD3M46kH>@8>qXNvTrO8FH@!S67M0<4jIY;&!`fG#aR?O08ByRaJie{7Jc7 z?mR$mZ!hEH<4jLa^XAPPgb=8z%E`$IySuxbo}MB}5~Wg!VzJnH0Mj(7)oP^EX^O=n z=jZ2WnucK*$g<44ckhVBVq9Haad2>8ZxP>4rS*FKj{)U!nWLj4s?{n30|NvC0p{oD z35Ub@{eF6Td-3^vq|<3+S+;KnZs(P`xj6!X0F6fDK_4!J;QIQSR4Rq0X)G)(5R1j| z`~3_J4dL;4sMqV{ayd>73*3xdHQgM)*_<8flK82NmjWHQP2_BMq=0bSSI0~87czI^$D+wE@aMM4OH zX_}NuC0eZ(Znqmv(+C6t1cO0Bp%9~^ql}G>5ex=-{`@)LzJ0qFAn1U9WUH&I#9}cb zkq9qezU0M=7i2OSK7IPc;o)KD1Ma?nM59p_78YnWo2;*|lh5a$4nPQjqA2+Ner|4V x&@}Dd*SU@Y{Lx^@6OS&S3+Mv6fG(h|_y b.exe_dir = "bin/Debug", + .ReleaseSafe => b.exe_dir = "bin/ReleaseSafe", + .ReleaseFast => b.exe_dir = "bin/ReleaseFast", + .ReleaseSmall => b.exe_dir = "bin/ReleaseSmall" + //else => b.exe_dir = "bin/Else", + } + b.installArtifact(exe); + + //Run + const run_cmd = b.addRunArtifact(exe); + run_cmd.step.dependOn(b.getInstallStep()); + if (b.args) |args| { + run_cmd.addArgs(args); + } + const run_step = b.step("run", "Run the app"); + run_step.dependOn(&run_cmd.step); + + //Tests + const unit_tests = b.addTest(.{ + .root_source_file = b.path(rootfile), + .target = target, + .optimize = optimize, + }); + const run_unit_tests = b.addRunArtifact(unit_tests); + const test_step = b.step("test", "Run unit tests"); + test_step.dependOn(&run_unit_tests.step); +} \ No newline at end of file diff --git a/BaseWebview/lib/webview/include/api.h b/BaseWebview/lib/webview/include/api.h new file mode 100644 index 0000000..3f27555 --- /dev/null +++ b/BaseWebview/lib/webview/include/api.h @@ -0,0 +1,244 @@ +/* + * MIT License + * + * Copyright (c) 2017 Serge Zaitsev + * Copyright (c) 2022 Steffen André Langnes + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef WEBVIEW_API_H +#define WEBVIEW_API_H + +#include "errors.h" +#include "macros.h" +#include "types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Creates a new webview instance. + * + * @param debug Enable developer tools if supported by the backend. + * @param window Optional native window handle, i.e. @c GtkWindow pointer + * @c NSWindow pointer (Cocoa) or @c HWND (Win32). If non-null, + * the webview widget is embedded into the given window, and the + * caller is expected to assume responsibility for the window as + * well as application lifecycle. If the window handle is null, + * a new window is created and both the window and application + * lifecycle are managed by the webview instance. + * @remark Win32: The function also accepts a pointer to @c HWND (Win32) in the + * window parameter for backward compatibility. + * @remark Win32/WebView2: @c CoInitializeEx should be called with + * @c COINIT_APARTMENTTHREADED before attempting to call this function + * with an existing window. Omitting this step may cause WebView2 + * initialization to fail. + * @return @c NULL on failure. Creation can fail for various reasons such + * as when required runtime dependencies are missing or when window + * creation fails. + * @retval WEBVIEW_ERROR_MISSING_DEPENDENCY + * May be returned if WebView2 is unavailable on Windows. + */ +WEBVIEW_API webview_t webview_create(int debug, void *window); + +/** + * Destroys a webview instance and closes the native window. + * + * @param w The webview instance. + */ +WEBVIEW_API webview_error_t webview_destroy(webview_t w); + +/** + * Runs the main loop until it's terminated. + * + * @param w The webview instance. + */ +WEBVIEW_API webview_error_t webview_run(webview_t w); + +/** + * Stops the main loop. It is safe to call this function from another other + * background thread. + * + * @param w The webview instance. + */ +WEBVIEW_API webview_error_t webview_terminate(webview_t w); + +/** + * Schedules a function to be invoked on the thread with the run/event loop. + * Use this function e.g. to interact with the library or native handles. + * + * @param w The webview instance. + * @param fn The function to be invoked. + * @param arg An optional argument passed along to the callback function. + */ +WEBVIEW_API webview_error_t webview_dispatch(webview_t w, + void (*fn)(webview_t w, void *arg), + void *arg); + +/** + * Returns the native handle of the window associated with the webview instance. + * The handle can be a @c GtkWindow pointer (GTK), @c NSWindow pointer (Cocoa) + * or @c HWND (Win32). + * + * @param w The webview instance. + * @return The handle of the native window. + */ +WEBVIEW_API void *webview_get_window(webview_t w); + +/** + * Get a native handle of choice. + * + * @param w The webview instance. + * @param kind The kind of handle to retrieve. + * @return The native handle or @c NULL. + * @since 0.11 + */ +WEBVIEW_API void *webview_get_native_handle(webview_t w, + webview_native_handle_kind_t kind); + +/** + * Updates the title of the native window. + * + * @param w The webview instance. + * @param title The new title. + */ +WEBVIEW_API webview_error_t webview_set_title(webview_t w, const char *title); + +/** + * Updates the size of the native window. + * + * Remarks: + * - Using WEBVIEW_HINT_MAX for setting the maximum window size is not + * supported with GTK 4 because X11-specific functions such as + * gtk_window_set_geometry_hints were removed. This option has no effect + * when using GTK 4. + * + * @param w The webview instance. + * @param width New width. + * @param height New height. + * @param hints Size hints. + */ +WEBVIEW_API webview_error_t webview_set_size(webview_t w, int width, int height, + webview_hint_t hints); + +/** + * Navigates webview to the given URL. URL may be a properly encoded data URI. + * + * Example: + * @code{.c} + * webview_navigate(w, "https://github.com/webview/webview"); + * webview_navigate(w, "data:text/html,%3Ch1%3EHello%3C%2Fh1%3E"); + * webview_navigate(w, "data:text/html;base64,PGgxPkhlbGxvPC9oMT4="); + * @endcode + * + * @param w The webview instance. + * @param url URL. + */ +WEBVIEW_API webview_error_t webview_navigate(webview_t w, const char *url); + +/** + * Load HTML content into the webview. + * + * Example: + * @code{.c} + * webview_set_html(w, "

Hello

"); + * @endcode + * + * @param w The webview instance. + * @param html HTML content. + */ +WEBVIEW_API webview_error_t webview_set_html(webview_t w, const char *html); + +/** + * Injects JavaScript code to be executed immediately upon loading a page. + * The code will be executed before @c window.onload. + * + * @param w The webview instance. + * @param js JS content. + */ +WEBVIEW_API webview_error_t webview_init(webview_t w, const char *js); + +/** + * Evaluates arbitrary JavaScript code. + * + * Use bindings if you need to communicate the result of the evaluation. + * + * @param w The webview instance. + * @param js JS content. + */ +WEBVIEW_API webview_error_t webview_eval(webview_t w, const char *js); + +/** + * Binds a function pointer to a new global JavaScript function. + * + * Internally, JS glue code is injected to create the JS function by the + * given name. The callback function is passed a request identifier, + * a request string and a user-provided argument. The request string is + * a JSON array of the arguments passed to the JS function. + * + * @param w The webview instance. + * @param name Name of the JS function. + * @param fn Callback function. + * @param arg User argument. + * @retval WEBVIEW_ERROR_DUPLICATE + * A binding already exists with the specified name. + */ +WEBVIEW_API webview_error_t webview_bind(webview_t w, const char *name, + void (*fn)(const char *id, + const char *req, void *arg), + void *arg); + +/** + * Removes a binding created with webview_bind(). + * + * @param w The webview instance. + * @param name Name of the binding. + * @retval WEBVIEW_ERROR_NOT_FOUND No binding exists with the specified name. + */ +WEBVIEW_API webview_error_t webview_unbind(webview_t w, const char *name); + +/** + * Responds to a binding call from the JS side. + * + * @param w The webview instance. + * @param id The identifier of the binding call. Pass along the value received + * in the binding handler (see webview_bind()). + * @param status A status of zero tells the JS side that the binding call was + * succesful; any other value indicates an error. + * @param result The result of the binding call to be returned to the JS side. + * This must either be a valid JSON value or an empty string for + * the primitive JS value @c undefined. + */ +WEBVIEW_API webview_error_t webview_return(webview_t w, const char *id, + int status, const char *result); + +/** + * Get the library's version information. + * + * @since 0.10 + */ +WEBVIEW_API const webview_version_info_t *webview_version(void); + +#ifdef __cplusplus +} +#endif + +#endif // WEBVIEW_API_H diff --git a/BaseWebview/lib/webview/include/backends.hh b/BaseWebview/lib/webview/include/backends.hh new file mode 100644 index 0000000..2a2d76d --- /dev/null +++ b/BaseWebview/lib/webview/include/backends.hh @@ -0,0 +1,43 @@ +/* + * MIT License + * + * Copyright (c) 2017 Serge Zaitsev + * Copyright (c) 2022 Steffen André Langnes + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef WEBVIEW_BACKENDS_H +#define WEBVIEW_BACKENDS_H + +#include "macros.h" + +#if defined(WEBVIEW_GTK) +#include "detail/backends/gtk_webkitgtk.hh" +#elif defined(WEBVIEW_COCOA) +#include "detail/backends/cocoa_webkit.hh" +#elif defined(WEBVIEW_EDGE) +#include "detail/backends/win32_edge.hh" +#endif + +namespace webview { +using webview = browser_engine; +} + +#endif // WEBVIEW_H diff --git a/BaseWebview/lib/webview/include/c_api_impl.hh b/BaseWebview/lib/webview/include/c_api_impl.hh new file mode 100644 index 0000000..5e589d2 --- /dev/null +++ b/BaseWebview/lib/webview/include/c_api_impl.hh @@ -0,0 +1,255 @@ +/* + * MIT License + * + * Copyright (c) 2017 Serge Zaitsev + * Copyright (c) 2022 Steffen André Langnes + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef WEBVIEW_C_API_IMPL_H +#define WEBVIEW_C_API_IMPL_H + +#include "backends.hh" +#include "errors.h" +#include "errors.hh" +#include "json_deprecated.hh" +#include "macros.h" +#include "types.h" +#include "version.h" + +namespace webview { +namespace detail { + +// The library's version information. +constexpr const webview_version_info_t library_version_info{ + {WEBVIEW_VERSION_MAJOR, WEBVIEW_VERSION_MINOR, WEBVIEW_VERSION_PATCH}, + WEBVIEW_VERSION_NUMBER, + WEBVIEW_VERSION_PRE_RELEASE, + WEBVIEW_VERSION_BUILD_METADATA}; + +template +webview_error_t api_filter(WorkFn &&do_work, ResultFn &&put_result) noexcept { + try { + auto result = do_work(); + if (result.ok()) { + put_result(result.value()); + return WEBVIEW_ERROR_OK; + } + return result.error().code(); + } catch (const exception &e) { + return e.error().code(); + } catch (...) { + return WEBVIEW_ERROR_UNSPECIFIED; + } +} + +template +webview_error_t api_filter(WorkFn &&do_work) noexcept { + try { + auto result = do_work(); + if (result.ok()) { + return WEBVIEW_ERROR_OK; + } + return result.error().code(); + } catch (const exception &e) { + return e.error().code(); + } catch (...) { + return WEBVIEW_ERROR_UNSPECIFIED; + } +} + +inline webview *cast_to_webview(void *w) { + if (!w) { + throw exception{WEBVIEW_ERROR_INVALID_ARGUMENT, + "Cannot cast null pointer to webview instance"}; + } + return static_cast(w); +} + +} // namespace detail +} // namespace webview + +WEBVIEW_API webview_t webview_create(int debug, void *wnd) { + using namespace webview::detail; + webview::webview *w{}; + auto err = api_filter( + [=]() -> webview::result { + return new webview::webview{static_cast(debug), wnd}; + }, + [&](webview::webview *w_) { w = w_; }); + if (err == WEBVIEW_ERROR_OK) { + return w; + } + return nullptr; +} + +WEBVIEW_API webview_error_t webview_destroy(webview_t w) { + using namespace webview::detail; + return api_filter([=]() -> webview::noresult { + delete cast_to_webview(w); + return {}; + }); +} + +WEBVIEW_API webview_error_t webview_run(webview_t w) { + using namespace webview::detail; + return api_filter([=] { return cast_to_webview(w)->run(); }); +} + +WEBVIEW_API webview_error_t webview_terminate(webview_t w) { + using namespace webview::detail; + return api_filter([=] { return cast_to_webview(w)->terminate(); }); +} + +WEBVIEW_API webview_error_t webview_dispatch(webview_t w, + void (*fn)(webview_t, void *), + void *arg) { + using namespace webview::detail; + if (!fn) { + return WEBVIEW_ERROR_INVALID_ARGUMENT; + } + return api_filter( + [=] { return cast_to_webview(w)->dispatch([=]() { fn(w, arg); }); }); +} + +WEBVIEW_API void *webview_get_window(webview_t w) { + using namespace webview::detail; + void *window = nullptr; + auto err = api_filter([=] { return cast_to_webview(w)->window(); }, + [&](void *value) { window = value; }); + if (err == WEBVIEW_ERROR_OK) { + return window; + } + return nullptr; +} + +WEBVIEW_API void *webview_get_native_handle(webview_t w, + webview_native_handle_kind_t kind) { + using namespace webview::detail; + void *handle{}; + auto err = api_filter( + [=]() -> webview::result { + auto *w_ = cast_to_webview(w); + switch (kind) { + case WEBVIEW_NATIVE_HANDLE_KIND_UI_WINDOW: + return w_->window(); + case WEBVIEW_NATIVE_HANDLE_KIND_UI_WIDGET: + return w_->widget(); + case WEBVIEW_NATIVE_HANDLE_KIND_BROWSER_CONTROLLER: + return w_->browser_controller(); + default: + return webview::error_info{WEBVIEW_ERROR_INVALID_ARGUMENT}; + } + }, + [&](void *handle_) { handle = handle_; }); + if (err == WEBVIEW_ERROR_OK) { + return handle; + } + return nullptr; +} + +WEBVIEW_API webview_error_t webview_set_title(webview_t w, const char *title) { + using namespace webview::detail; + if (!title) { + return WEBVIEW_ERROR_INVALID_ARGUMENT; + } + return api_filter([=] { return cast_to_webview(w)->set_title(title); }); +} + +WEBVIEW_API webview_error_t webview_set_size(webview_t w, int width, int height, + webview_hint_t hints) { + using namespace webview::detail; + return api_filter( + [=] { return cast_to_webview(w)->set_size(width, height, hints); }); +} + +WEBVIEW_API webview_error_t webview_navigate(webview_t w, const char *url) { + using namespace webview::detail; + if (!url) { + return WEBVIEW_ERROR_INVALID_ARGUMENT; + } + return api_filter([=] { return cast_to_webview(w)->navigate(url); }); +} + +WEBVIEW_API webview_error_t webview_set_html(webview_t w, const char *html) { + using namespace webview::detail; + if (!html) { + return WEBVIEW_ERROR_INVALID_ARGUMENT; + } + return api_filter([=] { return cast_to_webview(w)->set_html(html); }); +} + +WEBVIEW_API webview_error_t webview_init(webview_t w, const char *js) { + using namespace webview::detail; + if (!js) { + return WEBVIEW_ERROR_INVALID_ARGUMENT; + } + return api_filter([=] { return cast_to_webview(w)->init(js); }); +} + +WEBVIEW_API webview_error_t webview_eval(webview_t w, const char *js) { + using namespace webview::detail; + if (!js) { + return WEBVIEW_ERROR_INVALID_ARGUMENT; + } + return api_filter([=] { return cast_to_webview(w)->eval(js); }); +} + +WEBVIEW_API webview_error_t webview_bind(webview_t w, const char *name, + void (*fn)(const char *id, + const char *req, void *arg), + void *arg) { + using namespace webview::detail; + if (!name || !fn) { + return WEBVIEW_ERROR_INVALID_ARGUMENT; + } + return api_filter([=] { + return cast_to_webview(w)->bind( + name, + [=](const std::string &seq, const std::string &req, void *arg_) { + fn(seq.c_str(), req.c_str(), arg_); + }, + arg); + }); +} + +WEBVIEW_API webview_error_t webview_unbind(webview_t w, const char *name) { + using namespace webview::detail; + if (!name) { + return WEBVIEW_ERROR_INVALID_ARGUMENT; + } + return api_filter([=] { return cast_to_webview(w)->unbind(name); }); +} + +WEBVIEW_API webview_error_t webview_return(webview_t w, const char *id, + int status, const char *result) { + using namespace webview::detail; + if (!id || !result) { + return WEBVIEW_ERROR_INVALID_ARGUMENT; + } + return api_filter( + [=] { return cast_to_webview(w)->resolve(id, status, result); }); +} + +WEBVIEW_API const webview_version_info_t *webview_version(void) { + return &webview::detail::library_version_info; +} + +#endif // WEBVIEW_C_API_IMPL_H diff --git a/BaseWebview/lib/webview/include/detail/backends/cocoa_webkit.hh b/BaseWebview/lib/webview/include/detail/backends/cocoa_webkit.hh new file mode 100644 index 0000000..a0ddad5 --- /dev/null +++ b/BaseWebview/lib/webview/include/detail/backends/cocoa_webkit.hh @@ -0,0 +1,688 @@ +/* + * MIT License + * + * Copyright (c) 2017 Serge Zaitsev + * Copyright (c) 2022 Steffen André Langnes + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined(WEBVIEW_BACKENDS_COCOA_WEBKIT_HH) && \ + defined(WEBVIEW_PLATFORM_DARWIN) && defined(WEBVIEW_COCOA) +#define WEBVIEW_BACKENDS_COCOA_WEBKIT_HH + +// +// ==================================================================== +// +// This implementation uses Cocoa WKWebView backend on macOS. It is +// written using ObjC runtime and uses WKWebView class as a browser runtime. +// You should pass "-framework Webkit" flag to the compiler. +// +// ==================================================================== +// + +#include "../../types.hh" +#include "../engine_base.hh" +#include "../platform/darwin/cocoa.hh" +#include "../platform/darwin/objc.hh" +#include "../platform/darwin/webkit.hh" +#include "../user_script.hh" + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace webview { +namespace detail { + +using namespace objc::literals; + +class user_script::impl { +public: + impl(id script) : m_script{script} { + objc::msg_send(script, "retain"_sel); + } + + ~impl() { objc::msg_send(m_script, "release"_sel); } + + impl(const impl &) = delete; + impl &operator=(const impl &) = delete; + impl(impl &&) = delete; + impl &operator=(impl &&) = delete; + + id get_native() const { return m_script; } + +private: + id m_script{}; +}; + +class cocoa_wkwebview_engine : public engine_base { +public: + cocoa_wkwebview_engine(bool debug, void *window) + : m_debug{debug}, + m_window{static_cast(window)}, + m_owns_window{!window} { + auto app = get_shared_application(); + // See comments related to application lifecycle in create_app_delegate(). + if (!m_owns_window) { + set_up_window(); + } else { + // Only set the app delegate if it hasn't already been set. + auto delegate = objc::msg_send(app, "delegate"_sel); + if (delegate) { + set_up_window(); + } else { + m_app_delegate = create_app_delegate(); + objc_setAssociatedObject(m_app_delegate, "webview", (id)this, + OBJC_ASSOCIATION_ASSIGN); + objc::msg_send(app, "setDelegate:"_sel, m_app_delegate); + + // Start the main run loop so that the app delegate gets the + // NSApplicationDidFinishLaunchingNotification notification after the run + // loop has started in order to perform further initialization. + // We need to return from this constructor so this run loop is only + // temporary. + // Skip the main loop if this isn't the first instance of this class + // because the launch event is only sent once. Instead, proceed to + // create a window. + if (get_and_set_is_first_instance()) { + objc::msg_send(app, "run"_sel); + } else { + set_up_window(); + } + } + } + } + + cocoa_wkwebview_engine(const cocoa_wkwebview_engine &) = delete; + cocoa_wkwebview_engine &operator=(const cocoa_wkwebview_engine &) = delete; + cocoa_wkwebview_engine(cocoa_wkwebview_engine &&) = delete; + cocoa_wkwebview_engine &operator=(cocoa_wkwebview_engine &&) = delete; + + virtual ~cocoa_wkwebview_engine() { + objc::autoreleasepool arp; + if (m_window) { + if (m_webview) { + if (auto ui_delegate = + objc::msg_send(m_webview, "UIDelegate"_sel)) { + objc::msg_send(m_webview, "setUIDelegate:"_sel, nullptr); + objc::msg_send(ui_delegate, "release"_sel); + } + objc::msg_send(m_webview, "release"_sel); + m_webview = nullptr; + } + if (m_widget) { + if (m_widget == objc::msg_send(m_window, "contentView"_sel)) { + objc::msg_send(m_window, "setContentView:"_sel, nullptr); + } + objc::msg_send(m_widget, "release"_sel); + m_widget = nullptr; + } + if (m_owns_window) { + // Replace delegate to avoid callbacks and other bad things during + // destruction. + objc::msg_send(m_window, "setDelegate:"_sel, nullptr); + objc::msg_send(m_window, "close"_sel); + on_window_destroyed(true); + } + m_window = nullptr; + } + if (m_window_delegate) { + objc::msg_send(m_window_delegate, "release"_sel); + m_window_delegate = nullptr; + } + if (m_app_delegate) { + auto app = get_shared_application(); + objc::msg_send(app, "setDelegate:"_sel, nullptr); + // Make sure to release the delegate we created. + objc::msg_send(m_app_delegate, "release"_sel); + m_app_delegate = nullptr; + } + if (m_owns_window) { + // Needed for the window to close immediately. + deplete_run_loop_event_queue(); + } + // TODO: Figure out why m_manager is still alive after the autoreleasepool + // has been drained. + } + +protected: + result window_impl() override { + if (m_window) { + return m_window; + } + return error_info{WEBVIEW_ERROR_INVALID_STATE}; + } + + result widget_impl() override { + if (m_widget) { + return m_widget; + } + return error_info{WEBVIEW_ERROR_INVALID_STATE}; + } + + result browser_controller_impl() override { + if (m_webview) { + return m_webview; + } + return error_info{WEBVIEW_ERROR_INVALID_STATE}; + } + + noresult terminate_impl() override { + stop_run_loop(); + return {}; + } + + noresult run_impl() override { + auto app = get_shared_application(); + objc::msg_send(app, "run"_sel); + return {}; + } + + noresult dispatch_impl(std::function f) override { + dispatch_async_f(dispatch_get_main_queue(), new dispatch_fn_t(f), + (dispatch_function_t)([](void *arg) { + auto f = static_cast(arg); + (*f)(); + delete f; + })); + return {}; + } + + noresult set_title_impl(const std::string &title) override { + objc::autoreleasepool arp; + + objc::msg_send(m_window, "setTitle:"_sel, + objc::msg_send("NSString"_cls, + "stringWithUTF8String:"_sel, + title.c_str())); + + return {}; + } + noresult set_size_impl(int width, int height, webview_hint_t hints) override { + objc::autoreleasepool arp; + + auto style = static_cast( + NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | + NSWindowStyleMaskMiniaturizable); + if (hints != WEBVIEW_HINT_FIXED) { + style = + static_cast(style | NSWindowStyleMaskResizable); + } + objc::msg_send(m_window, "setStyleMask:"_sel, style); + + if (hints == WEBVIEW_HINT_MIN) { + objc::msg_send(m_window, "setContentMinSize:"_sel, + CGSizeMake(width, height)); + } else if (hints == WEBVIEW_HINT_MAX) { + objc::msg_send(m_window, "setContentMaxSize:"_sel, + CGSizeMake(width, height)); + } else { + CGRect rect = objc::msg_send_stret(m_window, "frame"_sel); + objc::msg_send( + m_window, "setFrame:display:animate:"_sel, + CGRectMake(rect.origin.x, rect.origin.y, width, height), YES, NO); + } + objc::msg_send(m_window, "center"_sel); + + return {}; + } + noresult navigate_impl(const std::string &url) override { + objc::autoreleasepool arp; + + auto nsurl = objc::msg_send( + "NSURL"_cls, "URLWithString:"_sel, + objc::msg_send("NSString"_cls, "stringWithUTF8String:"_sel, + url.c_str())); + + objc::msg_send( + m_webview, "loadRequest:"_sel, + objc::msg_send("NSURLRequest"_cls, "requestWithURL:"_sel, nsurl)); + + return {}; + } + noresult set_html_impl(const std::string &html) override { + objc::autoreleasepool arp; + objc::msg_send(m_webview, "loadHTMLString:baseURL:"_sel, + objc::msg_send("NSString"_cls, + "stringWithUTF8String:"_sel, + html.c_str()), + nullptr); + return {}; + } + noresult eval_impl(const std::string &js) override { + objc::autoreleasepool arp; + // URI is null before content has begun loading. + auto nsurl = objc::msg_send(m_webview, "URL"_sel); + if (!nsurl) { + return {}; + } + objc::msg_send(m_webview, "evaluateJavaScript:completionHandler:"_sel, + objc::msg_send("NSString"_cls, + "stringWithUTF8String:"_sel, + js.c_str()), + nullptr); + return {}; + } + + user_script add_user_script_impl(const std::string &js) override { + objc::autoreleasepool arp; + auto wk_script = objc::msg_send( + objc::msg_send("WKUserScript"_cls, "alloc"_sel), + "initWithSource:injectionTime:forMainFrameOnly:"_sel, + objc::msg_send("NSString"_cls, "stringWithUTF8String:"_sel, + js.c_str()), + WKUserScriptInjectionTimeAtDocumentStart, YES); + // Script is retained when added. + objc::msg_send(m_manager, "addUserScript:"_sel, wk_script); + user_script script{ + js, user_script::impl_ptr{new user_script::impl{wk_script}, + [](user_script::impl *p) { delete p; }}}; + objc::msg_send(wk_script, "release"_sel); + return script; + } + + void remove_all_user_scripts_impl( + const std::list & /*scripts*/) override { + objc::autoreleasepool arp; + // Removing scripts decreases the retain count of each script. + objc::msg_send(m_manager, "removeAllUserScripts"_sel); + } + + bool are_user_scripts_equal_impl(const user_script &first, + const user_script &second) override { + auto *wk_first = first.get_impl().get_native(); + auto *wk_second = second.get_impl().get_native(); + return wk_first == wk_second; + } + +private: + id create_app_delegate() { + objc::autoreleasepool arp; + constexpr auto class_name = "WebviewAppDelegate"; + // Avoid crash due to registering same class twice + auto cls = objc_lookUpClass(class_name); + if (!cls) { + // Note: Avoid registering the class name "AppDelegate" as it is the + // default name in projects created with Xcode, and using the same name + // causes objc_registerClassPair to crash. + cls = objc_allocateClassPair((Class) "NSResponder"_cls, class_name, 0); + class_addProtocol(cls, objc_getProtocol("NSTouchBarProvider")); + class_addMethod(cls, + "applicationShouldTerminateAfterLastWindowClosed:"_sel, + (IMP)(+[](id, SEL, id) -> BOOL { return NO; }), "c@:@"); + class_addMethod(cls, "applicationDidFinishLaunching:"_sel, + (IMP)(+[](id self, SEL, id notification) { + auto app = + objc::msg_send(notification, "object"_sel); + auto w = get_associated_webview(self); + w->on_application_did_finish_launching(self, app); + }), + "v@:@"); + objc_registerClassPair(cls); + } + return objc::msg_send((id)cls, "new"_sel); + } + id create_script_message_handler() { + objc::autoreleasepool arp; + constexpr auto class_name = "WebviewWKScriptMessageHandler"; + // Avoid crash due to registering same class twice + auto cls = objc_lookUpClass(class_name); + if (!cls) { + cls = objc_allocateClassPair((Class) "NSResponder"_cls, class_name, 0); + class_addProtocol(cls, objc_getProtocol("WKScriptMessageHandler")); + class_addMethod( + cls, "userContentController:didReceiveScriptMessage:"_sel, + (IMP)(+[](id self, SEL, id, id msg) { + auto w = get_associated_webview(self); + w->on_message(objc::msg_send( + objc::msg_send(msg, "body"_sel), "UTF8String"_sel)); + }), + "v@:@@"); + objc_registerClassPair(cls); + } + auto instance = objc::msg_send((id)cls, "new"_sel); + objc_setAssociatedObject(instance, "webview", (id)this, + OBJC_ASSOCIATION_ASSIGN); + return instance; + } + static id create_webkit_ui_delegate() { + objc::autoreleasepool arp; + constexpr auto class_name = "WebviewWKUIDelegate"; + // Avoid crash due to registering same class twice + auto cls = objc_lookUpClass(class_name); + if (!cls) { + cls = objc_allocateClassPair((Class) "NSObject"_cls, class_name, 0); + class_addProtocol(cls, objc_getProtocol("WKUIDelegate")); + class_addMethod( + cls, + "webView:runOpenPanelWithParameters:initiatedByFrame:completionHandler:"_sel, + (IMP)(+[](id, SEL, id, id parameters, id, id completion_handler) { + auto allows_multiple_selection = + objc::msg_send(parameters, "allowsMultipleSelection"_sel); + auto allows_directories = + objc::msg_send(parameters, "allowsDirectories"_sel); + + // Show a panel for selecting files. + auto panel = objc::msg_send("NSOpenPanel"_cls, "openPanel"_sel); + objc::msg_send(panel, "setCanChooseFiles:"_sel, YES); + objc::msg_send(panel, "setCanChooseDirectories:"_sel, + allows_directories); + objc::msg_send(panel, "setAllowsMultipleSelection:"_sel, + allows_multiple_selection); + auto modal_response = + objc::msg_send(panel, "runModal"_sel); + + // Get the URLs for the selected files. If the modal was canceled + // then we pass null to the completion handler to signify + // cancellation. + id urls = modal_response == NSModalResponseOK + ? objc::msg_send(panel, "URLs"_sel) + : nullptr; + + // Invoke the completion handler block. + auto sig = objc::msg_send( + "NSMethodSignature"_cls, "signatureWithObjCTypes:"_sel, "v@?@"); + auto invocation = objc::msg_send( + "NSInvocation"_cls, "invocationWithMethodSignature:"_sel, sig); + objc::msg_send(invocation, "setTarget:"_sel, + completion_handler); + objc::msg_send(invocation, "setArgument:atIndex:"_sel, &urls, + 1); + objc::msg_send(invocation, "invoke"_sel); + }), + "v@:@@@@"); + objc_registerClassPair(cls); + } + return objc::msg_send((id)cls, "new"_sel); + } + static id create_window_delegate() { + objc::autoreleasepool arp; + constexpr auto class_name = "WebviewNSWindowDelegate"; + // Avoid crash due to registering same class twice + auto cls = objc_lookUpClass(class_name); + if (!cls) { + cls = objc_allocateClassPair((Class) "NSObject"_cls, class_name, 0); + class_addProtocol(cls, objc_getProtocol("NSWindowDelegate")); + class_addMethod(cls, "windowWillClose:"_sel, + (IMP)(+[](id self, SEL, id notification) { + auto window = + objc::msg_send(notification, "object"_sel); + auto w = get_associated_webview(self); + w->on_window_will_close(self, window); + }), + "v@:@"); + objc_registerClassPair(cls); + } + return objc::msg_send((id)cls, "new"_sel); + } + static id get_shared_application() { + return objc::msg_send("NSApplication"_cls, "sharedApplication"_sel); + } + static cocoa_wkwebview_engine *get_associated_webview(id object) { + auto w = + (cocoa_wkwebview_engine *)objc_getAssociatedObject(object, "webview"); + assert(w); + return w; + } + static id get_main_bundle() noexcept { + return objc::msg_send("NSBundle"_cls, "mainBundle"_sel); + } + static bool is_app_bundled() noexcept { + auto bundle = get_main_bundle(); + if (!bundle) { + return false; + } + auto bundle_path = objc::msg_send(bundle, "bundlePath"_sel); + auto bundled = + objc::msg_send(bundle_path, "hasSuffix:"_sel, ".app"_str); + return !!bundled; + } + void on_application_did_finish_launching(id /*delegate*/, id app) { + // See comments related to application lifecycle in create_app_delegate(). + if (m_owns_window) { + // Stop the main run loop so that we can return + // from the constructor. + stop_run_loop(); + } + + // Activate the app if it is not bundled. + // Bundled apps launched from Finder are activated automatically but + // otherwise not. Activating the app even when it has been launched from + // Finder does not seem to be harmful but calling this function is rarely + // needed as proper activation is normally taken care of for us. + // Bundled apps have a default activation policy of + // NSApplicationActivationPolicyRegular while non-bundled apps have a + // default activation policy of NSApplicationActivationPolicyProhibited. + if (!is_app_bundled()) { + // "setActivationPolicy:" must be invoked before + // "activateIgnoringOtherApps:" for activation to work. + objc::msg_send(app, "setActivationPolicy:"_sel, + NSApplicationActivationPolicyRegular); + // Activate the app regardless of other active apps. + // This can be obtrusive so we only do it when necessary. + objc::msg_send(app, "activateIgnoringOtherApps:"_sel, YES); + } + + set_up_window(); + } + void on_window_will_close(id /*delegate*/, id /*window*/) { + // Widget destroyed along with window. + m_widget = nullptr; + m_webview = nullptr; + m_window = nullptr; + dispatch([this] { on_window_destroyed(); }); + } + void set_up_window() { + objc::autoreleasepool arp; + + // Main window + if (m_owns_window) { + m_window = objc::msg_send("NSWindow"_cls, "alloc"_sel); + auto style = NSWindowStyleMaskTitled; + m_window = objc::msg_send( + m_window, "initWithContentRect:styleMask:backing:defer:"_sel, + CGRectMake(0, 0, 0, 0), style, NSBackingStoreBuffered, NO); + + m_window_delegate = create_window_delegate(); + objc_setAssociatedObject(m_window_delegate, "webview", (id)this, + OBJC_ASSOCIATION_ASSIGN); + objc::msg_send(m_window, "setDelegate:"_sel, m_window_delegate); + + on_window_created(); + } + + set_up_web_view(); + set_up_widget(); + + objc::msg_send(m_window, "setContentView:"_sel, m_widget); + + if (m_owns_window) { + objc::msg_send(m_window, "makeKeyAndOrderFront:"_sel, nullptr); + } + } + void set_up_web_view() { + objc::autoreleasepool arp; + + auto config = objc::autoreleased( + objc::msg_send("WKWebViewConfiguration"_cls, "new"_sel)); + + m_manager = objc::msg_send(config, "userContentController"_sel); + m_webview = objc::msg_send("WKWebView"_cls, "alloc"_sel); + + auto preferences = objc::msg_send(config, "preferences"_sel); + auto yes_value = + objc::msg_send("NSNumber"_cls, "numberWithBool:"_sel, YES); + + if (m_debug) { + // Equivalent Obj-C: + // [[config preferences] setValue:@YES forKey:@"developerExtrasEnabled"]; + objc::msg_send(preferences, "setValue:forKey:"_sel, yes_value, + "developerExtrasEnabled"_str); + } + + // Equivalent Obj-C: + // [[config preferences] setValue:@YES forKey:@"fullScreenEnabled"]; + objc::msg_send(preferences, "setValue:forKey:"_sel, yes_value, + "fullScreenEnabled"_str); + +#if defined(__has_builtin) +#if __has_builtin(__builtin_available) + if (__builtin_available(macOS 10.13, *)) { + // Equivalent Obj-C: + // [[config preferences] setValue:@YES forKey:@"javaScriptCanAccessClipboard"]; + objc::msg_send(preferences, "setValue:forKey:"_sel, yes_value, + "javaScriptCanAccessClipboard"_str); + + // Equivalent Obj-C: + // [[config preferences] setValue:@YES forKey:@"DOMPasteAllowed"]; + objc::msg_send(preferences, "setValue:forKey:"_sel, yes_value, + "DOMPasteAllowed"_str); + } +#else +#error __builtin_available not supported by compiler +#endif +#else +#error __has_builtin not supported by compiler +#endif + + auto ui_delegate = create_webkit_ui_delegate(); + objc::msg_send(m_webview, "initWithFrame:configuration:"_sel, + CGRectMake(0, 0, 0, 0), config); + // Autoresizing mask is needed to prevent the Web Inspector pane from + // pushing the main web view out of bounds + auto autoresizing_mask = NSViewWidthSizable | NSViewMaxXMargin | + NSViewHeightSizable | NSViewMaxYMargin; + objc::msg_send(m_webview, "setAutoresizingMask:"_sel, + autoresizing_mask); + objc_setAssociatedObject(ui_delegate, "webview", (id)this, + OBJC_ASSOCIATION_ASSIGN); + objc::msg_send(m_webview, "setUIDelegate:"_sel, ui_delegate); + + if (m_debug) { + // Explicitly make WKWebView inspectable via Safari on OS versions that + // disable the feature by default (macOS 13.3 and later) and support + // enabling it. According to Apple, the behavior on older OS versions is + // for content to always be inspectable in "debug builds". + // Testing shows that this is true for macOS 12.6 but somehow not 10.15. + // https://webkit.org/blog/13936/enabling-the-inspection-of-web-content-in-apps/ +#if defined(__has_builtin) +#if __has_builtin(__builtin_available) + if (__builtin_available(macOS 13.3, iOS 16.4, tvOS 16.4, *)) { + objc::msg_send( + m_webview, "setInspectable:"_sel, + objc::msg_send("NSNumber"_cls, "numberWithBool:"_sel, YES)); + } +#else +#error __builtin_available not supported by compiler +#endif +#else +#error __has_builtin not supported by compiler +#endif + } + + auto script_message_handler = + objc::autoreleased(create_script_message_handler()); + objc::msg_send(m_manager, "addScriptMessageHandler:name:"_sel, + script_message_handler, "__webview__"_str); + + add_init_script("function(message) {\n\ + return window.webkit.messageHandlers.__webview__.postMessage(message);\n\ +}"); + } + void set_up_widget() { + objc::autoreleasepool arp; + // Create a new view that can contain both the web view and the Web Inspector pane + m_widget = objc::msg_send("NSView"_cls, "alloc"_sel); + objc::msg_send(m_widget, "initWithFrame:"_sel, + CGRectMake(0, 0, 0, 0)); + // Autoresizing is needed because the Web Inspector pane is a sibling of the web view + objc::msg_send(m_widget, "setAutoresizesSubviews:"_sel, YES); + objc::msg_send(m_widget, "addSubview:"_sel, m_webview); + objc::msg_send(m_webview, "setFrame:"_sel, + objc::msg_send_stret(m_widget, "bounds"_sel)); + } + void stop_run_loop() { + objc::autoreleasepool arp; + auto app = get_shared_application(); + // Request the run loop to stop. This doesn't immediately stop the loop. + objc::msg_send(app, "stop:"_sel, nullptr); + // The run loop will stop after processing an NSEvent. + // Event type: NSEventTypeApplicationDefined (macOS 10.12+), + // NSApplicationDefined (macOS 10.0–10.12) + int type = 15; + auto event = objc::msg_send( + "NSEvent"_cls, + "otherEventWithType:location:modifierFlags:timestamp:windowNumber:context:subtype:data1:data2:"_sel, + type, CGPointMake(0, 0), 0, 0, 0, nullptr, 0, 0, 0); + objc::msg_send(app, "postEvent:atStart:"_sel, event, YES); + } + static bool get_and_set_is_first_instance() noexcept { + static std::atomic_bool first{true}; + bool temp = first; + if (temp) { + first = false; + } + return temp; + } + + // Blocks while depleting the run loop of events. + void deplete_run_loop_event_queue() { + objc::autoreleasepool arp; + auto app = get_shared_application(); + bool done{}; + dispatch([&] { done = true; }); + auto mask = NSUIntegerMax; // NSEventMaskAny + // NSDefaultRunLoopMode + auto mode = objc::msg_send("NSString"_cls, "stringWithUTF8String:"_sel, + "kCFRunLoopDefaultMode"); + while (!done) { + objc::autoreleasepool arp2; + auto event = objc::msg_send( + app, "nextEventMatchingMask:untilDate:inMode:dequeue:"_sel, mask, + nullptr, mode, YES); + if (event) { + objc::msg_send(app, "sendEvent:"_sel, event); + } + } + } + + bool m_debug{}; + id m_app_delegate{}; + id m_window_delegate{}; + id m_window{}; + id m_widget{}; + id m_webview{}; + id m_manager{}; + bool m_owns_window{}; +}; + +} // namespace detail + +using browser_engine = detail::cocoa_wkwebview_engine; + +} // namespace webview + +#endif // WEBVIEW_BACKENDS_COCOA_WEBKIT_H diff --git a/BaseWebview/lib/webview/include/detail/backends/gtk_webkitgtk.hh b/BaseWebview/lib/webview/include/detail/backends/gtk_webkitgtk.hh new file mode 100644 index 0000000..005a133 --- /dev/null +++ b/BaseWebview/lib/webview/include/detail/backends/gtk_webkitgtk.hh @@ -0,0 +1,334 @@ +/* + * MIT License + * + * Copyright (c) 2017 Serge Zaitsev + * Copyright (c) 2022 Steffen André Langnes + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined(WEBVIEW_BACKENDS_GTK_WEBKITGTK_HH) && \ + defined(WEBVIEW_PLATFORM_LINUX) && defined(WEBVIEW_GTK) +#define WEBVIEW_BACKENDS_GTK_WEBKITGTK_HH + +// +// ==================================================================== +// +// This implementation uses webkit2gtk backend. It requires GTK and +// WebKitGTK libraries. Proper compiler flags can be retrieved via: +// +// pkg-config --cflags --libs gtk4 webkitgtk-6.0 +// pkg-config --cflags --libs gtk+-3.0 webkit2gtk-4.1 +// pkg-config --cflags --libs gtk+-3.0 webkit2gtk-4.0 +// +// ==================================================================== +// + +#include "../../errors.hh" +#include "../../types.hh" +#include "../engine_base.hh" +#include "../platform/linux/gtk/compat.hh" +#include "../platform/linux/webkitgtk/compat.hh" +#include "../platform/linux/webkitgtk/dmabuf.hh" +#include "../user_script.hh" + +#include +#include +#include +#include + +#include + +#if GTK_MAJOR_VERSION >= 4 + +#include +#include + +#elif GTK_MAJOR_VERSION >= 3 + +#include +#include + +#endif + +#include +#include + +namespace webview { +namespace detail { + +class user_script::impl { +public: + impl(WebKitUserScript *script) : m_script{script} { + webkit_user_script_ref(script); + } + + ~impl() { webkit_user_script_unref(m_script); } + + impl(const impl &) = delete; + impl &operator=(const impl &) = delete; + impl(impl &&) = delete; + impl &operator=(impl &&) = delete; + + WebKitUserScript *get_native() const { return m_script; } + +private: + WebKitUserScript *m_script{}; +}; + +class gtk_webkit_engine : public engine_base { +public: + gtk_webkit_engine(bool debug, void *window) + : m_owns_window{!window}, m_window(static_cast(window)) { + if (m_owns_window) { + if (!gtk_compat::init_check()) { + throw exception{WEBVIEW_ERROR_UNSPECIFIED, "GTK init failed"}; + } + m_window = gtk_compat::window_new(); + on_window_created(); + auto on_window_destroy = +[](GtkWidget *, gpointer arg) { + auto *w = static_cast(arg); + w->m_window = nullptr; + w->on_window_destroyed(); + }; + g_signal_connect(G_OBJECT(m_window), "destroy", + G_CALLBACK(on_window_destroy), this); + } + webkit_dmabuf::apply_webkit_dmabuf_workaround(); + // Initialize webview widget + m_webview = webkit_web_view_new(); + g_object_ref_sink(m_webview); + WebKitUserContentManager *manager = m_user_content_manager = + webkit_web_view_get_user_content_manager(WEBKIT_WEB_VIEW(m_webview)); + webkitgtk_compat::connect_script_message_received( + manager, "__webview__", + [this](WebKitUserContentManager *, const std::string &r) { + on_message(r); + }); + webkitgtk_compat::user_content_manager_register_script_message_handler( + manager, "__webview__"); + add_init_script("function(message) {\n\ + return window.webkit.messageHandlers.__webview__.postMessage(message);\n\ +}"); + + gtk_compat::window_set_child(GTK_WINDOW(m_window), GTK_WIDGET(m_webview)); + gtk_compat::widget_set_visible(GTK_WIDGET(m_webview), true); + + WebKitSettings *settings = + webkit_web_view_get_settings(WEBKIT_WEB_VIEW(m_webview)); + webkit_settings_set_javascript_can_access_clipboard(settings, true); + if (debug) { + webkit_settings_set_enable_write_console_messages_to_stdout(settings, + true); + webkit_settings_set_enable_developer_extras(settings, true); + } + + if (m_owns_window) { + gtk_widget_grab_focus(GTK_WIDGET(m_webview)); + gtk_compat::widget_set_visible(GTK_WIDGET(m_window), true); + } + } + + gtk_webkit_engine(const gtk_webkit_engine &) = delete; + gtk_webkit_engine &operator=(const gtk_webkit_engine &) = delete; + gtk_webkit_engine(gtk_webkit_engine &&) = delete; + gtk_webkit_engine &operator=(gtk_webkit_engine &&) = delete; + + virtual ~gtk_webkit_engine() { + if (m_window) { + if (m_owns_window) { + // Disconnect handlers to avoid callbacks invoked during destruction. + g_signal_handlers_disconnect_by_data(GTK_WINDOW(m_window), this); + gtk_window_close(GTK_WINDOW(m_window)); + on_window_destroyed(true); + } else { + gtk_compat::window_remove_child(GTK_WINDOW(m_window), + GTK_WIDGET(m_webview)); + } + } + if (m_webview) { + g_object_unref(m_webview); + } + if (m_owns_window) { + // Needed for the window to close immediately. + deplete_run_loop_event_queue(); + } + } + +protected: + result window_impl() override { + if (m_window) { + return m_window; + } + return error_info{WEBVIEW_ERROR_INVALID_STATE}; + } + + result widget_impl() override { + if (m_webview) { + return m_webview; + } + return error_info{WEBVIEW_ERROR_INVALID_STATE}; + } + + result browser_controller_impl() override { + if (m_webview) { + return m_webview; + } + return error_info{WEBVIEW_ERROR_INVALID_STATE}; + } + + noresult run_impl() override { + m_stop_run_loop = false; + while (!m_stop_run_loop) { + g_main_context_iteration(nullptr, TRUE); + } + return {}; + } + + noresult terminate_impl() override { + return dispatch_impl([&] { m_stop_run_loop = true; }); + } + + noresult dispatch_impl(std::function f) override { + g_idle_add_full(G_PRIORITY_HIGH_IDLE, (GSourceFunc)([](void *fn) -> int { + (*static_cast(fn))(); + return G_SOURCE_REMOVE; + }), + new std::function(f), + [](void *fn) { delete static_cast(fn); }); + return {}; + } + + noresult set_title_impl(const std::string &title) override { + gtk_window_set_title(GTK_WINDOW(m_window), title.c_str()); + return {}; + } + + noresult set_size_impl(int width, int height, webview_hint_t hints) override { + gtk_window_set_resizable(GTK_WINDOW(m_window), hints != WEBVIEW_HINT_FIXED); + if (hints == WEBVIEW_HINT_NONE) { + gtk_compat::window_set_size(GTK_WINDOW(m_window), width, height); + } else if (hints == WEBVIEW_HINT_FIXED || hints == WEBVIEW_HINT_MIN) { + gtk_widget_set_size_request(m_window, width, height); + } else if (hints == WEBVIEW_HINT_MAX) { + gtk_compat::window_set_max_size(GTK_WINDOW(m_window), width, height); + } + return error_info{WEBVIEW_ERROR_INVALID_ARGUMENT, "Invalid hint"}; + } + + noresult navigate_impl(const std::string &url) override { + webkit_web_view_load_uri(WEBKIT_WEB_VIEW(m_webview), url.c_str()); + return {}; + } + + noresult set_html_impl(const std::string &html) override { + webkit_web_view_load_html(WEBKIT_WEB_VIEW(m_webview), html.c_str(), + nullptr); + return {}; + } + + noresult eval_impl(const std::string &js) override { + // URI is null before content has begun loading. + if (!webkit_web_view_get_uri(WEBKIT_WEB_VIEW(m_webview))) { + return {}; + } +#if (WEBKIT_MAJOR_VERSION == 2 && WEBKIT_MINOR_VERSION >= 40) || \ + WEBKIT_MAJOR_VERSION > 2 + webkit_web_view_evaluate_javascript(WEBKIT_WEB_VIEW(m_webview), js.c_str(), + static_cast(js.size()), nullptr, + nullptr, nullptr, nullptr, nullptr); +#else + webkit_web_view_run_javascript(WEBKIT_WEB_VIEW(m_webview), js.c_str(), + nullptr, nullptr, nullptr); +#endif + return {}; + } + + user_script add_user_script_impl(const std::string &js) override { + auto *wk_script = webkit_user_script_new( + js.c_str(), WEBKIT_USER_CONTENT_INJECT_TOP_FRAME, + WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_START, nullptr, nullptr); + webkit_user_content_manager_add_script(m_user_content_manager, wk_script); + user_script script{ + js, user_script::impl_ptr{new user_script::impl{wk_script}, + [](user_script::impl *p) { delete p; }}}; + webkit_user_script_unref(wk_script); + return script; + } + + void remove_all_user_scripts_impl( + const std::list & /*scripts*/) override { + webkit_user_content_manager_remove_all_scripts(m_user_content_manager); + } + + bool are_user_scripts_equal_impl(const user_script &first, + const user_script &second) override { + auto *wk_first = first.get_impl().get_native(); + auto *wk_second = second.get_impl().get_native(); + return wk_first == wk_second; + } + +private: +#if GTK_MAJOR_VERSION >= 4 + static char *get_string_from_js_result(JSCValue *r) { + return jsc_value_to_string(r); + } +#else + static char *get_string_from_js_result(WebKitJavascriptResult *r) { + char *s; +#if (WEBKIT_MAJOR_VERSION == 2 && WEBKIT_MINOR_VERSION >= 22) || \ + WEBKIT_MAJOR_VERSION > 2 + JSCValue *value = webkit_javascript_result_get_js_value(r); + s = jsc_value_to_string(value); +#else + JSGlobalContextRef ctx = webkit_javascript_result_get_global_context(r); + JSValueRef value = webkit_javascript_result_get_value(r); + JSStringRef js = JSValueToStringCopy(ctx, value, nullptr); + size_t n = JSStringGetMaximumUTF8CStringSize(js); + s = g_new(char, n); + JSStringGetUTF8CString(js, s, n); + JSStringRelease(js); +#endif + return s; + } +#endif + + // Blocks while depleting the run loop of events. + void deplete_run_loop_event_queue() { + bool done{}; + dispatch([&] { done = true; }); + while (!done) { + g_main_context_iteration(nullptr, TRUE); + } + } + + bool m_owns_window{}; + GtkWidget *m_window{}; + GtkWidget *m_webview{}; + WebKitUserContentManager *m_user_content_manager{}; + bool m_stop_run_loop{}; +}; + +} // namespace detail + +using browser_engine = detail::gtk_webkit_engine; + +} // namespace webview + +#endif // WEBVIEW_BACKENDS_GTK_WEBKITGTK_H diff --git a/BaseWebview/lib/webview/include/detail/backends/win32_edge.hh b/BaseWebview/lib/webview/include/detail/backends/win32_edge.hh new file mode 100644 index 0000000..d4fba83 --- /dev/null +++ b/BaseWebview/lib/webview/include/detail/backends/win32_edge.hh @@ -0,0 +1,892 @@ +/* + * MIT License + * + * Copyright (c) 2017 Serge Zaitsev + * Copyright (c) 2022 Steffen André Langnes + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined(WEBVIEW_BACKENDS_WIN32_EDGE_HH) && \ + defined(WEBVIEW_PLATFORM_WINDOWS) && defined(WEBVIEW_EDGE) +#define WEBVIEW_BACKENDS_WIN32_EDGE_HH + +// +// ==================================================================== +// +// This implementation uses Win32 API to create a native window. It +// uses Edge/Chromium webview2 backend as a browser engine. +// +// ==================================================================== +// + +#include "../../errors.hh" +#include "../../types.hh" +#include "../engine_base.hh" +#include "../native_library.hh" +#include "../platform/windows/com_init_wrapper.hh" +#include "../platform/windows/dpi.hh" +#include "../platform/windows/iid.hh" +#include "../platform/windows/reg_key.hh" +#include "../platform/windows/theme.hh" +#include "../platform/windows/version.hh" +#include "../platform/windows/webview2/loader.hh" +#include "../user_script.hh" +#include "../utility/string.hh" + +#include +#include +#include +#include +#include +#include + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif + +#include + +#include +#include +#include + +#ifdef _MSC_VER +#pragma comment(lib, "ole32.lib") +#pragma comment(lib, "shell32.lib") +#pragma comment(lib, "shlwapi.lib") +#pragma comment(lib, "user32.lib") +#pragma comment(lib, "version.lib") +#endif + +namespace webview { +namespace detail { + +using msg_cb_t = std::function; + +class webview2_com_handler + : public ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler, + public ICoreWebView2CreateCoreWebView2ControllerCompletedHandler, + public ICoreWebView2WebMessageReceivedEventHandler, + public ICoreWebView2PermissionRequestedEventHandler { + using webview2_com_handler_cb_t = + std::function; + +public: + webview2_com_handler(HWND hwnd, msg_cb_t msgCb, webview2_com_handler_cb_t cb) + : m_window(hwnd), m_msgCb(msgCb), m_cb(cb) {} + + virtual ~webview2_com_handler() = default; + webview2_com_handler(const webview2_com_handler &other) = delete; + webview2_com_handler &operator=(const webview2_com_handler &other) = delete; + webview2_com_handler(webview2_com_handler &&other) = delete; + webview2_com_handler &operator=(webview2_com_handler &&other) = delete; + + ULONG STDMETHODCALLTYPE AddRef() { return ++m_ref_count; } + ULONG STDMETHODCALLTYPE Release() { + if (m_ref_count > 1) { + return --m_ref_count; + } + delete this; + return 0; + } + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID *ppv) { + using namespace mswebview2::cast_info; + + if (!ppv) { + return E_POINTER; + } + + // All of the COM interfaces we implement should be added here regardless + // of whether they are required. + // This is just to be on the safe side in case the WebView2 Runtime ever + // requests a pointer to an interface we implement. + // The WebView2 Runtime must at the very least be able to get a pointer to + // ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler when we use + // our custom WebView2 loader implementation, and observations have shown + // that it is the only interface requested in this case. None have been + // observed to be requested when using the official WebView2 loader. + + if (cast_if_equal_iid(this, riid, controller_completed, ppv) || + cast_if_equal_iid(this, riid, environment_completed, ppv) || + cast_if_equal_iid(this, riid, message_received, ppv) || + cast_if_equal_iid(this, riid, permission_requested, ppv)) { + return S_OK; + } + + return E_NOINTERFACE; + } + HRESULT STDMETHODCALLTYPE Invoke(HRESULT res, ICoreWebView2Environment *env) { + if (SUCCEEDED(res)) { + res = env->CreateCoreWebView2Controller(m_window, this); + if (SUCCEEDED(res)) { + return S_OK; + } + } + try_create_environment(); + return S_OK; + } + HRESULT STDMETHODCALLTYPE Invoke(HRESULT res, + ICoreWebView2Controller *controller) { + if (FAILED(res)) { + // See try_create_environment() regarding + // HRESULT_FROM_WIN32(ERROR_INVALID_STATE). + // The result is E_ABORT if the parent window has been destroyed already. + switch (res) { + case HRESULT_FROM_WIN32(ERROR_INVALID_STATE): + case E_ABORT: + return S_OK; + } + try_create_environment(); + return S_OK; + } + + ICoreWebView2 *webview; + ::EventRegistrationToken token; + controller->get_CoreWebView2(&webview); + webview->add_WebMessageReceived(this, &token); + webview->add_PermissionRequested(this, &token); + + m_cb(controller, webview); + return S_OK; + } + HRESULT STDMETHODCALLTYPE + Invoke(ICoreWebView2 * /*sender*/, + ICoreWebView2WebMessageReceivedEventArgs *args) { + LPWSTR message{}; + auto res = args->TryGetWebMessageAsString(&message); + if (SUCCEEDED(res)) { + m_msgCb(narrow_string(message)); + } + + CoTaskMemFree(message); + return S_OK; + } + HRESULT STDMETHODCALLTYPE + Invoke(ICoreWebView2 * /*sender*/, + ICoreWebView2PermissionRequestedEventArgs *args) { + COREWEBVIEW2_PERMISSION_KIND kind; + args->get_PermissionKind(&kind); + if (kind == COREWEBVIEW2_PERMISSION_KIND_CLIPBOARD_READ) { + args->put_State(COREWEBVIEW2_PERMISSION_STATE_ALLOW); + } + return S_OK; + } + + // Set the function that will perform the initiating logic for creating + // the WebView2 environment. + void set_attempt_handler(std::function attempt_handler) noexcept { + m_attempt_handler = attempt_handler; + } + + // Retry creating a WebView2 environment. + // The initiating logic for creating the environment is defined by the + // caller of set_attempt_handler(). + void try_create_environment() noexcept { + // WebView creation fails with HRESULT_FROM_WIN32(ERROR_INVALID_STATE) if + // a running instance using the same user data folder exists, and the + // Environment objects have different EnvironmentOptions. + // Source: https://docs.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2environment?view=webview2-1.0.1150.38 + if (m_attempts < m_max_attempts) { + ++m_attempts; + auto res = m_attempt_handler(); + if (SUCCEEDED(res)) { + return; + } + // Not entirely sure if this error code only applies to + // CreateCoreWebView2Controller so we check here as well. + if (res == HRESULT_FROM_WIN32(ERROR_INVALID_STATE)) { + return; + } + try_create_environment(); + return; + } + // Give up. + m_cb(nullptr, nullptr); + } + +private: + HWND m_window; + msg_cb_t m_msgCb; + webview2_com_handler_cb_t m_cb; + std::atomic m_ref_count{1}; + std::function m_attempt_handler; + unsigned int m_max_attempts = 5; + unsigned int m_attempts = 0; +}; + +class webview2_user_script_added_handler + : public ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandler { +public: + using callback_fn = std::function; + + webview2_user_script_added_handler(callback_fn cb) : m_cb{cb} {} + + virtual ~webview2_user_script_added_handler() = default; + webview2_user_script_added_handler( + const webview2_user_script_added_handler &other) = delete; + webview2_user_script_added_handler & + operator=(const webview2_user_script_added_handler &other) = delete; + webview2_user_script_added_handler( + webview2_user_script_added_handler &&other) = delete; + webview2_user_script_added_handler & + operator=(webview2_user_script_added_handler &&other) = delete; + + ULONG STDMETHODCALLTYPE AddRef() { return ++m_ref_count; } + ULONG STDMETHODCALLTYPE Release() { + if (m_ref_count > 1) { + return --m_ref_count; + } + delete this; + return 0; + } + + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID *ppv) { + using namespace mswebview2::cast_info; + + if (!ppv) { + return E_POINTER; + } + + if (cast_if_equal_iid(this, riid, + add_script_to_execute_on_document_created_completed, + ppv)) { + return S_OK; + } + + return E_NOINTERFACE; + } + + HRESULT STDMETHODCALLTYPE Invoke(HRESULT res, LPCWSTR id) { + m_cb(res, id); + return S_OK; + } + +private: + callback_fn m_cb; + std::atomic m_ref_count{1}; +}; + +class user_script::impl { +public: + impl(const std::wstring &id, const std::wstring &code) + : m_id{id}, m_code{code} {} + + impl(const impl &) = delete; + impl &operator=(const impl &) = delete; + impl(impl &&) = delete; + impl &operator=(impl &&) = delete; + + const std::wstring &get_id() const { return m_id; } + const std::wstring &get_code() const { return m_code; } + +private: + std::wstring m_id; + std::wstring m_code; +}; + +class win32_edge_engine : public engine_base { +public: + win32_edge_engine(bool debug, void *window) : m_owns_window{!window} { + if (!is_webview2_available()) { + throw exception{WEBVIEW_ERROR_MISSING_DEPENDENCY, + "WebView2 is unavailable"}; + } + + HINSTANCE hInstance = GetModuleHandle(nullptr); + + if (m_owns_window) { + m_com_init = {COINIT_APARTMENTTHREADED}; + enable_dpi_awareness(); + + HICON icon = (HICON)LoadImage( + hInstance, IDI_APPLICATION, IMAGE_ICON, GetSystemMetrics(SM_CXICON), + GetSystemMetrics(SM_CYICON), LR_DEFAULTCOLOR); + + // Create a top-level window. + WNDCLASSEXW wc; + ZeroMemory(&wc, sizeof(WNDCLASSEX)); + wc.cbSize = sizeof(WNDCLASSEX); + wc.hInstance = hInstance; + wc.lpszClassName = L"webview"; + wc.hIcon = icon; + wc.lpfnWndProc = (WNDPROC)(+[](HWND hwnd, UINT msg, WPARAM wp, + LPARAM lp) -> LRESULT { + win32_edge_engine *w{}; + + if (msg == WM_NCCREATE) { + auto *lpcs{reinterpret_cast(lp)}; + w = static_cast(lpcs->lpCreateParams); + w->m_window = hwnd; + SetWindowLongPtrW(hwnd, GWLP_USERDATA, reinterpret_cast(w)); + enable_non_client_dpi_scaling_if_needed(hwnd); + apply_window_theme(hwnd); + } else { + w = reinterpret_cast( + GetWindowLongPtrW(hwnd, GWLP_USERDATA)); + } + + if (!w) { + return DefWindowProcW(hwnd, msg, wp, lp); + } + + switch (msg) { + case WM_SIZE: + w->resize_widget(); + break; + case WM_CLOSE: + DestroyWindow(hwnd); + break; + case WM_DESTROY: + w->m_window = nullptr; + SetWindowLongPtrW(hwnd, GWLP_USERDATA, 0); + w->on_window_destroyed(); + break; + case WM_GETMINMAXINFO: { + auto lpmmi = (LPMINMAXINFO)lp; + if (w->m_maxsz.x > 0 && w->m_maxsz.y > 0) { + lpmmi->ptMaxSize = w->m_maxsz; + lpmmi->ptMaxTrackSize = w->m_maxsz; + } + if (w->m_minsz.x > 0 && w->m_minsz.y > 0) { + lpmmi->ptMinTrackSize = w->m_minsz; + } + } break; + case 0x02E4 /*WM_GETDPISCALEDSIZE*/: { + auto dpi = static_cast(wp); + auto *size{reinterpret_cast(lp)}; + *size = w->get_scaled_size(w->m_dpi, dpi); + return TRUE; + } + case 0x02E0 /*WM_DPICHANGED*/: { + // Windows 10: The size we get here is exactly what we supplied to WM_GETDPISCALEDSIZE. + // Windows 11: The size we get here is NOT what we supplied to WM_GETDPISCALEDSIZE. + // Due to this difference, don't use the suggested bounds. + auto dpi = static_cast(HIWORD(wp)); + w->on_dpi_changed(dpi); + break; + } + case WM_SETTINGCHANGE: { + auto *area = reinterpret_cast(lp); + if (area) { + w->on_system_setting_change(area); + } + break; + } + case WM_ACTIVATE: + if (LOWORD(wp) != WA_INACTIVE) { + w->focus_webview(); + } + break; + default: + return DefWindowProcW(hwnd, msg, wp, lp); + } + return 0; + }); + RegisterClassExW(&wc); + + CreateWindowW(L"webview", L"", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, + CW_USEDEFAULT, 0, 0, nullptr, nullptr, hInstance, this); + if (!m_window) { + throw exception{WEBVIEW_ERROR_INVALID_STATE, "Window is null"}; + } + on_window_created(); + + m_dpi = get_window_dpi(m_window); + constexpr const int initial_width = 640; + constexpr const int initial_height = 480; + set_size(initial_width, initial_height, WEBVIEW_HINT_NONE); + } else { + m_window = IsWindow(static_cast(window)) + ? static_cast(window) + : *(static_cast(window)); + m_dpi = get_window_dpi(m_window); + } + + // Create a window that WebView2 will be embedded into. + WNDCLASSEXW widget_wc{}; + widget_wc.cbSize = sizeof(WNDCLASSEX); + widget_wc.hInstance = hInstance; + widget_wc.lpszClassName = L"webview_widget"; + widget_wc.lpfnWndProc = (WNDPROC)(+[](HWND hwnd, UINT msg, WPARAM wp, + LPARAM lp) -> LRESULT { + win32_edge_engine *w{}; + + if (msg == WM_NCCREATE) { + auto *lpcs{reinterpret_cast(lp)}; + w = static_cast(lpcs->lpCreateParams); + w->m_widget = hwnd; + SetWindowLongPtrW(hwnd, GWLP_USERDATA, reinterpret_cast(w)); + } else { + w = reinterpret_cast( + GetWindowLongPtrW(hwnd, GWLP_USERDATA)); + } + + if (!w) { + return DefWindowProcW(hwnd, msg, wp, lp); + } + + switch (msg) { + case WM_SIZE: + w->resize_webview(); + break; + case WM_DESTROY: + w->m_widget = nullptr; + SetWindowLongPtrW(hwnd, GWLP_USERDATA, 0); + break; + default: + return DefWindowProcW(hwnd, msg, wp, lp); + } + return 0; + }); + RegisterClassExW(&widget_wc); + CreateWindowExW(WS_EX_CONTROLPARENT, L"webview_widget", nullptr, WS_CHILD, + 0, 0, 0, 0, m_window, nullptr, hInstance, this); + if (!m_widget) { + throw exception{WEBVIEW_ERROR_INVALID_STATE, "Widget window is null"}; + } + + // Create a message-only window for internal messaging. + WNDCLASSEXW message_wc{}; + message_wc.cbSize = sizeof(WNDCLASSEX); + message_wc.hInstance = hInstance; + message_wc.lpszClassName = L"webview_message"; + message_wc.lpfnWndProc = (WNDPROC)(+[](HWND hwnd, UINT msg, WPARAM wp, + LPARAM lp) -> LRESULT { + win32_edge_engine *w{}; + + if (msg == WM_NCCREATE) { + auto *lpcs{reinterpret_cast(lp)}; + w = static_cast(lpcs->lpCreateParams); + w->m_message_window = hwnd; + SetWindowLongPtrW(hwnd, GWLP_USERDATA, reinterpret_cast(w)); + } else { + w = reinterpret_cast( + GetWindowLongPtrW(hwnd, GWLP_USERDATA)); + } + + if (!w) { + return DefWindowProcW(hwnd, msg, wp, lp); + } + + switch (msg) { + case WM_APP: + if (auto f = (dispatch_fn_t *)(lp)) { + (*f)(); + delete f; + } + break; + case WM_DESTROY: + w->m_message_window = nullptr; + SetWindowLongPtrW(hwnd, GWLP_USERDATA, 0); + break; + default: + return DefWindowProcW(hwnd, msg, wp, lp); + } + return 0; + }); + RegisterClassExW(&message_wc); + CreateWindowExW(0, L"webview_message", nullptr, 0, 0, 0, 0, 0, HWND_MESSAGE, + nullptr, hInstance, this); + if (!m_message_window) { + throw exception{WEBVIEW_ERROR_INVALID_STATE, "Message window is null"}; + } + + if (m_owns_window) { + ShowWindow(m_window, SW_SHOW); + UpdateWindow(m_window); + SetFocus(m_window); + } + + auto cb = + std::bind(&win32_edge_engine::on_message, this, std::placeholders::_1); + + embed(m_widget, debug, cb).ensure_ok(); + } + + virtual ~win32_edge_engine() { + if (m_com_handler) { + m_com_handler->Release(); + m_com_handler = nullptr; + } + if (m_webview) { + m_webview->Release(); + m_webview = nullptr; + } + if (m_controller) { + m_controller->Release(); + m_controller = nullptr; + } + // Replace wndproc to avoid callbacks and other bad things during + // destruction. + auto wndproc = reinterpret_cast( + +[](HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) -> LRESULT { + return DefWindowProcW(hwnd, msg, wp, lp); + }); + if (m_widget) { + SetWindowLongPtrW(m_widget, GWLP_WNDPROC, wndproc); + } + if (m_window && m_owns_window) { + SetWindowLongPtrW(m_window, GWLP_WNDPROC, wndproc); + } + if (m_widget) { + DestroyWindow(m_widget); + m_widget = nullptr; + } + if (m_window) { + if (m_owns_window) { + DestroyWindow(m_window); + on_window_destroyed(true); + } + m_window = nullptr; + } + if (m_owns_window) { + // Not strictly needed for windows to close immediately but aligns + // behavior across backends. + deplete_run_loop_event_queue(); + } + // We need the message window in order to deplete the event queue. + if (m_message_window) { + SetWindowLongPtrW(m_message_window, GWLP_WNDPROC, wndproc); + DestroyWindow(m_message_window); + m_message_window = nullptr; + } + } + + win32_edge_engine(const win32_edge_engine &other) = delete; + win32_edge_engine &operator=(const win32_edge_engine &other) = delete; + win32_edge_engine(win32_edge_engine &&other) = delete; + win32_edge_engine &operator=(win32_edge_engine &&other) = delete; + +protected: + noresult run_impl() override { + MSG msg; + while (GetMessageW(&msg, nullptr, 0, 0) > 0) { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + return {}; + } + result window_impl() override { + if (m_window) { + return m_window; + } + return error_info{WEBVIEW_ERROR_INVALID_STATE}; + } + result widget_impl() override { + if (m_widget) { + return m_widget; + } + return error_info{WEBVIEW_ERROR_INVALID_STATE}; + } + result browser_controller_impl() override { + if (m_controller) { + return m_controller; + } + return error_info{WEBVIEW_ERROR_INVALID_STATE}; + } + noresult terminate_impl() override { + PostQuitMessage(0); + return {}; + } + noresult dispatch_impl(dispatch_fn_t f) override { + PostMessageW(m_message_window, WM_APP, 0, (LPARAM) new dispatch_fn_t(f)); + return {}; + } + + noresult set_title_impl(const std::string &title) override { + SetWindowTextW(m_window, widen_string(title).c_str()); + return {}; + } + + noresult set_size_impl(int width, int height, webview_hint_t hints) override { + auto style = GetWindowLong(m_window, GWL_STYLE); + if (hints == WEBVIEW_HINT_FIXED) { + style &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX); + } else { + style |= (WS_THICKFRAME | WS_MAXIMIZEBOX); + } + SetWindowLong(m_window, GWL_STYLE, style); + + if (hints == WEBVIEW_HINT_MAX) { + m_maxsz.x = width; + m_maxsz.y = height; + } else if (hints == WEBVIEW_HINT_MIN) { + m_minsz.x = width; + m_minsz.y = height; + } else { + auto dpi = get_window_dpi(m_window); + m_dpi = dpi; + auto scaled_size = + scale_size(width, height, get_default_window_dpi(), dpi); + auto frame_size = + make_window_frame_size(m_window, scaled_size.cx, scaled_size.cy, dpi); + SetWindowPos(m_window, nullptr, 0, 0, frame_size.cx, frame_size.cy, + SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE | + SWP_FRAMECHANGED); + } + return {}; + } + + noresult navigate_impl(const std::string &url) override { + auto wurl = widen_string(url); + m_webview->Navigate(wurl.c_str()); + return {}; + } + + noresult eval_impl(const std::string &js) override { + // TODO: Skip if no content has begun loading yet. Can't check with + // ICoreWebView2::get_Source because it returns "about:blank". + auto wjs = widen_string(js); + m_webview->ExecuteScript(wjs.c_str(), nullptr); + return {}; + } + + noresult set_html_impl(const std::string &html) override { + m_webview->NavigateToString(widen_string(html).c_str()); + return {}; + } + + user_script add_user_script_impl(const std::string &js) override { + auto wjs = widen_string(js); + std::wstring script_id; + bool done{}; + webview2_user_script_added_handler handler{[&](HRESULT res, LPCWSTR id) { + if (SUCCEEDED(res)) { + script_id = id; + } + done = true; + }}; + auto res = + m_webview->AddScriptToExecuteOnDocumentCreated(wjs.c_str(), &handler); + if (SUCCEEDED(res)) { + // Sadly we need to pump the even loop in order to get the script ID. + while (!done) { + deplete_run_loop_event_queue(); + } + } + // TODO: There's a non-zero chance that we didn't get the script ID. + // We need to convey the error somehow. + return user_script{ + js, user_script::impl_ptr{new user_script::impl{script_id, wjs}, + [](user_script::impl *p) { delete p; }}}; + } + + void + remove_all_user_scripts_impl(const std::list &scripts) override { + for (const auto &script : scripts) { + const auto &id = script.get_impl().get_id(); + m_webview->RemoveScriptToExecuteOnDocumentCreated(id.c_str()); + } + } + + bool are_user_scripts_equal_impl(const user_script &first, + const user_script &second) override { + const auto &first_id = first.get_impl().get_id(); + const auto &second_id = second.get_impl().get_id(); + return first_id == second_id; + } + +private: + noresult embed(HWND wnd, bool debug, msg_cb_t cb) { + std::atomic_flag flag = ATOMIC_FLAG_INIT; + flag.test_and_set(); + + wchar_t currentExePath[MAX_PATH]; + GetModuleFileNameW(nullptr, currentExePath, MAX_PATH); + wchar_t *currentExeName = PathFindFileNameW(currentExePath); + + wchar_t dataPath[MAX_PATH]; + if (!SUCCEEDED( + SHGetFolderPathW(nullptr, CSIDL_APPDATA, nullptr, 0, dataPath))) { + return error_info{WEBVIEW_ERROR_UNSPECIFIED, "SHGetFolderPathW failed"}; + } + wchar_t userDataFolder[MAX_PATH]; + PathCombineW(userDataFolder, dataPath, currentExeName); + + m_com_handler = new webview2_com_handler( + wnd, cb, + [&](ICoreWebView2Controller *controller, ICoreWebView2 *webview) { + if (!controller || !webview) { + flag.clear(); + return; + } + controller->AddRef(); + webview->AddRef(); + m_controller = controller; + m_webview = webview; + flag.clear(); + }); + + m_com_handler->set_attempt_handler([&] { + return m_webview2_loader.create_environment_with_options( + nullptr, userDataFolder, nullptr, m_com_handler); + }); + m_com_handler->try_create_environment(); + + // Pump the message loop until WebView2 has finished initialization. + bool got_quit_msg = false; + MSG msg; + while (flag.test_and_set() && GetMessageW(&msg, nullptr, 0, 0) >= 0) { + if (msg.message == WM_QUIT) { + got_quit_msg = true; + break; + } + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + if (got_quit_msg) { + return error_info{WEBVIEW_ERROR_CANCELED}; + } + if (!m_controller || !m_webview) { + return error_info{WEBVIEW_ERROR_INVALID_STATE}; + } + ICoreWebView2Settings *settings = nullptr; + auto res = m_webview->get_Settings(&settings); + if (res != S_OK) { + return error_info{WEBVIEW_ERROR_UNSPECIFIED, "get_Settings failed"}; + } + res = settings->put_AreDevToolsEnabled(debug ? TRUE : FALSE); + if (res != S_OK) { + return error_info{WEBVIEW_ERROR_UNSPECIFIED, + "put_AreDevToolsEnabled failed"}; + } + res = settings->put_IsStatusBarEnabled(FALSE); + if (res != S_OK) { + return error_info{WEBVIEW_ERROR_UNSPECIFIED, + "put_IsStatusBarEnabled failed"}; + } + add_init_script("function(message) {\n\ + return window.chrome.webview.postMessage(message);\n\ +}"); + resize_webview(); + m_controller->put_IsVisible(TRUE); + ShowWindow(m_widget, SW_SHOW); + UpdateWindow(m_widget); + if (m_owns_window) { + focus_webview(); + } + return {}; + } + + void resize_widget() { + if (m_widget) { + RECT r{}; + if (GetClientRect(GetParent(m_widget), &r)) { + MoveWindow(m_widget, r.left, r.top, r.right - r.left, r.bottom - r.top, + TRUE); + } + } + } + + void resize_webview() { + if (m_widget && m_controller) { + RECT bounds{}; + if (GetClientRect(m_widget, &bounds)) { + m_controller->put_Bounds(bounds); + } + } + } + + void focus_webview() { + if (m_controller) { + m_controller->MoveFocus(COREWEBVIEW2_MOVE_FOCUS_REASON_PROGRAMMATIC); + } + } + + bool is_webview2_available() const noexcept { + LPWSTR version_info = nullptr; + auto res = m_webview2_loader.get_available_browser_version_string( + nullptr, &version_info); + // The result will be equal to HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) + // if the WebView2 runtime is not installed. + auto ok = SUCCEEDED(res) && version_info; + if (version_info) { + CoTaskMemFree(version_info); + } + return ok; + } + + void on_dpi_changed(int dpi) { + auto scaled_size = get_scaled_size(m_dpi, dpi); + auto frame_size = + make_window_frame_size(m_window, scaled_size.cx, scaled_size.cy, dpi); + SetWindowPos(m_window, nullptr, 0, 0, frame_size.cx, frame_size.cy, + SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE | SWP_FRAMECHANGED); + m_dpi = dpi; + } + + SIZE get_size() const { + RECT bounds; + GetClientRect(m_window, &bounds); + auto width = bounds.right - bounds.left; + auto height = bounds.bottom - bounds.top; + return {width, height}; + } + + SIZE get_scaled_size(int from_dpi, int to_dpi) const { + auto size = get_size(); + return scale_size(size.cx, size.cy, from_dpi, to_dpi); + } + + void on_system_setting_change(const wchar_t *area) { + // Detect light/dark mode change in system. + if (lstrcmpW(area, L"ImmersiveColorSet") == 0) { + apply_window_theme(m_window); + } + } + + // Blocks while depleting the run loop of events. + void deplete_run_loop_event_queue() { + bool done{}; + dispatch([&] { done = true; }); + while (!done) { + MSG msg; + if (GetMessageW(&msg, nullptr, 0, 0) > 0) { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + } + } + + // The app is expected to call CoInitializeEx before + // CreateCoreWebView2EnvironmentWithOptions. + // Source: https://docs.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/webview2-idl#createcorewebview2environmentwithoptions + com_init_wrapper m_com_init; + HWND m_window = nullptr; + HWND m_widget = nullptr; + HWND m_message_window = nullptr; + POINT m_minsz = POINT{0, 0}; + POINT m_maxsz = POINT{0, 0}; + DWORD m_main_thread = GetCurrentThreadId(); + ICoreWebView2 *m_webview = nullptr; + ICoreWebView2Controller *m_controller = nullptr; + webview2_com_handler *m_com_handler = nullptr; + mswebview2::loader m_webview2_loader; + int m_dpi{}; + bool m_owns_window{}; +}; + +} // namespace detail + +using browser_engine = detail::win32_edge_engine; + +} // namespace webview + +#endif // WEBVIEW_BACKENDS_WIN32_EDGE_H diff --git a/BaseWebview/lib/webview/include/detail/basic_result.hh b/BaseWebview/lib/webview/include/detail/basic_result.hh new file mode 100644 index 0000000..ffdd3b3 --- /dev/null +++ b/BaseWebview/lib/webview/include/detail/basic_result.hh @@ -0,0 +1,116 @@ +/* + * MIT License + * + * Copyright (c) 2017 Serge Zaitsev + * Copyright (c) 2022 Steffen André Langnes + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef WEBVIEW_DETAIL_BASIC_RESULT_HH +#define WEBVIEW_DETAIL_BASIC_RESULT_HH + +#include "exceptions.hh" +#include "optional.hh" + +#include + +namespace webview { +namespace detail { + +template +class basic_result { +public: + using value_type = Value; + using error_type = Error; + using exception_type = Exception; + + basic_result() : basic_result(value_type{}) {} + + basic_result(const value_type &value) : m_value{value} {} + basic_result(value_type &&value) : m_value{std::forward(value)} {} + + basic_result(const error_type &error) : m_error{error} {} + basic_result(error_type &&error) : m_error{std::forward(error)} {} + + bool ok() const { return has_value() && !has_error(); } + bool has_value() const { return m_value.has_value(); } + bool has_error() const { return m_error.has_value(); } + + void ensure_ok() { + if (!ok()) { + throw exception_type{error()}; + } + } + + const value_type &value() const { + if (!has_value()) { + throw bad_access{}; + } + return m_value.get(); + } + + const error_type &error() const { + if (!has_error()) { + throw bad_access{}; + } + return m_error.get(); + } + +private: + optional m_value; + optional m_error; +}; + +template +class basic_result { +public: + using value_type = void; + using error_type = Error; + using exception_type = Exception; + + basic_result() = default; + + basic_result(error_type &&error) : m_error{std::forward(error)} {} + + bool ok() const { return !has_error(); } + + bool has_error() const { return m_error.has_value(); } + + void ensure_ok() { + if (!ok()) { + throw exception_type{error()}; + } + } + + const error_type &error() const { + if (!has_error()) { + throw bad_access{}; + } + return m_error.get(); + } + +private: + optional m_error; +}; + +} // namespace detail +} // namespace webview + +#endif // WEBVIEW_DETAIL_BASIC_RESULT_H diff --git a/BaseWebview/lib/webview/include/detail/engine_base.hh b/BaseWebview/lib/webview/include/detail/engine_base.hh new file mode 100644 index 0000000..a10e7ea --- /dev/null +++ b/BaseWebview/lib/webview/include/detail/engine_base.hh @@ -0,0 +1,343 @@ +/* + * MIT License + * + * Copyright (c) 2017 Serge Zaitsev + * Copyright (c) 2022 Steffen André Langnes + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef WEBVIEW_DETAIL_ENGINE_BASE_H +#define WEBVIEW_DETAIL_ENGINE_BASE_H + +#include "../errors.hh" +#include "../types.h" +#include "../types.hh" +#include "json.hh" +#include "user_script.hh" + +#include +#include +#include +#include +#include + +namespace webview { +namespace detail { + +class engine_base { +public: + virtual ~engine_base() = default; + + noresult navigate(const std::string &url) { + if (url.empty()) { + return navigate_impl("about:blank"); + } + return navigate_impl(url); + } + + using binding_t = std::function; + class binding_ctx_t { + public: + binding_ctx_t(binding_t callback, void *arg) + : m_callback(callback), m_arg(arg) {} + void call(std::string id, std::string args) const { + if (m_callback) { + m_callback(id, args, m_arg); + } + } + + private: + // This function is called upon execution of the bound JS function + binding_t m_callback; + // This user-supplied argument is passed to the callback + void *m_arg; + }; + + using sync_binding_t = std::function; + + // Synchronous bind + noresult bind(const std::string &name, sync_binding_t fn) { + auto wrapper = [this, fn](const std::string &id, const std::string &req, + void * /*arg*/) { resolve(id, 0, fn(req)); }; + return bind(name, wrapper, nullptr); + } + + // Asynchronous bind + noresult bind(const std::string &name, binding_t fn, void *arg) { + // NOLINTNEXTLINE(readability-container-contains): contains() requires C++20 + if (bindings.count(name) > 0) { + return error_info{WEBVIEW_ERROR_DUPLICATE}; + } + bindings.emplace(name, binding_ctx_t(fn, arg)); + replace_bind_script(); + // Notify that a binding was created if the init script has already + // set things up. + eval("if (window.__webview__) {\n\ +window.__webview__.onBind(" + + json_escape(name) + ")\n\ +}"); + return {}; + } + + noresult unbind(const std::string &name) { + auto found = bindings.find(name); + if (found == bindings.end()) { + return error_info{WEBVIEW_ERROR_NOT_FOUND}; + } + bindings.erase(found); + replace_bind_script(); + // Notify that a binding was created if the init script has already + // set things up. + eval("if (window.__webview__) {\n\ +window.__webview__.onUnbind(" + + json_escape(name) + ")\n\ +}"); + return {}; + } + + noresult resolve(const std::string &id, int status, + const std::string &result) { + // NOLINTNEXTLINE(modernize-avoid-bind): Lambda with move requires C++14 + return dispatch(std::bind( + [id, status, this](std::string escaped_result) { + std::string js = "window.__webview__.onReply(" + json_escape(id) + + ", " + std::to_string(status) + ", " + + escaped_result + ")"; + eval(js); + }, + result.empty() ? "undefined" : json_escape(result))); + } + + result window() { return window_impl(); } + result widget() { return widget_impl(); } + result browser_controller() { return browser_controller_impl(); } + noresult run() { return run_impl(); } + noresult terminate() { return terminate_impl(); } + noresult dispatch(std::function f) { return dispatch_impl(f); } + noresult set_title(const std::string &title) { return set_title_impl(title); } + + noresult set_size(int width, int height, webview_hint_t hints) { + return set_size_impl(width, height, hints); + } + + noresult set_html(const std::string &html) { return set_html_impl(html); } + + noresult init(const std::string &js) { + add_user_script(js); + return {}; + } + + noresult eval(const std::string &js) { return eval_impl(js); } + +protected: + virtual noresult navigate_impl(const std::string &url) = 0; + virtual result window_impl() = 0; + virtual result widget_impl() = 0; + virtual result browser_controller_impl() = 0; + virtual noresult run_impl() = 0; + virtual noresult terminate_impl() = 0; + virtual noresult dispatch_impl(std::function f) = 0; + virtual noresult set_title_impl(const std::string &title) = 0; + virtual noresult set_size_impl(int width, int height, + webview_hint_t hints) = 0; + virtual noresult set_html_impl(const std::string &html) = 0; + virtual noresult eval_impl(const std::string &js) = 0; + + virtual user_script *add_user_script(const std::string &js) { + return std::addressof(*m_user_scripts.emplace(m_user_scripts.end(), + add_user_script_impl(js))); + } + + virtual user_script add_user_script_impl(const std::string &js) = 0; + + virtual void + remove_all_user_scripts_impl(const std::list &scripts) = 0; + + virtual bool are_user_scripts_equal_impl(const user_script &first, + const user_script &second) = 0; + + virtual user_script *replace_user_script(const user_script &old_script, + const std::string &new_script_code) { + remove_all_user_scripts_impl(m_user_scripts); + user_script *old_script_ptr{}; + for (auto &script : m_user_scripts) { + auto is_old_script = are_user_scripts_equal_impl(script, old_script); + script = add_user_script_impl(is_old_script ? new_script_code + : script.get_code()); + if (is_old_script) { + old_script_ptr = std::addressof(script); + } + } + return old_script_ptr; + } + + void replace_bind_script() { + if (m_bind_script) { + m_bind_script = replace_user_script(*m_bind_script, create_bind_script()); + } else { + m_bind_script = add_user_script(create_bind_script()); + } + } + + void add_init_script(const std::string &post_fn) { + add_user_script(create_init_script(post_fn)); + } + + std::string create_init_script(const std::string &post_fn) { + auto js = std::string{} + "(function() {\n\ + 'use strict';\n\ + function generateId() {\n\ + var crypto = window.crypto || window.msCrypto;\n\ + var bytes = new Uint8Array(16);\n\ + crypto.getRandomValues(bytes);\n\ + return Array.prototype.slice.call(bytes).map(function(n) {\n\ + var s = n.toString(16);\n\ + return ((s.length % 2) == 1 ? '0' : '') + s;\n\ + }).join('');\n\ + }\n\ + var Webview = (function() {\n\ + var _promises = {};\n\ + function Webview_() {}\n\ + Webview_.prototype.post = function(message) {\n\ + return (" + + post_fn + ")(message);\n\ + };\n\ + Webview_.prototype.call = function(method) {\n\ + var _id = generateId();\n\ + var _params = Array.prototype.slice.call(arguments, 1);\n\ + var promise = new Promise(function(resolve, reject) {\n\ + _promises[_id] = { resolve, reject };\n\ + });\n\ + this.post(JSON.stringify({\n\ + id: _id,\n\ + method: method,\n\ + params: _params\n\ + }));\n\ + return promise;\n\ + };\n\ + Webview_.prototype.onReply = function(id, status, result) {\n\ + var promise = _promises[id];\n\ + if (result !== undefined) {\n\ + try {\n\ + result = JSON.parse(result);\n\ + } catch (e) {\n\ + promise.reject(new Error(\"Failed to parse binding result as JSON\"));\n\ + return;\n\ + }\n\ + }\n\ + if (status === 0) {\n\ + promise.resolve(result);\n\ + } else {\n\ + promise.reject(result);\n\ + }\n\ + };\n\ + Webview_.prototype.onBind = function(name) {\n\ + if (window.hasOwnProperty(name)) {\n\ + throw new Error('Property \"' + name + '\" already exists');\n\ + }\n\ + window[name] = (function() {\n\ + var params = [name].concat(Array.prototype.slice.call(arguments));\n\ + return Webview_.prototype.call.apply(this, params);\n\ + }).bind(this);\n\ + };\n\ + Webview_.prototype.onUnbind = function(name) {\n\ + if (!window.hasOwnProperty(name)) {\n\ + throw new Error('Property \"' + name + '\" does not exist');\n\ + }\n\ + delete window[name];\n\ + };\n\ + return Webview_;\n\ + })();\n\ + window.__webview__ = new Webview();\n\ +})()"; + return js; + } + + std::string create_bind_script() { + std::string js_names = "["; + bool first = true; + for (const auto &binding : bindings) { + if (first) { + first = false; + } else { + js_names += ","; + } + js_names += json_escape(binding.first); + } + js_names += "]"; + + auto js = std::string{} + "(function() {\n\ + 'use strict';\n\ + var methods = " + + js_names + ";\n\ + methods.forEach(function(name) {\n\ + window.__webview__.onBind(name);\n\ + });\n\ +})()"; + return js; + } + + virtual void on_message(const std::string &msg) { + auto id = json_parse(msg, "id", 0); + auto name = json_parse(msg, "method", 0); + auto args = json_parse(msg, "params", 0); + auto found = bindings.find(name); + if (found == bindings.end()) { + return; + } + const auto &context = found->second; + dispatch([=] { context.call(id, args); }); + } + + virtual void on_window_created() { inc_window_count(); } + + virtual void on_window_destroyed(bool skip_termination = false) { + if (dec_window_count() <= 0) { + if (!skip_termination) { + terminate(); + } + } + } + +private: + static std::atomic_uint &window_ref_count() { + static std::atomic_uint ref_count{0}; + return ref_count; + } + + static unsigned int inc_window_count() { return ++window_ref_count(); } + + static unsigned int dec_window_count() { + auto &count = window_ref_count(); + if (count > 0) { + return --count; + } + return 0; + } + + std::map bindings; + user_script *m_bind_script{}; + std::list m_user_scripts; +}; + +} // namespace detail +} // namespace webview + +#endif diff --git a/BaseWebview/lib/webview/include/detail/exceptions.hh b/BaseWebview/lib/webview/include/detail/exceptions.hh new file mode 100644 index 0000000..5a40e21 --- /dev/null +++ b/BaseWebview/lib/webview/include/detail/exceptions.hh @@ -0,0 +1,39 @@ +/* + * MIT License + * + * Copyright (c) 2017 Serge Zaitsev + * Copyright (c) 2022 Steffen André Langnes + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef WEBVIEW_DETAIL_EXCEPTIONS_HH +#define WEBVIEW_DETAIL_EXCEPTIONS_HH + +#include + +namespace webview { +namespace detail { + +class bad_access : public std::exception {}; + +} // namespace detail +} // namespace webview + +#endif // WEBVIEW_DETAIL_EXCEPTIONS_HH diff --git a/BaseWebview/lib/webview/include/detail/json.hh b/BaseWebview/lib/webview/include/detail/json.hh new file mode 100644 index 0000000..147a3b2 --- /dev/null +++ b/BaseWebview/lib/webview/include/detail/json.hh @@ -0,0 +1,331 @@ +/* + * MIT License + * + * Copyright (c) 2017 Serge Zaitsev + * Copyright (c) 2022 Steffen André Langnes + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef WEBVIEW_DETAIL_JSON_HH +#define WEBVIEW_DETAIL_JSON_HH + +#include +#include +#include + +namespace webview { +namespace detail { + +inline int json_parse_c(const char *s, size_t sz, const char *key, size_t keysz, + const char **value, size_t *valuesz) { + enum { + JSON_STATE_VALUE, + JSON_STATE_LITERAL, + JSON_STATE_STRING, + JSON_STATE_ESCAPE, + JSON_STATE_UTF8 + } state = JSON_STATE_VALUE; + const char *k = nullptr; + int index = 1; + int depth = 0; + int utf8_bytes = 0; + + *value = nullptr; + *valuesz = 0; + + if (key == nullptr) { + index = static_cast(keysz); + if (index < 0) { + return -1; + } + keysz = 0; + } + + for (; sz > 0; s++, sz--) { + enum { + JSON_ACTION_NONE, + JSON_ACTION_START, + JSON_ACTION_END, + JSON_ACTION_START_STRUCT, + JSON_ACTION_END_STRUCT + } action = JSON_ACTION_NONE; + auto c = static_cast(*s); + switch (state) { + case JSON_STATE_VALUE: + if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == ',' || + c == ':') { + continue; + } else if (c == '"') { + action = JSON_ACTION_START; + state = JSON_STATE_STRING; + } else if (c == '{' || c == '[') { + action = JSON_ACTION_START_STRUCT; + } else if (c == '}' || c == ']') { + action = JSON_ACTION_END_STRUCT; + } else if (c == 't' || c == 'f' || c == 'n' || c == '-' || + (c >= '0' && c <= '9')) { + action = JSON_ACTION_START; + state = JSON_STATE_LITERAL; + } else { + return -1; + } + break; + case JSON_STATE_LITERAL: + if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == ',' || + c == ']' || c == '}' || c == ':') { + state = JSON_STATE_VALUE; + s--; + sz++; + action = JSON_ACTION_END; + } else if (c < 32 || c > 126) { + return -1; + } // fallthrough + case JSON_STATE_STRING: + if (c < 32 || (c > 126 && c < 192)) { + return -1; + } else if (c == '"') { + action = JSON_ACTION_END; + state = JSON_STATE_VALUE; + } else if (c == '\\') { + state = JSON_STATE_ESCAPE; + } else if (c >= 192 && c < 224) { + utf8_bytes = 1; + state = JSON_STATE_UTF8; + } else if (c >= 224 && c < 240) { + utf8_bytes = 2; + state = JSON_STATE_UTF8; + } else if (c >= 240 && c < 247) { + utf8_bytes = 3; + state = JSON_STATE_UTF8; + } else if (c >= 128 && c < 192) { + return -1; + } + break; + case JSON_STATE_ESCAPE: + if (c == '"' || c == '\\' || c == '/' || c == 'b' || c == 'f' || + c == 'n' || c == 'r' || c == 't' || c == 'u') { + state = JSON_STATE_STRING; + } else { + return -1; + } + break; + case JSON_STATE_UTF8: + if (c < 128 || c > 191) { + return -1; + } + utf8_bytes--; + if (utf8_bytes == 0) { + state = JSON_STATE_STRING; + } + break; + default: + return -1; + } + + if (action == JSON_ACTION_END_STRUCT) { + depth--; + } + + if (depth == 1) { + if (action == JSON_ACTION_START || action == JSON_ACTION_START_STRUCT) { + if (index == 0) { + *value = s; + } else if (keysz > 0 && index == 1) { + k = s; + } else { + index--; + } + } else if (action == JSON_ACTION_END || + action == JSON_ACTION_END_STRUCT) { + if (*value != nullptr && index == 0) { + *valuesz = static_cast(s + 1 - *value); + return 0; + } else if (keysz > 0 && k != nullptr) { + if (keysz == static_cast(s - k - 1) && + memcmp(key, k + 1, keysz) == 0) { + index = 0; + } else { + index = 2; + } + k = nullptr; + } + } + } + + if (action == JSON_ACTION_START_STRUCT) { + depth++; + } + } + return -1; +} + +constexpr bool is_json_special_char(char c) { + return c == '"' || c == '\\' || c == '\b' || c == '\f' || c == '\n' || + c == '\r' || c == '\t'; +} + +constexpr bool is_ascii_control_char(char c) { return c >= 0 && c <= 0x1f; } + +inline std::string json_escape(const std::string &s, bool add_quotes = true) { + // Calculate the size of the resulting string. + // Add space for the double quotes. + size_t required_length = add_quotes ? 2 : 0; + for (auto c : s) { + if (is_json_special_char(c)) { + // '\' and a single following character + required_length += 2; + continue; + } + if (is_ascii_control_char(c)) { + // '\', 'u', 4 digits + required_length += 6; + continue; + } + ++required_length; + } + // Allocate memory for resulting string only once. + std::string result; + result.reserve(required_length); + if (add_quotes) { + result += '"'; + } + // Copy string while escaping characters. + for (auto c : s) { + if (is_json_special_char(c)) { + static constexpr char special_escape_table[256] = + "\0\0\0\0\0\0\0\0btn\0fr\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\"\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\\"; + result += '\\'; + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-constant-array-index) + result += special_escape_table[static_cast(c)]; + continue; + } + if (is_ascii_control_char(c)) { + // Escape as \u00xx + static constexpr char hex_alphabet[]{"0123456789abcdef"}; + auto uc = static_cast(c); + auto h = (uc >> 4) & 0x0f; + auto l = uc & 0x0f; + result += "\\u00"; + // NOLINTBEGIN(cppcoreguidelines-pro-bounds-constant-array-index) + result += hex_alphabet[h]; + result += hex_alphabet[l]; + // NOLINTEND(cppcoreguidelines-pro-bounds-constant-array-index) + continue; + } + result += c; + } + if (add_quotes) { + result += '"'; + } + // Should have calculated the exact amount of memory needed + assert(required_length == result.size()); + return result; +} + +inline int json_unescape(const char *s, size_t n, char *out) { + int r = 0; + if (*s++ != '"') { + return -1; + } + while (n > 2) { + char c = *s; + if (c == '\\') { + s++; + n--; + switch (*s) { + case 'b': + c = '\b'; + break; + case 'f': + c = '\f'; + break; + case 'n': + c = '\n'; + break; + case 'r': + c = '\r'; + break; + case 't': + c = '\t'; + break; + case '\\': + c = '\\'; + break; + case '/': + c = '/'; + break; + case '\"': + c = '\"'; + break; + default: // TODO: support unicode decoding + return -1; + } + } + if (out != nullptr) { + *out++ = c; + } + s++; + n--; + r++; + } + if (*s != '"') { + return -1; + } + if (out != nullptr) { + *out = '\0'; + } + return r; +} + +inline std::string json_parse(const std::string &s, const std::string &key, + const int index) { + const char *value; + size_t value_sz; + if (key.empty()) { + json_parse_c(s.c_str(), s.length(), nullptr, index, &value, &value_sz); + } else { + json_parse_c(s.c_str(), s.length(), key.c_str(), key.length(), &value, + &value_sz); + } + if (value != nullptr) { + if (value[0] != '"') { + return {value, value_sz}; + } + int n = json_unescape(value, value_sz, nullptr); + if (n > 0) { + char *decoded = new char[n + 1]; + json_unescape(value, value_sz, decoded); + std::string result(decoded, n); + delete[] decoded; + return result; + } + } + return ""; +} + +} // namespace detail +} // namespace webview + +#endif // WEBVIEW_DETAIL_JSON_HH diff --git a/BaseWebview/lib/webview/include/detail/native_library.hh b/BaseWebview/lib/webview/include/detail/native_library.hh new file mode 100644 index 0000000..db4f44c --- /dev/null +++ b/BaseWebview/lib/webview/include/detail/native_library.hh @@ -0,0 +1,167 @@ +/* + * MIT License + * + * Copyright (c) 2017 Serge Zaitsev + * Copyright (c) 2022 Steffen André Langnes + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef WEBVIEW_DETAIL_NATIVE_LIBRARY_HH +#define WEBVIEW_DETAIL_NATIVE_LIBRARY_HH + +#include "utility/string.hh" + +#include + +#if defined(_WIN32) +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#else +#include +#endif + +namespace webview { +namespace detail { + +// Holds a symbol name and associated type for code clarity. +template class library_symbol { +public: + using type = T; + + constexpr explicit library_symbol(const char *name) : m_name(name) {} + constexpr const char *get_name() const { return m_name; } + +private: + const char *m_name; +}; + +// Loads a native shared library and allows one to get addresses for those +// symbols. +class native_library { +public: + native_library() = default; + + explicit native_library(const std::string &name) + : m_handle{load_library(name)} {} + +#ifdef _WIN32 + explicit native_library(const std::wstring &name) + : m_handle{load_library(name)} {} +#endif + + ~native_library() { + if (m_handle) { +#ifdef _WIN32 + FreeLibrary(m_handle); +#else + dlclose(m_handle); +#endif + m_handle = nullptr; + } + } + + native_library(const native_library &other) = delete; + native_library &operator=(const native_library &other) = delete; + native_library(native_library &&other) noexcept { *this = std::move(other); } + + native_library &operator=(native_library &&other) noexcept { + if (this == &other) { + return *this; + } + m_handle = other.m_handle; + other.m_handle = nullptr; + return *this; + } + + // Returns true if the library is currently loaded; otherwise false. + operator bool() const { return is_loaded(); } + + // Get the address for the specified symbol or nullptr if not found. + template + typename Symbol::type get(const Symbol &symbol) const { + if (is_loaded()) { + // NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast) +#ifdef _WIN32 +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-function-type" +#endif + return reinterpret_cast( + GetProcAddress(m_handle, symbol.get_name())); +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif +#else + return reinterpret_cast( + dlsym(m_handle, symbol.get_name())); +#endif + // NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast) + } + return nullptr; + } + + // Returns true if the library is currently loaded; otherwise false. + bool is_loaded() const { return !!m_handle; } + + void detach() { m_handle = nullptr; } + + // Returns true if the library by the given name is currently loaded; otherwise false. + static inline bool is_loaded(const std::string &name) { +#ifdef _WIN32 + auto handle = GetModuleHandleW(widen_string(name).c_str()); +#else + auto handle = dlopen(name.c_str(), RTLD_NOW | RTLD_NOLOAD); + if (handle) { + dlclose(handle); + } +#endif + return !!handle; + } + +private: +#ifdef _WIN32 + using mod_handle_t = HMODULE; +#else + using mod_handle_t = void *; +#endif + + static inline mod_handle_t load_library(const std::string &name) { +#ifdef _WIN32 + return load_library(widen_string(name)); +#else + return dlopen(name.c_str(), RTLD_NOW); +#endif + } + +#ifdef _WIN32 + static inline mod_handle_t load_library(const std::wstring &name) { + return LoadLibraryW(name.c_str()); + } +#endif + + mod_handle_t m_handle{}; +}; + +} // namespace detail +} // namespace webview + +#endif // WEBVIEW_DETAIL_NATIVE_LIBRARY_HH diff --git a/BaseWebview/lib/webview/include/detail/optional.hh b/BaseWebview/lib/webview/include/detail/optional.hh new file mode 100644 index 0000000..d4eba1b --- /dev/null +++ b/BaseWebview/lib/webview/include/detail/optional.hh @@ -0,0 +1,115 @@ +/* + * MIT License + * + * Copyright (c) 2017 Serge Zaitsev + * Copyright (c) 2022 Steffen André Langnes + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef WEBVIEW_DETAIL_OPTIONAL_HH +#define WEBVIEW_DETAIL_OPTIONAL_HH + +#include "exceptions.hh" + +#include +#include +#include + +namespace webview { +namespace detail { + +template class optional { +public: + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init) + optional() = default; + + optional(const T &other) noexcept : m_has_data{true} { + new (&m_data) T{other}; + } + + optional(T &&other) noexcept : m_has_data{true} { + new (&m_data) T{std::move(other)}; + } + + optional(const optional &other) noexcept { *this = other; } + + optional &operator=(const optional &other) noexcept { + if (this == &other) { + return *this; + } + m_has_data = other.has_value(); + if (m_has_data) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + new (&m_data) T{*reinterpret_cast(&other.m_data)}; + } + return *this; + } + + optional(optional &&other) noexcept { *this = std::move(other); } + + optional &operator=(optional &&other) noexcept { + if (this == &other) { + return *this; + } + m_has_data = other.has_value(); + if (m_has_data) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + new (&m_data) T{std::move(*reinterpret_cast(&other.m_data))}; + } + return *this; + } + + ~optional() { + if (m_has_data) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + reinterpret_cast(&m_data)->~T(); + } + } + + const T &get() const { + if (!m_has_data) { + throw bad_access{}; + } + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + return *reinterpret_cast(&m_data); + } + + T &get() { + if (!m_has_data) { + throw bad_access{}; + } + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + return *reinterpret_cast(&m_data); + } + + bool has_value() const { return m_has_data; } + +private: + // NOLINTNEXTLINE(bugprone-sizeof-expression): pointer to aggregate is OK + typename std::aligned_storage::type m_data; + bool m_has_data{}; +}; + +template <> class optional {}; + +} // namespace detail +} // namespace webview + +#endif // WEBVIEW_DETAIL_OPTIONAL_HH diff --git a/BaseWebview/lib/webview/include/detail/platform/darwin/cocoa.hh b/BaseWebview/lib/webview/include/detail/platform/darwin/cocoa.hh new file mode 100644 index 0000000..74e5c2b --- /dev/null +++ b/BaseWebview/lib/webview/include/detail/platform/darwin/cocoa.hh @@ -0,0 +1,62 @@ +/* + * MIT License + * + * Copyright (c) 2017 Serge Zaitsev + * Copyright (c) 2022 Steffen André Langnes + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined(WEBVIEW_PLATFORM_DARWIN_COCOA_HH) && \ + defined(WEBVIEW_PLATFORM_DARWIN) +#define WEBVIEW_PLATFORM_DARWIN_COCOA_HH + +#include + +namespace webview { +namespace detail { + +enum NSBackingStoreType : NSUInteger { NSBackingStoreBuffered = 2 }; + +enum NSWindowStyleMask : NSUInteger { + NSWindowStyleMaskTitled = 1, + NSWindowStyleMaskClosable = 2, + NSWindowStyleMaskMiniaturizable = 4, + NSWindowStyleMaskResizable = 8 +}; + +enum NSApplicationActivationPolicy : NSInteger { + NSApplicationActivationPolicyRegular = 0 +}; + +enum NSModalResponse : NSInteger { NSModalResponseOK = 1 }; + +enum NSAutoresizingMaskOptions : NSUInteger { + NSViewMinXMargin = 1, + NSViewWidthSizable = 2, + NSViewMaxXMargin = 4, + NSViewMinYMargin = 8, + NSViewHeightSizable = 16, + NSViewMaxYMargin = 32 +}; + +} // namespace detail +} // namespace webview + +#endif // WEBVIEW_PLATFORM_DARWIN_COCOA_HH diff --git a/BaseWebview/lib/webview/include/detail/platform/darwin/objc.hh b/BaseWebview/lib/webview/include/detail/platform/darwin/objc.hh new file mode 100644 index 0000000..b01fbd8 --- /dev/null +++ b/BaseWebview/lib/webview/include/detail/platform/darwin/objc.hh @@ -0,0 +1,112 @@ +/* + * MIT License + * + * Copyright (c) 2017 Serge Zaitsev + * Copyright (c) 2022 Steffen André Langnes + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined(WEBVIEW_PLATFORM_DARWIN_OBJC_HH) && \ + defined(WEBVIEW_PLATFORM_DARWIN) +#define WEBVIEW_PLATFORM_DARWIN_OBJC_HH + +#include + +#include +#include + +namespace webview { +namespace detail { +namespace objc { + +// A convenient template function for unconditionally casting the specified +// C-like function into a function that can be called with the given return +// type and arguments. Caller takes full responsibility for ensuring that +// the function call is valid. It is assumed that the function will not +// throw exceptions. +template +Result invoke(Callable callable, Args... args) noexcept { + return reinterpret_cast(callable)(args...); +} + +// Calls objc_msgSend. +template +Result msg_send(Args... args) noexcept { + return invoke(objc_msgSend, args...); +} + +// Calls objc_msgSend_stret or objc_msgSend depending on architecture. +template +Result msg_send_stret(Args... args) noexcept { +#if defined(__arm64__) + return invoke(objc_msgSend, args...); +#else + return invoke(objc_msgSend_stret, args...); +#endif +} + +// Wrapper around NSAutoreleasePool that drains the pool on destruction. +class autoreleasepool { +public: + autoreleasepool() + : m_pool(msg_send(objc_getClass("NSAutoreleasePool"), + sel_registerName("new"))) {} + + ~autoreleasepool() { + if (m_pool) { + msg_send(m_pool, sel_registerName("drain")); + } + } + + autoreleasepool(const autoreleasepool &) = delete; + autoreleasepool &operator=(const autoreleasepool &) = delete; + autoreleasepool(autoreleasepool &&) = delete; + autoreleasepool &operator=(autoreleasepool &&) = delete; + +private: + id m_pool{}; +}; + +inline id autoreleased(id object) { + msg_send(object, sel_registerName("autorelease")); + return object; +} + +namespace literals { + +// Convenient conversion of string literals. +inline id operator"" _cls(const char *s, std::size_t) { + return (id)objc_getClass(s); +} + +inline SEL operator"" _sel(const char *s, std::size_t) { + return sel_registerName(s); +} + +inline id operator"" _str(const char *s, std::size_t) { + return msg_send("NSString"_cls, "stringWithUTF8String:"_sel, s); +} + +} // namespace literals +} // namespace objc +} // namespace detail +} // namespace webview + +#endif // WEBVIEW_PLATFORM_DARWIN_OBJC_HH diff --git a/BaseWebview/lib/webview/include/detail/platform/darwin/webkit.hh b/BaseWebview/lib/webview/include/detail/platform/darwin/webkit.hh new file mode 100644 index 0000000..e1f7105 --- /dev/null +++ b/BaseWebview/lib/webview/include/detail/platform/darwin/webkit.hh @@ -0,0 +1,42 @@ +/* + * MIT License + * + * Copyright (c) 2017 Serge Zaitsev + * Copyright (c) 2022 Steffen André Langnes + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined(WEBVIEW_PLATFORM_DARWIN_WEBKIT_HH) && \ + defined(WEBVIEW_PLATFORM_DARWIN) && defined(WEBVIEW_COCOA) +#define WEBVIEW_PLATFORM_DARWIN_WEBKIT_HH + +#include + +namespace webview { +namespace detail { + +enum WKUserScriptInjectionTime : NSInteger { + WKUserScriptInjectionTimeAtDocumentStart = 0 +}; + +} // namespace detail +} // namespace webview + +#endif // WEBVIEW_PLATFORM_DARWIN_WEBKIT_HH diff --git a/BaseWebview/lib/webview/include/detail/platform/linux/gtk/compat.hh b/BaseWebview/lib/webview/include/detail/platform/linux/gtk/compat.hh new file mode 100644 index 0000000..83eef56 --- /dev/null +++ b/BaseWebview/lib/webview/include/detail/platform/linux/gtk/compat.hh @@ -0,0 +1,128 @@ +/* + * MIT License + * + * Copyright (c) 2017 Serge Zaitsev + * Copyright (c) 2022 Steffen André Langnes + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined(WEBVIEW_PLATFORM_LINUX_GTK_COMPAT_HH) && \ + defined(WEBVIEW_PLATFORM_LINUX) +#define WEBVIEW_PLATFORM_LINUX_GTK_COMPAT_HH + +#include + +#if GTK_MAJOR_VERSION >= 4 + +#ifdef GDK_WINDOWING_X11 +#include +#endif + +#elif GTK_MAJOR_VERSION >= 3 + +#ifdef GDK_WINDOWING_X11 +#include +#endif + +#endif + +namespace webview { +namespace detail { + +/** + * GTK compatibility helper class. + */ +class gtk_compat { +public: + static gboolean init_check() { +#if GTK_MAJOR_VERSION >= 4 + return gtk_init_check(); +#else + return gtk_init_check(nullptr, nullptr); +#endif + } + + static GtkWidget *window_new() { +#if GTK_MAJOR_VERSION >= 4 + return gtk_window_new(); +#else + return gtk_window_new(GTK_WINDOW_TOPLEVEL); +#endif + } + + static void window_set_child(GtkWindow *window, GtkWidget *widget) { +#if GTK_MAJOR_VERSION >= 4 + gtk_window_set_child(window, widget); +#else + gtk_container_add(GTK_CONTAINER(window), widget); +#endif + } + + static void window_remove_child(GtkWindow *window, GtkWidget *widget) { +#if GTK_MAJOR_VERSION >= 4 + if (gtk_window_get_child(window) == widget) { + gtk_window_set_child(window, nullptr); + } +#else + gtk_container_remove(GTK_CONTAINER(window), widget); +#endif + } + + static void widget_set_visible(GtkWidget *widget, bool visible) { +#if GTK_MAJOR_VERSION >= 4 + gtk_widget_set_visible(widget, visible ? TRUE : FALSE); +#else + if (visible) { + gtk_widget_show(widget); + } else { + gtk_widget_hide(widget); + } +#endif + } + + static void window_set_size(GtkWindow *window, int width, int height) { +#if GTK_MAJOR_VERSION >= 4 + gtk_window_set_default_size(window, width, height); +#else + gtk_window_resize(window, width, height); +#endif + } + + static void window_set_max_size(GtkWindow *window, int width, int height) { +// X11-specific features are available in GTK 3 but not GTK 4 +#if GTK_MAJOR_VERSION < 4 + GdkGeometry g{}; + g.max_width = width; + g.max_height = height; + GdkWindowHints h = GDK_HINT_MAX_SIZE; + gtk_window_set_geometry_hints(GTK_WINDOW(window), nullptr, &g, h); +#else + // Avoid "unused parameter" warnings + (void)window; + (void)width; + (void)height; +#endif + } +}; + +} // namespace detail +} // namespace webview + +#endif // WEBVIEW_PLATFORM_LINUX_GTK_COMPAT_HH diff --git a/BaseWebview/lib/webview/include/detail/platform/linux/webkitgtk/compat.hh b/BaseWebview/lib/webview/include/detail/platform/linux/webkitgtk/compat.hh new file mode 100644 index 0000000..5b5fbd0 --- /dev/null +++ b/BaseWebview/lib/webview/include/detail/platform/linux/webkitgtk/compat.hh @@ -0,0 +1,135 @@ +/* + * MIT License + * + * Copyright (c) 2017 Serge Zaitsev + * Copyright (c) 2022 Steffen André Langnes + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined(WEBVIEW_PLATFORM_LINUX_WEBKITGTK_COMPAT_HH) && \ + defined(WEBVIEW_PLATFORM_LINUX) && defined(WEBVIEW_GTK) +#define WEBVIEW_PLATFORM_LINUX_WEBKITGTK_COMPAT_HH + +#include +#include + +#include + +#if GTK_MAJOR_VERSION >= 4 + +#include +#include + +#ifdef GDK_WINDOWING_X11 +#include +#endif + +#elif GTK_MAJOR_VERSION >= 3 + +#include +#include + +#ifdef GDK_WINDOWING_X11 +#include +#endif + +#endif + +namespace webview { +namespace detail { + +/** + * WebKitGTK compatibility helper class. + */ +class webkitgtk_compat { +public: +#if GTK_MAJOR_VERSION >= 4 + using wk_handler_js_value_t = JSCValue; +#else + using wk_handler_js_value_t = WebKitJavascriptResult; +#endif + + using on_script_message_received_t = + std::function; + static void + connect_script_message_received(WebKitUserContentManager *manager, + const std::string &handler_name, + on_script_message_received_t handler) { + std::string signal_name = "script-message-received::"; + signal_name += handler_name; + + auto callback = +[](WebKitUserContentManager *manager, + wk_handler_js_value_t *r, gpointer arg) { + auto *handler = static_cast(arg); + (*handler)(manager, get_string_from_js_result(r)); + }; + + auto deleter = +[](gpointer data, GClosure *) { + delete static_cast(data); + }; + + g_signal_connect_data(manager, signal_name.c_str(), G_CALLBACK(callback), + new on_script_message_received_t{handler}, deleter, + static_cast(0) /*G_CONNECT_DEFAULT*/); + } + + static std::string get_string_from_js_result(JSCValue *r) { + char *cs = jsc_value_to_string(r); + std::string s{cs}; + g_free(cs); + return s; + } + +#if GTK_MAJOR_VERSION < 4 + static std::string get_string_from_js_result(WebKitJavascriptResult *r) { +#if (WEBKIT_MAJOR_VERSION == 2 && WEBKIT_MINOR_VERSION >= 22) || \ + WEBKIT_MAJOR_VERSION > 2 + JSCValue *value = webkit_javascript_result_get_js_value(r); + return get_string_from_js_result(value); +#else + JSGlobalContextRef ctx = webkit_javascript_result_get_global_context(r); + JSValueRef value = webkit_javascript_result_get_value(r); + JSStringRef js = JSValueToStringCopy(ctx, value, nullptr); + size_t n = JSStringGetMaximumUTF8CStringSize(js); + char *cs = g_new(char, n); + JSStringGetUTF8CString(js, cs, n); + JSStringRelease(js); + std::string s{cs}; + g_free(cs); + return s; +#endif + } +#endif + + static void user_content_manager_register_script_message_handler( + WebKitUserContentManager *manager, const gchar *name) { +#if GTK_MAJOR_VERSION >= 4 + webkit_user_content_manager_register_script_message_handler(manager, name, + nullptr); +#else + webkit_user_content_manager_register_script_message_handler(manager, name); +#endif + } +}; + +} // namespace detail +} // namespace webview + +#endif // WEBVIEW_PLATFORM_LINUX_WEBKITGTK_COMPAT_HH diff --git a/BaseWebview/lib/webview/include/detail/platform/linux/webkitgtk/dmabuf.hh b/BaseWebview/lib/webview/include/detail/platform/linux/webkitgtk/dmabuf.hh new file mode 100644 index 0000000..559080e --- /dev/null +++ b/BaseWebview/lib/webview/include/detail/platform/linux/webkitgtk/dmabuf.hh @@ -0,0 +1,160 @@ +/* + * MIT License + * + * Copyright (c) 2017 Serge Zaitsev + * Copyright (c) 2022 Steffen André Langnes + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined(WEBVIEW_BACKENDS_GTK_WEBKITGTK_DMABUF_HH) && \ + defined(WEBVIEW_PLATFORM_LINUX) && defined(WEBVIEW_GTK) +#define WEBVIEW_BACKENDS_GTK_WEBKITGTK_DMABUF_HH + +#include +#include + +#include + +#if GTK_MAJOR_VERSION >= 4 + +#include +#include + +#ifdef GDK_WINDOWING_X11 +#include +#endif + +#elif GTK_MAJOR_VERSION >= 3 + +#include +#include + +#ifdef GDK_WINDOWING_X11 +#include +#endif + +#endif + +#include +#include + +namespace webview { +namespace detail { + +// Namespace containing workaround for WebKit 2.42 when using NVIDIA GPU +// driver. +// See WebKit bug: https://bugs.webkit.org/show_bug.cgi?id=261874 +// Please remove all of the code in this namespace when it's no longer needed. +namespace webkit_dmabuf { + +// Get environment variable. Not thread-safe. +static inline std::string get_env(const std::string &name) { + auto *value = std::getenv(name.c_str()); + if (value) { + return {value}; + } + return {}; +} + +// Set environment variable. Not thread-safe. +static inline void set_env(const std::string &name, const std::string &value) { + ::setenv(name.c_str(), value.c_str(), 1); +} + +// Checks whether the NVIDIA GPU driver is used based on whether the kernel +// module is loaded. +static inline bool is_using_nvidia_driver() { + struct ::stat buffer {}; + if (::stat("/sys/module/nvidia", &buffer) != 0) { + return false; + } + return S_ISDIR(buffer.st_mode); +} + +// Checks whether the windowing system is Wayland. +static inline bool is_wayland_display() { + if (!get_env("WAYLAND_DISPLAY").empty()) { + return true; + } + if (get_env("XDG_SESSION_TYPE") == "wayland") { + return true; + } + if (get_env("DESKTOP_SESSION").find("wayland") != std::string::npos) { + return true; + } + return false; +} + +// Checks whether the GDK X11 backend is used. +// See: https://docs.gtk.org/gdk3/class.DisplayManager.html +static inline bool is_gdk_x11_backend() { +#ifdef GDK_WINDOWING_X11 + auto *gdk_display = gdk_display_get_default(); + return GDK_IS_X11_DISPLAY(gdk_display); // NOLINT(misc-const-correctness) +#else + return false; +#endif +} + +// Checks whether WebKit is affected by bug when using DMA-BUF renderer. +// Returns true if all of the following conditions are met: +// - WebKit version is >= 2.42 (please narrow this down when there's a fix). +// - Environment variables are empty or not set: +// - WEBKIT_DISABLE_DMABUF_RENDERER +// - Windowing system is not Wayland. +// - GDK backend is X11. +// - NVIDIA GPU driver is used. +static inline bool is_webkit_dmabuf_bugged() { + auto wk_major = webkit_get_major_version(); + auto wk_minor = webkit_get_minor_version(); + // TODO: Narrow down affected WebKit version when there's a fixed version + auto is_affected_wk_version = wk_major == 2 && wk_minor >= 42; + if (!is_affected_wk_version) { + return false; + } + if (!get_env("WEBKIT_DISABLE_DMABUF_RENDERER").empty()) { + return false; + } + if (is_wayland_display()) { + return false; + } + if (!is_gdk_x11_backend()) { + return false; + } + if (!is_using_nvidia_driver()) { + return false; + } + return true; +} + +// Applies workaround for WebKit DMA-BUF bug if needed. +// See WebKit bug: https://bugs.webkit.org/show_bug.cgi?id=261874 +static inline void apply_webkit_dmabuf_workaround() { + if (!is_webkit_dmabuf_bugged()) { + return; + } + set_env("WEBKIT_DISABLE_DMABUF_RENDERER", "1"); +} + +} // namespace webkit_dmabuf +} // namespace detail +} // namespace webview + +#endif // WEBVIEW_BACKENDS_GTK_WEBKITGTK_DMABUF_HH diff --git a/BaseWebview/lib/webview/include/detail/platform/windows/com_init_wrapper.hh b/BaseWebview/lib/webview/include/detail/platform/windows/com_init_wrapper.hh new file mode 100644 index 0000000..86fcea9 --- /dev/null +++ b/BaseWebview/lib/webview/include/detail/platform/windows/com_init_wrapper.hh @@ -0,0 +1,115 @@ +/* + * MIT License + * + * Copyright (c) 2017 Serge Zaitsev + * Copyright (c) 2022 Steffen André Langnes + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined(WEBVIEW_PLATFORM_WINDOWS_COM_INIT_WRAPPER_HH) && \ + defined(WEBVIEW_PLATFORM_WINDOWS) +#define WEBVIEW_PLATFORM_WINDOWS_COM_INIT_WRAPPER_HH + +// +// ==================================================================== +// +// This implementation uses Win32 API to create a native window. It +// uses Edge/Chromium webview2 backend as a browser engine. +// +// ==================================================================== +// + +#include "../../../errors.hh" + +#include + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif + +#include + +#include + +#ifdef _MSC_VER +#pragma comment(lib, "ole32.lib") +#endif + +namespace webview { +namespace detail { + +/** + * A wrapper around COM library initialization. Calls CoInitializeEx in the + * constructor and CoUninitialize in the destructor. + * + * @exception exception Thrown if CoInitializeEx has already been called with a + * different concurrency model. + */ +class com_init_wrapper { +public: + com_init_wrapper() = default; + + com_init_wrapper(DWORD dwCoInit) { + // We can safely continue as long as COM was either successfully + // initialized or already initialized. + // RPC_E_CHANGED_MODE means that CoInitializeEx was already called with + // a different concurrency model. + switch (CoInitializeEx(nullptr, dwCoInit)) { + case S_OK: + case S_FALSE: + m_initialized = true; + break; + case RPC_E_CHANGED_MODE: + throw exception{ + WEBVIEW_ERROR_INVALID_STATE, + "CoInitializeEx already called with a different concurrency model"}; + default: + throw exception{WEBVIEW_ERROR_UNSPECIFIED, + "Unexpected result from CoInitializeEx"}; + } + } + + ~com_init_wrapper() { + if (m_initialized) { + CoUninitialize(); + m_initialized = false; + } + } + + com_init_wrapper(const com_init_wrapper &other) = delete; + com_init_wrapper &operator=(const com_init_wrapper &other) = delete; + com_init_wrapper(com_init_wrapper &&other) { *this = std::move(other); } + + com_init_wrapper &operator=(com_init_wrapper &&other) { + if (this == &other) { + return *this; + } + m_initialized = std::exchange(other.m_initialized, false); + return *this; + } + +private: + bool m_initialized = false; +}; + +} // namespace detail +} // namespace webview + +#endif // WEBVIEW_PLATFORM_WINDOWS_COM_INIT_WRAPPER_HH diff --git a/BaseWebview/lib/webview/include/detail/platform/windows/dpi.hh b/BaseWebview/lib/webview/include/detail/platform/windows/dpi.hh new file mode 100644 index 0000000..e9882ec --- /dev/null +++ b/BaseWebview/lib/webview/include/detail/platform/windows/dpi.hh @@ -0,0 +1,156 @@ +/* + * MIT License + * + * Copyright (c) 2017 Serge Zaitsev + * Copyright (c) 2022 Steffen André Langnes + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined(WEBVIEW_PLATFORM_WINDOWS_DPI_HH) && \ + defined(WEBVIEW_PLATFORM_WINDOWS) +#define WEBVIEW_PLATFORM_WINDOWS_DPI_HH + +// +// ==================================================================== +// +// This implementation uses Win32 API to create a native window. It +// uses Edge/Chromium webview2 backend as a browser engine. +// +// ==================================================================== +// + +#include "../../native_library.hh" +#include "shcore.hh" +#include "user32.hh" +#include "version.hh" + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif + +#include + +#ifdef _MSC_VER +#pragma comment(lib, "user32.lib") +#endif + +namespace webview { +namespace detail { + +inline bool is_per_monitor_v2_awareness_available() { + // Windows 10, version 1703 + return compare_os_version(10, 0, 15063) >= 0; +} + +inline bool enable_dpi_awareness() { + auto user32 = native_library(L"user32.dll"); + if (auto fn = user32.get(user32_symbols::SetProcessDpiAwarenessContext)) { + auto dpi_awareness = + reinterpret_cast( + is_per_monitor_v2_awareness_available() + ? user32_symbols::dpi_awareness::per_monitor_v2_aware + : user32_symbols::dpi_awareness::per_monitor_aware); + if (fn(dpi_awareness)) { + return true; + } + return GetLastError() == ERROR_ACCESS_DENIED; + } + if (auto shcore = native_library(L"shcore.dll")) { + if (auto fn = shcore.get(shcore_symbols::SetProcessDpiAwareness)) { + auto result = fn(shcore_symbols::PROCESS_PER_MONITOR_DPI_AWARE); + return result == S_OK || result == E_ACCESSDENIED; + } + } + if (auto fn = user32.get(user32_symbols::SetProcessDPIAware)) { + return !!fn(); + } + return true; +} + +inline bool enable_non_client_dpi_scaling_if_needed(HWND window) { + auto user32 = native_library(L"user32.dll"); + auto get_ctx_fn = user32.get(user32_symbols::GetWindowDpiAwarenessContext); + if (!get_ctx_fn) { + return true; + } + auto awareness = get_ctx_fn(window); + if (!awareness) { + return false; + } + auto ctx_equal_fn = user32.get(user32_symbols::AreDpiAwarenessContextsEqual); + if (!ctx_equal_fn) { + return true; + } + // EnableNonClientDpiScaling is only needed with per monitor v1 awareness. + auto per_monitor = reinterpret_cast( + user32_symbols::dpi_awareness::per_monitor_aware); + if (!ctx_equal_fn(awareness, per_monitor)) { + return true; + } + auto enable_fn = user32.get(user32_symbols::EnableNonClientDpiScaling); + if (!enable_fn) { + return true; + } + return !!enable_fn(window); +} + +constexpr int get_default_window_dpi() { + constexpr const int default_dpi = 96; // USER_DEFAULT_SCREEN_DPI + return default_dpi; +} + +inline int get_window_dpi(HWND window) { + auto user32 = native_library(L"user32.dll"); + if (auto fn = user32.get(user32_symbols::GetDpiForWindow)) { + auto dpi = static_cast(fn(window)); + return dpi; + } + return get_default_window_dpi(); +} + +constexpr int scale_value_for_dpi(int value, int from_dpi, int to_dpi) { + return (value * to_dpi) / from_dpi; +} + +constexpr SIZE scale_size(int width, int height, int from_dpi, int to_dpi) { + auto scaled_width = scale_value_for_dpi(width, from_dpi, to_dpi); + auto scaled_height = scale_value_for_dpi(height, from_dpi, to_dpi); + return {scaled_width, scaled_height}; +} + +inline SIZE make_window_frame_size(HWND window, int width, int height, + int dpi) { + auto style = GetWindowLong(window, GWL_STYLE); + RECT r{0, 0, width, height}; + auto user32 = native_library(L"user32.dll"); + if (auto fn = user32.get(user32_symbols::AdjustWindowRectExForDpi)) { + fn(&r, style, FALSE, 0, static_cast(dpi)); + } else { + AdjustWindowRect(&r, style, 0); + } + auto frame_width = r.right - r.left; + auto frame_height = r.bottom - r.top; + return {frame_width, frame_height}; +} + +} // namespace detail +} // namespace webview + +#endif // WEBVIEW_PLATFORM_WINDOWS_DPI_HH diff --git a/BaseWebview/lib/webview/include/detail/platform/windows/dwmapi.hh b/BaseWebview/lib/webview/include/detail/platform/windows/dwmapi.hh new file mode 100644 index 0000000..acd51b0 --- /dev/null +++ b/BaseWebview/lib/webview/include/detail/platform/windows/dwmapi.hh @@ -0,0 +1,60 @@ +/* + * MIT License + * + * Copyright (c) 2017 Serge Zaitsev + * Copyright (c) 2022 Steffen André Langnes + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined(WEBVIEW_PLATFORM_WINDOWS_DWMAPI_HH) && \ + defined(WEBVIEW_PLATFORM_WINDOWS) +#define WEBVIEW_PLATFORM_WINDOWS_DWMAPI_HH + +#include "../../native_library.hh" + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif + +#include + +namespace webview { +namespace detail { +namespace dwmapi_symbols { + +typedef enum { + // This undocumented value is used instead of DWMWA_USE_IMMERSIVE_DARK_MODE + // on Windows 10 older than build 19041 (2004/20H1). + DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_V10_0_19041 = 19, + // Documented as being supported since Windows 11 build 22000 (21H2) but it + // works since Windows 10 build 19041 (2004/20H1). + DWMWA_USE_IMMERSIVE_DARK_MODE = 20 +} DWMWINDOWATTRIBUTE; + +using DwmSetWindowAttribute_t = HRESULT(WINAPI *)(HWND, DWORD, LPCVOID, DWORD); + +constexpr auto DwmSetWindowAttribute = + library_symbol("DwmSetWindowAttribute"); + +} // namespace dwmapi_symbols +} // namespace detail +} // namespace webview + +#endif // WEBVIEW_PLATFORM_WINDOWS_DWMAPI_HH diff --git a/BaseWebview/lib/webview/include/detail/platform/windows/iid.hh b/BaseWebview/lib/webview/include/detail/platform/windows/iid.hh new file mode 100644 index 0000000..19cf688 --- /dev/null +++ b/BaseWebview/lib/webview/include/detail/platform/windows/iid.hh @@ -0,0 +1,71 @@ +/* + * MIT License + * + * Copyright (c) 2017 Serge Zaitsev + * Copyright (c) 2022 Steffen André Langnes + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined(WEBVIEW_PLATFORM_WINDOWS_IID_HH) && \ + defined(WEBVIEW_PLATFORM_WINDOWS) +#define WEBVIEW_PLATFORM_WINDOWS_IID_HH + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif + +#include + +#include + +#ifdef _MSC_VER +#pragma comment(lib, "ole32.lib") +#endif + +namespace webview { +namespace detail { + +template struct cast_info_t { + using type = T; + IID iid; +}; + +// Checks whether the specified IID equals the IID of the specified type and +// if so casts the "this" pointer to T and returns it. Returns nullptr on +// mismatching IIDs. +// If ppv is specified then the pointer will also be assigned to *ppv. +template +To *cast_if_equal_iid(From *from, REFIID riid, const cast_info_t &info, + LPVOID *ppv = nullptr) noexcept { + To *ptr = nullptr; + if (IsEqualIID(riid, info.iid)) { + ptr = static_cast(from); + ptr->AddRef(); + } + if (ppv) { + *ppv = ptr; + } + return ptr; +} + +} // namespace detail +} // namespace webview + +#endif // WEBVIEW_PLATFORM_WINDOWS_IID_HH diff --git a/BaseWebview/lib/webview/include/detail/platform/windows/ntdll.hh b/BaseWebview/lib/webview/include/detail/platform/windows/ntdll.hh new file mode 100644 index 0000000..70dfc0e --- /dev/null +++ b/BaseWebview/lib/webview/include/detail/platform/windows/ntdll.hh @@ -0,0 +1,51 @@ +/* + * MIT License + * + * Copyright (c) 2017 Serge Zaitsev + * Copyright (c) 2022 Steffen André Langnes + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined(WEBVIEW_PLATFORM_WINDOWS_NTDLL_HH) && \ + defined(WEBVIEW_PLATFORM_WINDOWS) +#define WEBVIEW_PLATFORM_WINDOWS_NTDLL_HH + +#include "../../native_library.hh" + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif + +#include + +namespace webview { +namespace detail { +namespace ntdll_symbols { + +using RtlGetVersion_t = + unsigned int /*NTSTATUS*/ (WINAPI *)(RTL_OSVERSIONINFOW *); + +constexpr auto RtlGetVersion = library_symbol("RtlGetVersion"); + +} // namespace ntdll_symbols +} // namespace detail +} // namespace webview + +#endif // WEBVIEW_PLATFORM_WINDOWS_NTDLL_HH diff --git a/BaseWebview/lib/webview/include/detail/platform/windows/reg_key.hh b/BaseWebview/lib/webview/include/detail/platform/windows/reg_key.hh new file mode 100644 index 0000000..4aec1dd --- /dev/null +++ b/BaseWebview/lib/webview/include/detail/platform/windows/reg_key.hh @@ -0,0 +1,128 @@ +/* + * MIT License + * + * Copyright (c) 2017 Serge Zaitsev + * Copyright (c) 2022 Steffen André Langnes + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined(WEBVIEW_PLATFORM_WINDOWS_REG_KEY_HH) && \ + defined(WEBVIEW_PLATFORM_WINDOWS) +#define WEBVIEW_PLATFORM_WINDOWS_REG_KEY_HH + +#include +#include + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif + +#include + +#ifdef _MSC_VER +#pragma comment(lib, "advapi32.lib") +#endif + +namespace webview { +namespace detail { + +class reg_key { +public: + explicit reg_key(HKEY root_key, const wchar_t *sub_key, DWORD options, + REGSAM sam_desired) { + HKEY handle; + auto status = + RegOpenKeyExW(root_key, sub_key, options, sam_desired, &handle); + if (status == ERROR_SUCCESS) { + m_handle = handle; + } + } + + explicit reg_key(HKEY root_key, const std::wstring &sub_key, DWORD options, + REGSAM sam_desired) + : reg_key(root_key, sub_key.c_str(), options, sam_desired) {} + + virtual ~reg_key() { + if (m_handle) { + RegCloseKey(m_handle); + m_handle = nullptr; + } + } + + reg_key(const reg_key &other) = delete; + reg_key &operator=(const reg_key &other) = delete; + reg_key(reg_key &&other) = delete; + reg_key &operator=(reg_key &&other) = delete; + + bool is_open() const { return !!m_handle; } + bool get_handle() const { return m_handle; } + + template + void query_bytes(const wchar_t *name, Container &result) const { + DWORD buf_length = 0; + // Get the size of the data in bytes. + auto status = RegQueryValueExW(m_handle, name, nullptr, nullptr, nullptr, + &buf_length); + if (status != ERROR_SUCCESS && status != ERROR_MORE_DATA) { + result.resize(0); + return; + } + // Read the data. + result.resize(buf_length / sizeof(typename Container::value_type)); + auto *buf = reinterpret_cast(&result[0]); + status = + RegQueryValueExW(m_handle, name, nullptr, nullptr, buf, &buf_length); + if (status != ERROR_SUCCESS) { + result.resize(0); + return; + } + } + + std::wstring query_string(const wchar_t *name) const { + std::wstring result; + query_bytes(name, result); + // Remove trailing null-characters. + for (std::size_t length = result.size(); length > 0; --length) { + if (result[length - 1] != 0) { + result.resize(length); + break; + } + } + return result; + } + + unsigned int query_uint(const wchar_t *name, + unsigned int default_value) const { + std::vector data; + query_bytes(name, data); + if (data.size() < sizeof(DWORD)) { + return default_value; + } + return static_cast(*reinterpret_cast(data.data())); + } + +private: + HKEY m_handle = nullptr; +}; + +} // namespace detail +} // namespace webview + +#endif // WEBVIEW_PLATFORM_WINDOWS_REG_KEY_HH diff --git a/BaseWebview/lib/webview/include/detail/platform/windows/shcore.hh b/BaseWebview/lib/webview/include/detail/platform/windows/shcore.hh new file mode 100644 index 0000000..be67792 --- /dev/null +++ b/BaseWebview/lib/webview/include/detail/platform/windows/shcore.hh @@ -0,0 +1,52 @@ +/* + * MIT License + * + * Copyright (c) 2017 Serge Zaitsev + * Copyright (c) 2022 Steffen André Langnes + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined(WEBVIEW_PLATFORM_WINDOWS_SHCORE_HH) && \ + defined(WEBVIEW_PLATFORM_WINDOWS) +#define WEBVIEW_PLATFORM_WINDOWS_SHCORE_HH + +#include "../../native_library.hh" + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif + +#include + +namespace webview { +namespace detail { +namespace shcore_symbols { + +typedef enum { PROCESS_PER_MONITOR_DPI_AWARE = 2 } PROCESS_DPI_AWARENESS; +using SetProcessDpiAwareness_t = HRESULT(WINAPI *)(PROCESS_DPI_AWARENESS); + +constexpr auto SetProcessDpiAwareness = + library_symbol("SetProcessDpiAwareness"); + +} // namespace shcore_symbols +} // namespace detail +} // namespace webview + +#endif // WEBVIEW_PLATFORM_WINDOWS_SHCORE_HH diff --git a/BaseWebview/lib/webview/include/detail/platform/windows/theme.hh b/BaseWebview/lib/webview/include/detail/platform/windows/theme.hh new file mode 100644 index 0000000..9d5f83f --- /dev/null +++ b/BaseWebview/lib/webview/include/detail/platform/windows/theme.hh @@ -0,0 +1,69 @@ +/* + * MIT License + * + * Copyright (c) 2017 Serge Zaitsev + * Copyright (c) 2022 Steffen André Langnes + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined(WEBVIEW_PLATFORM_WINDOWS_THEME_HH) && \ + defined(WEBVIEW_PLATFORM_WINDOWS) +#define WEBVIEW_PLATFORM_WINDOWS_THEME_HH + +#include "../../native_library.hh" +#include "dwmapi.hh" +#include "reg_key.hh" + +namespace webview { +namespace detail { + +inline bool is_dark_theme_enabled() { + constexpr auto *sub_key = + L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"; + reg_key key(HKEY_CURRENT_USER, sub_key, 0, KEY_READ); + if (!key.is_open()) { + // Default is light theme + return false; + } + return key.query_uint(L"AppsUseLightTheme", 1) == 0; +} + +inline void apply_window_theme(HWND window) { + auto dark_theme_enabled = is_dark_theme_enabled(); + + // Use "immersive dark mode" on systems that support it. + // Changes the color of the window's title bar (light or dark). + BOOL use_dark_mode{dark_theme_enabled ? TRUE : FALSE}; + static native_library dwmapi{L"dwmapi.dll"}; + if (auto fn = dwmapi.get(dwmapi_symbols::DwmSetWindowAttribute)) { + // Try the modern, documented attribute before the older, undocumented one. + if (fn(window, dwmapi_symbols::DWMWA_USE_IMMERSIVE_DARK_MODE, + &use_dark_mode, sizeof(use_dark_mode)) != S_OK) { + fn(window, + dwmapi_symbols::DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_V10_0_19041, + &use_dark_mode, sizeof(use_dark_mode)); + } + } +} + +} // namespace detail +} // namespace webview + +#endif // WEBVIEW_PLATFORM_WINDOWS_THEME_HH diff --git a/BaseWebview/lib/webview/include/detail/platform/windows/user32.hh b/BaseWebview/lib/webview/include/detail/platform/windows/user32.hh new file mode 100644 index 0000000..0df4481 --- /dev/null +++ b/BaseWebview/lib/webview/include/detail/platform/windows/user32.hh @@ -0,0 +1,83 @@ +/* + * MIT License + * + * Copyright (c) 2017 Serge Zaitsev + * Copyright (c) 2022 Steffen André Langnes + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined(WEBVIEW_PLATFORM_WINDOWS_USER32_HH) && \ + defined(WEBVIEW_PLATFORM_WINDOWS) +#define WEBVIEW_PLATFORM_WINDOWS_USER32_HH + +#include "../../native_library.hh" + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif + +#include + +namespace webview { +namespace detail { +namespace user32_symbols { + +using DPI_AWARENESS_CONTEXT = HANDLE; +using SetProcessDpiAwarenessContext_t = BOOL(WINAPI *)(DPI_AWARENESS_CONTEXT); +using SetProcessDPIAware_t = BOOL(WINAPI *)(); +using GetDpiForWindow_t = UINT(WINAPI *)(HWND); +using EnableNonClientDpiScaling_t = BOOL(WINAPI *)(HWND); +using AdjustWindowRectExForDpi_t = BOOL(WINAPI *)(LPRECT, DWORD, BOOL, DWORD, + UINT); +using GetWindowDpiAwarenessContext_t = DPI_AWARENESS_CONTEXT(WINAPI *)(HWND); +using AreDpiAwarenessContextsEqual_t = BOOL(WINAPI *)(DPI_AWARENESS_CONTEXT, + DPI_AWARENESS_CONTEXT); + +// Use intptr_t as the underlying type because we need to +// reinterpret_cast which is a pointer. +// Available since Windows 10, version 1607 +enum class dpi_awareness : intptr_t { + per_monitor_v2_aware = -4, // Available since Windows 10, version 1703 + per_monitor_aware = -3 +}; + +constexpr auto SetProcessDpiAwarenessContext = + library_symbol( + "SetProcessDpiAwarenessContext"); +constexpr auto SetProcessDPIAware = + library_symbol("SetProcessDPIAware"); +constexpr auto GetDpiForWindow = + library_symbol("GetDpiForWindow"); +constexpr auto EnableNonClientDpiScaling = + library_symbol("EnableNonClientDpiScaling"); +constexpr auto AdjustWindowRectExForDpi = + library_symbol("AdjustWindowRectExForDpi"); +constexpr auto GetWindowDpiAwarenessContext = + library_symbol( + "GetWindowDpiAwarenessContext"); +constexpr auto AreDpiAwarenessContextsEqual = + library_symbol( + "AreDpiAwarenessContextsEqual"); + +} // namespace user32_symbols +} // namespace detail +} // namespace webview + +#endif // WEBVIEW_PLATFORM_WINDOWS_USER32_HH diff --git a/BaseWebview/lib/webview/include/detail/platform/windows/version.hh b/BaseWebview/lib/webview/include/detail/platform/windows/version.hh new file mode 100644 index 0000000..5de0f7f --- /dev/null +++ b/BaseWebview/lib/webview/include/detail/platform/windows/version.hh @@ -0,0 +1,143 @@ +/* + * MIT License + * + * Copyright (c) 2017 Serge Zaitsev + * Copyright (c) 2022 Steffen André Langnes + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined(WEBVIEW_PLATFORM_WINDOWS_VERSION_HH) && \ + defined(WEBVIEW_PLATFORM_WINDOWS) +#define WEBVIEW_PLATFORM_WINDOWS_VERSION_HH + +#include "ntdll.hh" + +#include +#include +#include + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif + +#include + +#ifdef _MSC_VER +#pragma comment(lib, "version.lib") +#endif + +namespace webview { +namespace detail { + +// Parses a version string with 1-4 integral components, e.g. "1.2.3.4". +// Missing or invalid components default to 0, and excess components are ignored. +template +std::array +parse_version(const std::basic_string &version) noexcept { + auto parse_component = [](auto sb, auto se) -> unsigned int { + try { + auto n = std::stol(std::basic_string(sb, se)); + return n < 0 ? 0 : n; + } catch (std::exception &) { + return 0; + } + }; + auto end = version.end(); + auto sb = version.begin(); // subrange begin + auto se = sb; // subrange end + unsigned int ci = 0; // component index + std::array components{}; + while (sb != end && se != end && ci < components.size()) { + if (*se == static_cast('.')) { + components[ci++] = parse_component(sb, se); + sb = ++se; + continue; + } + ++se; + } + if (sb < se && ci < components.size()) { + components[ci] = parse_component(sb, se); + } + return components; +} + +template +auto parse_version(const T (&version)[Length]) noexcept { + return parse_version(std::basic_string(version, Length)); +} + +inline std::wstring +get_file_version_string(const std::wstring &file_path) noexcept { + DWORD dummy_handle; // Unused + DWORD info_buffer_length = + GetFileVersionInfoSizeW(file_path.c_str(), &dummy_handle); + if (info_buffer_length == 0) { + return std::wstring(); + } + std::vector info_buffer; + info_buffer.reserve(info_buffer_length); + if (!GetFileVersionInfoW(file_path.c_str(), 0, info_buffer_length, + info_buffer.data())) { + return std::wstring(); + } + auto sub_block = L"\\StringFileInfo\\040904B0\\ProductVersion"; + LPWSTR version = nullptr; + unsigned int version_length = 0; + if (!VerQueryValueW(info_buffer.data(), sub_block, + reinterpret_cast(&version), &version_length)) { + return std::wstring(); + } + if (!version || version_length == 0) { + return std::wstring(); + } + return std::wstring(version, version_length); +} + +// Compare the specified version against the OS version. +// Returns less than 0 if the OS version is less. +// Returns 0 if the versions are equal. +// Returns greater than 0 if the specified version is greater. +inline int compare_os_version(unsigned int major, unsigned int minor, + unsigned int build) { + // Use RtlGetVersion both to bypass potential issues related to + // VerifyVersionInfo and manifests, and because both GetVersion and + // GetVersionEx are deprecated. + auto ntdll = native_library(L"ntdll.dll"); + if (auto fn = ntdll.get(ntdll_symbols::RtlGetVersion)) { + RTL_OSVERSIONINFOW vi{}; + vi.dwOSVersionInfoSize = sizeof(vi); + if (fn(&vi) != 0) { + return false; + } + if (vi.dwMajorVersion == major) { + if (vi.dwMinorVersion == minor) { + return static_cast(vi.dwBuildNumber) - static_cast(build); + } + return static_cast(vi.dwMinorVersion) - static_cast(minor); + } + return static_cast(vi.dwMajorVersion) - static_cast(major); + } + return false; +} + +} // namespace detail +} // namespace webview + +#endif // WEBVIEW_PLATFORM_WINDOWS_VERSION_HH diff --git a/BaseWebview/lib/webview/include/detail/platform/windows/webview2/loader.hh b/BaseWebview/lib/webview/include/detail/platform/windows/webview2/loader.hh new file mode 100644 index 0000000..f3f2ed3 --- /dev/null +++ b/BaseWebview/lib/webview/include/detail/platform/windows/webview2/loader.hh @@ -0,0 +1,375 @@ +/* + * MIT License + * + * Copyright (c) 2017 Serge Zaitsev + * Copyright (c) 2022 Steffen André Langnes + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined(WEBVIEW_BACKENDS_WEBVIEW2_LOADER_HH) && \ + defined(WEBVIEW_PLATFORM_WINDOWS) && defined(WEBVIEW_EDGE) +#define WEBVIEW_BACKENDS_WEBVIEW2_LOADER_HH + +#include "../../../native_library.hh" +#include "../iid.hh" +#include "../reg_key.hh" +#include "../version.hh" + +#include + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif + +#include + +#include + +#include "WebView2.h" + +#ifdef _MSC_VER +#pragma comment(lib, "ole32.lib") +#endif + +namespace webview { +namespace detail { + +// Enable built-in WebView2Loader implementation by default. +#ifndef WEBVIEW_MSWEBVIEW2_BUILTIN_IMPL +#define WEBVIEW_MSWEBVIEW2_BUILTIN_IMPL 1 +#endif + +// Link WebView2Loader.dll explicitly by default only if the built-in +// implementation is enabled. +#ifndef WEBVIEW_MSWEBVIEW2_EXPLICIT_LINK +#define WEBVIEW_MSWEBVIEW2_EXPLICIT_LINK WEBVIEW_MSWEBVIEW2_BUILTIN_IMPL +#endif + +// Explicit linking of WebView2Loader.dll should be used along with +// the built-in implementation. +#if WEBVIEW_MSWEBVIEW2_BUILTIN_IMPL == 1 && \ + WEBVIEW_MSWEBVIEW2_EXPLICIT_LINK != 1 +#undef WEBVIEW_MSWEBVIEW2_EXPLICIT_LINK +#error Please set WEBVIEW_MSWEBVIEW2_EXPLICIT_LINK=1. +#endif + +#if WEBVIEW_MSWEBVIEW2_BUILTIN_IMPL == 1 +// Gets the last component of a Windows native file path. +// For example, if the path is "C:\a\b" then the result is "b". +template +std::basic_string +get_last_native_path_component(const std::basic_string &path) { + auto pos = path.find_last_of(static_cast('\\')); + if (pos != std::basic_string::npos) { + return path.substr(pos + 1); + } + return std::basic_string(); +} +#endif // WEBVIEW_MSWEBVIEW2_BUILTIN_IMPL + +namespace mswebview2 { +static constexpr IID + IID_ICoreWebView2CreateCoreWebView2ControllerCompletedHandler{ + 0x6C4819F3, + 0xC9B7, + 0x4260, + {0x81, 0x27, 0xC9, 0xF5, 0xBD, 0xE7, 0xF6, 0x8C}}; +static constexpr IID + IID_ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler{ + 0x4E8A3389, + 0xC9D8, + 0x4BD2, + {0xB6, 0xB5, 0x12, 0x4F, 0xEE, 0x6C, 0xC1, 0x4D}}; +static constexpr IID IID_ICoreWebView2PermissionRequestedEventHandler{ + 0x15E1C6A3, + 0xC72A, + 0x4DF3, + {0x91, 0xD7, 0xD0, 0x97, 0xFB, 0xEC, 0x6B, 0xFD}}; +static constexpr IID IID_ICoreWebView2WebMessageReceivedEventHandler{ + 0x57213F19, + 0x00E6, + 0x49FA, + {0x8E, 0x07, 0x89, 0x8E, 0xA0, 0x1E, 0xCB, 0xD2}}; +static constexpr IID + IID_ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandler{ + 0xB99369F3, + 0x9B11, + 0x47B5, + {0xBC, 0x6F, 0x8E, 0x78, 0x95, 0xFC, 0xEA, 0x17}}; + +#if WEBVIEW_MSWEBVIEW2_BUILTIN_IMPL == 1 +enum class webview2_runtime_type { installed = 0, embedded = 1 }; + +namespace webview2_symbols { +using CreateWebViewEnvironmentWithOptionsInternal_t = + HRESULT(STDMETHODCALLTYPE *)( + bool, webview2_runtime_type, PCWSTR, IUnknown *, + ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler *); +using DllCanUnloadNow_t = HRESULT(STDMETHODCALLTYPE *)(); + +static constexpr auto CreateWebViewEnvironmentWithOptionsInternal = + library_symbol( + "CreateWebViewEnvironmentWithOptionsInternal"); +static constexpr auto DllCanUnloadNow = + library_symbol("DllCanUnloadNow"); +} // namespace webview2_symbols +#endif // WEBVIEW_MSWEBVIEW2_BUILTIN_IMPL + +#if WEBVIEW_MSWEBVIEW2_EXPLICIT_LINK == 1 +namespace webview2_symbols { +using CreateCoreWebView2EnvironmentWithOptions_t = HRESULT(STDMETHODCALLTYPE *)( + PCWSTR, PCWSTR, ICoreWebView2EnvironmentOptions *, + ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler *); +using GetAvailableCoreWebView2BrowserVersionString_t = + HRESULT(STDMETHODCALLTYPE *)(PCWSTR, LPWSTR *); + +static constexpr auto CreateCoreWebView2EnvironmentWithOptions = + library_symbol( + "CreateCoreWebView2EnvironmentWithOptions"); +static constexpr auto GetAvailableCoreWebView2BrowserVersionString = + library_symbol( + "GetAvailableCoreWebView2BrowserVersionString"); +} // namespace webview2_symbols +#endif // WEBVIEW_MSWEBVIEW2_EXPLICIT_LINK + +class loader { +public: + HRESULT create_environment_with_options( + PCWSTR browser_dir, PCWSTR user_data_dir, + ICoreWebView2EnvironmentOptions *env_options, + ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler + *created_handler) const { +#if WEBVIEW_MSWEBVIEW2_EXPLICIT_LINK == 1 + if (m_lib.is_loaded()) { + if (auto fn = m_lib.get( + webview2_symbols::CreateCoreWebView2EnvironmentWithOptions)) { + return fn(browser_dir, user_data_dir, env_options, created_handler); + } + } +#if WEBVIEW_MSWEBVIEW2_BUILTIN_IMPL == 1 + return create_environment_with_options_impl(browser_dir, user_data_dir, + env_options, created_handler); +#else + return S_FALSE; +#endif +#else + return ::CreateCoreWebView2EnvironmentWithOptions( + browser_dir, user_data_dir, env_options, created_handler); +#endif // WEBVIEW_MSWEBVIEW2_EXPLICIT_LINK + } + + HRESULT + get_available_browser_version_string(PCWSTR browser_dir, + LPWSTR *version) const { +#if WEBVIEW_MSWEBVIEW2_EXPLICIT_LINK == 1 + if (m_lib.is_loaded()) { + if (auto fn = m_lib.get( + webview2_symbols::GetAvailableCoreWebView2BrowserVersionString)) { + return fn(browser_dir, version); + } + } +#if WEBVIEW_MSWEBVIEW2_BUILTIN_IMPL == 1 + return get_available_browser_version_string_impl(browser_dir, version); +#else + return S_FALSE; +#endif +#else + return ::GetAvailableCoreWebView2BrowserVersionString(browser_dir, version); +#endif // WEBVIEW_MSWEBVIEW2_EXPLICIT_LINK + } + +private: +#if WEBVIEW_MSWEBVIEW2_BUILTIN_IMPL == 1 + struct client_info_t { + bool found = false; + std::wstring dll_path; + std::wstring version; + webview2_runtime_type runtime_type; + }; + + HRESULT create_environment_with_options_impl( + PCWSTR browser_dir, PCWSTR user_data_dir, + ICoreWebView2EnvironmentOptions *env_options, + ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler + *created_handler) const { + auto found_client = find_available_client(browser_dir); + if (!found_client.found) { + return -1; + } + auto client_dll = native_library(found_client.dll_path.c_str()); + if (auto fn = client_dll.get( + webview2_symbols::CreateWebViewEnvironmentWithOptionsInternal)) { + return fn(true, found_client.runtime_type, user_data_dir, env_options, + created_handler); + } + if (auto fn = client_dll.get(webview2_symbols::DllCanUnloadNow)) { + if (!fn()) { + client_dll.detach(); + } + } + return ERROR_SUCCESS; + } + + HRESULT + get_available_browser_version_string_impl(PCWSTR browser_dir, + LPWSTR *version) const { + if (!version) { + return -1; + } + auto found_client = find_available_client(browser_dir); + if (!found_client.found) { + return -1; + } + auto info_length_bytes = + found_client.version.size() * sizeof(found_client.version[0]); + auto info = static_cast(CoTaskMemAlloc(info_length_bytes)); + if (!info) { + return -1; + } + CopyMemory(info, found_client.version.c_str(), info_length_bytes); + *version = info; + return 0; + } + + client_info_t find_available_client(PCWSTR browser_dir) const { + if (browser_dir) { + return find_embedded_client(api_version, browser_dir); + } + auto found_client = + find_installed_client(api_version, true, default_release_channel_guid); + if (!found_client.found) { + found_client = find_installed_client(api_version, false, + default_release_channel_guid); + } + return found_client; + } + + std::wstring make_client_dll_path(const std::wstring &dir) const { + auto dll_path = dir; + if (!dll_path.empty()) { + auto last_char = dir[dir.size() - 1]; + if (last_char != L'\\' && last_char != L'/') { + dll_path += L'\\'; + } + } + dll_path += L"EBWebView\\"; +#if defined(_M_X64) || defined(__x86_64__) + dll_path += L"x64"; +#elif defined(_M_IX86) || defined(__i386__) + dll_path += L"x86"; +#elif defined(_M_ARM64) || defined(__aarch64__) + dll_path += L"arm64"; +#else +#error WebView2 integration for this platform is not yet supported. +#endif + dll_path += L"\\EmbeddedBrowserWebView.dll"; + return dll_path; + } + + client_info_t + find_installed_client(unsigned int min_api_version, bool system, + const std::wstring &release_channel) const { + std::wstring sub_key = client_state_reg_sub_key; + sub_key += release_channel; + auto root_key = system ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + reg_key key(root_key, sub_key, 0, KEY_READ | KEY_WOW64_32KEY); + if (!key.is_open()) { + return {}; + } + auto ebwebview_value = key.query_string(L"EBWebView"); + + auto client_version_string = + get_last_native_path_component(ebwebview_value); + auto client_version = parse_version(client_version_string); + if (client_version[2] < min_api_version) { + // Our API version is greater than the runtime API version. + return {}; + } + + auto client_dll_path = make_client_dll_path(ebwebview_value); + return {true, client_dll_path, client_version_string, + webview2_runtime_type::installed}; + } + + client_info_t find_embedded_client(unsigned int min_api_version, + const std::wstring &dir) const { + auto client_dll_path = make_client_dll_path(dir); + + auto client_version_string = get_file_version_string(client_dll_path); + auto client_version = parse_version(client_version_string); + if (client_version[2] < min_api_version) { + // Our API version is greater than the runtime API version. + return {}; + } + + return {true, client_dll_path, client_version_string, + webview2_runtime_type::embedded}; + } + + // The minimum WebView2 API version we need regardless of the SDK release + // actually used. The number comes from the SDK release version, + // e.g. 1.0.1150.38. To be safe the SDK should have a number that is greater + // than or equal to this number. The Edge browser webview client must + // have a number greater than or equal to this number. + static constexpr unsigned int api_version = 1150; + + static constexpr auto client_state_reg_sub_key = + L"SOFTWARE\\Microsoft\\EdgeUpdate\\ClientState\\"; + + // GUID for the stable release channel. + static constexpr auto stable_release_guid = + L"{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}"; + + static constexpr auto default_release_channel_guid = stable_release_guid; +#endif // WEBVIEW_MSWEBVIEW2_BUILTIN_IMPL + +#if WEBVIEW_MSWEBVIEW2_EXPLICIT_LINK == 1 + native_library m_lib{L"WebView2Loader.dll"}; +#endif +}; + +namespace cast_info { +static constexpr auto controller_completed = + cast_info_t{ + IID_ICoreWebView2CreateCoreWebView2ControllerCompletedHandler}; + +static constexpr auto environment_completed = + cast_info_t{ + IID_ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler}; + +static constexpr auto message_received = + cast_info_t{ + IID_ICoreWebView2WebMessageReceivedEventHandler}; + +static constexpr auto permission_requested = + cast_info_t{ + IID_ICoreWebView2PermissionRequestedEventHandler}; + +static constexpr auto add_script_to_execute_on_document_created_completed = + cast_info_t< + ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandler>{ + IID_ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandler}; +} // namespace cast_info + +} // namespace mswebview2 +} // namespace detail +} // namespace webview + +#endif // WEBVIEW_BACKENDS_WEBVIEW2_LOADER_HH diff --git a/BaseWebview/lib/webview/include/detail/user_script.hh b/BaseWebview/lib/webview/include/detail/user_script.hh new file mode 100644 index 0000000..cd4ba4a --- /dev/null +++ b/BaseWebview/lib/webview/include/detail/user_script.hh @@ -0,0 +1,73 @@ +/* + * MIT License + * + * Copyright (c) 2017 Serge Zaitsev + * Copyright (c) 2022 Steffen André Langnes + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef WEBVIEW_DETAIL_USER_SCRIPT_HH +#define WEBVIEW_DETAIL_USER_SCRIPT_HH + +#include +#include +#include +#include + +namespace webview { +namespace detail { + +class user_script { +public: + class impl; + using impl_deleter = std::function; + using impl_ptr = std::unique_ptr; + + user_script(const std::string &code, impl_ptr &&impl_) + : m_code{code}, m_impl{std::move(impl_)} {} + + user_script(const user_script &other) = delete; + user_script &operator=(const user_script &other) = delete; + user_script(user_script &&other) noexcept { *this = std::move(other); } + + user_script &operator=(user_script &&other) noexcept { + if (this == &other) { + return *this; + } + m_code = std::move(other.m_code); + m_impl = std::move(other.m_impl); + return *this; + } + + const std::string &get_code() const { return m_code; } + + impl &get_impl() { return *m_impl; } + + const impl &get_impl() const { return *m_impl; } + +private: + std::string m_code; + impl_ptr m_impl; +}; + +} // namespace detail +} // namespace webview + +#endif // WEBVIEW_DETAIL_USER_SCRIPT_HH diff --git a/BaseWebview/lib/webview/include/detail/utility/string.hh b/BaseWebview/lib/webview/include/detail/utility/string.hh new file mode 100644 index 0000000..a1f95c4 --- /dev/null +++ b/BaseWebview/lib/webview/include/detail/utility/string.hh @@ -0,0 +1,96 @@ +/* + * MIT License + * + * Copyright (c) 2017 Serge Zaitsev + * Copyright (c) 2022 Steffen André Langnes + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef WEBVIEW_DETAIL_UTILITY_STRING_HH +#define WEBVIEW_DETAIL_UTILITY_STRING_HH + +#include + +#if defined(_WIN32) +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#endif + +namespace webview { +namespace detail { + +#if defined(_WIN32) +// Converts a narrow (UTF-8-encoded) string into a wide (UTF-16-encoded) string. +inline std::wstring widen_string(const std::string &input) { + if (input.empty()) { + return std::wstring(); + } + UINT cp = CP_UTF8; + DWORD flags = MB_ERR_INVALID_CHARS; + auto input_c = input.c_str(); + auto input_length = static_cast(input.size()); + auto required_length = + MultiByteToWideChar(cp, flags, input_c, input_length, nullptr, 0); + if (required_length > 0) { + std::wstring output(static_cast(required_length), L'\0'); + if (MultiByteToWideChar(cp, flags, input_c, input_length, &output[0], + required_length) > 0) { + return output; + } + } + // Failed to convert string from UTF-8 to UTF-16 + return std::wstring(); +} + +// Converts a wide (UTF-16-encoded) string into a narrow (UTF-8-encoded) string. +inline std::string narrow_string(const std::wstring &input) { + struct wc_flags { + enum TYPE : unsigned int { + // WC_ERR_INVALID_CHARS + err_invalid_chars = 0x00000080U + }; + }; + if (input.empty()) { + return std::string(); + } + UINT cp = CP_UTF8; + DWORD flags = wc_flags::err_invalid_chars; + auto input_c = input.c_str(); + auto input_length = static_cast(input.size()); + auto required_length = WideCharToMultiByte(cp, flags, input_c, input_length, + nullptr, 0, nullptr, nullptr); + if (required_length > 0) { + std::string output(static_cast(required_length), '\0'); + if (WideCharToMultiByte(cp, flags, input_c, input_length, &output[0], + required_length, nullptr, nullptr) > 0) { + return output; + } + } + // Failed to convert string from UTF-16 to UTF-8 + return std::string(); +} +#endif + +} // namespace detail +} // namespace webview + +#endif // WEBVIEW_DETAIL_UTILITY_STRING_HH diff --git a/BaseWebview/lib/webview/include/errors.h b/BaseWebview/lib/webview/include/errors.h new file mode 100644 index 0000000..cad64ce --- /dev/null +++ b/BaseWebview/lib/webview/include/errors.h @@ -0,0 +1,67 @@ +/* + * MIT License + * + * Copyright (c) 2017 Serge Zaitsev + * Copyright (c) 2022 Steffen André Langnes + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef WEBVIEW_ERRORS_H +#define WEBVIEW_ERRORS_H + +/// @name Errors +/// @{ + +/** + * @brief Error codes returned to callers of the API. + * + * The following codes are commonly used in the library: + * - @c WEBVIEW_ERROR_OK + * - @c WEBVIEW_ERROR_UNSPECIFIED + * - @c WEBVIEW_ERROR_INVALID_ARGUMENT + * - @c WEBVIEW_ERROR_INVALID_STATE + * + * With the exception of @c WEBVIEW_ERROR_OK which is normally expected, + * the other common codes do not normally need to be handled specifically. + * Refer to specific functions regarding handling of other codes. + */ +typedef enum { + /// Missing dependency. + WEBVIEW_ERROR_MISSING_DEPENDENCY = -5, + /// Operation canceled. + WEBVIEW_ERROR_CANCELED = -4, + /// Invalid state detected. + WEBVIEW_ERROR_INVALID_STATE = -3, + /// One or more invalid arguments have been specified e.g. in a function call. + WEBVIEW_ERROR_INVALID_ARGUMENT = -2, + /// An unspecified error occurred. A more specific error code may be needed. + WEBVIEW_ERROR_UNSPECIFIED = -1, + /// OK/Success. Functions that return error codes will typically return this + /// to signify successful operations. + WEBVIEW_ERROR_OK = 0, + /// Signifies that something already exists. + WEBVIEW_ERROR_DUPLICATE = 1, + /// Signifies that something does not exist. + WEBVIEW_ERROR_NOT_FOUND = 2 +} webview_error_t; + +/// @} + +#endif // WEBVIEW_ERRORS_H diff --git a/BaseWebview/lib/webview/include/errors.hh b/BaseWebview/lib/webview/include/errors.hh new file mode 100644 index 0000000..92b78f2 --- /dev/null +++ b/BaseWebview/lib/webview/include/errors.hh @@ -0,0 +1,82 @@ +/* + * MIT License + * + * Copyright (c) 2017 Serge Zaitsev + * Copyright (c) 2022 Steffen André Langnes + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef WEBVIEW_ERRORS_HH +#define WEBVIEW_ERRORS_HH + +#include "errors.h" + +#include +#include + +namespace webview { + +class error_info { +public: + error_info(webview_error_t code, const std::string &message = {}) noexcept + : m_code{code}, m_message{message} {} + error_info() = default; + + webview_error_t code() const { return m_code; } + const std::string &message() const { return m_message; } + +private: + webview_error_t m_code{WEBVIEW_ERROR_UNSPECIFIED}; + std::string m_message; +}; + +class exception : public std::exception { +public: + exception(webview_error_t code, const std::string &message, + std::exception_ptr cause) noexcept + : exception{error_info{code, message}, cause} {} + + exception(webview_error_t code, const std::string &message) noexcept + : exception{error_info{code, message}} {} + + exception(const error_info &error, std::exception_ptr cause) noexcept + : m_error{error}, + // NOLINTNEXTLINE(bugprone-throw-keyword-missing) + m_cause{cause} {} + + exception(const error_info &error) noexcept : m_error{error} {} + + exception() = default; + + const error_info &error() const { return m_error; } + std::exception_ptr cause() const { return m_cause; } + + const char *what() const noexcept override { + return m_error.message().c_str(); + } + +private: + error_info m_error{WEBVIEW_ERROR_UNSPECIFIED}; + std::exception_ptr m_cause; +}; + +} // namespace webview + +#endif // WEBVIEW_ERRORS_HH diff --git a/BaseWebview/lib/webview/include/json_deprecated.hh b/BaseWebview/lib/webview/include/json_deprecated.hh new file mode 100644 index 0000000..659a241 --- /dev/null +++ b/BaseWebview/lib/webview/include/json_deprecated.hh @@ -0,0 +1,58 @@ +/* + * MIT License + * + * Copyright (c) 2017 Serge Zaitsev + * Copyright (c) 2022 Steffen André Langnes + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef WEBVIEW_JSON_HH +#define WEBVIEW_JSON_HH + +#include "detail/json.hh" +#include "macros.h" + +namespace webview { + +WEBVIEW_DEPRECATED_PRIVATE +inline int json_parse_c(const char *s, size_t sz, const char *key, size_t keysz, + const char **value, size_t *valuesz) { + return detail::json_parse_c(s, sz, key, keysz, value, valuesz); +} + +WEBVIEW_DEPRECATED_PRIVATE +inline std::string json_escape(const std::string &s) { + return detail::json_escape(s); +} + +WEBVIEW_DEPRECATED_PRIVATE +inline int json_unescape(const char *s, size_t n, char *out) { + return detail::json_unescape(s, n, out); +} + +WEBVIEW_DEPRECATED_PRIVATE +inline std::string json_parse(const std::string &s, const std::string &key, + const int index) { + return detail::json_parse(s, key, index); +} + +} // namespace webview + +#endif // WEBVIEW_JSON_HH diff --git a/BaseWebview/lib/webview/include/macros.h b/BaseWebview/lib/webview/include/macros.h new file mode 100644 index 0000000..57297c6 --- /dev/null +++ b/BaseWebview/lib/webview/include/macros.h @@ -0,0 +1,123 @@ +/* + * MIT License + * + * Copyright (c) 2017 Serge Zaitsev + * Copyright (c) 2022 Steffen André Langnes + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef WEBVIEW_MACROS_H +#define WEBVIEW_MACROS_H + +/** + * Used to specify function linkage such as extern, inline, etc. + * + * When @c WEBVIEW_API is not already defined, the defaults are as follows: + * + * - @c inline when compiling C++ code. + * - @c extern when compiling C code. + * + * The following macros can be used to automatically set an appropriate + * value for @c WEBVIEW_API: + * + * - Define @c WEBVIEW_BUILD_SHARED when building a shared library. + * - Define @c WEBVIEW_SHARED when using a shared library. + * - Define @c WEBVIEW_STATIC when building or using a static library. + */ +#ifndef WEBVIEW_API +#if defined(WEBVIEW_SHARED) || defined(WEBVIEW_BUILD_SHARED) +#if defined(_WIN32) || defined(__CYGWIN__) +#if defined(WEBVIEW_BUILD_SHARED) +#define WEBVIEW_API __declspec(dllexport) +#else +#define WEBVIEW_API __declspec(dllimport) +#endif +#else +#define WEBVIEW_API __attribute__((visibility("default"))) +#endif +#elif !defined(WEBVIEW_STATIC) && defined(__cplusplus) +#define WEBVIEW_API inline +#else +#define WEBVIEW_API extern +#endif +#endif + +/// @name Used internally +/// @{ + +/// Utility macro for stringifying a macro argument. +#define WEBVIEW_STRINGIFY(x) #x + +/// Utility macro for stringifying the result of a macro argument expansion. +#define WEBVIEW_EXPAND_AND_STRINGIFY(x) WEBVIEW_STRINGIFY(x) + +/// @} + +/// @brief Evaluates to @c TRUE for error codes indicating success or +/// additional information. +#define WEBVIEW_SUCCEEDED(error) ((int)(error) >= 0) + +/// Evaluates to @c TRUE if the given error code indicates failure. +#define WEBVIEW_FAILED(error) ((int)(error) < 0) + +#ifdef __cplusplus +#ifndef WEBVIEW_HEADER + +#if defined(__APPLE__) +#define WEBVIEW_PLATFORM_DARWIN +#elif defined(__unix__) +#define WEBVIEW_PLATFORM_LINUX +#elif defined(_WIN32) +#define WEBVIEW_PLATFORM_WINDOWS +#else +#error "Unable to detect current platform" +#endif + +#if !defined(WEBVIEW_GTK) && !defined(WEBVIEW_COCOA) && !defined(WEBVIEW_EDGE) +#if defined(WEBVIEW_PLATFORM_DARWIN) +#define WEBVIEW_COCOA +#elif defined(WEBVIEW_PLATFORM_LINUX) +#define WEBVIEW_GTK +#elif defined(WEBVIEW_PLATFORM_WINDOWS) +#define WEBVIEW_EDGE +#else +#error "please, specify webview backend" +#endif +#endif + +#ifndef WEBVIEW_DEPRECATED +#if __cplusplus >= 201402L +#define WEBVIEW_DEPRECATED(reason) [[deprecated(reason)]] +#elif defined(_MSC_VER) +#define WEBVIEW_DEPRECATED(reason) __declspec(deprecated(reason)) +#else +#define WEBVIEW_DEPRECATED(reason) __attribute__((deprecated(reason))) +#endif +#endif + +#ifndef WEBVIEW_DEPRECATED_PRIVATE +#define WEBVIEW_DEPRECATED_PRIVATE \ + WEBVIEW_DEPRECATED("Private API should not be used") +#endif + +#endif // WEBVIEW_HEADER +#endif // __cplusplus + +#endif // WEBVIEW_MACROS_H diff --git a/BaseWebview/lib/webview/include/types.h b/BaseWebview/lib/webview/include/types.h new file mode 100644 index 0000000..af95579 --- /dev/null +++ b/BaseWebview/lib/webview/include/types.h @@ -0,0 +1,81 @@ +/* + * MIT License + * + * Copyright (c) 2017 Serge Zaitsev + * Copyright (c) 2022 Steffen André Langnes + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef WEBVIEW_TYPES_H +#define WEBVIEW_TYPES_H + +/// Holds the elements of a MAJOR.MINOR.PATCH version number. +typedef struct { + /// Major version. + unsigned int major; + /// Minor version. + unsigned int minor; + /// Patch version. + unsigned int patch; +} webview_version_t; + +/// Holds the library's version information. +typedef struct { + /// The elements of the version number. + webview_version_t version; + /// SemVer 2.0.0 version number in MAJOR.MINOR.PATCH format. + char version_number[32]; + /// SemVer 2.0.0 pre-release labels prefixed with "-" if specified, otherwise + /// an empty string. + char pre_release[48]; + /// SemVer 2.0.0 build metadata prefixed with "+", otherwise an empty string. + char build_metadata[48]; +} webview_version_info_t; + +/// Pointer to a webview instance. +typedef void *webview_t; + +/// Native handle kind. The actual type depends on the backend. +typedef enum { + /// Top-level window. @c GtkWindow pointer (GTK), @c NSWindow pointer (Cocoa) + /// or @c HWND (Win32). + WEBVIEW_NATIVE_HANDLE_KIND_UI_WINDOW, + /// Browser widget. @c GtkWidget pointer (GTK), @c NSView pointer (Cocoa) or + /// @c HWND (Win32). + WEBVIEW_NATIVE_HANDLE_KIND_UI_WIDGET, + /// Browser controller. @c WebKitWebView pointer (WebKitGTK), @c WKWebView + /// pointer (Cocoa/WebKit) or @c ICoreWebView2Controller pointer + /// (Win32/WebView2). + WEBVIEW_NATIVE_HANDLE_KIND_BROWSER_CONTROLLER +} webview_native_handle_kind_t; + +/// Window size hints +typedef enum { + /// Width and height are default size. + WEBVIEW_HINT_NONE, + /// Width and height are minimum bounds. + WEBVIEW_HINT_MIN, + /// Width and height are maximum bounds. + WEBVIEW_HINT_MAX, + /// Window size can not be changed by a user. + WEBVIEW_HINT_FIXED +} webview_hint_t; + +#endif // WEBVIEW_TYPES_H diff --git a/BaseWebview/lib/webview/include/types.hh b/BaseWebview/lib/webview/include/types.hh new file mode 100644 index 0000000..e04d6cf --- /dev/null +++ b/BaseWebview/lib/webview/include/types.hh @@ -0,0 +1,45 @@ +/* + * MIT License + * + * Copyright (c) 2017 Serge Zaitsev + * Copyright (c) 2022 Steffen André Langnes + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef WEBVIEW_TYPES_HH +#define WEBVIEW_TYPES_HH + +#include "detail/basic_result.hh" +#include "errors.hh" + +#include + +namespace webview { + +using dispatch_fn_t = std::function; + +template +using result = detail::basic_result; + +using noresult = detail::basic_result; + +} // namespace webview + +#endif // WEBVIEW_TYPES_HH diff --git a/BaseWebview/lib/webview/include/version.h b/BaseWebview/lib/webview/include/version.h new file mode 100644 index 0000000..ef96cd8 --- /dev/null +++ b/BaseWebview/lib/webview/include/version.h @@ -0,0 +1,67 @@ +/* + * MIT License + * + * Copyright (c) 2017 Serge Zaitsev + * Copyright (c) 2022 Steffen André Langnes + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef WEBVIEW_VERSION_H +#define WEBVIEW_VERSION_H + +#include "macros.h" + +/// @name Version +/// @{ + +#ifndef WEBVIEW_VERSION_MAJOR +/// The current library major version. +#define WEBVIEW_VERSION_MAJOR 0 +#endif + +#ifndef WEBVIEW_VERSION_MINOR +/// The current library minor version. +#define WEBVIEW_VERSION_MINOR 12 +#endif + +#ifndef WEBVIEW_VERSION_PATCH +/// The current library patch version. +#define WEBVIEW_VERSION_PATCH 0 +#endif + +#ifndef WEBVIEW_VERSION_PRE_RELEASE +/// SemVer 2.0.0 pre-release labels prefixed with "-". +#define WEBVIEW_VERSION_PRE_RELEASE "" +#endif + +#ifndef WEBVIEW_VERSION_BUILD_METADATA +/// SemVer 2.0.0 build metadata prefixed with "+". +#define WEBVIEW_VERSION_BUILD_METADATA "" +#endif + +/// SemVer 2.0.0 version number in MAJOR.MINOR.PATCH format. +#define WEBVIEW_VERSION_NUMBER \ + WEBVIEW_EXPAND_AND_STRINGIFY(WEBVIEW_VERSION_MAJOR) \ + "." WEBVIEW_EXPAND_AND_STRINGIFY( \ + WEBVIEW_VERSION_MINOR) "." WEBVIEW_EXPAND_AND_STRINGIFY(WEBVIEW_VERSION_PATCH) + +/// @} + +#endif // WEBVIEW_VERSION_H diff --git a/BaseWebview/lib/webview/include/webview.h b/BaseWebview/lib/webview/include/webview.h new file mode 100644 index 0000000..1cfeb71 --- /dev/null +++ b/BaseWebview/lib/webview/include/webview.h @@ -0,0 +1,37 @@ +/* + * MIT License + * + * Copyright (c) 2017 Serge Zaitsev + * Copyright (c) 2022 Steffen André Langnes + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef WEBVIEW_H +#define WEBVIEW_H + +#include "api.h" + +#ifdef __cplusplus +#ifndef WEBVIEW_HEADER +#include "c_api_impl.hh" +#endif +#endif + +#endif // WEBVIEW_H diff --git a/BaseWebview/lib/webview/webview.dll b/BaseWebview/lib/webview/webview.dll new file mode 100644 index 0000000000000000000000000000000000000000..cfc8452d35e3b0c3b979c37a8f698317f1b3cbb4 GIT binary patch literal 94208 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 literal 0 HcmV?d00001 diff --git a/BaseWebview/lib/webview/webview.exp b/BaseWebview/lib/webview/webview.exp new file mode 100644 index 0000000000000000000000000000000000000000..a48bda8236ed50ad61b3f6f24dbb6888ae26f79d GIT binary patch literal 2701 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 eQecy-^k8<(`(&-pr$aX=^QQzZ{tF=ZAp8d literal 0 HcmV?d00001 diff --git a/BaseWebview/lib/webview/webview_static.lib b/BaseWebview/lib/webview/webview_static.lib new file mode 100644 index 0000000000000000000000000000000000000000..5969c0993491ed332b593fdbef85f769780d727a GIT binary patch literal 779998 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ͯeYPSQyAr<|-4CZsvFb54@# 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ࢻ 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 a`hR-#Pf1FD%u7DDU2NcxDHr|w$NvN4No>Oa literal 0 HcmV?d00001 diff --git a/BaseWebview/main.zig b/BaseWebview/main.zig new file mode 100644 index 0000000..ab4913a --- /dev/null +++ b/BaseWebview/main.zig @@ -0,0 +1,115 @@ +//!zig-autodoc-section: BaseWebview\\main.zig +//! main.zig : +//! Template for Webview program. +// Build using Zig 0.13.0 + +const std = @import("std"); +const web = @cImport({ + @cInclude("lib/webview/include/webview.h"); +}); +const c = @cImport({ + @cInclude("stdlib.h"); // just strtol used by the original example +}); + +const context_t = struct{ + window: web.webview_t, + count: i32, +}; +pub fn main() u8 { + HideConsoleWindow(); + const window: web.webview_t = web.webview_create(0, null); + var context: context_t = .{ .window = window, .count = 0 }; + _ = web.webview_set_title(window, "BaseWebview"); + _ = web.webview_set_size(window, 480, 320, web.WEBVIEW_HINT_NONE); + + // A binding that counts up or down and immediately returns the new value. + _ = web.webview_bind(window, "count", count, &context); + + _ = web.webview_set_html(window, html); + _ = web.webview_run(window); + + _ = web.webview_destroy(window); + return 0; +} + +pub fn count(id: [*c]const u8, req: [*c]const u8, arg: ?*anyopaque) callconv(.C) void { + if (arg == null) return; + const context: *context_t = @ptrCast(@alignCast(arg.?)); + // Imagine that params->req is properly parsed or use your own JSON parser. + const direction = c.strtol(req + 1, null, 10); + var result: [10]u8 = std.mem.zeroes([10]u8); + result[0] = 0; // Null terminate + _ = std.fmt.bufPrint(&result, "{d}", .{context.count + direction}) catch unreachable; + context.count += direction; // Update count after formatting + _ = web.webview_return(context.window, id, 0, &result[0]); +} + +const html: [*c]const u8 = +\\

+\\
+\\ +; + +// ============================================================================ +// Helpers +// +const win = struct { + usingnamespace std.os.windows; + usingnamespace std.os.windows.kernel32; +}; + +fn HideConsoleWindow() void { + const BUF_TITLE = 1024; + var hwndFound: win.HWND = undefined; + var pszWindowTitle: [BUF_TITLE:0]win.CHAR = std.mem.zeroes([BUF_TITLE:0]win.CHAR); + + _ = GetConsoleTitleA(&pszWindowTitle, BUF_TITLE); + hwndFound=FindWindowA(null, &pszWindowTitle); + _ = ShowWindow(hwndFound, SW_HIDE); +} + +pub extern "kernel32" fn GetConsoleTitleA( + lpConsoleTitle: win.LPSTR, + nSize: win.DWORD, +) callconv(win.WINAPI) win.DWORD; + +pub extern "kernel32" fn FindWindowA( + lpClassName: ?win.LPSTR, + lpWindowName: ?win.LPSTR, +) callconv(win.WINAPI) win.HWND; + +pub const SW_HIDE = 0; +pub extern "user32" fn ShowWindow( + hWnd: win.HWND, + nCmdShow: i32 +) callconv(win.WINAPI) win.BOOL; + +pub const MB_OK = 0x00000000; +pub extern "user32" fn MessageBoxA( + hWnd: ?win.HWND, + lpText: [*:0]const u8, + lpCaption: [*:0]const u8, + uType: win.UINT +) callconv(win.WINAPI) win.INT; + +// ============================================================================ +// Tests +// +test " " { +} \ No newline at end of file diff --git a/BaseWebview/tools/buildReleaseStrip.bat b/BaseWebview/tools/buildReleaseStrip.bat new file mode 100644 index 0000000..d401f60 --- /dev/null +++ b/BaseWebview/tools/buildReleaseStrip.bat @@ -0,0 +1,75 @@ +@ECHO OFF + +REM Check if Tools folder, then go up to parent folder +SET FolderName= +FOR %%* IN (%CD%) DO SET FolderName=%%~n* +IF /I "%FolderName%" == "tools" ( + FOR %%* IN (%CD%\..) DO SET FolderName=%%~n* + CD .. +) + +REM EXTRA ARGS SHORTCUT +REM =================== +REM Linking system libraries (note: -l and library name all together): +REM -lSDL2 -lOpenGL32 +REM Adding libraries DLL+LIB folders (note: -L SPACE "Dir"): +REM -L %CD%\lib\SDL2 +REM Adding cImport .H include folders (note: -I"Dir", without spaces): +REM -I%CD%\lib\microui -I%CD%\lib\SDL2\include +REM +REM Full extra_args sample of a project that use SDL3 + OpenGL : +REM SET extra_args=-lSDL3 -lOpenGL32 -L "%CD%\lib\SDL3" -I"%CD%" -I"%CD%\lib" -I"%CD%\lib\SDL3" + +SET extra_args=-I. -Ilib/webview/include -Llib/webview -lwebview + +REM AddCSource +REM ========== +REM If your project use C Source Files, add here the list of files you want to add to your build. +REM +REM SET addCSourceFile="%CD%\lib\SDL3\glad.c" + +SET addCSourceFile= + +IF NOT EXIST %CD%\bin\ReleaseStrip ( + MKDIR %CD%\bin\ReleaseStrip +) +IF NOT EXIST %CD%\bin\ReleaseStrip\obj ( + MKDIR %CD%\bin\ReleaseStrip\obj +) + +REM GET CURRENT FOLDER NAME +SET ProjectName=%FolderName% + +SET rcmd= +IF EXIST "*.rc" ( + SET rcmd=-rcflags /c65001 -- %CD%\%ProjectName%.rc +) + +SET singlethread=-fsingle-threaded +SET libc= +FINDSTR /L linkLibC build.zig > NUL && ( + SET libc=-lc +) +SET libcpp= +FINDSTR /L linkLibCpp build.zig > NUL && ( + SET libcpp=-lc++ + SET singlethread= +) + +REM OUTPUT TO ZIG_REPORT.TXT +> bin/ReleaseStrip/obj/zig_report.txt ( + zig build-exe -O ReleaseSmall %rcmd% %libc% %libcpp% %singlethread% -fstrip --color off -femit-bin=bin/ReleaseStrip/%ProjectName%.exe -femit-asm=bin/ReleaseStrip/obj/%ProjectName%.s -femit-llvm-ir=bin/ReleaseStrip/obj/%ProjectName%.ll -femit-llvm-bc=bin/ReleaseStrip/obj/%ProjectName%.bc -femit-h=bin/ReleaseStrip/obj/%ProjectName%.h -ftime-report -fstack-report %extra_args% --name %ProjectName% main.zig %addCSourceFile% +) 2>&1 + +REM OUTPUT BUILD COMMAND LINE TO ZIG_BUILD_CMD.TXT +> bin/ReleaseStrip/obj/zig_build_cmd.txt ( + zig build-exe -O ReleaseSmall %rcmd% %libc% %libcpp% %singlethread% -fstrip --color off -femit-bin=bin/ReleaseStrip/%ProjectName%.exe -femit-asm=bin/ReleaseStrip/obj/%ProjectName%.s -femit-llvm-ir=bin/ReleaseStrip/obj/%ProjectName%.ll -femit-llvm-bc=bin/ReleaseStrip/obj/%ProjectName%.bc -femit-h=bin/ReleaseStrip/obj/%ProjectName%.h -ftime-report -fstack-report %extra_args% --name %ProjectName% main.zig %addCSourceFile% +) 2>&1 + +IF EXIST "%CD%\bin\ReleaseStrip\%ProjectName%.exe.obj" ( + MOVE %CD%\bin\ReleaseStrip\%ProjectName%.exe.obj %CD%\bin\ReleaseStrip\obj > NUL +) + +ECHO. +ECHO Done! +REM PAUSE \ No newline at end of file diff --git a/README.md b/README.md index 70c7b51..2982eba 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,7 @@ Using Windows 10, Zig x86_64 Version : **0.13.0** | **[BaseLVGL](/BaseLVGL/)** | Template with [LVGL](https://lvgl.io/) UI. | Console | | **[BaseMicroui](/Basemicroui/)** | Template with [microui](https://github.com/rxi/). Renderers: SDL2, Windows GDI. | Windows | | **[BaseNuklear](/BaseNuklear/)** | Template with [Nuklear](https://github.com/Immediate-Mode-UI/Nuklear) UI using Windows GDI native. | Windows | +| **[BaseWebview](/BaseWebview/)** | Template with [Webview](https://github.com/webview/webview) . | Console | | **[BaseOpenGL](/BaseOpenGL/)** | Template with [OpenGL](https://www.opengl.org/) (GL.h). | Windows | | **[BaseGLFW](/BaseGLFW/)** | Template with [GLFW](https://www.glfw.org/) and [GLAD](https://github.com/Dav1dde/glad/). | Console | | **[BaseDX11](/BaseDX11/)** | Template with [DirectX Direct3D 11](https://learn.microsoft.com/en-us/windows/win32/direct3d11/atoc-dx-graphics-direct3d-11). | Windows | @@ -361,6 +362,7 @@ Yellow lightbulbs sometimes show up to notify "There are no fix", JSON files org [Allegro5](https://liballeg.org/) from Allegro 5 Development Team.
[NanoVG](https://github.com/memononen/nanovg) from Memononen.
[SFML2](https://www.sfml-dev.org/) from Laurent Gomila.
+[Webview](https://github.com/webview/webview/) from Webview Team.

## License @@ -388,5 +390,6 @@ BaseNuklear = Zig Nuklear UI program template. BaseClay = Zig Clay UI program template. BaseAllegro = Zig Allegro5 program template. BaseNanoVG = Zig NanoVG program template. +BaseWebview = Zig Webview program template.