[webview_flutter] Update webview packages for Android and iOS to implement `runJavascript` and `runJavascriptReturningResult`. (#4402)
* [webview_flutter_platform_interface] Update webview platform interface with new methods for running JavaScript
* Fix tests
* [webview_flutter] Implemented `runJavaScript` and `runJavaScriptForResult` in iOS and Android packages.
* Remove accidental development team inclusion from project.pbxproj
* Implemented PR feedback
* Updated runJavaScriptForResult behaviour
* Implemented PR feedback partially
* Implement PR feedback
* Update changelog
* Implement PR feedback from interface PR
* Update changelog
* Revert inclusion of development team
* Implement PR feedback
* Implemented platform interface PR feedback
* Update pubspec dependency
* Fixed capitalisation
* Fixed capitalisation
* Fix warning
* Partially implement PR feedback
* Partially implement PR feedback
* Format
* Update podfile
* Update podfile
* Update podfiles
* Update iOS project files
* Update podspec
* Remove unnecessary podfile configuration
* Implemented PR feedback
* Format
* Revert podfile changes
* Implemented PR feedback
* Fix formatting
* Fix formatting
* Fixed test.
* Re-add integration tests for deprecated evaluateJavascript method.
* Fix merge conflicts
diff --git a/packages/webview_flutter/webview_flutter/example/ios/Runner.xcodeproj/project.pbxproj b/packages/webview_flutter/webview_flutter/example/ios/Runner.xcodeproj/project.pbxproj
index 62428d0..0759b31 100644
--- a/packages/webview_flutter/webview_flutter/example/ios/Runner.xcodeproj/project.pbxproj
+++ b/packages/webview_flutter/webview_flutter/example/ios/Runner.xcodeproj/project.pbxproj
@@ -11,13 +11,13 @@
334734012669319100DCC49E /* FLTWebViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 68BDCAF523C3F97800D9C032 /* FLTWebViewTests.m */; };
334734022669319400DCC49E /* FLTWKNavigationDelegateTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 686B4BF82548DBC7000AEA36 /* FLTWKNavigationDelegateTests.m */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
+ 63D2F2FB307F1F037702C198 /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BEC8CD326B252E47ABE6C037 /* libPods-RunnerTests.a */; };
978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; };
97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
- 9D26F6F82D91F92CC095EBA9 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B845D8FBDE0AAD6BE1A0386 /* libPods-Runner.a */; };
- D9A9D48F1A75E5C682944DDD /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 27CC950C9005575711528C12 /* libPods-RunnerTests.a */; };
+ E6159E2B6496F35B1D4F4096 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C0ABA59F25635F077C9EA161 /* libPods-Runner.a */; };
F7151F77266057800028CB91 /* FLTWebViewUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = F7151F76266057800028CB91 /* FLTWebViewUITests.m */; };
/* End PBXBuildFile section */
@@ -52,11 +52,13 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
- 127772EEA7782174BE0D74B5 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
+ 11DF059E983DF25F078B44CC /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
- 27CC950C9005575711528C12 /* libPods-RunnerTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RunnerTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
+ 3CEFE8F0E91B9792E4EE427B /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
+ 4D2B3F45D8E6CA81EA52591E /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
+ 5D19D984A61169BB95DB0FED /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
686B4BF82548DBC7000AEA36 /* FLTWKNavigationDelegateTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLTWKNavigationDelegateTests.m; sourceTree = "<group>"; };
68BDCAE923C3F7CB00D9C032 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
68BDCAED23C3F7CB00D9C032 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@@ -64,7 +66,6 @@
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
- 8B845D8FBDE0AAD6BE1A0386 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -73,9 +74,8 @@
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
- C475C484BD510DD9CB2E403C /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
- E14113434CCE6D3186B5CBC3 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
- F674B2A05DAC369B4FF27850 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
+ BEC8CD326B252E47ABE6C037 /* libPods-RunnerTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RunnerTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+ C0ABA59F25635F077C9EA161 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; };
F7151F74266057800028CB91 /* RunnerUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
F7151F76266057800028CB91 /* FLTWebViewUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLTWebViewUITests.m; sourceTree = "<group>"; };
F7151F78266057800028CB91 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@@ -86,7 +86,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
- D9A9D48F1A75E5C682944DDD /* libPods-RunnerTests.a in Frameworks */,
+ 63D2F2FB307F1F037702C198 /* libPods-RunnerTests.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -94,7 +94,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
- 9D26F6F82D91F92CC095EBA9 /* libPods-Runner.a in Frameworks */,
+ E6159E2B6496F35B1D4F4096 /* libPods-Runner.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -108,6 +108,15 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
+ 00D2395F7DDFEE571DF3C0B1 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ C0ABA59F25635F077C9EA161 /* libPods-Runner.a */,
+ BEC8CD326B252E47ABE6C037 /* libPods-RunnerTests.a */,
+ );
+ name = Frameworks;
+ sourceTree = "<group>";
+ };
68BDCAEA23C3F7CB00D9C032 /* RunnerTests */ = {
isa = PBXGroup;
children = (
@@ -137,8 +146,8 @@
68BDCAEA23C3F7CB00D9C032 /* RunnerTests */,
F7151F75266057800028CB91 /* RunnerUITests */,
97C146EF1CF9000F007C117D /* Products */,
- C6FFB52F5C2B8A41A7E39DE2 /* Pods */,
- B6736FC417BDCCDA377E779D /* Frameworks */,
+ EA36D6F90B795550E32A139A /* Pods */,
+ 00D2395F7DDFEE571DF3C0B1 /* Frameworks */,
);
sourceTree = "<group>";
};
@@ -176,24 +185,16 @@
name = "Supporting Files";
sourceTree = "<group>";
};
- B6736FC417BDCCDA377E779D /* Frameworks */ = {
+ EA36D6F90B795550E32A139A /* Pods */ = {
isa = PBXGroup;
children = (
- 8B845D8FBDE0AAD6BE1A0386 /* libPods-Runner.a */,
- 27CC950C9005575711528C12 /* libPods-RunnerTests.a */,
- );
- name = Frameworks;
- sourceTree = "<group>";
- };
- C6FFB52F5C2B8A41A7E39DE2 /* Pods */ = {
- isa = PBXGroup;
- children = (
- 127772EEA7782174BE0D74B5 /* Pods-Runner.debug.xcconfig */,
- C475C484BD510DD9CB2E403C /* Pods-Runner.release.xcconfig */,
- F674B2A05DAC369B4FF27850 /* Pods-RunnerTests.debug.xcconfig */,
- E14113434CCE6D3186B5CBC3 /* Pods-RunnerTests.release.xcconfig */,
+ 4D2B3F45D8E6CA81EA52591E /* Pods-Runner.debug.xcconfig */,
+ 11DF059E983DF25F078B44CC /* Pods-Runner.release.xcconfig */,
+ 3CEFE8F0E91B9792E4EE427B /* Pods-RunnerTests.debug.xcconfig */,
+ 5D19D984A61169BB95DB0FED /* Pods-RunnerTests.release.xcconfig */,
);
name = Pods;
+ path = Pods;
sourceTree = "<group>";
};
F7151F75266057800028CB91 /* RunnerUITests */ = {
@@ -212,7 +213,7 @@
isa = PBXNativeTarget;
buildConfigurationList = 68BDCAF223C3F7CB00D9C032 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
buildPhases = (
- 53FD4CBDD9756D74B5A3B4C1 /* [CP] Check Pods Manifest.lock */,
+ EA0C9BB56C9A98B4F095051B /* [CP] Check Pods Manifest.lock */,
68BDCAE523C3F7CB00D9C032 /* Sources */,
68BDCAE623C3F7CB00D9C032 /* Frameworks */,
68BDCAE723C3F7CB00D9C032 /* Resources */,
@@ -231,7 +232,7 @@
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
- B71376B4FB8384EF9D5F3F84 /* [CP] Check Pods Manifest.lock */,
+ 1B3EA6BF26F6D525A8503093 /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
@@ -338,21 +339,7 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
- 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
- isa = PBXShellScriptBuildPhase;
- buildActionMask = 2147483647;
- files = (
- );
- inputPaths = (
- );
- name = "Thin Binary";
- outputPaths = (
- );
- runOnlyForDeploymentPostprocessing = 0;
- shellPath = /bin/sh;
- shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed\n/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin\n";
- };
- 53FD4CBDD9756D74B5A3B4C1 /* [CP] Check Pods Manifest.lock */ = {
+ 1B3EA6BF26F6D525A8503093 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@@ -367,13 +354,27 @@
outputFileListPaths = (
);
outputPaths = (
- "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt",
+ "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
+ 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "Thin Binary";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed\n/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin\n";
+ };
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@@ -388,18 +389,22 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n";
};
- B71376B4FB8384EF9D5F3F84 /* [CP] Check Pods Manifest.lock */ = {
+ EA0C9BB56C9A98B4F095051B /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
+ inputFileListPaths = (
+ );
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
+ outputFileListPaths = (
+ );
outputPaths = (
- "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
+ "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
@@ -473,7 +478,7 @@
/* Begin XCBuildConfiguration section */
68BDCAF023C3F7CB00D9C032 /* Debug */ = {
isa = XCBuildConfiguration;
- baseConfigurationReference = F674B2A05DAC369B4FF27850 /* Pods-RunnerTests.debug.xcconfig */;
+ baseConfigurationReference = 3CEFE8F0E91B9792E4EE427B /* Pods-RunnerTests.debug.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
@@ -487,7 +492,7 @@
};
68BDCAF123C3F7CB00D9C032 /* Release */ = {
isa = XCBuildConfiguration;
- baseConfigurationReference = E14113434CCE6D3186B5CBC3 /* Pods-RunnerTests.release.xcconfig */;
+ baseConfigurationReference = 5D19D984A61169BB95DB0FED /* Pods-RunnerTests.release.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md
index c083726..176028f 100644
--- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md
+++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 2.2.0
+
+* Implemented new `runJavascript` and `runJavascriptReturningResult` methods in platform interface.
+
## 2.1.0
* Add `zoomEnabled` functionality.
diff --git a/packages/webview_flutter/webview_flutter_android/android/build.gradle b/packages/webview_flutter/webview_flutter_android/android/build.gradle
index ef1485a..bb9fe97 100644
--- a/packages/webview_flutter/webview_flutter_android/android/build.gradle
+++ b/packages/webview_flutter/webview_flutter_android/android/build.gradle
@@ -58,4 +58,8 @@
}
}
}
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
}
diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java
index b2a453a..ed14107 100644
--- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java
+++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java
@@ -245,7 +245,11 @@
currentUrl(result);
break;
case "evaluateJavascript":
- evaluateJavaScript(methodCall, result);
+ case "runJavascriptReturningResult":
+ evaluateJavaScript(methodCall, result, true);
+ break;
+ case "runJavascript":
+ evaluateJavaScript(methodCall, result, false);
break;
case "addJavascriptChannels":
addJavaScriptChannels(methodCall, result);
@@ -326,7 +330,8 @@
}
@TargetApi(Build.VERSION_CODES.KITKAT)
- private void evaluateJavaScript(MethodCall methodCall, final Result result) {
+ private void evaluateJavaScript(
+ MethodCall methodCall, final Result result, final boolean returnValue) {
String jsString = (String) methodCall.arguments;
if (jsString == null) {
throw new UnsupportedOperationException("JavaScript string cannot be null");
@@ -336,7 +341,11 @@
new android.webkit.ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
- result.success(value);
+ if (returnValue) {
+ result.success(value);
+ } else {
+ result.success(null);
+ }
}
});
}
diff --git a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/FlutterWebViewTest.java b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/FlutterWebViewTest.java
index fd79bcc..f26a0ea 100644
--- a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/FlutterWebViewTest.java
+++ b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/FlutterWebViewTest.java
@@ -6,31 +6,46 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.content.Context;
import android.webkit.DownloadListener;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
+import io.flutter.plugin.common.MethodCall;
+import io.flutter.plugin.common.MethodChannel;
import java.util.HashMap;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
+import org.mockito.MockedStatic;
public class FlutterWebViewTest {
private WebChromeClient mockWebChromeClient;
private DownloadListener mockDownloadListener;
private WebViewBuilder mockWebViewBuilder;
private WebView mockWebView;
+ private MethodChannel.Result mockResult;
+ private Context mockContext;
+ private MethodChannel mockMethodChannel;
@Before
public void before() {
+
mockWebChromeClient = mock(WebChromeClient.class);
mockWebViewBuilder = mock(WebViewBuilder.class);
mockWebView = mock(WebView.class);
mockDownloadListener = mock(DownloadListener.class);
+ mockResult = mock(MethodChannel.Result.class);
+ mockContext = mock(Context.class);
+ mockMethodChannel = mock(MethodChannel.class);
when(mockWebViewBuilder.setDomStorageEnabled(anyBoolean())).thenReturn(mockWebViewBuilder);
when(mockWebViewBuilder.setJavaScriptCanOpenWindowsAutomatically(anyBoolean()))
@@ -42,12 +57,11 @@
.thenReturn(mockWebViewBuilder);
when(mockWebViewBuilder.setDownloadListener(any(DownloadListener.class)))
.thenReturn(mockWebViewBuilder);
-
when(mockWebViewBuilder.build()).thenReturn(mockWebView);
}
@Test
- public void createWebView_should_create_webview_with_default_configuration() {
+ public void createWebView_shouldCreateWebViewWithDefaultConfiguration() {
FlutterWebView.createWebView(
mockWebViewBuilder, createParameterMap(false), mockWebChromeClient, mockDownloadListener);
@@ -59,6 +73,97 @@
verify(mockWebViewBuilder, times(1)).setZoomControlsEnabled(true);
}
+ @Test(expected = UnsupportedOperationException.class)
+ public void evaluateJavaScript_shouldThrowForNullString() {
+ try (MockedStatic<FlutterWebView> mockedFlutterWebView = mockStatic(FlutterWebView.class)) {
+ // Setup
+ mockedFlutterWebView
+ .when(
+ new MockedStatic.Verification() {
+ @Override
+ public void apply() throws Throwable {
+ FlutterWebView.createWebView(
+ (WebViewBuilder) any(),
+ (Map<String, Object>) any(),
+ (WebChromeClient) any(),
+ (DownloadListener) any());
+ }
+ })
+ .thenReturn(mockWebView);
+ FlutterWebView flutterWebView =
+ new FlutterWebView(mockContext, mockMethodChannel, new HashMap<String, Object>(), null);
+
+ // Run
+ flutterWebView.onMethodCall(new MethodCall("runJavascript", null), mockResult);
+ }
+ }
+
+ @Test
+ public void evaluateJavaScript_shouldReturnValueOnSuccessForReturnValue() {
+ try (MockedStatic<FlutterWebView> mockedFlutterWebView = mockStatic(FlutterWebView.class)) {
+ // Setup
+ mockedFlutterWebView
+ .when(
+ () ->
+ FlutterWebView.createWebView(
+ (WebViewBuilder) any(),
+ (Map<String, Object>) any(),
+ (WebChromeClient) any(),
+ (DownloadListener) any()))
+ .thenReturn(mockWebView);
+ doAnswer(
+ invocation -> {
+ android.webkit.ValueCallback<String> callback = invocation.getArgument(1);
+ callback.onReceiveValue("Test JavaScript Result");
+ return null;
+ })
+ .when(mockWebView)
+ .evaluateJavascript(eq("Test JavaScript String"), any());
+ FlutterWebView flutterWebView =
+ new FlutterWebView(mockContext, mockMethodChannel, new HashMap<String, Object>(), null);
+
+ // Run
+ flutterWebView.onMethodCall(
+ new MethodCall("runJavascriptReturningResult", "Test JavaScript String"), mockResult);
+
+ // Verify
+ verify(mockResult, times(1)).success("Test JavaScript Result");
+ }
+ }
+
+ @Test
+ public void evaluateJavaScript_shouldReturnNilOnSuccessForNoReturnValue() {
+ try (MockedStatic<FlutterWebView> mockedFlutterWebView = mockStatic(FlutterWebView.class)) {
+ // Setup
+ mockedFlutterWebView
+ .when(
+ () ->
+ FlutterWebView.createWebView(
+ (WebViewBuilder) any(),
+ (Map<String, Object>) any(),
+ (WebChromeClient) any(),
+ (DownloadListener) any()))
+ .thenReturn(mockWebView);
+ doAnswer(
+ invocation -> {
+ android.webkit.ValueCallback<String> callback = invocation.getArgument(1);
+ callback.onReceiveValue("Test JavaScript Result");
+ return null;
+ })
+ .when(mockWebView)
+ .evaluateJavascript(eq("Test JavaScript String"), any());
+ FlutterWebView flutterWebView =
+ new FlutterWebView(mockContext, mockMethodChannel, new HashMap<String, Object>(), null);
+
+ // Run
+ flutterWebView.onMethodCall(
+ new MethodCall("runJavascript", "Test JavaScript String"), mockResult);
+
+ // Verify
+ verify(mockResult, times(1)).success(isNull());
+ }
+ }
+
private Map<String, Object> createParameterMap(boolean usesHybridComposition) {
Map<String, Object> params = new HashMap<>();
params.put("usesHybridComposition", usesHybridComposition);
diff --git a/packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart
index c57d2bd..8e3dcb4 100644
--- a/packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart
+++ b/packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart
@@ -76,6 +76,27 @@
expect(currentUrl, secondaryUrl);
}, skip: _skipDueToIssue86757);
+ testWidgets('evaluateJavascript', (WidgetTester tester) async {
+ final Completer<WebViewController> controllerCompleter =
+ Completer<WebViewController>();
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: WebView(
+ key: GlobalKey(),
+ initialUrl: primaryUrl,
+ onWebViewCreated: (WebViewController controller) {
+ controllerCompleter.complete(controller);
+ },
+ javascriptMode: JavascriptMode.unrestricted,
+ ),
+ ),
+ );
+ final WebViewController controller = await controllerCompleter.future;
+ final String result = await controller.evaluateJavascript('1 + 1');
+ expect(result, equals('2'));
+ });
+
// TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757.
testWidgets('loadUrl with headers', (WidgetTester tester) async {
final Completer<WebViewController> controllerCompleter =
@@ -114,12 +135,12 @@
await pageLoads.stream.firstWhere((String url) => url == currentUrl);
final String content = await controller
- .evaluateJavascript('document.documentElement.innerText');
+ .runJavascriptReturningResult('document.documentElement.innerText');
expect(content.contains('flutter_test_header'), isTrue);
}, skip: _skipDueToIssue86757);
// TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757.
- testWidgets('JavaScriptChannel', (WidgetTester tester) async {
+ testWidgets('JavascriptChannel', (WidgetTester tester) async {
final Completer<WebViewController> controllerCompleter =
Completer<WebViewController>();
final Completer<void> pageStarted = Completer<void>();
@@ -159,11 +180,7 @@
await pageLoaded.future;
expect(messagesReceived, isEmpty);
- // Append a return value "1" in the end will prevent an iOS platform exception.
- // See: https://github.com/flutter/flutter/issues/66318#issuecomment-701105380
- // TODO(cyanglaz): remove the workaround "1" in the end when the below issue is fixed.
- // https://github.com/flutter/flutter/issues/66318
- await controller.evaluateJavascript('Echo.postMessage("hello");1;');
+ await controller.runJavascript('Echo.postMessage("hello");');
expect(messagesReceived, equals(<String>['hello']));
}, skip: _skipDueToIssue86757);
@@ -410,7 +427,8 @@
WebViewController controller = await controllerCompleter.future;
await pageLoaded.future;
- String isPaused = await controller.evaluateJavascript('isPaused();');
+ String isPaused =
+ await controller.runJavascriptReturningResult('isPaused();');
expect(isPaused, _webviewBool(false));
controllerCompleter = Completer<WebViewController>();
@@ -439,7 +457,7 @@
controller = await controllerCompleter.future;
await pageLoaded.future;
- isPaused = await controller.evaluateJavascript('isPaused();');
+ isPaused = await controller.runJavascriptReturningResult('isPaused();');
expect(isPaused, _webviewBool(true));
});
@@ -470,7 +488,8 @@
final WebViewController controller = await controllerCompleter.future;
await pageLoaded.future;
- String isPaused = await controller.evaluateJavascript('isPaused();');
+ String isPaused =
+ await controller.runJavascriptReturningResult('isPaused();');
expect(isPaused, _webviewBool(false));
pageLoaded = Completer<void>();
@@ -498,7 +517,7 @@
await pageLoaded.future;
- isPaused = await controller.evaluateJavascript('isPaused();');
+ isPaused = await controller.runJavascriptReturningResult('isPaused();');
expect(isPaused, _webviewBool(false));
});
@@ -548,7 +567,7 @@
await videoPlaying.future;
String fullScreen =
- await controller.evaluateJavascript('isFullScreen();');
+ await controller.runJavascriptReturningResult('isFullScreen();');
expect(fullScreen, _webviewBool(false));
});
});
@@ -614,7 +633,8 @@
await pageStarted.future;
await pageLoaded.future;
- String isPaused = await controller.evaluateJavascript('isPaused();');
+ String isPaused =
+ await controller.runJavascriptReturningResult('isPaused();');
expect(isPaused, _webviewBool(false));
controllerCompleter = Completer<WebViewController>();
@@ -648,7 +668,7 @@
await pageStarted.future;
await pageLoaded.future;
- isPaused = await controller.evaluateJavascript('isPaused();');
+ isPaused = await controller.runJavascriptReturningResult('isPaused();');
expect(isPaused, _webviewBool(true));
});
@@ -684,7 +704,8 @@
await pageStarted.future;
await pageLoaded.future;
- String isPaused = await controller.evaluateJavascript('isPaused();');
+ String isPaused =
+ await controller.runJavascriptReturningResult('isPaused();');
expect(isPaused, _webviewBool(false));
pageStarted = Completer<void>();
@@ -717,7 +738,7 @@
await pageStarted.future;
await pageLoaded.future;
- isPaused = await controller.evaluateJavascript('isPaused();');
+ isPaused = await controller.runJavascriptReturningResult('isPaused();');
expect(isPaused, _webviewBool(false));
});
});
@@ -982,15 +1003,16 @@
final WebViewController controller = await controllerCompleter.future;
await pageLoaded.future;
- final String viewportRectJSON = await _evaluateJavascript(
+ final String viewportRectJSON = await _runJavaScriptReturningResult(
controller, 'JSON.stringify(viewport.getBoundingClientRect())');
final Map<String, dynamic> viewportRectRelativeToViewport =
jsonDecode(viewportRectJSON);
// Check that the input is originally outside of the viewport.
- final String initialInputClientRectJSON = await _evaluateJavascript(
- controller, 'JSON.stringify(inputEl.getBoundingClientRect())');
+ final String initialInputClientRectJSON =
+ await _runJavaScriptReturningResult(
+ controller, 'JSON.stringify(inputEl.getBoundingClientRect())');
final Map<String, dynamic> initialInputClientRectRelativeToViewport =
jsonDecode(initialInputClientRectJSON);
@@ -999,12 +1021,13 @@
viewportRectRelativeToViewport['bottom'],
isFalse);
- await controller.evaluateJavascript('inputEl.focus()');
+ await controller.runJavascript('inputEl.focus()');
// Check that focusing the input brought it into view.
- final String lastInputClientRectJSON = await _evaluateJavascript(
- controller, 'JSON.stringify(inputEl.getBoundingClientRect())');
+ final String lastInputClientRectJSON =
+ await _runJavaScriptReturningResult(
+ controller, 'JSON.stringify(inputEl.getBoundingClientRect())');
final Map<String, dynamic> lastInputClientRectRelativeToViewport =
jsonDecode(lastInputClientRectJSON);
@@ -1060,7 +1083,7 @@
await pageLoads.stream.first; // Wait for initial page load.
final WebViewController controller = await controllerCompleter.future;
- await controller.evaluateJavascript('location.href = "$secondaryUrl"');
+ await controller.runJavascript('location.href = "$secondaryUrl"');
await pageLoads.stream.first; // Wait for the next page load.
final String? currentUrl = await controller.currentUrl();
@@ -1186,7 +1209,7 @@
await pageLoads.stream.first; // Wait for initial page load.
final WebViewController controller = await controllerCompleter.future;
await controller
- .evaluateJavascript('location.href = "https://www.youtube.com/"');
+ .runJavascript('location.href = "https://www.youtube.com/"');
// There should never be any second page load, since our new URL is
// blocked. Still wait for a potential page change for some time in order
@@ -1226,7 +1249,7 @@
await pageLoads.stream.first; // Wait for initial page load.
final WebViewController controller = await controllerCompleter.future;
- await controller.evaluateJavascript('location.href = "$secondaryUrl"');
+ await controller.runJavascript('location.href = "$secondaryUrl"');
await pageLoads.stream.first; // Wait for second page to load.
final String? currentUrl = await controller.currentUrl();
@@ -1281,7 +1304,7 @@
),
);
final WebViewController controller = await controllerCompleter.future;
- await controller.evaluateJavascript('window.open("$primaryUrl", "_blank")');
+ await controller.runJavascript('window.open("$primaryUrl", "_blank")');
await pageLoaded.future;
final String? currentUrl = await controller.currentUrl();
expect(currentUrl, primaryUrl);
@@ -1317,7 +1340,7 @@
await pageLoaded.future;
pageLoaded = Completer<void>();
- await controller.evaluateJavascript('window.open("$secondaryUrl")');
+ await controller.runJavascript('window.open("$secondaryUrl")');
await pageLoaded.future;
pageLoaded = Completer<void>();
expect(controller.currentUrl(), completion(secondaryUrl));
@@ -1331,7 +1354,7 @@
);
testWidgets(
- 'javascript does not run in parent window',
+ 'JavaScript does not run in parent window',
(WidgetTester tester) async {
final String iframe = '''
<!DOCTYPE html>
@@ -1388,9 +1411,10 @@
final WebViewController controller = await controllerCompleter.future;
await pageLoadCompleter.future;
- expect(controller.evaluateJavascript('iframeLoaded'), completion('true'));
+ expect(controller.runJavascriptReturningResult('iframeLoaded'),
+ completion('true'));
expect(
- controller.evaluateJavascript(
+ controller.runJavascriptReturningResult(
'document.querySelector("p") && document.querySelector("p").textContent'),
completion('null'),
);
@@ -1409,10 +1433,10 @@
/// Returns the value used for the HTTP User-Agent: request header in subsequent HTTP requests.
Future<String> _getUserAgent(WebViewController controller) async {
- return _evaluateJavascript(controller, 'navigator.userAgent;');
+ return _runJavaScriptReturningResult(controller, 'navigator.userAgent;');
}
-Future<String> _evaluateJavascript(
+Future<String> _runJavaScriptReturningResult(
WebViewController controller, String js) async {
- return jsonDecode(await controller.evaluateJavascript(js));
+ return jsonDecode(await controller.runJavascriptReturningResult(js));
}
diff --git a/packages/webview_flutter/webview_flutter_android/example/lib/main.dart b/packages/webview_flutter/webview_flutter_android/example/lib/main.dart
index 65f4971..a22d165 100644
--- a/packages/webview_flutter/webview_flutter_android/example/lib/main.dart
+++ b/packages/webview_flutter/webview_flutter_android/example/lib/main.dart
@@ -195,14 +195,14 @@
WebViewController controller, BuildContext context) async {
// Send a message with the user agent string to the Snackbar JavaScript channel we registered
// with the WebView.
- await controller.evaluateJavascript(
+ await controller.runJavascript(
'Snackbar.postMessage("User Agent: " + navigator.userAgent);');
}
void _onListCookies(
WebViewController controller, BuildContext context) async {
final String cookies =
- await controller.evaluateJavascript('document.cookie');
+ await controller.runJavascriptReturningResult('document.cookie');
// ignore: deprecated_member_use
Scaffold.of(context).showSnackBar(SnackBar(
content: Column(
@@ -217,7 +217,7 @@
}
void _onAddToCache(WebViewController controller, BuildContext context) async {
- await controller.evaluateJavascript(
+ await controller.runJavascript(
'caches.open("test_caches_entry"); localStorage["test_localStorage"] = "dummy_entry";');
// ignore: deprecated_member_use
Scaffold.of(context).showSnackBar(const SnackBar(
@@ -226,7 +226,7 @@
}
void _onListCache(WebViewController controller, BuildContext context) async {
- await controller.evaluateJavascript('caches.keys()'
+ await controller.runJavascript('caches.keys()'
'.then((cacheKeys) => JSON.stringify({"cacheKeys" : cacheKeys, "localStorage" : localStorage}))'
'.then((caches) => Snackbar.postMessage(caches))');
}
@@ -340,5 +340,5 @@
}
}
-/// Callback type for handling messages sent from Javascript running in a web view.
+/// Callback type for handling messages sent from JavaScript running in a web view.
typedef void JavascriptMessageHandler(JavascriptMessage message);
diff --git a/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart b/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart
index 5e8a279..b435074 100644
--- a/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart
+++ b/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart
@@ -116,7 +116,7 @@
/// The initial URL to load.
final String? initialUrl;
- /// Whether Javascript execution is enabled.
+ /// Whether JavaScript execution is enabled.
final JavascriptMode javascriptMode;
/// The set of [JavascriptChannel]s available to JavaScript code running in the web view.
@@ -126,7 +126,7 @@
/// The JavaScript code can then call `postMessage` on that object to send a message that will be
/// passed to [JavascriptChannel.onMessageReceived].
///
- /// For example for the following JavascriptChannel:
+ /// For example for the following [JavascriptChannel]:
///
/// ```dart
/// JavascriptChannel(name: 'Print', onMessageReceived: (JavascriptMessage message) { print(message.message); });
@@ -189,7 +189,7 @@
/// When [onPageFinished] is invoked on Android, the page being rendered may
/// not be updated yet.
///
- /// When invoked on iOS or Android, any Javascript code that is embedded
+ /// When invoked on iOS or Android, any JavaScript code that is embedded
/// directly in the HTML has been loaded and code injected with
/// [WebViewController.evaluateJavascript] can assume this.
final PageFinishedCallback? onPageFinished;
@@ -487,33 +487,48 @@
_javascriptChannelRegistry.updateJavascriptChannelsFromSet(newChannels);
}
- /// Evaluates a JavaScript expression in the context of the current page.
- ///
- /// On Android returns the evaluation result as a JSON formatted string.
- ///
- /// On iOS depending on the value type the return value would be one of:
- ///
- /// - For primitive JavaScript types: the value string formatted (e.g JavaScript 100 returns '100').
- /// - For JavaScript arrays of supported types: a string formatted NSArray(e.g '(1,2,3), note that the string for NSArray is formatted and might contain newlines and extra spaces.').
- /// - Other non-primitive types are not supported on iOS and will complete the Future with an error.
- ///
- /// The Future completes with an error if a JavaScript error occurred, or on iOS, if the type of the
- /// evaluated expression is not supported as described above.
- ///
- /// When evaluating Javascript in a [WebView], it is best practice to wait for
- /// the [WebView.onPageFinished] callback. This guarantees all the Javascript
- /// embedded in the main frame HTML has been loaded.
+ @visibleForTesting
+ // ignore: public_member_api_docs
Future<String> evaluateJavascript(String javascriptString) {
if (_settings.javascriptMode == JavascriptMode.disabled) {
return Future<String>.error(FlutterError(
'JavaScript mode must be enabled/unrestricted when calling evaluateJavascript.'));
}
- // TODO(amirh): remove this on when the invokeMethod update makes it to stable Flutter.
- // https://github.com/flutter/flutter/issues/26431
- // ignore: strong_mode_implicit_dynamic_method
return _webViewPlatformController.evaluateJavascript(javascriptString);
}
+ /// Runs the given JavaScript in the context of the current page.
+ /// If you are looking for the result, use [runJavascriptReturningResult] instead.
+ /// The Future completes with an error if a JavaScript error occurred.
+ ///
+ /// When running JavaScript in a [WebView], it is best practice to wait for
+ // the [WebView.onPageFinished] callback. This guarantees all the JavaScript
+ // embedded in the main frame HTML has been loaded.
+ Future<void> runJavascript(String javaScriptString) {
+ if (_settings.javascriptMode == JavascriptMode.disabled) {
+ return Future<void>.error(FlutterError(
+ 'Javascript mode must be enabled/unrestricted when calling runJavascript.'));
+ }
+ return _webViewPlatformController.runJavascript(javaScriptString);
+ }
+
+ /// Runs the given JavaScript in the context of the current page, and returns the result.
+ ///
+ /// Returns the evaluation result as a JSON formatted string.
+ /// The Future completes with an error if a JavaScript error occurred.
+ ///
+ /// When evaluating JavaScript in a [WebView], it is best practice to wait for
+ /// the [WebView.onPageFinished] callback. This guarantees all the JavaScript
+ /// embedded in the main frame HTML has been loaded.
+ Future<String> runJavascriptReturningResult(String javaScriptString) {
+ if (_settings.javascriptMode == JavascriptMode.disabled) {
+ return Future<String>.error(FlutterError(
+ 'Javascript mode must be enabled/unrestricted when calling runJavascriptReturningResult.'));
+ }
+ return _webViewPlatformController
+ .runJavascriptReturningResult(javaScriptString);
+ }
+
/// Returns the title of the currently loaded page.
Future<String?> getTitle() {
return _webViewPlatformController.getTitle();
diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml
index 577d373..ac208a0 100644
--- a/packages/webview_flutter/webview_flutter_android/pubspec.yaml
+++ b/packages/webview_flutter/webview_flutter_android/pubspec.yaml
@@ -2,7 +2,7 @@
description: A Flutter plugin that provides a WebView widget on Android.
repository: https://github.com/flutter/plugins/tree/master/packages/webview_flutter/webview_flutter_android
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22
-version: 2.1.0
+version: 2.2.0
environment:
sdk: ">=2.14.0 <3.0.0"
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md
index b75519a..4db6dbf 100644
--- a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md
+++ b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 2.2.0
+
+* Implemented new `runJavascript` and `runJavascriptReturningResult` methods in platform interface.
+
## 2.1.0
* Add `zoomEnabled` functionality.
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart
index 8ba17a2..17e896c 100644
--- a/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart
+++ b/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart
@@ -73,6 +73,27 @@
expect(currentUrl, secondaryUrl);
}, skip: _skipDueToIssue86757);
+ testWidgets('evaluateJavascript', (WidgetTester tester) async {
+ final Completer<WebViewController> controllerCompleter =
+ Completer<WebViewController>();
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: WebView(
+ key: GlobalKey(),
+ initialUrl: primaryUrl,
+ onWebViewCreated: (WebViewController controller) {
+ controllerCompleter.complete(controller);
+ },
+ javascriptMode: JavascriptMode.unrestricted,
+ ),
+ ),
+ );
+ final WebViewController controller = await controllerCompleter.future;
+ final String result = await controller.evaluateJavascript('1 + 1');
+ expect(result, equals('2'));
+ });
+
testWidgets('loadUrl with headers', (WidgetTester tester) async {
final Completer<WebViewController> controllerCompleter =
Completer<WebViewController>();
@@ -110,11 +131,11 @@
await pageLoads.stream.firstWhere((String url) => url == currentUrl);
final String content = await controller
- .evaluateJavascript('document.documentElement.innerText');
+ .runJavascriptReturningResult('document.documentElement.innerText');
expect(content.contains('flutter_test_header'), isTrue);
});
- testWidgets('JavaScriptChannel', (WidgetTester tester) async {
+ testWidgets('JavascriptChannel', (WidgetTester tester) async {
final Completer<WebViewController> controllerCompleter =
Completer<WebViewController>();
final Completer<void> pageStarted = Completer<void>();
@@ -154,11 +175,7 @@
await pageLoaded.future;
expect(messagesReceived, isEmpty);
- // Append a return value "1" in the end will prevent an iOS platform exception.
- // See: https://github.com/flutter/flutter/issues/66318#issuecomment-701105380
- // TODO(cyanglaz): remove the workaround "1" in the end when the below issue is fixed.
- // https://github.com/flutter/flutter/issues/66318
- await controller.evaluateJavascript('Echo.postMessage("hello");1;');
+ await controller.runJavascript('Echo.postMessage("hello");');
expect(messagesReceived, equals(<String>['hello']));
});
@@ -404,7 +421,8 @@
WebViewController controller = await controllerCompleter.future;
await pageLoaded.future;
- String isPaused = await controller.evaluateJavascript('isPaused();');
+ String isPaused =
+ await controller.runJavascriptReturningResult('isPaused();');
expect(isPaused, _webviewBool(false));
controllerCompleter = Completer<WebViewController>();
@@ -433,7 +451,7 @@
controller = await controllerCompleter.future;
await pageLoaded.future;
- isPaused = await controller.evaluateJavascript('isPaused();');
+ isPaused = await controller.runJavascriptReturningResult('isPaused();');
expect(isPaused, _webviewBool(true));
});
@@ -464,7 +482,8 @@
final WebViewController controller = await controllerCompleter.future;
await pageLoaded.future;
- String isPaused = await controller.evaluateJavascript('isPaused();');
+ String isPaused =
+ await controller.runJavascriptReturningResult('isPaused();');
expect(isPaused, _webviewBool(false));
pageLoaded = Completer<void>();
@@ -492,7 +511,7 @@
await pageLoaded.future;
- isPaused = await controller.evaluateJavascript('isPaused();');
+ isPaused = await controller.runJavascriptReturningResult('isPaused();');
expect(isPaused, _webviewBool(false));
});
@@ -542,7 +561,7 @@
await videoPlaying.future;
String fullScreen =
- await controller.evaluateJavascript('isFullScreen();');
+ await controller.runJavascriptReturningResult('isFullScreen();');
expect(fullScreen, _webviewBool(false));
});
@@ -593,7 +612,7 @@
await videoPlaying.future;
String fullScreen =
- await controller.evaluateJavascript('isFullScreen();');
+ await controller.runJavascriptReturningResult('isFullScreen();');
expect(fullScreen, _webviewBool(true));
});
});
@@ -659,7 +678,8 @@
await pageStarted.future;
await pageLoaded.future;
- String isPaused = await controller.evaluateJavascript('isPaused();');
+ String isPaused =
+ await controller.runJavascriptReturningResult('isPaused();');
expect(isPaused, _webviewBool(false));
controllerCompleter = Completer<WebViewController>();
@@ -693,7 +713,7 @@
await pageStarted.future;
await pageLoaded.future;
- isPaused = await controller.evaluateJavascript('isPaused();');
+ isPaused = await controller.runJavascriptReturningResult('isPaused();');
expect(isPaused, _webviewBool(true));
});
@@ -729,7 +749,8 @@
await pageStarted.future;
await pageLoaded.future;
- String isPaused = await controller.evaluateJavascript('isPaused();');
+ String isPaused =
+ await controller.runJavascriptReturningResult('isPaused();');
expect(isPaused, _webviewBool(false));
pageStarted = Completer<void>();
@@ -762,7 +783,7 @@
await pageStarted.future;
await pageLoaded.future;
- isPaused = await controller.evaluateJavascript('isPaused();');
+ isPaused = await controller.runJavascriptReturningResult('isPaused();');
expect(isPaused, _webviewBool(false));
});
});
@@ -919,7 +940,7 @@
await pageLoads.stream.first; // Wait for initial page load.
final WebViewController controller = await controllerCompleter.future;
- await controller.evaluateJavascript('location.href = "$secondaryUrl"');
+ await controller.runJavascript('location.href = "$secondaryUrl"');
await pageLoads.stream.first; // Wait for the next page load.
final String? currentUrl = await controller.currentUrl();
@@ -1050,7 +1071,7 @@
await pageLoads.stream.first; // Wait for initial page load.
final WebViewController controller = await controllerCompleter.future;
await controller
- .evaluateJavascript('location.href = "https://www.youtube.com/"');
+ .runJavascript('location.href = "https://www.youtube.com/"');
// There should never be any second page load, since our new URL is
// blocked. Still wait for a potential page change for some time in order
@@ -1090,7 +1111,7 @@
await pageLoads.stream.first; // Wait for initial page load.
final WebViewController controller = await controllerCompleter.future;
- await controller.evaluateJavascript('location.href = "$secondaryUrl"');
+ await controller.runJavascript('location.href = "$secondaryUrl"');
await pageLoads.stream.first; // Wait for second page to load.
final String? currentUrl = await controller.currentUrl();
@@ -1145,7 +1166,7 @@
),
);
final WebViewController controller = await controllerCompleter.future;
- await controller.evaluateJavascript('window.open("$primaryUrl", "_blank")');
+ await controller.runJavascript('window.open("$primaryUrl", "_blank")');
await pageLoaded.future;
final String? currentUrl = await controller.currentUrl();
expect(currentUrl, primaryUrl);
@@ -1179,7 +1200,7 @@
await pageLoaded.future;
pageLoaded = Completer<void>();
- await controller.evaluateJavascript('window.open("$secondaryUrl")');
+ await controller.runJavascript('window.open("$secondaryUrl")');
await pageLoaded.future;
pageLoaded = Completer<void>();
expect(controller.currentUrl(), completion(secondaryUrl));
@@ -1204,13 +1225,13 @@
/// Returns the value used for the HTTP User-Agent: request header in subsequent HTTP requests.
Future<String> _getUserAgent(WebViewController controller) async {
- return _evaluateJavascript(controller, 'navigator.userAgent;');
+ return _runJavascriptReturningResult(controller, 'navigator.userAgent;');
}
-Future<String> _evaluateJavascript(
+Future<String> _runJavascriptReturningResult(
WebViewController controller, String js) async {
if (defaultTargetPlatform == TargetPlatform.iOS) {
- return await controller.evaluateJavascript(js);
+ return await controller.runJavascriptReturningResult(js);
}
- return jsonDecode(await controller.evaluateJavascript(js));
+ return jsonDecode(await controller.runJavascriptReturningResult(js));
}
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj
index 62428d0..ba0deb4 100644
--- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj
+++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj
@@ -16,8 +16,8 @@
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
- 9D26F6F82D91F92CC095EBA9 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B845D8FBDE0AAD6BE1A0386 /* libPods-Runner.a */; };
- D9A9D48F1A75E5C682944DDD /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 27CC950C9005575711528C12 /* libPods-RunnerTests.a */; };
+ AE8C124DC8CA68E4D9B30EAB /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 528CB85D53C983D2C5DAFDC5 /* libPods-RunnerTests.a */; };
+ DAF0E91266956134538CC667 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 572FFC2B2BA326B420B22679 /* libPods-Runner.a */; };
F7151F77266057800028CB91 /* FLTWebViewUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = F7151F76266057800028CB91 /* FLTWebViewUITests.m */; };
/* End PBXBuildFile section */
@@ -52,11 +52,12 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
- 127772EEA7782174BE0D74B5 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
- 27CC950C9005575711528C12 /* libPods-RunnerTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RunnerTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
+ 528CB85D53C983D2C5DAFDC5 /* libPods-RunnerTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RunnerTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 572FFC2B2BA326B420B22679 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 5C776D27D0DDA247ED5EA72B /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
686B4BF82548DBC7000AEA36 /* FLTWKNavigationDelegateTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLTWKNavigationDelegateTests.m; sourceTree = "<group>"; };
68BDCAE923C3F7CB00D9C032 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
68BDCAED23C3F7CB00D9C032 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@@ -64,7 +65,6 @@
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
- 8B845D8FBDE0AAD6BE1A0386 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -73,12 +73,12 @@
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
- C475C484BD510DD9CB2E403C /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
- E14113434CCE6D3186B5CBC3 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
- F674B2A05DAC369B4FF27850 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
+ B89AA31A64040E4A2F1E0CAF /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
+ C370F140C3A19241FD8C5E64 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
F7151F74266057800028CB91 /* RunnerUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
F7151F76266057800028CB91 /* FLTWebViewUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLTWebViewUITests.m; sourceTree = "<group>"; };
F7151F78266057800028CB91 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+ F7A1921261392D1CBDAEC2E8 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -86,7 +86,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
- D9A9D48F1A75E5C682944DDD /* libPods-RunnerTests.a in Frameworks */,
+ AE8C124DC8CA68E4D9B30EAB /* libPods-RunnerTests.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -94,7 +94,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
- 9D26F6F82D91F92CC095EBA9 /* libPods-Runner.a in Frameworks */,
+ DAF0E91266956134538CC667 /* libPods-Runner.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -108,6 +108,15 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
+ 52FBC2B567345431F81A0A0F /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 572FFC2B2BA326B420B22679 /* libPods-Runner.a */,
+ 528CB85D53C983D2C5DAFDC5 /* libPods-RunnerTests.a */,
+ );
+ name = Frameworks;
+ sourceTree = "<group>";
+ };
68BDCAEA23C3F7CB00D9C032 /* RunnerTests */ = {
isa = PBXGroup;
children = (
@@ -137,8 +146,8 @@
68BDCAEA23C3F7CB00D9C032 /* RunnerTests */,
F7151F75266057800028CB91 /* RunnerUITests */,
97C146EF1CF9000F007C117D /* Products */,
- C6FFB52F5C2B8A41A7E39DE2 /* Pods */,
- B6736FC417BDCCDA377E779D /* Frameworks */,
+ B8AEEA11D6ECBD09750349AE /* Pods */,
+ 52FBC2B567345431F81A0A0F /* Frameworks */,
);
sourceTree = "<group>";
};
@@ -176,24 +185,16 @@
name = "Supporting Files";
sourceTree = "<group>";
};
- B6736FC417BDCCDA377E779D /* Frameworks */ = {
+ B8AEEA11D6ECBD09750349AE /* Pods */ = {
isa = PBXGroup;
children = (
- 8B845D8FBDE0AAD6BE1A0386 /* libPods-Runner.a */,
- 27CC950C9005575711528C12 /* libPods-RunnerTests.a */,
- );
- name = Frameworks;
- sourceTree = "<group>";
- };
- C6FFB52F5C2B8A41A7E39DE2 /* Pods */ = {
- isa = PBXGroup;
- children = (
- 127772EEA7782174BE0D74B5 /* Pods-Runner.debug.xcconfig */,
- C475C484BD510DD9CB2E403C /* Pods-Runner.release.xcconfig */,
- F674B2A05DAC369B4FF27850 /* Pods-RunnerTests.debug.xcconfig */,
- E14113434CCE6D3186B5CBC3 /* Pods-RunnerTests.release.xcconfig */,
+ F7A1921261392D1CBDAEC2E8 /* Pods-Runner.debug.xcconfig */,
+ B89AA31A64040E4A2F1E0CAF /* Pods-Runner.release.xcconfig */,
+ C370F140C3A19241FD8C5E64 /* Pods-RunnerTests.debug.xcconfig */,
+ 5C776D27D0DDA247ED5EA72B /* Pods-RunnerTests.release.xcconfig */,
);
name = Pods;
+ path = Pods;
sourceTree = "<group>";
};
F7151F75266057800028CB91 /* RunnerUITests */ = {
@@ -212,7 +213,7 @@
isa = PBXNativeTarget;
buildConfigurationList = 68BDCAF223C3F7CB00D9C032 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
buildPhases = (
- 53FD4CBDD9756D74B5A3B4C1 /* [CP] Check Pods Manifest.lock */,
+ 0067CEC0658A36CBFF8074E7 /* [CP] Check Pods Manifest.lock */,
68BDCAE523C3F7CB00D9C032 /* Sources */,
68BDCAE623C3F7CB00D9C032 /* Frameworks */,
68BDCAE723C3F7CB00D9C032 /* Resources */,
@@ -231,7 +232,7 @@
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
- B71376B4FB8384EF9D5F3F84 /* [CP] Check Pods Manifest.lock */,
+ 6F536C27DD48B395A30EBB65 /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
@@ -338,21 +339,7 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
- 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
- isa = PBXShellScriptBuildPhase;
- buildActionMask = 2147483647;
- files = (
- );
- inputPaths = (
- );
- name = "Thin Binary";
- outputPaths = (
- );
- runOnlyForDeploymentPostprocessing = 0;
- shellPath = /bin/sh;
- shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed\n/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin\n";
- };
- 53FD4CBDD9756D74B5A3B4C1 /* [CP] Check Pods Manifest.lock */ = {
+ 0067CEC0658A36CBFF8074E7 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@@ -374,6 +361,42 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
+ 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "Thin Binary";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed\n/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin\n";
+ };
+ 6F536C27DD48B395A30EBB65 /* [CP] Check Pods Manifest.lock */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+ "${PODS_ROOT}/Manifest.lock",
+ );
+ name = "[CP] Check Pods Manifest.lock";
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+ showEnvVarsInLog = 0;
+ };
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@@ -388,24 +411,6 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n";
};
- B71376B4FB8384EF9D5F3F84 /* [CP] Check Pods Manifest.lock */ = {
- isa = PBXShellScriptBuildPhase;
- buildActionMask = 2147483647;
- files = (
- );
- inputPaths = (
- "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
- "${PODS_ROOT}/Manifest.lock",
- );
- name = "[CP] Check Pods Manifest.lock";
- outputPaths = (
- "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
- );
- runOnlyForDeploymentPostprocessing = 0;
- shellPath = /bin/sh;
- shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
- showEnvVarsInLog = 0;
- };
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
@@ -473,7 +478,7 @@
/* Begin XCBuildConfiguration section */
68BDCAF023C3F7CB00D9C032 /* Debug */ = {
isa = XCBuildConfiguration;
- baseConfigurationReference = F674B2A05DAC369B4FF27850 /* Pods-RunnerTests.debug.xcconfig */;
+ baseConfigurationReference = C370F140C3A19241FD8C5E64 /* Pods-RunnerTests.debug.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
@@ -487,7 +492,7 @@
};
68BDCAF123C3F7CB00D9C032 /* Release */ = {
isa = XCBuildConfiguration;
- baseConfigurationReference = E14113434CCE6D3186B5CBC3 /* Pods-RunnerTests.release.xcconfig */;
+ baseConfigurationReference = 5C776D27D0DDA247ED5EA72B /* Pods-RunnerTests.release.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FLTWebViewTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FLTWebViewTests.m
index 631c4a1..9d127c2 100644
--- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FLTWebViewTests.m
+++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FLTWebViewTests.m
@@ -88,4 +88,217 @@
}
}
+- (void)testRunJavascriptFailsForNullString {
+ // Setup
+ FLTWebViewController *controller =
+ [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400)
+ viewIdentifier:1
+ arguments:nil
+ binaryMessenger:self.mockBinaryMessenger];
+ XCTestExpectation *resultExpectation =
+ [self expectationWithDescription:@"Should return error result over the method channel."];
+
+ // Run
+ [controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"runJavascript"
+ arguments:nil]
+ result:^(id _Nullable result) {
+ XCTAssertTrue([result class] == [FlutterError class]);
+ [resultExpectation fulfill];
+ }];
+
+ // Verify
+ [self waitForExpectationsWithTimeout:30.0 handler:nil];
+}
+
+- (void)testRunJavascriptRunsStringWithSuccessResult {
+ // Setup
+ FLTWebViewController *controller =
+ [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400)
+ viewIdentifier:1
+ arguments:nil
+ binaryMessenger:self.mockBinaryMessenger];
+ XCTestExpectation *resultExpectation =
+ [self expectationWithDescription:@"Should return successful result over the method channel."];
+ FLTWKWebView *mockView = OCMClassMock(FLTWKWebView.class);
+ [OCMStub([mockView evaluateJavaScript:[OCMArg any]
+ completionHandler:[OCMArg any]]) andDo:^(NSInvocation *invocation) {
+ // __unsafe_unretained: https://github.com/erikdoe/ocmock/issues/384#issuecomment-589376668
+ __unsafe_unretained void (^evalResultHandler)(id, NSError *);
+ [invocation getArgument:&evalResultHandler atIndex:3];
+ evalResultHandler(@"RESULT", nil);
+ }];
+ controller.webView = mockView;
+
+ // Run
+ [controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"runJavascript"
+ arguments:@"Test JavaScript String"]
+ result:^(id _Nullable result) {
+ XCTAssertNil(result);
+ [resultExpectation fulfill];
+ }];
+
+ // Verify
+ [self waitForExpectationsWithTimeout:30.0 handler:nil];
+}
+
+- (void)testRunJavascriptReturnsErrorResultForWKError {
+ // Setup
+ FLTWebViewController *controller =
+ [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400)
+ viewIdentifier:1
+ arguments:nil
+ binaryMessenger:self.mockBinaryMessenger];
+ XCTestExpectation *resultExpectation =
+ [self expectationWithDescription:@"Should return error result over the method channel."];
+ NSError *testError =
+ [NSError errorWithDomain:@""
+ // Any error code but WKErrorJavascriptResultTypeIsUnsupported
+ code:WKErrorJavaScriptResultTypeIsUnsupported + 1
+ userInfo:@{NSLocalizedDescriptionKey : @"Test Error"}];
+ FLTWKWebView *mockView = OCMClassMock(FLTWKWebView.class);
+ [OCMStub([mockView evaluateJavaScript:[OCMArg any]
+ completionHandler:[OCMArg any]]) andDo:^(NSInvocation *invocation) {
+ // __unsafe_unretained: https://github.com/erikdoe/ocmock/issues/384#issuecomment-589376668
+ __unsafe_unretained void (^evalResultHandler)(id, NSError *);
+ [invocation getArgument:&evalResultHandler atIndex:3];
+ evalResultHandler(nil, testError);
+ }];
+ controller.webView = mockView;
+
+ // Run
+ [controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"runJavascript"
+ arguments:@"Test JavaScript String"]
+ result:^(id _Nullable result) {
+ XCTAssertTrue([result class] == [FlutterError class]);
+ [resultExpectation fulfill];
+ }];
+
+ // Verify
+ [self waitForExpectationsWithTimeout:30.0 handler:nil];
+}
+
+- (void)testRunJavascriptReturnsSuccessForWKErrorJavascriptResultTypeIsUnsupported {
+ // Setup
+ FLTWebViewController *controller =
+ [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400)
+ viewIdentifier:1
+ arguments:nil
+ binaryMessenger:self.mockBinaryMessenger];
+ XCTestExpectation *resultExpectation =
+ [self expectationWithDescription:@"Should return nil result over the method channel."];
+ NSError *testError = [NSError errorWithDomain:@""
+ code:WKErrorJavaScriptResultTypeIsUnsupported
+ userInfo:@{NSLocalizedDescriptionKey : @"Test Error"}];
+ FLTWKWebView *mockView = OCMClassMock(FLTWKWebView.class);
+ [OCMStub([mockView evaluateJavaScript:[OCMArg any]
+ completionHandler:[OCMArg any]]) andDo:^(NSInvocation *invocation) {
+ // __unsafe_unretained: https://github.com/erikdoe/ocmock/issues/384#issuecomment-589376668
+ __unsafe_unretained void (^evalResultHandler)(id, NSError *);
+ [invocation getArgument:&evalResultHandler atIndex:3];
+ evalResultHandler(nil, testError);
+ }];
+ controller.webView = mockView;
+
+ // Run
+ [controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"runJavascript"
+ arguments:@"Test JavaScript String"]
+ result:^(id _Nullable result) {
+ XCTAssertNil(result);
+ [resultExpectation fulfill];
+ }];
+
+ // Verify
+ [self waitForExpectationsWithTimeout:30.0 handler:nil];
+}
+
+- (void)testRunJavascriptReturningResultFailsForNullString {
+ // Setup
+ FLTWebViewController *controller =
+ [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400)
+ viewIdentifier:1
+ arguments:nil
+ binaryMessenger:self.mockBinaryMessenger];
+ XCTestExpectation *resultExpectation =
+ [self expectationWithDescription:@"Should return error result over the method channel."];
+
+ // Run
+ [controller
+ onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"runJavascriptReturningResult"
+ arguments:nil]
+ result:^(id _Nullable result) {
+ XCTAssertTrue([result class] == [FlutterError class]);
+ [resultExpectation fulfill];
+ }];
+
+ // Verify
+ [self waitForExpectationsWithTimeout:30.0 handler:nil];
+}
+
+- (void)testRunJavascriptReturningResultRunsStringWithSuccessResult {
+ // Setup
+ FLTWebViewController *controller =
+ [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400)
+ viewIdentifier:1
+ arguments:nil
+ binaryMessenger:self.mockBinaryMessenger];
+ XCTestExpectation *resultExpectation =
+ [self expectationWithDescription:@"Should return successful result over the method channel."];
+ FLTWKWebView *mockView = OCMClassMock(FLTWKWebView.class);
+ [OCMStub([mockView evaluateJavaScript:[OCMArg any]
+ completionHandler:[OCMArg any]]) andDo:^(NSInvocation *invocation) {
+ // __unsafe_unretained: https://github.com/erikdoe/ocmock/issues/384#issuecomment-589376668
+ __unsafe_unretained void (^evalResultHandler)(id, NSError *);
+ [invocation getArgument:&evalResultHandler atIndex:3];
+ evalResultHandler(@"RESULT", nil);
+ }];
+ controller.webView = mockView;
+
+ // Run
+ [controller
+ onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"runJavascriptReturningResult"
+ arguments:@"Test JavaScript String"]
+ result:^(id _Nullable result) {
+ XCTAssertTrue([@"RESULT" isEqualToString:result]);
+ [resultExpectation fulfill];
+ }];
+
+ // Verify
+ [self waitForExpectationsWithTimeout:30.0 handler:nil];
+}
+
+- (void)testRunJavascriptReturningResultReturnsErrorResultForWKError {
+ // Setup
+ FLTWebViewController *controller =
+ [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400)
+ viewIdentifier:1
+ arguments:nil
+ binaryMessenger:self.mockBinaryMessenger];
+ XCTestExpectation *resultExpectation =
+ [self expectationWithDescription:@"Should return error result over the method channel."];
+ NSError *testError = [NSError errorWithDomain:@""
+ code:5
+ userInfo:@{NSLocalizedDescriptionKey : @"Test Error"}];
+ FLTWKWebView *mockView = OCMClassMock(FLTWKWebView.class);
+ [OCMStub([mockView evaluateJavaScript:[OCMArg any]
+ completionHandler:[OCMArg any]]) andDo:^(NSInvocation *invocation) {
+ // __unsafe_unretained: https://github.com/erikdoe/ocmock/issues/384#issuecomment-589376668
+ __unsafe_unretained void (^evalResultHandler)(id, NSError *);
+ [invocation getArgument:&evalResultHandler atIndex:3];
+ evalResultHandler(nil, testError);
+ }];
+ controller.webView = mockView;
+
+ // Run
+ [controller
+ onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"runJavascriptReturningResult"
+ arguments:@"Test JavaScript String"]
+ result:^(id _Nullable result) {
+ XCTAssertTrue([result class] == [FlutterError class]);
+ [resultExpectation fulfill];
+ }];
+
+ // Verify
+ [self waitForExpectationsWithTimeout:30.0 handler:nil];
+}
+
@end
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart
index 15b4cfc..21240f6 100644
--- a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart
+++ b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart
@@ -199,14 +199,14 @@
WebViewController controller, BuildContext context) async {
// Send a message with the user agent string to the Snackbar JavaScript channel we registered
// with the WebView.
- await controller.evaluateJavascript(
+ await controller.runJavascript(
'Snackbar.postMessage("User Agent: " + navigator.userAgent);');
}
void _onListCookies(
WebViewController controller, BuildContext context) async {
final String cookies =
- await controller.evaluateJavascript('document.cookie');
+ await controller.runJavascriptReturningResult('document.cookie');
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Column(
mainAxisAlignment: MainAxisAlignment.end,
@@ -220,7 +220,7 @@
}
void _onAddToCache(WebViewController controller, BuildContext context) async {
- await controller.evaluateJavascript(
+ await controller.runJavascript(
'caches.open("test_caches_entry"); localStorage["test_localStorage"] = "dummy_entry";');
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content: Text('Added a test entry to cache.'),
@@ -228,7 +228,7 @@
}
void _onListCache(WebViewController controller, BuildContext context) async {
- await controller.evaluateJavascript('caches.keys()'
+ await controller.runJavascript('caches.keys()'
'.then((cacheKeys) => JSON.stringify({"cacheKeys" : cacheKeys, "localStorage" : localStorage}))'
'.then((caches) => Snackbar.postMessage(caches))');
}
@@ -340,5 +340,5 @@
}
}
-/// Callback type for handling messages sent from Javascript running in a web view.
+/// Callback type for handling messages sent from JavaScript running in a web view.
typedef void JavascriptMessageHandler(JavascriptMessage message);
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart
index d57499d..403db1f 100644
--- a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart
+++ b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart
@@ -94,7 +94,7 @@
/// The initial URL to load.
final String? initialUrl;
- /// Whether Javascript execution is enabled.
+ /// Whether JavaScript execution is enabled.
final JavascriptMode javascriptMode;
/// The set of [JavascriptChannel]s available to JavaScript code running in the web view.
@@ -167,7 +167,7 @@
/// When [onPageFinished] is invoked on Android, the page being rendered may
/// not be updated yet.
///
- /// When invoked on iOS or Android, any Javascript code that is embedded
+ /// When invoked on iOS or Android, any JavaScript code that is embedded
/// directly in the HTML has been loaded and code injected with
/// [WebViewController.evaluateJavascript] can assume this.
final PageFinishedCallback? onPageFinished;
@@ -416,33 +416,53 @@
_javascriptChannelRegistry.updateJavascriptChannelsFromSet(newChannels);
}
- /// Evaluates a JavaScript expression in the context of the current page.
- ///
- /// On Android returns the evaluation result as a JSON formatted string.
- ///
- /// On iOS depending on the value type the return value would be one of:
- ///
- /// - For primitive JavaScript types: the value string formatted (e.g JavaScript 100 returns '100').
- /// - For JavaScript arrays of supported types: a string formatted NSArray(e.g '(1,2,3), note that the string for NSArray is formatted and might contain newlines and extra spaces.').
- /// - Other non-primitive types are not supported on iOS and will complete the Future with an error.
- ///
- /// The Future completes with an error if a JavaScript error occurred, or on iOS, if the type of the
- /// evaluated expression is not supported as described above.
- ///
- /// When evaluating Javascript in a [WebView], it is best practice to wait for
- /// the [WebView.onPageFinished] callback. This guarantees all the Javascript
- /// embedded in the main frame HTML has been loaded.
+ @visibleForTesting
+ // ignore: public_member_api_docs
Future<String> evaluateJavascript(String javascriptString) {
if (_settings.javascriptMode == JavascriptMode.disabled) {
return Future<String>.error(FlutterError(
'JavaScript mode must be enabled/unrestricted when calling evaluateJavascript.'));
}
- // TODO(amirh): remove this on when the invokeMethod update makes it to stable Flutter.
- // https://github.com/flutter/flutter/issues/26431
- // ignore: strong_mode_implicit_dynamic_method
return _webViewPlatformController.evaluateJavascript(javascriptString);
}
+ /// Runs the given JavaScript in the context of the current page.
+ /// If you are looking for the result, use [runJavascriptReturningResult] instead.
+ /// The Future completes with an error if a JavaScript error occurred.
+ ///
+ /// When running JavaScript in a [WebView], it is best practice to wait for
+ /// the [WebView.onPageFinished] callback. This guarantees all the JavaScript
+ /// embedded in the main frame HTML has been loaded.
+ Future<void> runJavascript(String javaScriptString) {
+ if (_settings.javascriptMode == JavascriptMode.disabled) {
+ return Future<void>.error(FlutterError(
+ 'Javascript mode must be enabled/unrestricted when calling runJavascript.'));
+ }
+ return _webViewPlatformController.runJavascript(javaScriptString);
+ }
+
+ /// Runs the given JavaScript in the context of the current page, and returns the result.
+ ///
+ /// Depending on the value type the return value would be one of:
+ /// - For primitive JavaScript types: the value string formatted (e.g JavaScript 100 returns '100').
+ /// - For JavaScript arrays of supported types: a string formatted NSArray(e.g '(1,2,3), note that the string for NSArray is formatted and might contain newlines and extra spaces.').
+ ///
+ /// The Future completes with an error if a JavaScript error occurred, or if the
+ /// type the given expression evaluates to is unsupported. Unsupported values include
+ /// certain non primitive types, as well as `undefined` or `null` on iOS 14+.
+ ///
+ /// When evaluating JavaScript in a [WebView], it is best practice to wait for
+ /// the [WebView.onPageFinished] callback. This guarantees all the JavaScript
+ /// embedded in the main frame HTML has been loaded.
+ Future<String> runJavascriptReturningResult(String javaScriptString) {
+ if (_settings.javascriptMode == JavascriptMode.disabled) {
+ return Future<String>.error(FlutterError(
+ 'Javascript mode must be enabled/unrestricted when calling runJavascriptReturningResult.'));
+ }
+ return _webViewPlatformController
+ .runJavascriptReturningResult(javaScriptString);
+ }
+
/// Returns the title of the currently loaded page.
Future<String?> getTitle() {
return _webViewPlatformController.getTitle();
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.h
index 6e795f7..db52124 100644
--- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.h
+++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.h
@@ -7,26 +7,30 @@
NS_ASSUME_NONNULL_BEGIN
+/**
+ * The WkWebView used for the plugin.
+ *
+ * This class overrides some methods in `WKWebView` to serve the needs for the plugin.
+ */
+@interface FLTWKWebView : WKWebView
+@end
+
@interface FLTWebViewController : NSObject <FlutterPlatformView, WKUIDelegate>
+@property(nonatomic) FLTWKWebView* webView;
+
- (instancetype)initWithFrame:(CGRect)frame
viewIdentifier:(int64_t)viewId
arguments:(id _Nullable)args
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger;
- (UIView*)view;
+
+- (void)onMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result;
@end
@interface FLTWebViewFactory : NSObject <FlutterPlatformViewFactory>
- (instancetype)initWithMessenger:(NSObject<FlutterBinaryMessenger>*)messenger;
@end
-/**
- * The WkWebView used for the plugin.
- *
- * This class overrides some methods in `WKWebView` to serve the needs for the plugin.
- */
-@interface FLTWKWebView : WKWebView
-@end
-
NS_ASSUME_NONNULL_END
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.m
index 7fd78ee..5e12f8a 100644
--- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.m
+++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.m
@@ -151,6 +151,10 @@
[self onCurrentUrl:call result:result];
} else if ([[call method] isEqualToString:@"evaluateJavascript"]) {
[self onEvaluateJavaScript:call result:result];
+ } else if ([[call method] isEqualToString:@"runJavascript"]) {
+ [self onRunJavaScript:call result:result sendReturnValue:NO];
+ } else if ([[call method] isEqualToString:@"runJavascriptReturningResult"]) {
+ [self onRunJavaScript:call result:result sendReturnValue:YES];
} else if ([[call method] isEqualToString:@"addJavascriptChannels"]) {
[self onAddJavaScriptChannels:call result:result];
} else if ([[call method] isEqualToString:@"removeJavascriptChannels"]) {
@@ -244,6 +248,41 @@
}];
}
+- (void)onRunJavaScript:(FlutterMethodCall*)call
+ result:(FlutterResult)result
+ sendReturnValue:(BOOL)sendReturnValue {
+ NSString* jsString = [call arguments];
+ if (!jsString) {
+ result([FlutterError errorWithCode:@"runJavascript_failed"
+ message:@"JavaScript String cannot be null"
+ details:nil]);
+ return;
+ }
+ [_webView
+ evaluateJavaScript:jsString
+ completionHandler:^(_Nullable id evaluateResult, NSError* _Nullable error) {
+ if (error) {
+ // WebKit will throw an error (WKErrorJavaScriptResultTypeIsUnsupported) when the
+ // type of the evaluated value is unsupported. This also goes for
+ // `null` and `undefined` on iOS 14+, for example when running a void function.
+ // For ease of use this specific error is ignored when no return value is expected.
+ BOOL sendError =
+ sendReturnValue || error.code != WKErrorJavaScriptResultTypeIsUnsupported;
+ result(sendError
+ ? [FlutterError
+ errorWithCode:(sendReturnValue ? @"runJavascriptReturningResult_failed"
+ : @"runJavascript_failed")
+ message:@"Failed running JavaScript"
+ details:[NSString
+ stringWithFormat:@"JavaScript string was: '%@'\n%@",
+ jsString, error]]
+ : nil);
+ return;
+ }
+ result(sendReturnValue ? [NSString stringWithFormat:@"%@", evaluateResult] : nil);
+ }];
+}
+
- (void)onAddJavaScriptChannels:(FlutterMethodCall*)call result:(FlutterResult)result {
NSArray* channelNames = [call arguments];
NSSet* channelNamesSet = [[NSSet alloc] initWithArray:channelNames];
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/webview_flutter_wkwebview.podspec b/packages/webview_flutter/webview_flutter_wkwebview/ios/webview_flutter_wkwebview.podspec
index 37905f1..2dfb336 100644
--- a/packages/webview_flutter/webview_flutter_wkwebview/ios/webview_flutter_wkwebview.podspec
+++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/webview_flutter_wkwebview.podspec
@@ -18,6 +18,6 @@
s.public_header_files = 'Classes/**/*.h'
s.dependency 'Flutter'
- s.platform = :ios, '8.0'
+ s.platform = :ios, '9.0'
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' }
end
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml
index bfa4b80..5176adb 100644
--- a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml
+++ b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml
@@ -2,7 +2,7 @@
description: A Flutter plugin that provides a WebView widget based on Apple's WKWebView control.
repository: https://github.com/flutter/plugins/tree/master/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: 2.1.0
+version: 2.2.0
environment:
sdk: ">=2.14.0 <3.0.0"