Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
6 changes: 6 additions & 0 deletions packages/webview_flutter/webview_flutter/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## 4.15.0

* Adds `WebViewController.addDocumentStartJavaScript` and
`WebViewController.supportsAddDocumentStartJavaScript` to support injecting JavaScript that runs
before the page's own scripts.

## 4.14.1

* Adds documentation for `NavigationDelegate` callback parameters.
Expand Down
6 changes: 6 additions & 0 deletions packages/webview_flutter/webview_flutter/example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,9 @@ flutter:
- assets/sample_video.mp4
- assets/www/index.html
- assets/www/styles/style.css
# FOR TESTING AND INITIAL REVIEW ONLY. DO NOT MERGE.
# See https://github.com/flutter/flutter/blob/master/docs/ecosystem/contributing/README.md#changing-federated-plugins
dependency_overrides:
webview_flutter_android: {path: ../../../../packages/webview_flutter/webview_flutter_android}
webview_flutter_platform_interface: {path: ../../../../packages/webview_flutter/webview_flutter_platform_interface}
webview_flutter_wkwebview: {path: ../../../../packages/webview_flutter/webview_flutter_wkwebview}
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,40 @@ class WebViewController {
return platform.runJavaScriptReturningResult(javaScript);
}

/// Injects JavaScript that runs when the document element is created,
/// before any other content of the page loads.
///
/// Registered scripts run on the current page and every page loaded
/// afterwards. This can be used to set up state that the page's own scripts
/// rely on before they run:
///
/// ```dart
/// final WebViewController controller = WebViewController();
/// controller.addDocumentStartJavaScript(
/// const DocumentStartJavaScriptParams(
/// source: "localStorage.setItem('theme', 'dark');",
/// ),
/// );
/// ```
///
/// Platform implementations may extend [DocumentStartJavaScriptParams] with
/// additional platform-specific parameters, such as the origins a script is
/// allowed to run in.
///
/// May not be supported on all platforms and WebView versions; check
/// [supportsAddDocumentStartJavaScript] before calling this method.
Future<void> addDocumentStartJavaScript(DocumentStartJavaScriptParams params) {
return platform.addDocumentStartJavaScript(params);
}

/// Returns true if the current platform and WebView implementation support
/// injecting JavaScript at document start.
///
/// See [addDocumentStartJavaScript].
Future<bool> supportsAddDocumentStartJavaScript() {
return platform.supportsAddDocumentStartJavaScript();
}

/// Adds a new JavaScript channel to the set of enabled channels.
///
/// The JavaScript code can then call `postMessage` on that object to send a
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

export 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'
show
DocumentStartJavaScriptParams,
HttpAuthRequest,
HttpResponseError,
HttpResponseErrorCallback,
Expand Down
14 changes: 10 additions & 4 deletions packages/webview_flutter/webview_flutter/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: webview_flutter
description: A Flutter plugin that provides a WebView widget backed by the system webview.
repository: https://github.com/flutter/packages/tree/main/packages/webview_flutter/webview_flutter
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22
version: 4.14.1
version: 4.15.0

environment:
sdk: ^3.10.0
Expand All @@ -21,9 +21,9 @@ flutter:
dependencies:
flutter:
sdk: flutter
webview_flutter_android: ^4.12.0
webview_flutter_platform_interface: ^2.15.1
webview_flutter_wkwebview: ^3.25.1
webview_flutter_android: ^4.14.0
webview_flutter_platform_interface: ^2.16.0
webview_flutter_wkwebview: ^3.27.0

dev_dependencies:
build_runner: ^2.1.5
Expand All @@ -36,3 +36,9 @@ topics:
- html
- webview
- webview-flutter
# FOR TESTING AND INITIAL REVIEW ONLY. DO NOT MERGE.
# See https://github.com/flutter/flutter/blob/master/docs/ecosystem/contributing/README.md#changing-federated-plugins
dependency_overrides:
webview_flutter_android: {path: ../../../packages/webview_flutter/webview_flutter_android}
webview_flutter_platform_interface: {path: ../../../packages/webview_flutter/webview_flutter_platform_interface}
webview_flutter_wkwebview: {path: ../../../packages/webview_flutter/webview_flutter_wkwebview}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,27 @@ void main() {
verify(mockPlatformWebViewController.loadHtmlString('html', baseUrl: 'baseUrl'));
});

test('addDocumentStartJavaScript', () async {
final mockPlatformWebViewController = MockPlatformWebViewController();

final webViewController = WebViewController.fromPlatform(mockPlatformWebViewController);

const params = DocumentStartJavaScriptParams(source: 'window.injected = true;');
await webViewController.addDocumentStartJavaScript(params);
verify(mockPlatformWebViewController.addDocumentStartJavaScript(params));
});

test('supportsAddDocumentStartJavaScript', () async {
final mockPlatformWebViewController = MockPlatformWebViewController();
when(
mockPlatformWebViewController.supportsAddDocumentStartJavaScript(),
).thenAnswer((_) => Future<bool>.value(true));

final webViewController = WebViewController.fromPlatform(mockPlatformWebViewController);

await expectLater(webViewController.supportsAddDocumentStartJavaScript(), completion(true));
});

test('loadRequest', () async {
final mockPlatformWebViewController = MockPlatformWebViewController();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,23 @@ class MockPlatformWebViewController extends _i1.Mock implements _i4.PlatformWebV
)
as _i5.Future<void>);

@override
_i5.Future<void> addDocumentStartJavaScript(_i2.DocumentStartJavaScriptParams? params) =>
(super.noSuchMethod(
Invocation.method(#addDocumentStartJavaScript, [params]),
returnValue: _i5.Future<void>.value(),
returnValueForMissingStub: _i5.Future<void>.value(),
)
as _i5.Future<void>);

@override
_i5.Future<bool> supportsAddDocumentStartJavaScript() =>
(super.noSuchMethod(
Invocation.method(#supportsAddDocumentStartJavaScript, []),
returnValue: _i5.Future<bool>.value(false),
)
as _i5.Future<bool>);

@override
_i5.Future<String?> getTitle() =>
(super.noSuchMethod(
Expand Down
7 changes: 7 additions & 0 deletions packages/webview_flutter/webview_flutter_android/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## 4.14.0

* Adds support for `PlatformWebViewController.addDocumentStartJavaScript` and
`PlatformWebViewController.supportsAddDocumentStartJavaScript`.
* Introduces `AndroidDocumentStartJavaScriptParams`, a platform-specific extension of
`DocumentStartJavaScriptParams` for Android that adds support for `allowedOriginRules`.

## 4.13.0

* Adds new method for accessing a native `WebView` from a `FlutterPluginBinding`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,12 @@ abstract class AndroidWebkitLibraryPigeonProxyApiRegistrar(val binaryMessenger:
*/
abstract fun getPigeonApiWebViewFeature(): PigeonApiWebViewFeature

/**
* An implementation of [PigeonApiWebViewCompat] used to add a new Dart instance of
* `WebViewCompat` to the Dart `InstanceManager`.
*/
abstract fun getPigeonApiWebViewCompat(): PigeonApiWebViewCompat

fun setUp() {
AndroidWebkitLibraryPigeonInstanceManagerApi.setUpMessageHandlers(
binaryMessenger, instanceManager)
Expand Down Expand Up @@ -643,6 +649,7 @@ abstract class AndroidWebkitLibraryPigeonProxyApiRegistrar(val binaryMessenger:
PigeonApiWebSettingsCompat.setUpMessageHandlers(
binaryMessenger, getPigeonApiWebSettingsCompat())
PigeonApiWebViewFeature.setUpMessageHandlers(binaryMessenger, getPigeonApiWebViewFeature())
PigeonApiWebViewCompat.setUpMessageHandlers(binaryMessenger, getPigeonApiWebViewCompat())
}

fun tearDown() {
Expand Down Expand Up @@ -670,6 +677,7 @@ abstract class AndroidWebkitLibraryPigeonProxyApiRegistrar(val binaryMessenger:
PigeonApiCertificate.setUpMessageHandlers(binaryMessenger, null)
PigeonApiWebSettingsCompat.setUpMessageHandlers(binaryMessenger, null)
PigeonApiWebViewFeature.setUpMessageHandlers(binaryMessenger, null)
PigeonApiWebViewCompat.setUpMessageHandlers(binaryMessenger, null)
}
}

Expand Down Expand Up @@ -913,6 +921,12 @@ private class AndroidWebkitLibraryPigeonProxyApiBaseCodec(
logNewInstanceFailure("WebViewFeature", value, it.exceptionOrNull())
}
}
} else if (value is androidx.webkit.WebViewCompat) {
registrar.getPigeonApiWebViewCompat().pigeon_newInstance(value) {
if (it.isFailure) {
logNewInstanceFailure("WebViewCompat", value, it.exceptionOrNull())
}
}
}

when {
Expand Down Expand Up @@ -6971,3 +6985,94 @@ abstract class PigeonApiWebViewFeature(
}
}
}
/**
* Compatibility version of `WebView`.
*
* See https://developer.android.com/reference/kotlin/androidx/webkit/WebViewCompat.
*/
@Suppress("UNCHECKED_CAST")
abstract class PigeonApiWebViewCompat(
open val pigeonRegistrar: AndroidWebkitLibraryPigeonProxyApiRegistrar
) {
/**
* Adds a JavaScript script to the `WebView` which will be executed in any frame whose origin
* matches `allowedOriginRules` when the document begins to load.
*
* This method should only be called if `WebViewFeature.isFeatureSupported` returns true for
* `WebViewFeature.DOCUMENT_START_SCRIPT`.
*/
abstract fun addDocumentStartJavaScript(
webView: android.webkit.WebView,
script: String,
allowedOriginRules: List<String>
)

companion object {
@Suppress("LocalVariableName")
fun setUpMessageHandlers(binaryMessenger: BinaryMessenger, api: PigeonApiWebViewCompat?) {
val codec = api?.pigeonRegistrar?.codec ?: AndroidWebkitLibraryPigeonCodec()
run {
val channel =
BasicMessageChannel<Any?>(
binaryMessenger,
"dev.flutter.pigeon.webview_flutter_android.WebViewCompat.addDocumentStartJavaScript",
codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val webViewArg = args[0] as android.webkit.WebView
val scriptArg = args[1] as String
val allowedOriginRulesArg = args[2] as List<String>
val wrapped: List<Any?> =
try {
api.addDocumentStartJavaScript(webViewArg, scriptArg, allowedOriginRulesArg)
listOf(null)
} catch (exception: Throwable) {
AndroidWebkitLibraryPigeonUtils.wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
}
}

@Suppress("LocalVariableName", "FunctionName")
/** Creates a Dart instance of WebViewCompat and attaches it to [pigeon_instanceArg]. */
fun pigeon_newInstance(
pigeon_instanceArg: androidx.webkit.WebViewCompat,
callback: (Result<Unit>) -> Unit
) {
if (pigeonRegistrar.ignoreCallsToDart) {
callback(
Result.failure(
AndroidWebKitError("ignore-calls-error", "Calls to Dart are being ignored.", "")))
} else if (pigeonRegistrar.instanceManager.containsInstance(pigeon_instanceArg)) {
callback(Result.success(Unit))
} else {
val pigeon_identifierArg =
pigeonRegistrar.instanceManager.addHostCreatedInstance(pigeon_instanceArg)
val binaryMessenger = pigeonRegistrar.binaryMessenger
val codec = pigeonRegistrar.codec
val channelName =
"dev.flutter.pigeon.webview_flutter_android.WebViewCompat.pigeon_newInstance"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(listOf(pigeon_identifierArg)) {
if (it is List<*>) {
if (it.size > 1) {
callback(
Result.failure(
AndroidWebKitError(it[0] as String, it[1] as String, it[2] as String?)))
} else {
callback(Result.success(Unit))
}
} else {
callback(
Result.failure(AndroidWebkitLibraryPigeonUtils.createConnectionError(channelName)))
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -261,4 +261,10 @@ public PigeonApiWebViewFeature getPigeonApiWebViewFeature() {
public PigeonApiWebSettingsCompat getPigeonApiWebSettingsCompat() {
return new WebSettingsCompatProxyApi(this);
}

@NonNull
@Override
public PigeonApiWebViewCompat getPigeonApiWebViewCompat() {
return new WebViewCompatProxyApi(this);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright 2013 The Flutter Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package io.flutter.plugins.webviewflutter;

import android.annotation.SuppressLint;
import android.webkit.WebView;
import androidx.annotation.NonNull;
import androidx.webkit.WebViewCompat;
import java.util.HashSet;
import java.util.List;

/**
* Proxy API implementation for {@link WebViewCompat}.
*
* <p>This class may handle instantiating and adding native object instances that are attached to a
* Dart instance or handle method calls on the associated native class or an instance of the class.
*/
public class WebViewCompatProxyApi extends PigeonApiWebViewCompat {
public WebViewCompatProxyApi(@NonNull ProxyApiRegistrar pigeonRegistrar) {
super(pigeonRegistrar);
}

/**
* This method should only be called if {@link WebViewFeatureProxyApi#isFeatureSupported(String)}
* with DOCUMENT_START_SCRIPT returns true.
*/
@SuppressLint("RequiresFeature")
@Override
public void addDocumentStartJavaScript(
@NonNull WebView webView, @NonNull String script, @NonNull List<String> allowedOriginRules) {
WebViewCompat.addDocumentStartJavaScript(webView, script, new HashSet<>(allowedOriginRules));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright 2013 The Flutter Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package io.flutter.plugins.webviewflutter;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockStatic;

import android.webkit.WebView;
import androidx.webkit.WebViewCompat;
import java.util.Collections;
import java.util.HashSet;
import org.junit.Test;
import org.mockito.MockedStatic;

public class WebViewCompatTest {
@Test
public void addDocumentStartJavaScript() {
final PigeonApiWebViewCompat api = new TestProxyApiRegistrar().getPigeonApiWebViewCompat();

final WebView webView = mock(WebView.class);

try (MockedStatic<WebViewCompat> mockedStatic = mockStatic(WebViewCompat.class)) {
api.addDocumentStartJavaScript(
webView, "window.injected = true;", Collections.singletonList("https://example.com"));

mockedStatic.verify(
() ->
WebViewCompat.addDocumentStartJavaScript(
webView,
"window.injected = true;",
new HashSet<>(Collections.singletonList("https://example.com"))));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,7 @@ flutter:
- assets/sample_video.mp4
- assets/www/index.html
- assets/www/styles/style.css
# FOR TESTING AND INITIAL REVIEW ONLY. DO NOT MERGE.
# See https://github.com/flutter/flutter/blob/master/docs/ecosystem/contributing/README.md#changing-federated-plugins
dependency_overrides:
webview_flutter_platform_interface: {path: ../../../../packages/webview_flutter/webview_flutter_platform_interface}
Loading