Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
fabfce2
build(windows): update VS 2026 NuGet dependencies
Y-PLONI Aug 27, 2026
72a964c
fix(windows): handle new WebView2 permission kinds
Y-PLONI Aug 27, 2026
c568a24
fix(windows): keep WebView hosts as child windows
Y-PLONI Aug 27, 2026
27859a4
fix(android): restrict captured media FileProvider paths
Y-PLONI Aug 27, 2026
f47daea
fix(ios): repair legacy async JavaScript wrapper
Y-PLONI Aug 27, 2026
58b5b65
macos: constrain ASWebAuthenticationPresentationContextProviding conf…
zhupengjia Aug 5, 2026
0d8b496
feat(linux): enforce scroll disable settings via embedder filtering
zhupengjia Aug 7, 2026
399ed86
fix(ios, macos): terminate PrintJS assignment with a semicolon
anies1212 Jul 29, 2026
1477f7b
fix(ios): restore content inset after keyboard dismissal
Manuito83 Aug 27, 2026
542d6d5
Fix nullable Android context menu title
Jun 26, 2026
59a54db
fix(ios): avoid popup content-world evaluation crashes
studiogaram Aug 27, 2026
2ce259a
fix(macos): guard unavailable WebKit HTTPS upgrade selectors
liuyanqing11 Aug 27, 2026
87e9dd2
add support for WebAuthenticationSession additionalHeaderFields
schulzp May 11, 2026
babedb2
Fix macOS web view frame drift
jay-lan May 4, 2026
a556958
[flutter_inappwebview_android] Fix easy Java deprecation warnings
Khairul989 Apr 21, 2026
30220da
fix(android): avoid deprecated setStatusBarColor on Android 15+ to fi…
ndenissov Apr 7, 2026
29a1762
Add audio intent
PrimozRatej Apr 7, 2026
2294236
Android: add InAppWebViewController.setBackgroundColor to override th…
Manuito83 Jul 5, 2026
7d275b4
feat: Add PreferredColorScheme support for WebView2 color scheme mana…
Jan 17, 2026
ae4eebf
fix: remove printing headers inside server listen
May 26, 2025
9244482
fix(windows): guard async setState/localToGlobal after dispose in Cus…
zenalex Apr 19, 2026
9c2714c
fix: `ChannelController.debugAssertNotDisposed()` throwing when calli…
MSOB7YY Mar 12, 2025
0b997be
bugfix/ajax-blob
EArminjon Apr 9, 2024
8c412f4
Fix CVE-2020-6563: reject sandbox file:// URIs from file picker
AlexV525 Aug 9, 2026
d2f4af1
style(macos): normalize frame-sync line endings
Y-PLONI Aug 27, 2026
041465f
fix(ios): preserve popup content-world isolation
Y-PLONI Aug 27, 2026
23e0155
fix(windows): compare color scheme with shared profile
Y-PLONI Aug 27, 2026
f754b6f
style(ios): normalize popup evaluation line endings
Y-PLONI Aug 27, 2026
b535c8f
fix(ios): keep popup page-world async evaluation
Y-PLONI Aug 27, 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
3 changes: 3 additions & 0 deletions flutter_inappwebview/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
## 6.2.0-beta.3

- Added Android `InAppWebViewController.setBackgroundColor`.
- Added `WebAuthenticationSession.additionalHeaderFields` on iOS 17.4+ and
macOS 14.4+.
- Added Linux support
- Updated dependencies to the latest versions for all platform implementations:
- `flutter_inappwebview_platform_interface`: `^1.4.0-beta.2` -> `^1.4.0-beta.3`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -391,5 +391,77 @@ void interceptAjaxRequest() {
true,
);
});

skippableTestWidgets('receive blob response', (WidgetTester tester) async {
final readyStateResponse = Completer<List<int>>();
final progressResponse = Completer<List<int>>();
List<int> responseBytes(dynamic response) {
if (response is List) return List<int>.from(response);
final entries = (response as Map).entries.toList()
..sort(
(a, b) => int.parse(
a.key.toString(),
).compareTo(int.parse(b.key.toString())),
);
return List<int>.from(entries.map((entry) => entry.value));
}

await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: InAppWebView(
key: GlobalKey(),
initialData: InAppWebViewInitialData(
data: """
<!doctype html>
<html lang="en">
<body>
<script>
window.addEventListener('flutterInAppWebViewPlatformReady', function() {
const blob = new Blob([new Uint8Array([0, 1, 255])]);
const blobUrl = URL.createObjectURL(blob);
const xhr = new XMLHttpRequest();
xhr.open('GET', blobUrl);
xhr.responseType = 'blob';
xhr.addEventListener('loadend', function() {
URL.revokeObjectURL(blobUrl);
});
xhr.send();
});
</script>
</body>
</html>
""",
),
shouldInterceptAjaxRequest: (controller, request) async => request,
onAjaxReadyStateChange: (controller, request) async {
if (request.readyState == AjaxRequestReadyState.DONE &&
request.response != null &&
!readyStateResponse.isCompleted) {
readyStateResponse.complete(responseBytes(request.response));
}
return AjaxRequestAction.PROCEED;
},
onAjaxProgress: (controller, request) async {
if (request.event?.type == AjaxRequestEventType.LOAD &&
request.response != null &&
!progressResponse.isCompleted) {
progressResponse.complete(responseBytes(request.response));
}
return AjaxRequestAction.PROCEED;
},
),
),
);

expect(
await readyStateResponse.future.timeout(const Duration(seconds: 10)),
[0, 1, 255],
);
expect(
await progressResponse.future.timeout(const Duration(seconds: 10)),
[0, 1, 255],
);
});
}, skip: shouldSkip);
}
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,65 @@ void javascriptCodeEvaluation() {
expect(result, 49);
}, skip: shouldSkipTest2);

final shouldSkipPrintBoundaryTest =
kIsWeb ||
(!Platform.isIOS && !Platform.isMacOS) ||
!InAppWebViewController.isMethodSupported(
PlatformInAppWebViewControllerMethod.evaluateJavascript,
);

skippableTestWidgets(
'content world IIFE does not invoke print',
(WidgetTester tester) async {
final controllerCompleter = Completer<InAppWebViewController>();
final firstLoad = Completer<void>();
final secondLoad = Completer<void>();
var printRequests = 0;

await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: InAppWebView(
key: GlobalKey(),
initialUrlRequest: URLRequest(url: TEST_URL_ABOUT_BLANK),
onWebViewCreated: controllerCompleter.complete,
onLoadStop: (controller, url) {
if (!firstLoad.isCompleted) {
firstLoad.complete();
} else if (!secondLoad.isCompleted) {
secondLoad.complete();
}
},
onPrintRequest: (controller, url, printJob) async {
printRequests++;
return false;
},
),
),
);

final controller = await controllerCompleter.future;
await firstLoad.future;
final contentWorld = ContentWorld.world(name: 'print-boundary-world');

Future<void> expectIifeEvaluation() async {
final result = await controller.evaluateJavascript(
source: "(function () { return 'content-world-ok'; })();",
contentWorld: contentWorld,
);
expect(result, 'content-world-ok');
await tester.pump();
expect(printRequests, 0);
}

await expectIifeEvaluation();
await controller.reload();
await secondLoad.future;
await expectIifeEvaluation();
},
skip: shouldSkipPrintBoundaryTest,
);

final shouldSkipTest3 = !InAppWebViewController.isMethodSupported(
PlatformInAppWebViewControllerMethod.callAsyncJavaScript,
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ void webViewWindows() {

var windowId = await onCreateWindowCompleter.future;

final Completer windowControllerCompleter =
final Completer<InAppWebViewController> windowControllerCompleter =
Completer<InAppWebViewController>();
final Completer<String> windowPageLoaded = Completer<String>();
final Completer<void> onCloseWindowCompleter = Completer<void>();
Expand All @@ -105,7 +105,6 @@ void webViewWindows() {
onLoadStop: (controller, url) async {
if (url!.scheme != "about" && !windowPageLoaded.isCompleted) {
windowPageLoaded.complete(url.toString());
await controller.evaluateJavascript(source: "window.close();");
}
},
onCloseWindow: (controller) {
Expand All @@ -118,8 +117,144 @@ void webViewWindows() {
await tester.pump();

final String windowUrlLoaded = await windowPageLoaded.future;
final windowController = await windowControllerCompleter.future;

expect(windowUrlLoaded, TEST_URL_EXAMPLE.toString());

if (Platform.isIOS) {
final popupWorld = ContentWorld.world(name: 'popup-evaluation');
expect(
await windowController.evaluateJavascript(
source: 'window.popupPageValue = 7; window.popupPageValue;',
),
7,
);

final namedWorldValue = await windowController.evaluateJavascript(
source: 'window.popupPageValue = 49; window.popupPageValue;',
contentWorld: popupWorld,
);
// iOS 18+ preserves the named world; iOS 14-17 fails closed because
// WebKit can crash when that overload is used by a popup WebView.
expect(namedWorldValue, anyOf(isNull, 49));

// A named-world request must never fall back to the page world.
expect(
await windowController.evaluateJavascript(
source: 'window.popupPageValue;',
),
7,
);

final persistedNamedWorldValue = await windowController
.evaluateJavascript(
source: 'window.popupPageValue;',
contentWorld: popupWorld,
);
if (namedWorldValue == null) {
expect(persistedNamedWorldValue, isNull);
} else {
expect(persistedNamedWorldValue, 49);
expect(
await windowController.evaluateJavascript(
source: 'typeof window.popupOnlyValue;',
),
'undefined',
);
expect(
await windowController.evaluateJavascript(
source: 'window.popupOnlyValue = 11;',
contentWorld: popupWorld,
),
11,
);
expect(
await windowController.evaluateJavascript(
source: 'typeof window.popupOnlyValue;',
),
'undefined',
);
}

final asyncPageWorldResult = await windowController.callAsyncJavaScript(
functionBody:
'await Promise.resolve(); '
'window.popupPageAsyncValue = n + 2; '
'return window.popupPageAsyncValue;',
arguments: {'n': 40},
);
expect(asyncPageWorldResult, isNotNull);
expect(asyncPageWorldResult!.value, 42);
expect(asyncPageWorldResult.error, isNull);
expect(
await windowController.evaluateJavascript(
source: 'window.popupPageAsyncValue;',
),
42,
);
final explicitPageWorldResult = await windowController
.callAsyncJavaScript(
functionBody: 'return n + 3;',
arguments: {'n': 39},
contentWorld: ContentWorld.PAGE,
);
expect(explicitPageWorldResult, isNotNull);
expect(explicitPageWorldResult!.value, 42);
expect(explicitPageWorldResult.error, isNull);

final asyncPageWorldError = await windowController.callAsyncJavaScript(
functionBody: "throw new Error('popup-page-async-error');",
);
expect(asyncPageWorldError, isNotNull);
expect(asyncPageWorldError!.value, isNull);
expect(asyncPageWorldError.error, contains('popup-page-async-error'));

final asyncNamedWorldResult = await windowController
.callAsyncJavaScript(
functionBody:
'window.popupAsyncOnlyValue = 23; '
'return window.popupAsyncOnlyValue;',
contentWorld: popupWorld,
);
expect(asyncNamedWorldResult, isNotNull);
if (namedWorldValue == null) {
expect(asyncNamedWorldResult!.value, isNull);
expect(asyncNamedWorldResult.error, isNotNull);
} else {
expect(asyncNamedWorldResult!.value, 23);
expect(asyncNamedWorldResult.error, isNull);
}
expect(
await windowController.evaluateJavascript(
source: 'typeof window.popupAsyncOnlyValue;',
),
'undefined',
);

expect(
await windowController.evaluateJavascript(
source: 'null;',
contentWorld: popupWorld,
),
isNull,
);
expect(
await windowController.evaluateJavascript(
source: 'undefined;',
contentWorld: popupWorld,
),
isNull,
);
expect(
await windowController.evaluateJavascript(
source: "throw new Error('popup-eval-error');",
contentWorld: popupWorld,
),
isNull,
);
}

await windowController.evaluateJavascript(source: 'window.close();');
await expectLater(onCloseWindowCompleter.future, completes);
}, skip: shouldSkipTest2);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,12 @@ class InAppWebViewController {
///{@macro flutter_inappwebview_platform_interface.PlatformInAppWebViewController.reload.supported_platforms}
Future<void> reload() => platform.reload();

///{@macro flutter_inappwebview_platform_interface.PlatformInAppWebViewController.setBackgroundColor}
///
///{@macro flutter_inappwebview_platform_interface.PlatformInAppWebViewController.setBackgroundColor.supported_platforms}
Future<void> setBackgroundColor(Color color) =>
platform.setBackgroundColor(color);

///{@macro flutter_inappwebview_platform_interface.PlatformInAppWebViewController.goBack}
///
///{@macro flutter_inappwebview_platform_interface.PlatformInAppWebViewController.goBack.supported_platforms}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ class WebAuthenticationSession {
///{@macro flutter_inappwebview_platform_interface.PlatformWebAuthenticationSession.callbackURLScheme.supported_platforms}
String? get callbackURLScheme => platform.callbackURLScheme;

///{@macro flutter_inappwebview_platform_interface.PlatformWebAuthenticationSession.additionalHeaderFields}
///
///{@macro flutter_inappwebview_platform_interface.PlatformWebAuthenticationSession.additionalHeaderFields.supported_platforms}
Map<String, String>? get additionalHeaderFields =>
platform.additionalHeaderFields;

///{@macro flutter_inappwebview_platform_interface.PlatformWebAuthenticationSession.initialSettings}
///
///{@macro flutter_inappwebview_platform_interface.PlatformWebAuthenticationSession.initialSettings.supported_platforms}
Expand All @@ -62,13 +68,15 @@ class WebAuthenticationSession {
static Future<WebAuthenticationSession> create({
required WebUri url,
String? callbackURLScheme,
Map<String, String>? additionalHeaderFields,
WebAuthenticationSessionCompletionHandler onComplete,
WebAuthenticationSessionSettings? initialSettings,
}) async {
return WebAuthenticationSession.fromPlatform(
platform: await PlatformWebAuthenticationSession.static().create(
url: url,
callbackURLScheme: callbackURLScheme,
additionalHeaderFields: additionalHeaderFields,
onComplete: onComplete,
initialSettings: initialSettings,
),
Expand Down
12 changes: 12 additions & 0 deletions flutter_inappwebview_android/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
## 1.2.0-beta.3

- Added `InAppWebViewController.setBackgroundColor`, retaining the selected
native color across `transparentBackground` toggles.
- Added audio capture to Android file choosers, with file-selection fallback
when no recorder is installed.
- Avoided the deprecated status-bar color call when Android 15 edge-to-edge is
enforced, while preserving legacy target-SDK behavior.
- Reduced Java deprecation warnings while preserving synchronous session-cookie
clearing [#2641](https://github.com/pichillilorenzo/flutter_inappwebview/issues/2641).
- Fixed nullable Android context-menu action titles.
- Restricted captured-media FileProvider access to the app-scoped `Captures`
directory.
- Rejected unsafe `file://` URIs returned by Android file pickers.
- Updated flutter_inappwebview_platform_interface version to ^1.4.0-beta.3
- Updated native dependencies:
- implementation from `'androidx.webkit:webkit:1.12.0'` to `'androidx.webkit:webkit:1.14.0'`
Expand Down
1 change: 1 addition & 0 deletions flutter_inappwebview_android/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -58,5 +58,6 @@ android {
implementation 'androidx.browser:browser:1.9.0'
implementation 'androidx.appcompat:appcompat:1.7.1'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.2.0'
testImplementation 'junit:junit:4.13.2'
}
}
Loading
Loading