diff --git a/api/CONFIG.md b/api/CONFIG.md
index 03935e112d..4dd4686f16 100644
--- a/api/CONFIG.md
+++ b/api/CONFIG.md
@@ -56,6 +56,22 @@ sources | |
Key | Default Value | Description
:--- | :--- | :---
root | "/" | Make root open index.html
+default_index | "" | Set default 'index.html' path to open for implicit routes
+
+# Section `permissions`
+
+Key | Default Value | Description
+:--- | :--- | :---
+allow_fullscreen | true | Allow/Disallow fullscreen in application
+allow_microphone | true | Allow/Disallow microphone in application
+allow_camera | true | Allow/Disallow camera in application
+allow_user_media | true | Allow/Disallow user media (microphone + camera) in application
+allow_geolocation | true | Allow/Disallow geolocation in application
+allow_notifications | true | Allow/Disallow notifications in application
+allow_sensors | true | Allow/Disallow sensors in application
+allow_clipboard | true | Allow/Disallow clipboard in application
+allow_bluetooth | true | Allow/Disallow bluetooth in application
+allow_data_access | true | Allow/Disallow data access in application
watch | false | Enable watch mode
# Section `debug`
diff --git a/src/android/runtime.cc b/src/android/runtime.cc
index 0a8f99806a..b038f82f90 100644
--- a/src/android/runtime.cc
+++ b/src/android/runtime.cc
@@ -12,6 +12,18 @@ namespace SSC::android {
this->rootDirectory = rootDirectory;
}
+ bool isPermissionAllowed (const String& name) {
+ static const auto config = SSC::getUserConfig();
+ const auto permission = String("permissions_allow_") + name;
+
+ // `true` by default
+ if (!config.contains(permission)) {
+ return true;
+ }
+
+ return config.at(permission) != "false";
+ }
+
Runtime::~Runtime () {
this->env->DeleteGlobalRef(this->self);
}
@@ -149,4 +161,20 @@ extern "C" {
return true;
}
+
+ jboolean external(Runtime, isPermissionAllowed)(
+ JNIEnv *env,
+ jobject self,
+ jstring permission
+ ) {
+ auto runtime = Runtime::from(env, self);
+ auto name = StringWrap(env, rootDirectory).str();
+
+ if (runtime == nullptr) {
+ Throw(env, RuntimeNotInitializedException);
+ return false;
+ }
+
+ return runtime->isPermissionAllowed(name);
+ }
}
diff --git a/src/android/runtime.kt b/src/android/runtime.kt
index a584a995dc..121b1cdb85 100644
--- a/src/android/runtime.kt
+++ b/src/android/runtime.kt
@@ -78,4 +78,7 @@ open class Runtime (
@Throws(java.lang.Exception::class)
external fun stopTimers (): Boolean;
+
+ @Throws(java.lang.Exception::class)
+ external fun isPermissionAllowed (permission: String): Long;
}
diff --git a/src/android/window.kt b/src/android/window.kt
index a5b7e917bf..f7a9678c00 100644
--- a/src/android/window.kt
+++ b/src/android/window.kt
@@ -25,6 +25,7 @@ open class Window (runtime: Runtime, activity: MainActivity) {
}
fun load () {
+ val runtime = this.runtime.get() ?: return
val isDebugEnabled = this.runtime.get()?.isDebugEnabled() ?: false
val filename = this.getPathToFileToLoad()
val activity = this.activity.get() ?: return
@@ -47,7 +48,13 @@ open class Window (runtime: Runtime, activity: MainActivity) {
activity.webview?.apply {
// features
settings.javaScriptEnabled = true
- settings.domStorageEnabled = true
+
+ settings.domStorageEnabled = runtime.isPermissionAllowed("data_access")
+ settings.databaseEnabled = runtime.isPermissionAllowed("data_access")
+
+ settings.geolocationEnabled = runtime.isPermissionAllowed("geolocation")
+ settings.appCacheEnabled = true
+ settings.javaScriptCanOpenWindowsAutomatically = true
// allow list
settings.allowFileAccess = true
diff --git a/src/cli/cli.cc b/src/cli/cli.cc
index e04471fd5d..5fc9a0e6ee 100644
--- a/src/cli/cli.cc
+++ b/src/cli/cli.cc
@@ -2683,6 +2683,7 @@ int main (const int argc, const char* argv[]) {
flags = "-std=c++2a -ObjC++ -v";
flags += " -framework UniformTypeIdentifiers";
flags += " -framework CoreBluetooth";
+ flags += " -framework CoreLocation";
flags += " -framework Network";
flags += " -framework UserNotifications";
flags += " -framework WebKit";
@@ -2940,6 +2941,21 @@ int main (const int argc, const char* argv[]) {
manifestContext["android_manifest_xml_permissions"] = "";
+ if (settings["permission_allow_geolocation"] != "false") {
+ manifestContext["android_manifest_xml_permissions"] += "\n";
+ manifestContext["android_manifest_xml_permissions"] += "\n";
+ }
+
+ if (settings["permission_allow_user_media"] != "false") {
+ if (settings["permission_allow_camera"] != "false") {
+ manifestContext["android_manifest_xml_permissions"] += "\n";
+ }
+
+ if (settings["permission_allow_microphone"] != "false") {
+ manifestContext["android_manifest_xml_permissions"] += "\n";
+ }
+ }
+
if (settings["android_manifest_permissions"].size() > 0) {
settings["android_manifest_permissions"] = replace(settings["android_manifest_permissions"], ",", " ");
for (auto const &value: parseStringList(settings["android_manifest_permissions"])) {
@@ -3749,6 +3765,7 @@ int main (const int argc, const char* argv[]) {
settings["build_extensions_" + extension + "_ios_compiler_flags"] +
" -framework UniformTypeIdentifiers" +
" -framework CoreBluetooth" +
+ " -framework CoreLocation" +
" -framework Network" +
" -framework UserNotifications" +
" -framework WebKit" +
@@ -3872,6 +3889,7 @@ int main (const int argc, const char* argv[]) {
<< " -F " << iosSdkPath << "/System/Library/Frameworks/"
<< " -framework UniformTypeIdentifiers"
<< " -framework CoreBluetooth"
+ << " -framework CoreLocation"
<< " -framework Foundation"
<< " -framework Network"
<< " -framework UserNotifications"
@@ -4643,6 +4661,7 @@ int main (const int argc, const char* argv[]) {
if (platform.mac) {
compilerFlags += " -framework UniformTypeIdentifiers";
compilerFlags += " -framework CoreBluetooth";
+ compilerFlags += " -framework CoreLocation";
compilerFlags += " -framework Network";
compilerFlags += " -framework UserNotifications";
compilerFlags += " -framework WebKit";
@@ -5476,20 +5495,29 @@ int main (const int argc, const char* argv[]) {
}
}
- FileSystemWatcher* watcher = new FileSystemWatcher(sources);
- auto watching = watcher->start([&](
+ // allow changes to 'socket.ini' to be observed
+ sources.push_back("socket.ini");
+
+ FileSystemWatcher* sourcesWatcher = new FileSystemWatcher(sources);
+ auto watchingSources = sourcesWatcher->start([=](
const String& path,
const Vector& events,
const FileSystemWatcher::Context& context
) {
+ auto settingsForSourcesWatcher = settings;
+ extendMap(
+ settingsForSourcesWatcher,
+ parseINI(readFile(targetPath / "socket.ini"))
+ );
+
handleBuildPhaseForCopyMappedFiles(
- settings,
+ settingsForSourcesWatcher,
targetPlatform,
pathResourcesRelativeToUserBuild
);
handleBuildPhaseForUserScript(
- settings,
+ settingsForSourcesWatcher,
targetPlatform,
pathResourcesRelativeToUserBuild,
targetPath,
@@ -5497,10 +5525,10 @@ int main (const int argc, const char* argv[]) {
false
);
- log("file '" + path +"' did change");
+ log("File '" + path + "' did change");
});
- if (!watching) {
+ if (!watchingSources) {
log("Unable to start watching");
exit(1);
}
diff --git a/src/cli/templates.hh b/src/cli/templates.hh
index 7c5a82cebb..31cf9f21d8 100644
--- a/src/cli/templates.hh
+++ b/src/cli/templates.hh
@@ -193,6 +193,16 @@ constexpr auto gPListInfo = R"XML(
This app needs access to the camera
NSBluetoothAlwaysUsageDescription
The app would like to discover and connect to peers
+
+ NSLocationUsageDescription
+ {{meta_title}} would like access to your location
+
+ NSLocationWhenInUseUsageDescription
+ {{meta_title}} would like access to your location while open
+
+ NSLocationAlwaysAndWhenInUseUsageDescription
+ {{meta_title}} would like access to your location
+
NSMainNibFile
MainMenu
LSMultipleInstancesProhibited
@@ -317,7 +327,9 @@ constexpr auto gAndroidManifest = R"XML(
>
+ android:exported="true"
+ android:configChanges="orientation|screenSize"
+ >
@@ -974,12 +986,21 @@ constexpr auto gXCodePlist = R"XML(
NSHighResolutionCapable
+
NSLocalNetworkUsageDescription
- The app would like to discover and connect to peers
+ {{meta_title}} would like to discover and connect to peers
NSBluetoothAlwaysUsageDescription
- The app would like to discover and connect to peers
+ {{meta_title}} would like to discover and connect to peers
NSBluetoothPeripheralUsageDescription
- The app would like to discover and connect to peers
+ {{meta_title}} would like to discover and connect to peers
+
+ NSLocationUsageDescription
+ {{meta_title}} would like access to your location
+ NSLocationWhenInUseUsageDescription
+ {{meta_title}} would like access to your location while open
+ NSLocationAlwaysAndWhenInUseUsageDescription
+ {{meta_title}} would like access to your location
+
NSRequiresAquaSystemAppearance
NSSupportsAutomaticGraphicsSwitching
@@ -1426,6 +1447,51 @@ sources = "src"
; default value: "/"
root = "/"
+; Set default 'index.html' path to open for implicit routes
+; default value: ""
+; default_index = ""
+
+[permissions]
+; Allow/Disallow fullscreen in application
+; default value: true
+; allow_fullscreen = true
+
+; Allow/Disallow microphone in application
+; default value: true
+; allow_microphone = true
+
+; Allow/Disallow camera in application
+; default value: true
+; allow_camera = true
+
+; Allow/Disallow user media (microphone + camera) in application
+; default value: true
+; allow_user_media = true
+
+; Allow/Disallow geolocation in application
+; default value: true
+; allow_geolocation = true
+
+; Allow/Disallow notifications in application
+; default value: true
+; allow_notifications = true
+
+; Allow/Disallow sensors in application
+; default value: true
+; allow_sensors = true
+
+; Allow/Disallow clipboard in application
+; default value: true
+; allow_clipboard = true
+
+; Allow/Disallow bluetooth in application
+; default value: true
+; allow_bluetooth = true
+
+; Allow/Disallow data access in application
+; default value: true
+; allow_data_access = true
+
; Enable watch mode
; default value: false
watch = false
diff --git a/src/common.hh b/src/common.hh
index a3275f8c6c..d16f149350 100644
--- a/src/common.hh
+++ b/src/common.hh
@@ -369,8 +369,12 @@ namespace SSC {
const String& separator
) {
StringStream joined;
+ auto missing = vector.size();
for (const auto& item : vector) {
- joined << item << separator << " ";
+ joined << item;
+ if (--missing > 0) {
+ joined << separator << " ";
+ }
}
return trim(joined.str());
diff --git a/src/window/apple.mm b/src/window/apple.mm
index 3c7e8660fe..a6bae6c171 100644
--- a/src/window/apple.mm
+++ b/src/window/apple.mm
@@ -50,6 +50,145 @@ @implementation SSCBridgedWebView
int lastX = 0;
int lastY = 0;
+- (void) webView: (WKWebView*) webView
+ requestDeviceOrientationAndMotionPermissionForOrigin: (WKSecurityOrigin*) origin
+ initiatedByFrame: (WKFrameInfo*) frame
+ decisionHandler: (void (^)(WKPermissionDecision decision)) decisionHandler {
+ static auto userConfig = SSC::getUserConfig();
+
+ if (userConfig["permissions_allow_device_orientation"] == "false") {
+ decisionHandler(WKPermissionDecisionDeny);
+ return;
+ }
+
+ decisionHandler(WKPermissionDecisionGrant);
+}
+
+- (void) webView: (WKWebView*) webView
+ requestMediaCapturePermissionForOrigin: (WKSecurityOrigin*) origin
+ initiatedByFrame: (WKFrameInfo*) frame
+ type: (WKMediaCaptureType) type
+ decisionHandler: (void (^)(WKPermissionDecision decision)) decisionHandler {
+ static auto userConfig = SSC::getUserConfig();
+
+ if (userConfig["permissions_allow_user_media"] == "false") {
+ decisionHandler(WKPermissionDecisionDeny);
+ return;
+ }
+
+ if (type == WKMediaCaptureTypeCameraAndMicrophone) {
+ if (
+ userConfig["permissions_allow_camera"] == "false" ||
+ userConfig["permissions_allow_microphone"] == "false"
+ ) {
+ decisionHandler(WKPermissionDecisionDeny);
+ return;
+ }
+ }
+
+ if (
+ type == WKMediaCaptureTypeCamera &&
+ userConfig["permissions_allow_camera"] == "false"
+ ) {
+ decisionHandler(WKPermissionDecisionDeny);
+ return;
+ }
+
+ if (
+ type == WKMediaCaptureTypeMicrophone &&
+ userConfig["permissions_allow_microphone"] == "false"
+ ) {
+ decisionHandler(WKPermissionDecisionDeny);
+ return;
+ }
+
+ decisionHandler(WKPermissionDecisionGrant);
+}
+
+- (void) _webView: (WKWebView*) webView
+ requestGeolocationPermissionForOrigin: (WKSecurityOrigin*) origin
+ initiatedByFrame: (WKFrameInfo*) frame
+ decisionHandler: (void (^)(WKPermissionDecision decision)) decisionHandler {
+ decisionHandler(WKPermissionDecisionGrant);
+}
+
+- (void) _webView: (WKWebView*) webView
+ requestGeolocationPermissionForFrame: (WKFrameInfo*) frame
+ decisionHandler: (void (^)(WKPermissionDecision decision)) decisionHandler {
+
+ decisionHandler(WKPermissionDecisionGrant);
+}
+
+- (void) webView: (WKWebView*) webView
+ runJavaScriptAlertPanelWithMessage: (NSString*) message
+ initiatedByFrame: (WKFrameInfo*) frame
+ completionHandler: (void (^)(void)) completionHandler {
+#if TARGET_OS_IPHONE || TARGET_OS_IPHONE
+ auto alert = [UIAlertController
+ alertControllerWithTitle: nil
+ message: message
+ preferredStyle: UIAlertControllerStyleAlert
+ ];
+
+ auto ok = [UIAlertAction
+ actionWithTitle: @"OK"
+ style: UIAlertActionStyleDefault
+ handler: ^(UIAlertAction * action) {
+ completionHandler();
+ }];
+
+ [alert addAction: ok];
+
+ [webView presentViewController:alert animated: YES completion: nil];
+#else
+ NSAlert *alert = [[NSAlert alloc] init];
+ [alert setMessageText: message];
+ [alert setInformativeText: message];
+ [alert addButtonWithTitle: @"OK"];
+ [alert runModal];
+ completionHandler();
+#endif
+}
+
+- (void) webView: (WKWebView*) webView
+ runJavaScriptConfirmPanelWithMessage: (NSString*) message
+ initiatedByFrame: (WKFrameInfo*) frame
+ completionHandler: (void (^)(BOOL result)) completionHandler {
+#if TARGET_OS_IPHONE || TARGET_OS_IPHONE
+ auto alert = [UIAlertController
+ alertControllerWithTitle: nil
+ message: message
+ preferredStyle: UIAlertControllerStyleAlert
+ ];
+
+ auto ok = [UIAlertAction
+ actionWithTitle: @"OK"
+ style: UIAlertActionStyleDefault
+ handler: ^(UIAlertAction * action) {
+ completionHandler(YES);
+ }];
+
+ auto cancel = [UIAlertAction
+ actionWithTitle: @"Cancel"
+ style: UIAlertActionStyleDefault
+ handler: ^(UIAlertAction * action) {
+ completionHandler(NO);
+ }];
+
+ [alert addAction: ok];
+ [alert addAction: cancel];
+
+ [webView presentViewController:alert animated: YES completion: nil];
+#else
+ NSAlert *alert = [[NSAlert alloc] init];
+ [alert setMessageText: message];
+ [alert setInformativeText: message];
+ [alert addButtonWithTitle: @"OK"];
+ [alert addButtonWithTitle: @"Cancel"];
+ completionHandler([alert runModal] == NSAlertFirstButtonReturn);
+#endif
+}
+
- (BOOL) prepareForDragOperation: (id)info {
[info setDraggingFormation: NSDraggingFormationNone];
return NO;
@@ -413,50 +552,6 @@ - (NSString*) filePromiseProvider: (NSFilePromiseProvider*)filePromiseProvider f
static bool isDelegateSet = false;
Window::Window (App& app, WindowOptions opts) : app(app), opts(opts) {
- this->bridge = new IPC::Bridge(app.core);
-
- this->bridge->router.dispatchFunction = [this] (auto callback) {
- this->app.dispatch(callback);
- };
-
- this->bridge->router.evaluateJavaScriptFunction = [this](auto js) {
- dispatch_async(dispatch_get_main_queue(), ^{ this->eval(js); });
- };
-
- this->bridge->router.map("window.eval", [=](auto message, auto router, auto reply) {
- auto value = message.value;
- auto seq = message.seq;
- auto script = [NSString stringWithUTF8String: value.c_str()];
-
- dispatch_async(dispatch_get_main_queue(), ^{
- [webview evaluateJavaScript: script completionHandler: ^(id result, NSError *error) {
- if (result) {
- auto msg = String([[NSString stringWithFormat:@"%@", result] UTF8String]);
- this->bridge->router.send(seq, msg, Post{});
- } else if (error) {
- auto exception = (NSString *) error.userInfo[@"WKJavaScriptExceptionMessage"];
- auto message = [[NSString stringWithFormat:@"%@", exception] UTF8String];
- auto err = encodeURIComponent(String(message));
-
- if (err == "(null)") {
- this->bridge->router.send(seq, "null", Post{});
- return;
- }
-
- auto json = JSON::Object::Entries {
- {"err", JSON::Object::Entries {
- {"message", String("Error: ") + err}
- }}
- };
-
- this->bridge->router.send(seq, JSON::Object(json).str(), Post{});
- } else {
- this->bridge->router.send(seq, "undefined", Post{});
- }
- }];
- });
- });
-
// Window style: titled, closable, minimizable
uint style = NSWindowStyleMaskTitled;
@@ -506,6 +601,53 @@ - (NSString*) filePromiseProvider: (NSFilePromiseProvider*)filePromiseProvider f
// window.movableByWindowBackground = true;
window.titlebarAppearsTransparent = true;
+ static auto userConfig = SSC::getUserConfig();
+
+ this->bridge = new IPC::Bridge(app.core);
+
+ this->bridge->router.dispatchFunction = [this] (auto callback) {
+ this->app.dispatch(callback);
+ };
+
+ this->bridge->router.evaluateJavaScriptFunction = [this](auto js) {
+ dispatch_async(dispatch_get_main_queue(), ^{ this->eval(js); });
+ };
+
+ this->bridge->router.map("window.eval", [=](auto message, auto router, auto reply) {
+ auto value = message.value;
+ auto seq = message.seq;
+ auto script = [NSString stringWithUTF8String: value.c_str()];
+
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [webview evaluateJavaScript: script completionHandler: ^(id result, NSError *error) {
+ if (result) {
+ auto msg = String([[NSString stringWithFormat:@"%@", result] UTF8String]);
+ this->bridge->router.send(seq, msg, Post{});
+ } else if (error) {
+ auto exception = (NSString *) error.userInfo[@"WKJavaScriptExceptionMessage"];
+ auto message = [[NSString stringWithFormat:@"%@", exception] UTF8String];
+ auto err = encodeURIComponent(String(message));
+
+ if (err == "(null)") {
+ this->bridge->router.send(seq, "null", Post{});
+ return;
+ }
+
+ auto json = JSON::Object::Entries {
+ {"err", JSON::Object::Entries {
+ {"message", String("Error: ") + err}
+ }}
+ };
+
+ this->bridge->router.send(seq, JSON::Object(json).str(), Post{});
+ } else {
+ this->bridge->router.send(seq, "undefined", Post{});
+ }
+ }];
+ });
+ });
+
+
// Initialize WKWebView
WKWebViewConfiguration* config = [WKWebViewConfiguration new];
// https://webkit.org/blog/10882/app-bound-domains/
@@ -519,7 +661,13 @@ - (NSString*) filePromiseProvider: (NSFilePromiseProvider*)filePromiseProvider f
forURLScheme: @"socket"];
WKPreferences* prefs = [config preferences];
- [prefs setJavaScriptCanOpenWindowsAutomatically:NO];
+ prefs.javaScriptCanOpenWindowsAutomatically = NO;
+
+ if (userConfig["permissions_allow_fullscreen"] == "false") {
+ [prefs setValue: @NO forKey: @"fullScreenEnabled"];
+ } else {
+ [prefs setValue: @YES forKey: @"fullScreenEnabled"];
+ }
if (SSC::isDebugEnabled()) {
[prefs setValue:@YES forKey:@"developerExtrasEnabled"];
@@ -528,6 +676,55 @@ - (NSString*) filePromiseProvider: (NSFilePromiseProvider*)filePromiseProvider f
}
}
+ if (userConfig["permissions_allow_clipboard"] == "false") {
+ [prefs setValue: @NO forKey: @"javaScriptCanAccessClipboard"];
+ } else {
+ [prefs setValue: @YES forKey: @"javaScriptCanAccessClipboard"];
+ }
+
+ if (userConfig["permissions_allow_data_access"] == "false") {
+ [prefs setValue: @NO forKey: @"storageAPIEnabled"];
+ } else {
+ [prefs setValue: @YES forKey: @"storageAPIEnabled"];
+ }
+
+ if (userConfig["permissions_allow_device_orientation"] == "false") {
+ [prefs setValue: @NO forKey: @"deviceOrientationEventEnabled"];
+ } else {
+ [prefs setValue: @YES forKey: @"deviceOrientationEventEnabled"];
+ }
+
+ if (userConfig["permissions_allow_notifications"] == "false") {
+ [prefs setValue: @NO forKey: @"appBadgeEnabled"];
+ [prefs setValue: @NO forKey: @"notificationsEnabled"];
+ [prefs setValue: @NO forKey: @"notificationEventEnabled"];
+ } else {
+ [prefs setValue: @YES forKey: @"appBadgeEnabled"];
+ [prefs setValue: @YES forKey: @"notificationsEnabled"];
+ [prefs setValue: @YES forKey: @"notificationEventEnabled"];
+ }
+
+ #if !TARGET_OS_IPHONE
+ [prefs setValue: @YES forKey: @"cookieEnabled"];
+
+ if (userConfig["permissions_allow_user_media"] == "false") {
+ [prefs setValue: @NO forKey: @"mediaStreamEnabled"];
+ } else {
+ [prefs setValue: @YES forKey: @"mediaStreamEnabled"];
+ }
+ #endif
+
+ [prefs setValue: @YES forKey: @"offlineApplicationCacheIsEnabled"];
+
+/*
+
+ if (userConfig["permissions_allow_geolocation"] == "false") {
+ [prefs setValue: @NO forKey: @"WebKitAlwaysRequestGeolocationPermission"];
+ } else {
+ [prefs setValue: @YES forKey: @"WebKitAlwaysRequestGeolocationPermission"];
+ }
+*/
+
WKUserContentController* controller = [config userContentController];
// Add preload script, normalizing the interface to be cross-platform.
@@ -597,6 +794,7 @@ - (NSString*) filePromiseProvider: (NSFilePromiseProvider*)filePromiseProvider f
SSCNavigationDelegate *navDelegate = [[SSCNavigationDelegate alloc] init];
[webview setNavigationDelegate: navDelegate];
+ webview.UIDelegate = webview;
if (!isDelegateSet) {
isDelegateSet = true;
diff --git a/src/window/linux.cc b/src/window/linux.cc
index 697580d5b3..b83370e041 100644
--- a/src/window/linux.cc
+++ b/src/window/linux.cc
@@ -184,7 +184,105 @@ namespace SSC {
this
);
- webview = webkit_web_view_new_with_user_content_manager(cm);
+
+ static auto userConfig = SSC::getUserConfig();
+ auto webContext = webkit_web_context_get_default();
+ auto cookieManager = webkit_web_context_get_cookie_manager(webContext);
+ auto settings = webkit_settings_new();
+ auto policies = webkit_website_policies_new_with_policies(
+ "autoplay", userConfig["permission_allow_autoplay"] != "false" ? WEBKIT_AUTOPLAY_ALLOW : WEBKIT_AUTOPLAY_DENY,
+ NULL
+ );
+
+ webview = GTK_WIDGET(WEBKIT_WEB_VIEW(g_object_new(WEBKIT_TYPE_WEB_VIEW,
+ "web-context", webContext,
+ "settings", settings,
+ "user-content-manager", cm,
+ "website-policies", policies,
+ NULL
+ )));
+
+ webkit_cookie_manager_set_accept_policy(cookieManager, WEBKIT_COOKIE_POLICY_ACCEPT_ALWAYS);
+
+ g_signal_connect(
+ G_OBJECT(webview),
+ "permission-request",
+ G_CALLBACK(+[](
+ WebKitWebView* webview,
+ WebKitPermissionRequest *request,
+ gpointer userData
+ ) -> bool {
+ static auto userConfig = SSC::getUserConfig();
+
+ if (WEBKIT_IS_GEOLOCATION_PERMISSION_REQUEST(request)) {
+ if (userConfig["permissions_allow_geolocation"] != "false") {
+ webkit_permission_request_allow(request);
+ return TRUE;
+ } else {
+ webkit_permission_request_deny(request);
+ return FALSE;
+ }
+ } else if (WEBKIT_IS_NOTIFICATION_PERMISSION_REQUEST(request)) {
+ if (userConfig["permissions_allow_notifications"] != "false") {
+ webkit_permission_request_allow(request);
+ return TRUE;
+ } else {
+ webkit_permission_request_deny(request);
+ return FALSE;
+ }
+ } else if (WEBKIT_IS_USER_MEDIA_PERMISSION_REQUEST(request)) {
+ if (userConfig["permissions_allow_user_media"] == "false") {
+ webkit_permission_request_deny(request);
+ return FALSE;
+ } else {
+ if (webkit_user_media_permission_is_for_audio_device(WEBKIT_USER_MEDIA_PERMISSION_REQUEST(request))) {
+ if (userConfig["permissions_allow_microphone"] == "false") {
+ webkit_permission_request_deny(request);
+ return FALSE;
+ }
+ }
+
+ if (webkit_user_media_permission_is_for_video_device(WEBKIT_USER_MEDIA_PERMISSION_REQUEST(request))) {
+ if (userConfig["permissions_allow_camera"] == "false") {
+ webkit_permission_request_deny(request);
+ return FALSE;
+ }
+ }
+
+ webkit_permission_request_allow(request);
+ return TRUE;
+ }
+ } else if (WEBKIT_IS_WEBSITE_DATA_ACCESS_PERMISSION_REQUEST(request)) {
+ if (userConfig["permissions_allow_data_access"] != "false") {
+ webkit_permission_request_allow(request);
+ return TRUE;
+ } else {
+ webkit_permission_request_deny(request);
+ return FALSE;
+ }
+ } else if (WEBKIT_IS_DEVICE_INFO_PERMISSION_REQUEST(request)) {
+ if (userConfig["permissions_allow_device_info"] != "false") {
+ webkit_permission_request_allow(request);
+ return TRUE;
+ } else {
+ webkit_permission_request_deny(request);
+ return FALSE;
+ }
+ } else if (WEBKIT_IS_MEDIA_KEY_SYSTEM_PERMISSION_REQUEST(request)) {
+ if (userConfig["permissions_allow_media_key_system"] != "false") {
+ webkit_permission_request_allow(request);
+ return TRUE;
+ } else {
+ webkit_permission_request_deny(request);
+ return FALSE;
+ }
+ }
+
+ webkit_permission_request_deny(request);
+ return FALSE;
+ }),
+ this
+ );
g_signal_connect(
G_OBJECT(webview),
@@ -575,10 +673,16 @@ namespace SSC {
)
);
- WebKitSettings *settings = webkit_web_view_get_settings(WEBKIT_WEB_VIEW(webview));
- webkit_settings_set_javascript_can_access_clipboard(settings, true);
webkit_settings_set_zoom_text_only(settings, false);
+ if (userConfig["permissions_allow_clipboard"] != "false") {
+ webkit_settings_set_javascript_can_access_clipboard(settings, true);
+ }
+
+ if (userConfig["permissions_allow_fullscreen"] != "false") {
+ webkit_settings_set_enable_fullscreen(settings, true);
+ }
+
GdkRGBA rgba = {0};
webkit_web_view_set_background_color(WEBKIT_WEB_VIEW(webview), &rgba);
diff --git a/src/window/win.cc b/src/window/win.cc
index 832c66cb38..bea8ceec55 100644
--- a/src/window/win.cc
+++ b/src/window/win.cc
@@ -699,6 +699,7 @@ namespace SSC {
window,
Microsoft::WRL::Callback(
[&, preload](HRESULT result, ICoreWebView2Controller* c) -> HRESULT {
+ static auto userConfig = SSC::getUserConfig();
if (c != nullptr) {
controller = c;
controller->get_CoreWebView2(&webview);
@@ -1226,11 +1227,78 @@ namespace SSC {
ICoreWebView2 *webview,
ICoreWebView2PermissionRequestedEventArgs *args
) -> HRESULT {
+ static auto userConfig = SSC::getUserConfig();
COREWEBVIEW2_PERMISSION_KIND kind;
args->get_PermissionKind(&kind);
+ if (kind == COREWEBVIEW2_PERMISSION_KIND_MICROPHONE) {
+ if (
+ userConfig["permissions_allow_microphone"] == "false" ||
+ userConfig["permissions_allow_user_media"] == "false"
+ ) {
+ args->put_State(COREWEBVIEW2_PERMISSION_STATE_DENY);
+ } else {
+ args->put_State(COREWEBVIEW2_PERMISSION_STATE_ALLOW);
+ }
+ }
+
+ if (kind == COREWEBVIEW2_PERMISSION_KIND_CAMERA) {
+ if (
+ userConfig["permissions_allow_camera"] == "false" ||
+ userConfig["permissions_allow_user_media"] == "false"
+ ) {
+ args->put_State(COREWEBVIEW2_PERMISSION_STATE_DENY);
+ } else {
+ args->put_State(COREWEBVIEW2_PERMISSION_STATE_ALLOW);
+ }
+ }
+
+ if (kind == COREWEBVIEW2_PERMISSION_KIND_GEOLOCATION) {
+ if (userConfig["permissions_allow_geolocation"] == "false") {
+ args->put_State(COREWEBVIEW2_PERMISSION_STATE_DENY);
+ } else {
+ args->put_State(COREWEBVIEW2_PERMISSION_STATE_ALLOW);
+ }
+ }
+
+ if (kind == COREWEBVIEW2_PERMISSION_KIND_NOTIFICATIONS) {
+ if (userConfig["permissions_allow_notifications"] == "false") {
+ args->put_State(COREWEBVIEW2_PERMISSION_STATE_DENY);
+ } else {
+ args->put_State(COREWEBVIEW2_PERMISSION_STATE_ALLOW);
+ }
+ }
+
+ if (kind == COREWEBVIEW2_PERMISSION_KIND_OTHER_SENSORS) {
+ if (userConfig["permissions_allow_sensors"] == "false") {
+ args->put_State(COREWEBVIEW2_PERMISSION_STATE_DENY);
+ } else {
+ args->put_State(COREWEBVIEW2_PERMISSION_STATE_ALLOW);
+ }
+ }
+
if (kind == COREWEBVIEW2_PERMISSION_KIND_CLIPBOARD_READ) {
- args->put_State(COREWEBVIEW2_PERMISSION_STATE_ALLOW);
+ if (userConfig["permissions_allow_clipboard"] == "false") {
+ args->put_State(COREWEBVIEW2_PERMISSION_STATE_DENY);
+ } else {
+ args->put_State(COREWEBVIEW2_PERMISSION_STATE_ALLOW);
+ }
+ }
+
+ if (kind == COREWEBVIEW2_PERMISSION_KIND_AUTOPLAY) {
+ if (userConfig["permissions_allow_autoplay"] == "false") {
+ args->put_State(COREWEBVIEW2_PERMISSION_STATE_DENY);
+ } else {
+ args->put_State(COREWEBVIEW2_PERMISSION_STATE_ALLOW);
+ }
+ }
+
+ if (kind == COREWEBVIEW2_PERMISSION_KIND_LOCAL_FONTS) {
+ if (userConfig["permissions_allow_local_fonts"] == "false") {
+ args->put_State(COREWEBVIEW2_PERMISSION_STATE_DENY);
+ } else {
+ args->put_State(COREWEBVIEW2_PERMISSION_STATE_ALLOW);
+ }
}
return S_OK;
diff --git a/src/window/window.hh b/src/window/window.hh
index 3269f73488..6c63ed4b51 100644
--- a/src/window/window.hh
+++ b/src/window/window.hh
@@ -22,6 +22,36 @@
>
- (NSDragOperation) draggingSession: (NSDraggingSession *) session
sourceOperationMaskForDraggingContext: (NSDraggingContext) context;
+
+- (void) webView: (WKWebView*) webView
+ requestDeviceOrientationAndMotionPermissionForOrigin: (WKSecurityOrigin*) origin
+ initiatedByFrame: (WKFrameInfo*) frame
+ decisionHandler: (void (^)(WKPermissionDecision decision)) decisionHandler;
+
+- (void) webView: (WKWebView*) webView
+ requestMediaCapturePermissionForOrigin: (WKSecurityOrigin*) origin
+ initiatedByFrame: (WKFrameInfo*) frame
+ type: (WKMediaCaptureType) type
+ decisionHandler: (void (^)(WKPermissionDecision decision)) decisionHandler;
+
+- (void) _webView: (WKWebView*) webView
+ requestGeolocationPermissionForOrigin: (WKSecurityOrigin*) origin
+ initiatedByFrame: (WKFrameInfo*) frame
+ decisionHandler: (void (^)(WKPermissionDecision decision)) decisionHandler;
+
+- (void) _webView: (WKWebView*) webView
+ requestGeolocationPermissionForFrame: (WKFrameInfo*) frame
+ decisionHandler: (void (^)(WKPermissionDecision decision)) decisionHandler;
+
+- (void) webView: (WKWebView*) webView
+ runJavaScriptAlertPanelWithMessage: (NSString*) message
+ initiatedByFrame: (WKFrameInfo*) frame
+ completionHandler: (void (^)(void)) completionHandler;
+
+- (void) webView: (WKWebView*) webView
+ runJavaScriptConfirmPanelWithMessage: (NSString*) message
+ initiatedByFrame: (WKFrameInfo*) frame
+ completionHandler: (void (^)(BOOL result)) completionHandler;
@end
#endif