sparkbun 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/sparkbun.cjs +18 -0
- package/dist-linux-arm64/bsdiff +0 -0
- package/dist-linux-arm64/bspatch +0 -0
- package/dist-linux-arm64/libElectrobunCore.so +0 -0
- package/dist-linux-arm64/libNativeWrapper.so +0 -0
- package/dist-linux-arm64/libasar.so +0 -0
- package/dist-linux-x64/bsdiff +0 -0
- package/dist-linux-x64/bspatch +0 -0
- package/dist-linux-x64/libElectrobunCore.so +0 -0
- package/dist-linux-x64/libNativeWrapper.so +0 -0
- package/dist-linux-x64/libasar.so +0 -0
- package/dist-macos-arm64/bsdiff +0 -0
- package/dist-macos-arm64/bspatch +0 -0
- package/dist-macos-arm64/libElectrobunCore.dylib +0 -0
- package/dist-macos-arm64/libNativeWrapper.dylib +0 -0
- package/dist-macos-arm64/libasar.dylib +0 -0
- package/dist-macos-arm64/libwebgpu_dawn.dylib +0 -0
- package/dist-macos-arm64/preload-full.js +885 -0
- package/dist-macos-arm64/preload-sandboxed.js +111 -0
- package/dist-macos-arm64/process_helper +0 -0
- package/dist-win-x64/ElectrobunCore.dll +0 -0
- package/dist-win-x64/WebView2Loader.dll +0 -0
- package/dist-win-x64/bsdiff.exe +0 -0
- package/dist-win-x64/bspatch.exe +0 -0
- package/dist-win-x64/libNativeWrapper.dll +0 -0
- package/dist-win-x64/zig-asar/arm64/libasar.dll +0 -0
- package/dist-win-x64/zig-asar/x64/libasar.dll +0 -0
- package/package.json +47 -0
- package/scripts/build-and-upload-artifacts.js +207 -0
- package/scripts/gen-webgpu-ffi.mjs +162 -0
- package/scripts/install-windows-deps.ps1 +80 -0
- package/scripts/package-release.js +237 -0
- package/scripts/push-version.js +84 -0
- package/scripts/update-bun-version.ts +122 -0
- package/scripts/update-cef-version.ts +145 -0
- package/src/browser/builtinrpcSchema.ts +19 -0
- package/src/browser/global.d.ts +36 -0
- package/src/browser/index.ts +234 -0
- package/src/browser/webviewtag.ts +88 -0
- package/src/browser/wgputag.ts +48 -0
- package/src/bun/SparkBunConfig.ts +497 -0
- package/src/bun/__tests__/ffi-contract.test.ts +105 -0
- package/src/bun/core/ApplicationMenu.ts +70 -0
- package/src/bun/core/BrowserView.ts +416 -0
- package/src/bun/core/BrowserWindow.ts +396 -0
- package/src/bun/core/BuildConfig.ts +71 -0
- package/src/bun/core/ContextMenu.ts +75 -0
- package/src/bun/core/GpuWindow.ts +289 -0
- package/src/bun/core/Paths.ts +5 -0
- package/src/bun/core/Socket.ts +22 -0
- package/src/bun/core/Tray.ts +197 -0
- package/src/bun/core/Updater.ts +1131 -0
- package/src/bun/core/Utils.ts +487 -0
- package/src/bun/core/WGPUView.ts +167 -0
- package/src/bun/core/menuRoles.ts +181 -0
- package/src/bun/events/ApplicationEvents.ts +22 -0
- package/src/bun/events/event.ts +27 -0
- package/src/bun/events/eventEmitter.ts +45 -0
- package/src/bun/events/trayEvents.ts +11 -0
- package/src/bun/events/webviewEvents.ts +39 -0
- package/src/bun/events/windowEvents.ts +23 -0
- package/src/bun/index.ts +120 -0
- package/src/bun/preload/.generated/compiled.ts +2 -0
- package/src/bun/preload/build.ts +65 -0
- package/src/bun/preload/dragRegions.ts +41 -0
- package/src/bun/preload/encryption.ts +86 -0
- package/src/bun/preload/events.ts +171 -0
- package/src/bun/preload/globals.d.ts +45 -0
- package/src/bun/preload/index-sandboxed.ts +28 -0
- package/src/bun/preload/index.ts +77 -0
- package/src/bun/preload/internalRpc.ts +80 -0
- package/src/bun/preload/overlaySync.ts +107 -0
- package/src/bun/preload/webviewTag.ts +451 -0
- package/src/bun/preload/wgpuTag.ts +246 -0
- package/src/bun/proc/linux.md +43 -0
- package/src/bun/proc/native.ts +3253 -0
- package/src/bun/webGPU.ts +346 -0
- package/src/bun/webgpuAdapter.ts +3011 -0
- package/src/cli/bun.lockb +0 -0
- package/src/cli/index.ts +4653 -0
- package/src/cli/package-lock.json +81 -0
- package/src/cli/package.json +11 -0
- package/src/cli/templates/embedded.ts +2 -0
- package/src/core/build.zig +16 -0
- package/src/core/main.zig +3378 -0
- package/src/extractor/build.zig +22 -0
- package/src/installer/installer-template.ts +216 -0
- package/src/launcher/main.ts +221 -0
- package/src/native/build/libNativeWrapper.so +0 -0
- package/src/native/linux/build/nativeWrapper.o +0 -0
- package/src/native/linux/cef_loader.cpp +110 -0
- package/src/native/linux/cef_loader.h +28 -0
- package/src/native/linux/cef_process_helper_linux.cpp +160 -0
- package/src/native/linux/nativeWrapper.cpp +11768 -0
- package/src/native/macos/cef_process_helper_mac.cc +160 -0
- package/src/native/macos/nativeWrapper.mm +9172 -0
- package/src/native/shared/accelerator_parser.h +72 -0
- package/src/native/shared/app_paths.h +110 -0
- package/src/native/shared/asar.h +35 -0
- package/src/native/shared/cache_migration.h +244 -0
- package/src/native/shared/callbacks.h +57 -0
- package/src/native/shared/cef_response_filter.h +189 -0
- package/src/native/shared/chromium_flags.h +181 -0
- package/src/native/shared/config.h +66 -0
- package/src/native/shared/download_event.h +197 -0
- package/src/native/shared/ffi_helpers.h +139 -0
- package/src/native/shared/glob_match.h +59 -0
- package/src/native/shared/json_menu_parser.h +223 -0
- package/src/native/shared/mime_types.h +101 -0
- package/src/native/shared/navigation_rules.h +98 -0
- package/src/native/shared/partition_context.h +137 -0
- package/src/native/shared/pending_resize_queue.h +45 -0
- package/src/native/shared/permissions.h +118 -0
- package/src/native/shared/permissions_cef.h +74 -0
- package/src/native/shared/preload_script.h +71 -0
- package/src/native/shared/shutdown_guard.h +134 -0
- package/src/native/shared/thread_safe_map.h +138 -0
- package/src/native/shared/webview_storage.h +91 -0
- package/src/native/win/cef_process_helper_win.cpp +143 -0
- package/src/native/win/dcomp_compositor.h +352 -0
- package/src/native/win/nativeWrapper.cpp +12434 -0
- package/src/npmbin/index.js +34 -0
- package/src/shared/bun-version.ts +3 -0
- package/src/shared/cef-version.ts +5 -0
- package/src/shared/naming.test.ts +327 -0
- package/src/shared/naming.ts +188 -0
- package/src/shared/platform.ts +48 -0
- package/src/shared/rpc.ts +541 -0
- package/src/shared/sparkbun-version.ts +2 -0
- package/src/types/three.d.ts +1 -0
- package/tsconfig.json +31 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// partition_context.h - Cross-platform CEF partition (CefRequestContext) management.
|
|
2
|
+
//
|
|
3
|
+
// Consumers must already have included CEF headers that define
|
|
4
|
+
// CefRequestContext / CefRequestContextSettings / CefSchemeHandlerFactory
|
|
5
|
+
// (e.g. include/cef_app.h, which pulls them in transitively).
|
|
6
|
+
//
|
|
7
|
+
// Behavior (consistent across macOS, Linux, Windows):
|
|
8
|
+
// - partition == "" → fresh ephemeral CefRequestContext per call.
|
|
9
|
+
// We deliberately do NOT route the default partition to GetGlobalContext():
|
|
10
|
+
// under Chrome runtime, attaching multiple browsers to the global context in
|
|
11
|
+
// rapid succession races with Chrome's window-framework code and intermittently
|
|
12
|
+
// opens the browser in its own top-level chrome window (with URL bar) instead
|
|
13
|
+
// of honoring the embedder's SetAsChild. A per-webview ephemeral context
|
|
14
|
+
// sidesteps that race and matches the pre-consolidation Linux/Windows behavior.
|
|
15
|
+
// - partition starts with "persist:" → cached per identifier; first call creates
|
|
16
|
+
// a new CefRequestContext with a custom cache_path on disk, subsequent calls
|
|
17
|
+
// for the same identifier reuse it. Caching is required because CEF's Chrome
|
|
18
|
+
// runtime refuses to bind two CefRequestContexts to the same on-disk profile,
|
|
19
|
+
// and prior code crashed when a second webview tried to share a partition.
|
|
20
|
+
// - any other partition (e.g. "temp:foo") → fresh ephemeral in-memory context
|
|
21
|
+
// per call, never cached. Closing and reopening a webview that uses
|
|
22
|
+
// "temp:foo" therefore yields fresh storage.
|
|
23
|
+
// - if CefRequestContext::CreateContext returns null (some Chrome runtime
|
|
24
|
+
// configurations refuse custom profile paths), we fall back to the global
|
|
25
|
+
// context for that partition with a warning. The webview still loads;
|
|
26
|
+
// isolation is lost for that one partition.
|
|
27
|
+
|
|
28
|
+
#ifndef ELECTROBUN_PARTITION_CONTEXT_H
|
|
29
|
+
#define ELECTROBUN_PARTITION_CONTEXT_H
|
|
30
|
+
|
|
31
|
+
#include <string>
|
|
32
|
+
#include <map>
|
|
33
|
+
#include <mutex>
|
|
34
|
+
#include <cstdio>
|
|
35
|
+
|
|
36
|
+
namespace electrobun {
|
|
37
|
+
|
|
38
|
+
// Platform-provided. Returns the absolute filesystem cache_path for a
|
|
39
|
+
// persistent partition with the given name (e.g. "test1"), creating any
|
|
40
|
+
// missing parent directories. Returning an empty string causes the caller
|
|
41
|
+
// to fall back to an ephemeral (in-memory) context for that webview.
|
|
42
|
+
std::string buildAndEnsurePartitionCachePath(const std::string& partitionName);
|
|
43
|
+
|
|
44
|
+
inline std::map<std::string, CefRefPtr<CefRequestContext>>& partitionContextMap_() {
|
|
45
|
+
static std::map<std::string, CefRefPtr<CefRequestContext>> map;
|
|
46
|
+
return map;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
inline std::mutex& partitionContextMutex_() {
|
|
50
|
+
static std::mutex m;
|
|
51
|
+
return m;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Returns the CefRequestContext to use for a webview with the given partition
|
|
55
|
+
// identifier. Registers `schemeFactory` on every returned context so views://
|
|
56
|
+
// resolves correctly regardless of which context handles the request.
|
|
57
|
+
inline CefRefPtr<CefRequestContext> getOrCreateRequestContextForPartition(
|
|
58
|
+
const char* partitionIdentifier,
|
|
59
|
+
uint32_t webviewId,
|
|
60
|
+
CefRefPtr<CefSchemeHandlerFactory> schemeFactory) {
|
|
61
|
+
|
|
62
|
+
std::string identifier(partitionIdentifier ? partitionIdentifier : "");
|
|
63
|
+
|
|
64
|
+
auto registerScheme = [&](CefRefPtr<CefRequestContext> ctx) {
|
|
65
|
+
if (ctx && schemeFactory) {
|
|
66
|
+
ctx->RegisterSchemeHandlerFactory("views", "", schemeFactory);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
bool isPersistent = identifier.size() >= 8 && identifier.compare(0, 8, "persist:") == 0;
|
|
71
|
+
|
|
72
|
+
// Default partition → fresh ephemeral context per webview.
|
|
73
|
+
// Avoids a Chrome-runtime race where multiple browsers attached to the
|
|
74
|
+
// global context can spawn their own top-level chrome window instead of
|
|
75
|
+
// embedding via SetAsChild. See behavior comment at top of file.
|
|
76
|
+
if (identifier.empty()) {
|
|
77
|
+
CefRequestContextSettings defaultSettings;
|
|
78
|
+
defaultSettings.persist_session_cookies = false;
|
|
79
|
+
CefRefPtr<CefRequestContext> ctx = CefRequestContext::CreateContext(defaultSettings, nullptr);
|
|
80
|
+
if (!ctx) {
|
|
81
|
+
fprintf(stderr, "[partition_context] WARNING: CreateContext returned null for default partition "
|
|
82
|
+
"— falling back to global context\n");
|
|
83
|
+
ctx = CefRequestContext::GetGlobalContext();
|
|
84
|
+
}
|
|
85
|
+
registerScheme(ctx);
|
|
86
|
+
return ctx;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Reuse cached context for persist:* partitions only.
|
|
90
|
+
if (isPersistent) {
|
|
91
|
+
std::lock_guard<std::mutex> lock(partitionContextMutex_());
|
|
92
|
+
auto& map = partitionContextMap_();
|
|
93
|
+
auto it = map.find(identifier);
|
|
94
|
+
if (it != map.end()) {
|
|
95
|
+
return it->second;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
CefRequestContextSettings settings;
|
|
100
|
+
if (isPersistent) {
|
|
101
|
+
std::string partitionName = identifier.substr(8);
|
|
102
|
+
std::string cachePathStr = buildAndEnsurePartitionCachePath(partitionName);
|
|
103
|
+
if (!cachePathStr.empty()) {
|
|
104
|
+
settings.persist_session_cookies = true;
|
|
105
|
+
CefString(&settings.cache_path).FromString(cachePathStr);
|
|
106
|
+
} else {
|
|
107
|
+
fprintf(stderr, "[partition_context] webview %u: failed to build cache path for '%s', "
|
|
108
|
+
"falling back to ephemeral\n",
|
|
109
|
+
webviewId, identifier.c_str());
|
|
110
|
+
settings.persist_session_cookies = false;
|
|
111
|
+
}
|
|
112
|
+
} else {
|
|
113
|
+
settings.persist_session_cookies = false;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
CefRefPtr<CefRequestContext> context = CefRequestContext::CreateContext(settings, nullptr);
|
|
117
|
+
|
|
118
|
+
if (!context) {
|
|
119
|
+
fprintf(stderr, "[partition_context] WARNING: CreateContext returned null for partition '%s' "
|
|
120
|
+
"— falling back to global context (this partition will NOT be isolated)\n",
|
|
121
|
+
identifier.c_str());
|
|
122
|
+
context = CefRequestContext::GetGlobalContext();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
registerScheme(context);
|
|
126
|
+
|
|
127
|
+
if (isPersistent) {
|
|
128
|
+
std::lock_guard<std::mutex> lock(partitionContextMutex_());
|
|
129
|
+
partitionContextMap_()[identifier] = context;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return context;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
} // namespace electrobun
|
|
136
|
+
|
|
137
|
+
#endif // ELECTROBUN_PARTITION_CONTEXT_H
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <algorithm>
|
|
4
|
+
#include <mutex>
|
|
5
|
+
#include <unordered_set>
|
|
6
|
+
#include <vector>
|
|
7
|
+
|
|
8
|
+
// Thread-safe queue that stores a set of dirty view pointers.
|
|
9
|
+
// Enqueue is idempotent per drain cycle.
|
|
10
|
+
class PendingResizeQueue {
|
|
11
|
+
public:
|
|
12
|
+
void enqueue(void* view) {
|
|
13
|
+
if (!view) return;
|
|
14
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
15
|
+
if (set_.insert(view).second) {
|
|
16
|
+
queue_.push_back(view);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
std::vector<void*> drain() {
|
|
21
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
22
|
+
std::vector<void*> out;
|
|
23
|
+
out.swap(queue_);
|
|
24
|
+
set_.clear();
|
|
25
|
+
return out;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
void remove(void* view) {
|
|
29
|
+
if (!view) return;
|
|
30
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
31
|
+
if (set_.erase(view)) {
|
|
32
|
+
queue_.erase(std::remove(queue_.begin(), queue_.end(), view), queue_.end());
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
bool empty() const {
|
|
37
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
38
|
+
return queue_.empty();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
private:
|
|
42
|
+
mutable std::mutex mutex_;
|
|
43
|
+
std::unordered_set<void*> set_;
|
|
44
|
+
std::vector<void*> queue_;
|
|
45
|
+
};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
// permissions.h - Cross-platform permission cache management
|
|
2
|
+
// Used for caching user media, geolocation, and notification permissions
|
|
3
|
+
// across Windows, macOS, and Linux
|
|
4
|
+
//
|
|
5
|
+
// This is a header-only implementation to avoid build complexity.
|
|
6
|
+
|
|
7
|
+
#ifndef ELECTROBUN_PERMISSIONS_H
|
|
8
|
+
#define ELECTROBUN_PERMISSIONS_H
|
|
9
|
+
|
|
10
|
+
#include <string>
|
|
11
|
+
#include <map>
|
|
12
|
+
#include <chrono>
|
|
13
|
+
#include <mutex>
|
|
14
|
+
#include <utility>
|
|
15
|
+
|
|
16
|
+
namespace electrobun {
|
|
17
|
+
|
|
18
|
+
enum class PermissionType {
|
|
19
|
+
USER_MEDIA,
|
|
20
|
+
GEOLOCATION,
|
|
21
|
+
NOTIFICATIONS,
|
|
22
|
+
OTHER
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
enum class PermissionStatus {
|
|
26
|
+
UNKNOWN,
|
|
27
|
+
ALLOWED,
|
|
28
|
+
DENIED
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
struct PermissionCacheEntry {
|
|
32
|
+
PermissionStatus status;
|
|
33
|
+
std::chrono::system_clock::time_point expiry;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// Thread-safe permission cache
|
|
37
|
+
class PermissionCache {
|
|
38
|
+
public:
|
|
39
|
+
static PermissionCache& getInstance() {
|
|
40
|
+
static PermissionCache instance;
|
|
41
|
+
return instance;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Extract origin from a URL (e.g., "https://example.com/path" -> "https://example.com")
|
|
45
|
+
static std::string getOriginFromUrl(const std::string& url) {
|
|
46
|
+
// For views:// scheme, use a constant origin since these are local files
|
|
47
|
+
if (url.find("views://") == 0) {
|
|
48
|
+
return "views://";
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// For other schemes, extract origin from URL
|
|
52
|
+
size_t protocolEnd = url.find("://");
|
|
53
|
+
if (protocolEnd == std::string::npos) return url;
|
|
54
|
+
|
|
55
|
+
size_t domainStart = protocolEnd + 3;
|
|
56
|
+
size_t pathStart = url.find('/', domainStart);
|
|
57
|
+
|
|
58
|
+
if (pathStart == std::string::npos) {
|
|
59
|
+
return url;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return url.substr(0, pathStart);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
PermissionStatus get(const std::string& origin, PermissionType type) {
|
|
66
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
67
|
+
auto key = std::make_pair(origin, type);
|
|
68
|
+
auto it = cache_.find(key);
|
|
69
|
+
|
|
70
|
+
if (it != cache_.end()) {
|
|
71
|
+
// Check if permission hasn't expired
|
|
72
|
+
auto now = std::chrono::system_clock::now();
|
|
73
|
+
if (now < it->second.expiry) {
|
|
74
|
+
return it->second.status;
|
|
75
|
+
} else {
|
|
76
|
+
// Permission expired, remove from cache
|
|
77
|
+
cache_.erase(it);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return PermissionStatus::UNKNOWN;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
void set(const std::string& origin, PermissionType type, PermissionStatus status) {
|
|
85
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
86
|
+
auto key = std::make_pair(origin, type);
|
|
87
|
+
|
|
88
|
+
// Cache permission for 24 hours
|
|
89
|
+
auto expiry = std::chrono::system_clock::now() + std::chrono::hours(24);
|
|
90
|
+
|
|
91
|
+
cache_[key] = {status, expiry};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
private:
|
|
95
|
+
PermissionCache() = default;
|
|
96
|
+
PermissionCache(const PermissionCache&) = delete;
|
|
97
|
+
PermissionCache& operator=(const PermissionCache&) = delete;
|
|
98
|
+
|
|
99
|
+
std::map<std::pair<std::string, PermissionType>, PermissionCacheEntry> cache_;
|
|
100
|
+
std::mutex mutex_;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
// Convenience functions that use the singleton (for easier migration from existing code)
|
|
104
|
+
inline std::string getOriginFromUrl(const std::string& url) {
|
|
105
|
+
return PermissionCache::getOriginFromUrl(url);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
inline PermissionStatus getPermissionFromCache(const std::string& origin, PermissionType type) {
|
|
109
|
+
return PermissionCache::getInstance().get(origin, type);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
inline void cachePermission(const std::string& origin, PermissionType type, PermissionStatus status) {
|
|
113
|
+
PermissionCache::getInstance().set(origin, type, status);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
} // namespace electrobun
|
|
117
|
+
|
|
118
|
+
#endif // ELECTROBUN_PERMISSIONS_H
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// permissions_cef.h - CEF-aware permission helpers
|
|
2
|
+
// Must be included AFTER include/cef_app.h (or any header that pulls in
|
|
3
|
+
// internal/cef_types.h) so the CEF_PERMISSION_TYPE_* constants are available.
|
|
4
|
+
|
|
5
|
+
#ifndef ELECTROBUN_PERMISSIONS_CEF_H
|
|
6
|
+
#define ELECTROBUN_PERMISSIONS_CEF_H
|
|
7
|
+
|
|
8
|
+
// Consumers must already have included CEF headers that define
|
|
9
|
+
// CEF_PERMISSION_TYPE_* (e.g. include/cef_app.h, which pulls in
|
|
10
|
+
// include/internal/cef_types.h transitively).
|
|
11
|
+
|
|
12
|
+
#include <string>
|
|
13
|
+
#include <cstdint>
|
|
14
|
+
#include <cstdio>
|
|
15
|
+
|
|
16
|
+
namespace electrobun {
|
|
17
|
+
|
|
18
|
+
// Decode a CEF permission bitmask (as passed to OnShowPermissionPrompt) into a
|
|
19
|
+
// human-readable comma-separated list of permission names. Used to build
|
|
20
|
+
// informative dialogs for permission types we don't have a dedicated UI for.
|
|
21
|
+
inline std::string describeCefPermissions(uint32_t mask) {
|
|
22
|
+
std::string out;
|
|
23
|
+
auto add = [&](const char* name) {
|
|
24
|
+
if (!out.empty()) out += ", ";
|
|
25
|
+
out += name;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
if (mask & CEF_PERMISSION_TYPE_AR_SESSION) add("AR session");
|
|
29
|
+
if (mask & CEF_PERMISSION_TYPE_CAMERA_PAN_TILT_ZOOM) add("Camera pan/tilt/zoom");
|
|
30
|
+
if (mask & CEF_PERMISSION_TYPE_CAMERA_STREAM) add("Camera");
|
|
31
|
+
if (mask & CEF_PERMISSION_TYPE_CAPTURED_SURFACE_CONTROL) add("Captured surface control");
|
|
32
|
+
if (mask & CEF_PERMISSION_TYPE_CLIPBOARD) add("Clipboard");
|
|
33
|
+
if (mask & CEF_PERMISSION_TYPE_TOP_LEVEL_STORAGE_ACCESS) add("Top-level storage access");
|
|
34
|
+
if (mask & CEF_PERMISSION_TYPE_DISK_QUOTA) add("Disk quota");
|
|
35
|
+
if (mask & CEF_PERMISSION_TYPE_LOCAL_FONTS) add("Local fonts");
|
|
36
|
+
if (mask & CEF_PERMISSION_TYPE_GEOLOCATION) add("Location");
|
|
37
|
+
if (mask & CEF_PERMISSION_TYPE_HAND_TRACKING) add("Hand tracking");
|
|
38
|
+
if (mask & CEF_PERMISSION_TYPE_IDENTITY_PROVIDER) add("Identity provider");
|
|
39
|
+
if (mask & CEF_PERMISSION_TYPE_IDLE_DETECTION) add("Idle detection");
|
|
40
|
+
if (mask & CEF_PERMISSION_TYPE_MIC_STREAM) add("Microphone");
|
|
41
|
+
if (mask & CEF_PERMISSION_TYPE_MIDI_SYSEX) add("MIDI system-exclusive");
|
|
42
|
+
if (mask & CEF_PERMISSION_TYPE_MULTIPLE_DOWNLOADS) add("Multiple downloads");
|
|
43
|
+
if (mask & CEF_PERMISSION_TYPE_NOTIFICATIONS) add("Notifications");
|
|
44
|
+
if (mask & CEF_PERMISSION_TYPE_KEYBOARD_LOCK) add("Keyboard lock");
|
|
45
|
+
if (mask & CEF_PERMISSION_TYPE_POINTER_LOCK) add("Pointer lock");
|
|
46
|
+
if (mask & CEF_PERMISSION_TYPE_PROTECTED_MEDIA_IDENTIFIER) add("Protected media identifier");
|
|
47
|
+
if (mask & CEF_PERMISSION_TYPE_REGISTER_PROTOCOL_HANDLER) add("Register protocol handler");
|
|
48
|
+
if (mask & CEF_PERMISSION_TYPE_STORAGE_ACCESS) add("Storage access");
|
|
49
|
+
if (mask & CEF_PERMISSION_TYPE_VR_SESSION) add("VR session");
|
|
50
|
+
if (mask & CEF_PERMISSION_TYPE_WEB_APP_INSTALLATION) add("Web app installation");
|
|
51
|
+
if (mask & CEF_PERMISSION_TYPE_WINDOW_MANAGEMENT) add("Window management");
|
|
52
|
+
if (mask & CEF_PERMISSION_TYPE_FILE_SYSTEM_ACCESS) add("File system access");
|
|
53
|
+
#if CEF_API_ADDED(13600)
|
|
54
|
+
if (mask & CEF_PERMISSION_TYPE_LOCAL_NETWORK_ACCESS) add("Local network access");
|
|
55
|
+
#endif
|
|
56
|
+
#if CEF_API_ADDED(14500)
|
|
57
|
+
if (mask & CEF_PERMISSION_TYPE_LOCAL_NETWORK) add("Local network");
|
|
58
|
+
if (mask & CEF_PERMISSION_TYPE_LOOPBACK_NETWORK) add("Loopback network");
|
|
59
|
+
#endif
|
|
60
|
+
#if CEF_API_ADDED(14700)
|
|
61
|
+
if (mask & CEF_PERMISSION_TYPE_SENSORS) add("Sensors");
|
|
62
|
+
#endif
|
|
63
|
+
|
|
64
|
+
if (out.empty()) {
|
|
65
|
+
char buf[64];
|
|
66
|
+
snprintf(buf, sizeof(buf), "unknown (bitmask 0x%x)", mask);
|
|
67
|
+
out = buf;
|
|
68
|
+
}
|
|
69
|
+
return out;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
} // namespace electrobun
|
|
73
|
+
|
|
74
|
+
#endif // ELECTROBUN_PERMISSIONS_CEF_H
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// preload_script.h - Cross-platform preload script management
|
|
2
|
+
// Used for storing and managing preload scripts across Windows, macOS, and Linux
|
|
3
|
+
//
|
|
4
|
+
// This is a header-only implementation to avoid build complexity.
|
|
5
|
+
|
|
6
|
+
#ifndef ELECTROBUN_PRELOAD_SCRIPT_H
|
|
7
|
+
#define ELECTROBUN_PRELOAD_SCRIPT_H
|
|
8
|
+
|
|
9
|
+
#include <string>
|
|
10
|
+
#include <map>
|
|
11
|
+
#include <mutex>
|
|
12
|
+
|
|
13
|
+
namespace electrobun {
|
|
14
|
+
|
|
15
|
+
// Represents a preload script to be injected into webviews
|
|
16
|
+
struct PreloadScript {
|
|
17
|
+
std::string code;
|
|
18
|
+
bool mainFrameOnly = true;
|
|
19
|
+
|
|
20
|
+
PreloadScript() = default;
|
|
21
|
+
|
|
22
|
+
PreloadScript(const std::string& scriptCode, bool mainOnly = true)
|
|
23
|
+
: code(scriptCode), mainFrameOnly(mainOnly) {}
|
|
24
|
+
|
|
25
|
+
bool empty() const { return code.empty(); }
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// Thread-safe storage for preload scripts by browser/webview ID
|
|
29
|
+
class PreloadScriptStorage {
|
|
30
|
+
public:
|
|
31
|
+
static PreloadScriptStorage& getInstance() {
|
|
32
|
+
static PreloadScriptStorage instance;
|
|
33
|
+
return instance;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
void set(int browserId, const std::string& script) {
|
|
37
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
38
|
+
scripts_[browserId] = script;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
std::string get(int browserId) const {
|
|
42
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
43
|
+
auto it = scripts_.find(browserId);
|
|
44
|
+
if (it != scripts_.end()) {
|
|
45
|
+
return it->second;
|
|
46
|
+
}
|
|
47
|
+
return "";
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
void remove(int browserId) {
|
|
51
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
52
|
+
scripts_.erase(browserId);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
void clear() {
|
|
56
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
57
|
+
scripts_.clear();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
private:
|
|
61
|
+
PreloadScriptStorage() = default;
|
|
62
|
+
PreloadScriptStorage(const PreloadScriptStorage&) = delete;
|
|
63
|
+
PreloadScriptStorage& operator=(const PreloadScriptStorage&) = delete;
|
|
64
|
+
|
|
65
|
+
mutable std::mutex mutex_;
|
|
66
|
+
std::map<int, std::string> scripts_;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
} // namespace electrobun
|
|
70
|
+
|
|
71
|
+
#endif // ELECTROBUN_PRELOAD_SCRIPT_H
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
// shutdown_guard.h - Cross-platform shutdown state management
|
|
2
|
+
// Prevents race conditions during application cleanup
|
|
3
|
+
// Used across Windows, macOS, and Linux
|
|
4
|
+
//
|
|
5
|
+
// This is a header-only implementation to avoid build complexity.
|
|
6
|
+
|
|
7
|
+
#ifndef ELECTROBUN_SHUTDOWN_GUARD_H
|
|
8
|
+
#define ELECTROBUN_SHUTDOWN_GUARD_H
|
|
9
|
+
|
|
10
|
+
#include <atomic>
|
|
11
|
+
#include <mutex>
|
|
12
|
+
#include <thread>
|
|
13
|
+
#include <chrono>
|
|
14
|
+
|
|
15
|
+
namespace electrobun {
|
|
16
|
+
|
|
17
|
+
// Singleton for managing global shutdown state
|
|
18
|
+
// Allows code to check if shutdown is in progress and avoid race conditions
|
|
19
|
+
class ShutdownManager {
|
|
20
|
+
public:
|
|
21
|
+
static ShutdownManager& getInstance() {
|
|
22
|
+
static ShutdownManager instance;
|
|
23
|
+
return instance;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Signal that shutdown has begun
|
|
27
|
+
void initiateShutdown() {
|
|
28
|
+
shuttingDown_.store(true);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Check if shutdown is in progress
|
|
32
|
+
bool isShuttingDown() const {
|
|
33
|
+
return shuttingDown_.load();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Reset shutdown state (use with caution, mainly for testing)
|
|
37
|
+
void reset() {
|
|
38
|
+
shuttingDown_.store(false);
|
|
39
|
+
activeOperations_.store(0);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Increment active operations counter
|
|
43
|
+
void beginOperation() {
|
|
44
|
+
activeOperations_.fetch_add(1);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Decrement active operations counter
|
|
48
|
+
void endOperation() {
|
|
49
|
+
activeOperations_.fetch_sub(1);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Get count of active operations
|
|
53
|
+
int getActiveOperations() const {
|
|
54
|
+
return activeOperations_.load();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Wait for all operations to complete (with timeout)
|
|
58
|
+
// Returns true if all operations completed, false if timeout
|
|
59
|
+
bool waitForOperationsToComplete(int maxWaitMs = 5000) {
|
|
60
|
+
int waited = 0;
|
|
61
|
+
const int sleepMs = 10;
|
|
62
|
+
while (activeOperations_.load() > 0 && waited < maxWaitMs) {
|
|
63
|
+
std::this_thread::sleep_for(std::chrono::milliseconds(sleepMs));
|
|
64
|
+
waited += sleepMs;
|
|
65
|
+
}
|
|
66
|
+
return activeOperations_.load() == 0;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
private:
|
|
70
|
+
ShutdownManager() : shuttingDown_(false), activeOperations_(0) {}
|
|
71
|
+
ShutdownManager(const ShutdownManager&) = delete;
|
|
72
|
+
ShutdownManager& operator=(const ShutdownManager&) = delete;
|
|
73
|
+
|
|
74
|
+
std::atomic<bool> shuttingDown_;
|
|
75
|
+
std::atomic<int> activeOperations_;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// RAII guard for operations that shouldn't run during shutdown
|
|
79
|
+
// Automatically checks shutdown state and tracks operation lifetime
|
|
80
|
+
class OperationGuard {
|
|
81
|
+
public:
|
|
82
|
+
OperationGuard() : valid_(!ShutdownManager::getInstance().isShuttingDown()) {
|
|
83
|
+
if (valid_) {
|
|
84
|
+
ShutdownManager::getInstance().beginOperation();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
~OperationGuard() {
|
|
89
|
+
if (valid_) {
|
|
90
|
+
ShutdownManager::getInstance().endOperation();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Check if the operation is valid (not during shutdown)
|
|
95
|
+
bool isValid() const { return valid_; }
|
|
96
|
+
|
|
97
|
+
// Implicit conversion to bool for easy use in if statements
|
|
98
|
+
explicit operator bool() const { return valid_; }
|
|
99
|
+
|
|
100
|
+
// Non-copyable, non-movable
|
|
101
|
+
OperationGuard(const OperationGuard&) = delete;
|
|
102
|
+
OperationGuard& operator=(const OperationGuard&) = delete;
|
|
103
|
+
OperationGuard(OperationGuard&&) = delete;
|
|
104
|
+
OperationGuard& operator=(OperationGuard&&) = delete;
|
|
105
|
+
|
|
106
|
+
private:
|
|
107
|
+
bool valid_;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// Lightweight guard that only checks shutdown state without tracking
|
|
111
|
+
// Use when you don't need operation tracking, just shutdown checking
|
|
112
|
+
class ShutdownCheckGuard {
|
|
113
|
+
public:
|
|
114
|
+
ShutdownCheckGuard() : valid_(!ShutdownManager::getInstance().isShuttingDown()) {}
|
|
115
|
+
|
|
116
|
+
bool isValid() const { return valid_; }
|
|
117
|
+
explicit operator bool() const { return valid_; }
|
|
118
|
+
|
|
119
|
+
private:
|
|
120
|
+
bool valid_;
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
// Convenience functions
|
|
124
|
+
inline bool isShuttingDown() {
|
|
125
|
+
return ShutdownManager::getInstance().isShuttingDown();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
inline void initiateShutdown() {
|
|
129
|
+
ShutdownManager::getInstance().initiateShutdown();
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
} // namespace electrobun
|
|
133
|
+
|
|
134
|
+
#endif // ELECTROBUN_SHUTDOWN_GUARD_H
|