From 0f08fb3d1c96cd61e2dd84402665fdd894a0cf83 Mon Sep 17 00:00:00 2001 From: Brandon-T Date: Wed, 9 Feb 2022 10:28:49 -0500 Subject: [PATCH] Fix #4967: Guard iOS 14.3 and up to be sandboxed. iOS 14.2 and below has a crash: "Fatal error: Bug in WebKit: Received neither result or failure.: file WebKit/WebKitSwiftOverlay.swift, line 66" (#4968) --- .../Frontend/Browser/DomainUserScript.swift | 8 ++--- .../Browser/MetadataParserHelper.swift | 2 +- .../PlaylistCacheLoader.swift | 2 +- Client/Frontend/Browser/Tab.swift | 8 +++-- .../Frontend/Browser/UserScriptManager.swift | 20 +++++------ Shared/Extensions/WKWebViewExtensions.swift | 34 +++++++++++++++---- 6 files changed, 50 insertions(+), 24 deletions(-) diff --git a/Client/Frontend/Browser/DomainUserScript.swift b/Client/Frontend/Browser/DomainUserScript.swift index bf184d528d6..fc9b877ddef 100644 --- a/Client/Frontend/Browser/DomainUserScript.swift +++ b/Client/Frontend/Browser/DomainUserScript.swift @@ -98,9 +98,9 @@ enum DomainUserScript: CaseIterable { alteredSource = alteredSource.replacingOccurrences(of: "$", with: "ABSSJ\(token)", options: .literal) - return WKUserScript(source: alteredSource, injectionTime: .atDocumentStart, forMainFrameOnly: false, in: .page) + return WKUserScript.create(source: alteredSource, injectionTime: .atDocumentStart, forMainFrameOnly: false, in: .page) case .archive: - return WKUserScript(source: source, injectionTime: .atDocumentStart, forMainFrameOnly: false, in: .page) + return WKUserScript.create(source: source, injectionTime: .atDocumentStart, forMainFrameOnly: false, in: .page) case .braveSearch: var alteredSource = source @@ -111,7 +111,7 @@ enum DomainUserScript: CaseIterable { options: .literal) .replacingOccurrences(of: "$", with: securityToken) - return WKUserScript(source: alteredSource, injectionTime: .atDocumentStart, forMainFrameOnly: false, in: .page) + return WKUserScript.create(source: alteredSource, injectionTime: .atDocumentStart, forMainFrameOnly: false, in: .page) case .braveTalk: var alteredSource = source @@ -122,7 +122,7 @@ enum DomainUserScript: CaseIterable { options: .literal) .replacingOccurrences(of: "$", with: securityToken) - return WKUserScript(source: alteredSource, injectionTime: .atDocumentStart, forMainFrameOnly: false, in: .page) + return WKUserScript.create(source: alteredSource, injectionTime: .atDocumentStart, forMainFrameOnly: false, in: .page) } } diff --git a/Client/Frontend/Browser/MetadataParserHelper.swift b/Client/Frontend/Browser/MetadataParserHelper.swift index 29ce3aa2b48..777a30bde10 100644 --- a/Client/Frontend/Browser/MetadataParserHelper.swift +++ b/Client/Frontend/Browser/MetadataParserHelper.swift @@ -32,7 +32,7 @@ class MetadataParserHelper: TabEventHandler { tab.pageMetadata = nil return } - + webView.evaluateSafeJavaScript(functionName: "__firefox__.metadata && __firefox__.metadata.getMetadata()", contentWorld: .defaultClient, asFunction: false) { (result, error) in guard error == nil else { // TabEvent.post(.pageMetadataNotAvailable, for: tab) diff --git a/Client/Frontend/Browser/Playlist/Managers & Cache/PlaylistCacheLoader.swift b/Client/Frontend/Browser/Playlist/Managers & Cache/PlaylistCacheLoader.swift index b2ea22fe1a3..2f47150171c 100644 --- a/Client/Frontend/Browser/Playlist/Managers & Cache/PlaylistCacheLoader.swift +++ b/Client/Frontend/Browser/Playlist/Managers & Cache/PlaylistCacheLoader.swift @@ -378,7 +378,7 @@ class PlaylistWebLoader: UIView { replacements.forEach({ alteredSource = alteredSource.replacingOccurrences(of: $0.key, with: $0.value, options: .literal) }) - return WKUserScript(source: alteredSource, injectionTime: .atDocumentStart, forMainFrameOnly: false, in: .page) + return WKUserScript.create(source: alteredSource, injectionTime: .atDocumentStart, forMainFrameOnly: false, in: .page) }() private weak var certStore: CertStore? diff --git a/Client/Frontend/Browser/Tab.swift b/Client/Frontend/Browser/Tab.swift index 6f847eb715b..03c19853d31 100644 --- a/Client/Frontend/Browser/Tab.swift +++ b/Client/Frontend/Browser/Tab.swift @@ -633,7 +633,7 @@ class Tab: NSObject { } if let path = Bundle.main.path(forResource: fileName, ofType: type), let source = try? String(contentsOfFile: path) { - let userScript = WKUserScript(source: source, injectionTime: injectionTime, forMainFrameOnly: mainFrameOnly, in: contentWorld) + let userScript = WKUserScript.create(source: source, injectionTime: injectionTime, forMainFrameOnly: mainFrameOnly, in: contentWorld) webView.configuration.userContentController.addUserScript(userScript) } } @@ -691,7 +691,11 @@ private class TabContentScriptManager: NSObject, WKScriptMessageHandler { // If this helper handles script messages, then get the handler name and register it. The Tab // receives all messages and then dispatches them to the right TabHelper. if let scriptMessageHandlerName = helper.scriptMessageHandlerName() { - tab.webView?.configuration.userContentController.add(self, contentWorld: contentWorld, name: scriptMessageHandlerName) + if #available(iOS 14.3, *) { + tab.webView?.configuration.userContentController.add(self, contentWorld: contentWorld, name: scriptMessageHandlerName) + } else { + tab.webView?.configuration.userContentController.add(self, name: scriptMessageHandlerName) + } } } diff --git a/Client/Frontend/Browser/UserScriptManager.swift b/Client/Frontend/Browser/UserScriptManager.swift index 5fda531cb7e..2e9f8982452 100644 --- a/Client/Frontend/Browser/UserScriptManager.swift +++ b/Client/Frontend/Browser/UserScriptManager.swift @@ -138,7 +138,7 @@ class UserScriptManager { let source = try? NSString(contentsOfFile: path, encoding: String.Encoding.utf8.rawValue) as String { let wrappedSource = "(function() { const SECURITY_TOKEN = '\(UserScriptManager.messageHandlerTokenString)'; \(source) })()" - return WKUserScript(source: wrappedSource, + return WKUserScript.create(source: wrappedSource, injectionTime: injectionTime, forMainFrameOnly: mainFrameOnly, in: sandboxed ? .defaultClient : .page) @@ -154,7 +154,7 @@ class UserScriptManager { } var alteredSource = source alteredSource = alteredSource.replacingOccurrences(of: "$", with: "FingerprintingProtection\(messageHandlerTokenString)", options: .literal) - return WKUserScript(source: alteredSource, + return WKUserScript.create(source: alteredSource, injectionTime: .atDocumentStart, forMainFrameOnly: false, in: .page) @@ -166,7 +166,7 @@ class UserScriptManager { return nil } - return WKUserScript(source: source, + return WKUserScript.create(source: source, injectionTime: .atDocumentStart, forMainFrameOnly: false, in: .page) @@ -188,7 +188,7 @@ class UserScriptManager { alteredSource = alteredSource.replacingOccurrences(of: "$", with: "PaymentRequestCallback\(securityTokenString)", options: .literal) alteredSource = alteredSource.replacingOccurrences(of: "$", with: "PaymentRequest\(messageHandlerTokenString)", options: .literal) - return WKUserScript(source: alteredSource, + return WKUserScript.create(source: alteredSource, injectionTime: .atDocumentStart, forMainFrameOnly: false, in: .page) @@ -204,7 +204,7 @@ class UserScriptManager { alteredSource = alteredSource.replacingOccurrences(of: "$", with: "D\(securityTokenString)", options: .literal) alteredSource = alteredSource.replacingOccurrences(of: "$", with: "ResourceDownloadManager\(messageHandlerTokenString)", options: .literal) - return WKUserScript(source: alteredSource, + return WKUserScript.create(source: alteredSource, injectionTime: .atDocumentEnd, forMainFrameOnly: false, in: .defaultClient) @@ -224,7 +224,7 @@ class UserScriptManager { alteredSource = alteredSource.replacingOccurrences(of: "$", with: "W\(securityTokenString)", options: .literal) alteredSource = alteredSource.replacingOccurrences(of: "$", with: "WindowRenderHelper\(messageHandlerTokenString)", options: .literal) - return WKUserScript(source: alteredSource, + return WKUserScript.create(source: alteredSource, injectionTime: .atDocumentStart, forMainFrameOnly: false, in: .defaultClient) @@ -236,7 +236,7 @@ class UserScriptManager { return nil } - return WKUserScript(source: source, + return WKUserScript.create(source: source, injectionTime: .atDocumentStart, forMainFrameOnly: false, in: .page) @@ -249,7 +249,7 @@ class UserScriptManager { return nil } - return WKUserScript(source: source, + return WKUserScript.create(source: source, injectionTime: .atDocumentStart, forMainFrameOnly: false, in: .page) @@ -294,7 +294,7 @@ class UserScriptManager { alteredSource = alteredSource.replacingOccurrences(of: $0.key, with: $0.value, options: .literal) }) - return WKUserScript(source: alteredSource, + return WKUserScript.create(source: alteredSource, injectionTime: .atDocumentStart, forMainFrameOnly: false, in: .page) @@ -318,7 +318,7 @@ class UserScriptManager { alteredSource = alteredSource.replacingOccurrences(of: $0.key, with: $0.value, options: .literal) }) - return WKUserScript(source: alteredSource, + return WKUserScript.create(source: alteredSource, injectionTime: .atDocumentStart, forMainFrameOnly: false, in: .page) diff --git a/Shared/Extensions/WKWebViewExtensions.swift b/Shared/Extensions/WKWebViewExtensions.swift index 3b1398a3931..77929245a25 100644 --- a/Shared/Extensions/WKWebViewExtensions.swift +++ b/Shared/Extensions/WKWebViewExtensions.swift @@ -11,6 +11,17 @@ enum JavascriptError: Error { case invalid } +public extension WKUserScript { + static func create(source: String, injectionTime: WKUserScriptInjectionTime, forMainFrameOnly: Bool, in contentWorld: WKContentWorld) -> WKUserScript { + + if #available(iOS 14.3, *) { + return WKUserScript(source: source, injectionTime: injectionTime, forMainFrameOnly: forMainFrameOnly, in: contentWorld) + } else { + return WKUserScript(source: source, injectionTime: injectionTime, forMainFrameOnly: forMainFrameOnly) + } + } +} + public extension WKWebView { func generateJSFunctionString(functionName: String, args: [Any?], escapeArgs: Bool = true) -> (javascript: String, error: Error?) { var sanitizedArgs = [String]() @@ -58,13 +69,24 @@ public extension WKWebView { javascript = js.javascript } - // swiftlint:disable:next safe_javascript - evaluateJavaScript(javascript, in: nil, in: contentWorld) { result in - switch result { - case .success(let value): - completion?(value, nil) - case .failure(let error): + if #available(iOS 14.3, *) { + // swiftlint:disable:next safe_javascript + evaluateJavaScript(javascript, in: nil, in: contentWorld) { result in + switch result { + case .success(let value): + completion?(value, nil) + case .failure(let error): + completion?(nil, error) + } + } + } else { + // swiftlint:disable:next safe_javascript + evaluateJavaScript(javascript) { result, error in + if let error = error { completion?(nil, error) + } else { + completion?(result, error) + } } } }