Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
85e36de
[webview_flutter_wkwebview] Add support for javaScriptCanOpenWindowsA…
fummicc1 Dec 11, 2025
767e4a5
Format Swift and Dart files
fummicc1 Dec 12, 2025
b58764b
Merge branch 'main' into add-support-for-javaScriptCanOpenWindowsAuto…
fummicc1 Dec 14, 2025
bd3b12f
Bump version to 3.24.0 for javaScriptCanOpenWindowsAutomatically feature
fummicc1 Dec 15, 2025
4133b20
Make javaScriptCanOpenWindowsAutomatically nullable to respect platfo…
fummicc1 Dec 15, 2025
58ecb77
Remove failable `try` to improve test reliability. Updates the docume…
fummicc1 Dec 15, 2025
9dde6ef
Merge branch 'main' into add-support-for-javaScriptCanOpenWindowsAuto…
fummicc1 Dec 22, 2025
91f96b3
Merge branch 'main' into add-support-for-javaScriptCanOpenWindowsAuto…
fummicc1 Jan 9, 2026
b5785c7
Format dart code of webview_flutter_wkwebview package.
fummicc1 Jan 9, 2026
a700da7
Merge branch 'main' into add-support-for-javaScriptCanOpenWindowsAuto…
fummicc1 Jan 31, 2026
ec04126
Merge branch 'main' into add-support-for-javaScriptCanOpenWindowsAuto…
fummicc1 Feb 9, 2026
cf4f2c9
Refactor: remove duplicate javaScriptCanOpenWindowsAutomatically logi…
fummicc1 Feb 9, 2026
0d1533c
Apply formatter
fummicc1 Feb 11, 2026
f630604
Consolidate javaScriptCanOpenWindowsAutomatically documentation
fummicc1 Feb 11, 2026
7df6f3e
Add explicit namespace of WebKitWebViewControllerCreationParams in `C…
fummicc1 Feb 11, 2026
130f1c7
Merge branch 'main' of github.com:flutter/packages into add-support-f…
bparrishMines Feb 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 3.24.0

* Adds support for `WebKitWebViewControllerCreationParams.javaScriptCanOpenWindowsAutomatically` to allow JavaScript's
`window.open()` to work without user interaction on iOS and macOS.

## 3.23.7

* Fixes crash when calling setOnConsoleMessage multiple times.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,11 +170,12 @@ class TestNavigationDelegateApi: PigeonApiProtocolWKNavigationDelegate {
func decidePolicyForNavigationAction(
pigeonInstance pigeonInstanceArg: WKNavigationDelegate, webView webViewArg: WKWebView,
navigationAction navigationActionArg: WKNavigationAction,
completion: @escaping (
Result<
webview_flutter_wkwebview.NavigationActionPolicy, webview_flutter_wkwebview.PigeonError
>
) -> Void
completion:
@escaping (
Result<
webview_flutter_wkwebview.NavigationActionPolicy, webview_flutter_wkwebview.PigeonError
>
) -> Void
) {
decidePolicyForNavigationActionArgs = [webViewArg, navigationActionArg]
completion(.success(.allow))
Expand All @@ -183,11 +184,12 @@ class TestNavigationDelegateApi: PigeonApiProtocolWKNavigationDelegate {
func decidePolicyForNavigationResponse(
pigeonInstance pigeonInstanceArg: WKNavigationDelegate, webView webViewArg: WKWebView,
navigationResponse navigationResponseArg: WKNavigationResponse,
completion: @escaping (
Result<
webview_flutter_wkwebview.NavigationResponsePolicy, webview_flutter_wkwebview.PigeonError
>
) -> Void
completion:
@escaping (
Result<
webview_flutter_wkwebview.NavigationResponsePolicy, webview_flutter_wkwebview.PigeonError
>
) -> Void
) {
decidePolicyForNavigationResponseArgs = [webViewArg, navigationResponseArg]
completion(.success(.cancel))
Expand Down Expand Up @@ -219,12 +221,13 @@ class TestNavigationDelegateApi: PigeonApiProtocolWKNavigationDelegate {
func didReceiveAuthenticationChallenge(
pigeonInstance pigeonInstanceArg: WKNavigationDelegate, webView webViewArg: WKWebView,
challenge challengeArg: URLAuthenticationChallenge,
completion: @escaping (
Result<
webview_flutter_wkwebview.AuthenticationChallengeResponse,
webview_flutter_wkwebview.PigeonError
>
) -> Void
completion:
@escaping (
Result<
webview_flutter_wkwebview.AuthenticationChallengeResponse,
webview_flutter_wkwebview.PigeonError
>
) -> Void
) {
didReceiveAuthenticationChallengeArgs = [webViewArg, challengeArg]
completion(
Expand All @@ -241,7 +244,8 @@ class TestWebView: WKWebView {
}
}

class TestURLAuthenticationChallengeSender: NSObject, URLAuthenticationChallengeSender, @unchecked
class TestURLAuthenticationChallengeSender: NSObject, URLAuthenticationChallengeSender,
@unchecked
Sendable
{
func use(_ credential: URLCredential, for challenge: URLAuthenticationChallenge) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,16 @@ class PreferencesProxyAPITests: XCTestCase {
XCTAssertEqual(instance.javaScriptEnabled, enabled)
}
}

@MainActor func testSetJavaScriptCanOpenWindowsAutomatically() throws {
let registrar = TestProxyApiRegistrar()
let api = registrar.apiDelegate.pigeonApiWKPreferences(registrar)

let instance = WKPreferences()
let enabled = true
try api.pigeonDelegate.setJavaScriptCanOpenWindowsAutomatically(
pigeonApi: api, pigeonInstance: instance, enabled: enabled)

XCTAssertEqual(instance.javaScriptCanOpenWindowsAutomatically, enabled)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,8 @@ public class NavigationDelegateImpl: NSObject, WKNavigationDelegate {
#if compiler(>=6.0)
public func webView(
_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge,
completionHandler: @escaping @MainActor (URLSession.AuthChallengeDisposition, URLCredential?)
completionHandler:
@escaping @MainActor (URLSession.AuthChallengeDisposition, URLCredential?)
->
Void
) {
Expand All @@ -256,7 +257,8 @@ public class NavigationDelegateImpl: NSObject, WKNavigationDelegate {
#else
public func webView(
_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge,
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) ->
completionHandler:
@escaping (URLSession.AuthChallengeDisposition, URLCredential?) ->
Void
) {
registrar.dispatchOnMainThread { onFailure in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,10 @@ class PreferencesProxyAPIDelegate: PigeonApiDelegateWKPreferences {
pigeonInstance.javaScriptEnabled = enabled
}
}

func setJavaScriptCanOpenWindowsAutomatically(
pigeonApi: PigeonApiWKPreferences, pigeonInstance: WKPreferences, enabled: Bool
) throws {
pigeonInstance.javaScriptCanOpenWindowsAutomatically = enabled
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,10 @@ open class ProxyAPIRegistrar: WebKitLibraryPigeonProxyApiRegistrar {

/// Handles calling a Flutter method on the main thread.
func dispatchOnMainThread(
execute work: @escaping (
_ onFailure: @escaping (_ methodName: String, _ error: PigeonError) -> Void
) -> Void
execute work:
@escaping (
_ onFailure: @escaping (_ methodName: String, _ error: PigeonError) -> Void
) -> Void
) {
DispatchQueue.main.async {
work { methodName, error in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3917,6 +3917,14 @@ protocol PigeonApiDelegateWKPreferences {
/// A Boolean value that indicates whether JavaScript is enabled.
func setJavaScriptEnabled(
pigeonApi: PigeonApiWKPreferences, pigeonInstance: WKPreferences, enabled: Bool) throws
/// A Boolean value that indicates whether JavaScript can open windows
/// without user interaction.
///
/// The default value is `false` on iOS and `true` on macOS.
///
/// See https://developer.apple.com/documentation/webkit/wkpreferences/javascriptcanopenwindowsautomatically
func setJavaScriptCanOpenWindowsAutomatically(
pigeonApi: PigeonApiWKPreferences, pigeonInstance: WKPreferences, enabled: Bool) throws
}

protocol PigeonApiProtocolWKPreferences {
Expand Down Expand Up @@ -3964,6 +3972,26 @@ final class PigeonApiWKPreferences: PigeonApiProtocolWKPreferences {
} else {
setJavaScriptEnabledChannel.setMessageHandler(nil)
}
let setJavaScriptCanOpenWindowsAutomaticallyChannel = FlutterBasicMessageChannel(
name:
"dev.flutter.pigeon.webview_flutter_wkwebview.WKPreferences.setJavaScriptCanOpenWindowsAutomatically",
binaryMessenger: binaryMessenger, codec: codec)
if let api = api {
setJavaScriptCanOpenWindowsAutomaticallyChannel.setMessageHandler { message, reply in
let args = message as! [Any?]
let pigeonInstanceArg = args[0] as! WKPreferences
let enabledArg = args[1] as! Bool
do {
try api.pigeonDelegate.setJavaScriptCanOpenWindowsAutomatically(
pigeonApi: api, pigeonInstance: pigeonInstanceArg, enabled: enabledArg)
reply(wrapResult(nil))
} catch {
reply(wrapError(error))
}
}
} else {
setJavaScriptCanOpenWindowsAutomaticallyChannel.setMessageHandler(nil)
}
}

///Creates a Dart instance of WKPreferences and attaches it to [pigeonInstance].
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4687,6 +4687,40 @@ class WKPreferences extends NSObject {
}
}

/// A Boolean value that indicates whether JavaScript can open windows
/// without user interaction.
///
/// The default value is `false` on iOS and `true` on macOS.
///
/// See https://developer.apple.com/documentation/webkit/wkpreferences/javascriptcanopenwindowsautomatically
Future<void> setJavaScriptCanOpenWindowsAutomatically(bool enabled) async {
final _PigeonInternalProxyApiBaseCodec pigeonChannelCodec =
_pigeonVar_codecWKPreferences;
final BinaryMessenger? pigeonVar_binaryMessenger = pigeon_binaryMessenger;
const pigeonVar_channelName =
'dev.flutter.pigeon.webview_flutter_wkwebview.WKPreferences.setJavaScriptCanOpenWindowsAutomatically';
final pigeonVar_channel = BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(
<Object?>[this, enabled],
);
final pigeonVar_replyList = await pigeonVar_sendFuture as List<Object?>?;
if (pigeonVar_replyList == null) {
throw _createConnectionError(pigeonVar_channelName);
} else if (pigeonVar_replyList.length > 1) {
throw PlatformException(
code: pigeonVar_replyList[0]! as String,
message: pigeonVar_replyList[1] as String?,
details: pigeonVar_replyList[2],
);
} else {
return;
}
}

@override
WKPreferences pigeon_copy() {
return WKPreferences.pigeon_detached(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ class WebKitWebViewControllerCreationParams
},
this.allowsInlineMediaPlayback = false,
this.limitsNavigationsToAppBoundDomains = false,
this.javaScriptCanOpenWindowsAutomatically,
}) {
_configuration = WKWebViewConfiguration();

Expand Down Expand Up @@ -122,10 +123,13 @@ class WebKitWebViewControllerCreationParams
},
bool allowsInlineMediaPlayback = false,
bool limitsNavigationsToAppBoundDomains = false,
bool? javaScriptCanOpenWindowsAutomatically,
}) : this(
mediaTypesRequiringUserAction: mediaTypesRequiringUserAction,
allowsInlineMediaPlayback: allowsInlineMediaPlayback,
limitsNavigationsToAppBoundDomains: limitsNavigationsToAppBoundDomains,
javaScriptCanOpenWindowsAutomatically:
javaScriptCanOpenWindowsAutomatically,
);

late final WKWebViewConfiguration _configuration;
Expand All @@ -147,6 +151,12 @@ class WebKitWebViewControllerCreationParams
/// (Only available for iOS > 14.0)
/// Defaults to false.
final bool limitsNavigationsToAppBoundDomains;

/// Whether JavaScript can open windows without user interaction.
///
/// When `null`, the platform's native default is used
/// (`false` on iOS, `true` on macOS).
final bool? javaScriptCanOpenWindowsAutomatically;
}

/// An implementation of [PlatformWebViewController] with the WebKit api.
Expand Down Expand Up @@ -634,6 +644,16 @@ class WebKitWebViewController extends PlatformWebViewController {

@override
Future<void> setJavaScriptMode(JavaScriptMode javaScriptMode) async {
final bool? javaScriptCanOpenWindowsAutomatically =
_webKitParams.javaScriptCanOpenWindowsAutomatically;
if (javaScriptCanOpenWindowsAutomatically != null) {
final WKPreferences preferences = await _webView.configuration
.getPreferences();
await preferences.setJavaScriptCanOpenWindowsAutomatically(
javaScriptCanOpenWindowsAutomatically,
);
}

// Attempt to set the value that requires iOS 14+.
try {
final WKWebpagePreferences webpagePreferences = await _webView
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -747,6 +747,14 @@ abstract class WKUserContentController extends NSObject {
abstract class WKPreferences extends NSObject {
/// A Boolean value that indicates whether JavaScript is enabled.
void setJavaScriptEnabled(bool enabled);

/// A Boolean value that indicates whether JavaScript can open windows
/// without user interaction.
///
/// The default value is `false` on iOS and `true` on macOS.
///
/// See https://developer.apple.com/documentation/webkit/wkpreferences/javascriptcanopenwindowsautomatically
void setJavaScriptCanOpenWindowsAutomatically(bool enabled);
}

/// An interface for receiving messages from JavaScript code running in a webpage.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: webview_flutter_wkwebview
description: A Flutter plugin that provides a WebView widget based on Apple's WKWebView control.
repository: https://github.com/flutter/packages/tree/main/packages/webview_flutter/webview_flutter_wkwebview
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22
version: 3.23.7
version: 3.24.0

environment:
sdk: ^3.9.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,17 @@ class MockWKPreferences extends _i1.Mock implements _i2.WKPreferences {
)
as _i4.Future<void>);

@override
_i4.Future<void> setJavaScriptCanOpenWindowsAutomatically(bool? enabled) =>
(super.noSuchMethod(
Invocation.method(#setJavaScriptCanOpenWindowsAutomatically, [
enabled,
]),
returnValue: _i4.Future<void>.value(),
returnValueForMissingStub: _i4.Future<void>.value(),
)
as _i4.Future<void>);

@override
_i2.WKPreferences pigeon_copy() =>
(super.noSuchMethod(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ void main() {
MockURLRequest Function({required String url, dynamic observeValue})?
createURLRequest,
MockWKWebpagePreferences? mockWebpagePreferences,
bool? javaScriptCanOpenWindowsAutomatically,
}) {
final MockWKWebViewConfiguration nonNullMockWebViewConfiguration =
mockWebViewConfiguration ?? MockWKWebViewConfiguration();
Expand Down Expand Up @@ -190,7 +191,10 @@ void main() {
);
};
final PlatformWebViewControllerCreationParams controllerCreationParams =
WebKitWebViewControllerCreationParams();
WebKitWebViewControllerCreationParams(
javaScriptCanOpenWindowsAutomatically:
javaScriptCanOpenWindowsAutomatically,
);

final controller = WebKitWebViewController(controllerCreationParams);

Expand Down Expand Up @@ -870,6 +874,43 @@ void main() {
},
);

test(
'setJavaScriptMode sets javaScriptCanOpenWindowsAutomatically from creation params',
() async {
final mockPreferences = MockWKPreferences();
final mockWebpagePreferences = MockWKWebpagePreferences();

final WebKitWebViewController controller = createControllerWithMocks(
mockPreferences: mockPreferences,
mockWebpagePreferences: mockWebpagePreferences,
javaScriptCanOpenWindowsAutomatically: true,
);

await controller.setJavaScriptMode(JavaScriptMode.unrestricted);

verify(mockPreferences.setJavaScriptCanOpenWindowsAutomatically(true));
},
);

test(
'setJavaScriptMode does not set javaScriptCanOpenWindowsAutomatically when null',
() async {
final mockPreferences = MockWKPreferences();
final mockWebpagePreferences = MockWKWebpagePreferences();

final WebKitWebViewController controller = createControllerWithMocks(
mockPreferences: mockPreferences,
mockWebpagePreferences: mockWebpagePreferences,
);

await controller.setJavaScriptMode(JavaScriptMode.unrestricted);

verifyNever(
mockPreferences.setJavaScriptCanOpenWindowsAutomatically(any),
);
},
);

test('clearCache', () {
final mockWebsiteDataStore = MockWKWebsiteDataStore();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,17 @@ class MockWKPreferences extends _i1.Mock implements _i2.WKPreferences {
)
as _i3.Future<void>);

@override
_i3.Future<void> setJavaScriptCanOpenWindowsAutomatically(bool? enabled) =>
(super.noSuchMethod(
Invocation.method(#setJavaScriptCanOpenWindowsAutomatically, [
enabled,
]),
returnValue: _i3.Future<void>.value(),
returnValueForMissingStub: _i3.Future<void>.value(),
)
as _i3.Future<void>);

@override
_i2.WKPreferences pigeon_copy() =>
(super.noSuchMethod(
Expand Down