Embed element call (#3939)

* it works but only with the baseURL for now

* works but strings are not referenced properly

and we are using a dummy config.json which maybe is not required at all?

* test with EmbeddedElementCall repo

* updated the version

* ignore our own package

* updated version

removed using EC through the well known URL

* fix for remote URL overriding

* updated version

* fix for microphone and camera using local URL

* better solution

* Use version 0.9.0-release-test.3

* fix project

* removed workaround for emebedded EC url generation

* updated EC

* pr suggestions

* fix

* removed unnecessary configuration flag

---------

Co-authored-by: Hugh Nimmo-Smith <hughns@element.io>
This commit is contained in:
Mauro
2025-03-25 17:14:23 +01:00
committed by GitHub
parent 791ce67808
commit 60024c78eb
10 changed files with 94 additions and 61 deletions
@@ -148,8 +148,6 @@ class CallScreenViewModel: CallScreenViewModelType, CallScreenViewModelProtocol
let baseURL = if let elementCallBaseURLOverride {
elementCallBaseURLOverride
} else if case .success(let wellKnown) = await clientProxy.getElementWellKnown(), let wellKnownCall = wellKnown?.call {
wellKnownCall.widgetURL
} else {
elementCallBaseURL
}
@@ -7,6 +7,7 @@
import AVKit
import Combine
import EmbeddedElementCall
import SFSafeSymbols
import SwiftUI
import WebKit
@@ -98,6 +99,8 @@ private struct CallView: UIViewRepresentable {
let userContentController = WKUserContentController()
userContentController.add(WKScriptMessageHandlerWrapper(self), name: viewModelContext.viewState.messageHandler)
// Required to allow a webview that uses file URL to load its own assets
configuration.preferences.setValue(true, forKey: "allowFileAccessFromFileURLs")
configuration.userContentController = userContentController
configuration.allowsInlineMediaPlayback = true
configuration.allowsPictureInPictureMediaPlayback = true
@@ -133,8 +136,13 @@ private struct CallView: UIViewRepresentable {
func load(_ url: URL) {
self.url = url
let request = URLRequest(url: url)
webView.load(request)
// The only file URL we allow is the one coming from our own local ElementCall bundle, so it's okay to allow read permission only to our local EC bundle
if url.isFileURL {
webView.loadFileURL(url, allowingReadAccessTo: EmbeddedElementCall.bundle.bundleURL)
} else {
let request = URLRequest(url: url)
webView.load(request)
}
}
func evaluateJavaScript(_ script: String) async throws -> Any? {
@@ -159,8 +167,8 @@ private struct CallView: UIViewRepresentable {
// MARK: - WKUIDelegate
func webView(_ webView: WKWebView, decideMediaCapturePermissionsFor origin: WKSecurityOrigin, initiatedBy frame: WKFrameInfo, type: WKMediaCaptureType) async -> WKPermissionDecision {
// Don't allow permissions for domains different than what the call was started on
guard origin.host == url.host else {
// Allow if the origin is local, otherwise don't allow permissions for domains different than what the call was started on
guard origin.protocol == "file" || origin.host == url.host else {
return .deny
}
@@ -174,9 +182,16 @@ private struct CallView: UIViewRepresentable {
}
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction) async -> WKNavigationActionPolicy {
// Allow any content from the main URL.
if navigationAction.request.url?.host == url.host {
return .allow
if let navigationURL = navigationAction.request.url {
// Do not allow navigation to a different URL scheme.
if navigationURL.scheme != url.scheme {
return .cancel
}
// Allow any content from the main URL.
if navigationURL.host == url.host {
return .allow
}
}
// Additionally allow any embedded content such as captchas.