Added ProcessGlobalConfig for Android WebViews, Added disableWebView static method on InAppWebViewController for Android, Added support for Android WebViewFeature.isStartupFeatureSupported, WebViewFeature.STARTUP_FEATURE_SET_DIRECTORY_BASE_PATHS, WebViewFeature.STARTUP_FEATURE_SET_DATA_DIRECTORY_SUFFIX, WebViewFeature.WEB_MESSAGE_ARRAY_BUFFER, Added WebMessage.type property, WebMessage.data property is of type dynamic, JavaScriptReplyProxy.postMessage is of type WebMessage, changed WebMessageListener.onPostMessage and WebMessagePort.setWebMessageCallback methods signature
This commit is contained in:
parent
a0bbbba7f1
commit
8dfddc2e12
13
CHANGELOG.md
13
CHANGELOG.md
|
@ -1,3 +1,16 @@
|
|||
## 6.0.0-beta.28
|
||||
|
||||
- Added `ProcessGlobalConfig` for Android WebViews
|
||||
- Added `disableWebView` static method on `InAppWebViewController` for Android
|
||||
- Added support for Android `WebViewFeature.isStartupFeatureSupported`, `WebViewFeature.STARTUP_FEATURE_SET_DIRECTORY_BASE_PATHS`, `WebViewFeature.STARTUP_FEATURE_SET_DATA_DIRECTORY_SUFFIX`, `WebViewFeature.WEB_MESSAGE_ARRAY_BUFFER`
|
||||
- Added `WebMessage.type` property
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
- `WebMessage.data` property is of type `dynamic`
|
||||
- `JavaScriptReplyProxy.postMessage` is of type `WebMessage`
|
||||
- `WebMessageListener.onPostMessage` and `WebMessagePort.setWebMessageCallback` methods signature
|
||||
|
||||
## 6.0.0-beta.27
|
||||
|
||||
- Added `requestPostMessageChannel`, `postMessage`, `isEngagementSignalsApiAvailable` methods on `ChromeSafariBrowser` for Android
|
||||
|
|
|
@ -2,29 +2,28 @@ package com.pichillilorenzo.flutter_inappwebview;
|
|||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.webkit.ValueCallback;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.pichillilorenzo.flutter_inappwebview.chrome_custom_tabs.ChromeSafariBrowserManager;
|
||||
import com.pichillilorenzo.flutter_inappwebview.credential_database.CredentialDatabaseHandler;
|
||||
import com.pichillilorenzo.flutter_inappwebview.in_app_browser.InAppBrowserManager;
|
||||
import com.pichillilorenzo.flutter_inappwebview.headless_in_app_webview.HeadlessInAppWebViewManager;
|
||||
import com.pichillilorenzo.flutter_inappwebview.in_app_browser.InAppBrowserManager;
|
||||
import com.pichillilorenzo.flutter_inappwebview.print_job.PrintJobManager;
|
||||
import com.pichillilorenzo.flutter_inappwebview.process_global_config.ProcessGlobalConfigManager;
|
||||
import com.pichillilorenzo.flutter_inappwebview.proxy.ProxyManager;
|
||||
import com.pichillilorenzo.flutter_inappwebview.service_worker.ServiceWorkerManager;
|
||||
import com.pichillilorenzo.flutter_inappwebview.tracing.TracingControllerManager;
|
||||
import com.pichillilorenzo.flutter_inappwebview.webview.FlutterWebViewFactory;
|
||||
import com.pichillilorenzo.flutter_inappwebview.webview.InAppWebViewManager;
|
||||
|
||||
import io.flutter.embedding.engine.plugins.FlutterPlugin;
|
||||
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
|
||||
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
|
||||
import io.flutter.plugin.common.BinaryMessenger;
|
||||
import io.flutter.plugin.common.PluginRegistry;
|
||||
import io.flutter.embedding.engine.plugins.FlutterPlugin;
|
||||
import io.flutter.plugin.platform.PlatformViewRegistry;
|
||||
import io.flutter.view.FlutterView;
|
||||
|
||||
|
@ -58,6 +57,8 @@ public class InAppWebViewFlutterPlugin implements FlutterPlugin, ActivityAware {
|
|||
public PrintJobManager printJobManager;
|
||||
@Nullable
|
||||
public TracingControllerManager tracingControllerManager;
|
||||
@Nullable
|
||||
public ProcessGlobalConfigManager processGlobalConfigManager;
|
||||
public FlutterWebViewFactory flutterWebViewFactory;
|
||||
public Context applicationContext;
|
||||
public PluginRegistry.Registrar registrar;
|
||||
|
@ -122,6 +123,7 @@ public class InAppWebViewFlutterPlugin implements FlutterPlugin, ActivityAware {
|
|||
printJobManager = new PrintJobManager(this);
|
||||
}
|
||||
tracingControllerManager = new TracingControllerManager(this);
|
||||
processGlobalConfigManager = new ProcessGlobalConfigManager(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -178,6 +180,10 @@ public class InAppWebViewFlutterPlugin implements FlutterPlugin, ActivityAware {
|
|||
tracingControllerManager.dispose();
|
||||
tracingControllerManager = null;
|
||||
}
|
||||
if (processGlobalConfigManager != null) {
|
||||
processGlobalConfigManager.dispose();
|
||||
processGlobalConfigManager = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -28,6 +28,12 @@ public class WebViewFeatureManager extends ChannelDelegateImpl {
|
|||
String feature = (String) call.argument("feature");
|
||||
result.success(WebViewFeature.isFeatureSupported(feature));
|
||||
break;
|
||||
case "isStartupFeatureSupported":
|
||||
if (plugin != null && plugin.activity != null) {
|
||||
String startupFeature = (String) call.argument("startupFeature");
|
||||
result.success(WebViewFeature.isStartupFeatureSupported(plugin.activity, startupFeature));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
result.notImplemented();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
package com.pichillilorenzo.flutter_inappwebview.process_global_config;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.webkit.ProcessGlobalConfig;
|
||||
import androidx.webkit.WebViewFeature;
|
||||
|
||||
import com.pichillilorenzo.flutter_inappwebview.InAppWebViewFlutterPlugin;
|
||||
import com.pichillilorenzo.flutter_inappwebview.pull_to_refresh.PullToRefreshSettings;
|
||||
import com.pichillilorenzo.flutter_inappwebview.types.ChannelDelegateImpl;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import io.flutter.plugin.common.MethodCall;
|
||||
import io.flutter.plugin.common.MethodChannel;
|
||||
|
||||
public class ProcessGlobalConfigManager extends ChannelDelegateImpl {
|
||||
protected static final String LOG_TAG = "ProcessGlobalConfigManager";
|
||||
public static final String METHOD_CHANNEL_NAME = "com.pichillilorenzo/flutter_inappwebview_processglobalconfig";
|
||||
|
||||
@Nullable
|
||||
public InAppWebViewFlutterPlugin plugin;
|
||||
|
||||
public ProcessGlobalConfigManager(@NonNull final InAppWebViewFlutterPlugin plugin) {
|
||||
super(new MethodChannel(plugin.messenger, METHOD_CHANNEL_NAME));
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
|
||||
switch (call.method) {
|
||||
case "apply":
|
||||
if (plugin != null && plugin.activity != null) {
|
||||
ProcessGlobalConfigSettings settings = (new ProcessGlobalConfigSettings())
|
||||
.parse((Map<String, Object>) call.argument("settings"));
|
||||
ProcessGlobalConfig.apply(settings.toProcessGlobalConfig(plugin.activity));
|
||||
}
|
||||
result.success(true);
|
||||
break;
|
||||
default:
|
||||
result.notImplemented();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
super.dispose();
|
||||
plugin = null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
package com.pichillilorenzo.flutter_inappwebview.process_global_config;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.webkit.ProcessGlobalConfig;
|
||||
import androidx.webkit.WebViewFeature;
|
||||
|
||||
import com.pichillilorenzo.flutter_inappwebview.ISettings;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class ProcessGlobalConfigSettings implements ISettings<ProcessGlobalConfig> {
|
||||
public static final String LOG_TAG = "ProcessGlobalConfigSettings";
|
||||
|
||||
@Nullable
|
||||
public String dataDirectorySuffix;
|
||||
@Nullable
|
||||
public DirectoryBasePaths directoryBasePaths;
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ProcessGlobalConfigSettings parse(@NonNull Map<String, Object> settings) {
|
||||
for (Map.Entry<String, Object> pair : settings.entrySet()) {
|
||||
String key = pair.getKey();
|
||||
Object value = pair.getValue();
|
||||
if (value == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (key) {
|
||||
case "dataDirectorySuffix":
|
||||
dataDirectorySuffix = (String) value;
|
||||
break;
|
||||
case "directoryBasePaths":
|
||||
directoryBasePaths = (new DirectoryBasePaths()).parse((Map<String, Object>) value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public ProcessGlobalConfig toProcessGlobalConfig(@NonNull Context context) {
|
||||
ProcessGlobalConfig config = new ProcessGlobalConfig();
|
||||
if (dataDirectorySuffix != null &&
|
||||
WebViewFeature.isStartupFeatureSupported(context, WebViewFeature.STARTUP_FEATURE_SET_DATA_DIRECTORY_SUFFIX)) {
|
||||
config.setDataDirectorySuffix(context, dataDirectorySuffix);
|
||||
}
|
||||
if (directoryBasePaths != null &&
|
||||
WebViewFeature.isStartupFeatureSupported(context, WebViewFeature.STARTUP_FEATURE_SET_DIRECTORY_BASE_PATHS)) {
|
||||
config.setDirectoryBasePaths(context,
|
||||
new File(directoryBasePaths.dataDirectoryBasePath),
|
||||
new File(directoryBasePaths.cacheDirectoryBasePath));
|
||||
}
|
||||
return config;
|
||||
}
|
||||
@NonNull
|
||||
public Map<String, Object> toMap() {
|
||||
Map<String, Object> settings = new HashMap<>();
|
||||
settings.put("dataDirectorySuffix", dataDirectorySuffix);
|
||||
return settings;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Map<String, Object> getRealSettings(@NonNull ProcessGlobalConfig processGlobalConfig) {
|
||||
Map<String, Object> realSettings = toMap();
|
||||
return realSettings;
|
||||
}
|
||||
|
||||
static class DirectoryBasePaths implements ISettings<Object> {
|
||||
public static final String LOG_TAG = "ProcessGlobalConfigSettings";
|
||||
|
||||
public String cacheDirectoryBasePath;
|
||||
public String dataDirectoryBasePath;
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public DirectoryBasePaths parse(@NonNull Map<String, Object> settings) {
|
||||
for (Map.Entry<String, Object> pair : settings.entrySet()) {
|
||||
String key = pair.getKey();
|
||||
Object value = pair.getValue();
|
||||
if (value == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (key) {
|
||||
case "cacheDirectoryBasePath":
|
||||
cacheDirectoryBasePath = (String) value;
|
||||
break;
|
||||
case "dataDirectoryBasePath":
|
||||
dataDirectoryBasePath = (String) value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Map<String, Object> toMap() {
|
||||
Map<String, Object> settings = new HashMap<>();
|
||||
settings.put("cacheDirectoryBasePath", cacheDirectoryBasePath);
|
||||
settings.put("dataDirectoryBasePath", dataDirectoryBasePath);
|
||||
return settings;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Map<String, Object> getRealSettings(@NonNull Object obj) {
|
||||
Map<String, Object> realSettings = toMap();
|
||||
return realSettings;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
package com.pichillilorenzo.flutter_inappwebview.types;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.webkit.WebMessageCompat;
|
||||
import androidx.webkit.WebViewFeature;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
public class WebMessageCompatExt {
|
||||
@Nullable
|
||||
private Object data;
|
||||
private @WebMessageCompat.Type int type;
|
||||
|
||||
@Nullable
|
||||
private List<WebMessagePortCompatExt> ports;
|
||||
|
||||
public WebMessageCompatExt(@Nullable Object data, int type, @Nullable List<WebMessagePortCompatExt> ports) {
|
||||
this.data = data;
|
||||
this.type = type;
|
||||
this.ports = ports;
|
||||
}
|
||||
|
||||
public static WebMessageCompatExt fromMapWebMessageCompat(@NonNull WebMessageCompat message) {
|
||||
Object data;
|
||||
if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_ARRAY_BUFFER) && message.getType() == WebMessageCompat.TYPE_ARRAY_BUFFER) {
|
||||
data = message.getArrayBuffer();
|
||||
} else {
|
||||
data = message.getData();
|
||||
}
|
||||
return new WebMessageCompatExt(data, message.getType(), null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static WebMessageCompatExt fromMap(@Nullable Map<String, Object> map) {
|
||||
if (map == null) {
|
||||
return null;
|
||||
}
|
||||
Object data = map.get("data");
|
||||
Integer type = (Integer) map.get("type");
|
||||
List<Map<String, Object>> portMapList = (List<Map<String, Object>>) map.get("ports");
|
||||
List<WebMessagePortCompatExt> ports = null;
|
||||
if (portMapList != null && !portMapList.isEmpty()) {
|
||||
ports = new ArrayList<>();
|
||||
for (Map<String, Object> portMap : portMapList) {
|
||||
ports.add(WebMessagePortCompatExt.fromMap(portMap));
|
||||
}
|
||||
}
|
||||
return new WebMessageCompatExt(data, type, ports);
|
||||
}
|
||||
|
||||
public Map<String, Object> toMap() {
|
||||
Map<String, Object> proxyRuleMap = new HashMap<>();
|
||||
proxyRuleMap.put("data", data);
|
||||
proxyRuleMap.put("type", type);
|
||||
return proxyRuleMap;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Object getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setData(@Nullable Object data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public int getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(int type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public List<WebMessagePortCompatExt> getPorts() {
|
||||
return ports;
|
||||
}
|
||||
|
||||
public void setPorts(@Nullable List<WebMessagePortCompatExt> ports) {
|
||||
this.ports = ports;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
WebMessageCompatExt that = (WebMessageCompatExt) o;
|
||||
|
||||
if (type != that.type) return false;
|
||||
if (!Objects.equals(data, that.data)) return false;
|
||||
return Objects.equals(ports, that.ports);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = data != null ? data.hashCode() : 0;
|
||||
result = 31 * result + type;
|
||||
result = 31 * result + (ports != null ? ports.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "WebMessageCompatExt{" +
|
||||
"data=" + data +
|
||||
", type=" + type +
|
||||
", ports=" + ports +
|
||||
'}';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
package com.pichillilorenzo.flutter_inappwebview.types;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class WebMessagePortCompatExt {
|
||||
private int index;
|
||||
@NonNull
|
||||
private String webMessageChannelId;
|
||||
|
||||
public WebMessagePortCompatExt(int index, @NonNull String webMessageChannelId) {
|
||||
this.index = index;
|
||||
this.webMessageChannelId = webMessageChannelId;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static WebMessagePortCompatExt fromMap(@Nullable Map<String, Object> map) {
|
||||
if (map == null) {
|
||||
return null;
|
||||
}
|
||||
Integer index = (Integer) map.get("index");
|
||||
String webMessageChannelId = (String) map.get("webMessageChannelId");
|
||||
return new WebMessagePortCompatExt(index, webMessageChannelId);
|
||||
}
|
||||
|
||||
public Map<String, Object> toMap() {
|
||||
Map<String, Object> proxyRuleMap = new HashMap<>();
|
||||
proxyRuleMap.put("index", index);
|
||||
proxyRuleMap.put("webMessageChannelId", webMessageChannelId);
|
||||
return proxyRuleMap;
|
||||
}
|
||||
|
||||
public int getIndex() {
|
||||
return index;
|
||||
}
|
||||
|
||||
public void setIndex(int index) {
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String getWebMessageChannelId() {
|
||||
return webMessageChannelId;
|
||||
}
|
||||
|
||||
public void setWebMessageChannelId(@NonNull String webMessageChannelId) {
|
||||
this.webMessageChannelId = webMessageChannelId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
WebMessagePortCompatExt that = (WebMessagePortCompatExt) o;
|
||||
|
||||
if (index != that.index) return false;
|
||||
return webMessageChannelId.equals(that.webMessageChannelId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = index;
|
||||
result = 31 * result + webMessageChannelId.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "WebMessagePortCompatExt{" +
|
||||
"index=" + index +
|
||||
", webMessageChannelId='" + webMessageChannelId + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
|
@ -133,6 +133,12 @@ public class InAppWebViewManager extends ChannelDelegateImpl {
|
|||
result.success(false);
|
||||
}
|
||||
break;
|
||||
case "disableWebView":
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
WebView.disableWebView();
|
||||
}
|
||||
result.success(true);
|
||||
break;
|
||||
case "disposeKeepAlive":
|
||||
final String keepAliveId = (String) call.argument("keepAliveId");
|
||||
if (keepAliveId != null) {
|
||||
|
|
|
@ -43,7 +43,9 @@ import com.pichillilorenzo.flutter_inappwebview.types.SslCertificateExt;
|
|||
import com.pichillilorenzo.flutter_inappwebview.types.SyncBaseCallbackResultImpl;
|
||||
import com.pichillilorenzo.flutter_inappwebview.types.URLRequest;
|
||||
import com.pichillilorenzo.flutter_inappwebview.types.UserScript;
|
||||
import com.pichillilorenzo.flutter_inappwebview.types.WebMessageCompatExt;
|
||||
import com.pichillilorenzo.flutter_inappwebview.types.WebMessagePort;
|
||||
import com.pichillilorenzo.flutter_inappwebview.types.WebMessagePortCompatExt;
|
||||
import com.pichillilorenzo.flutter_inappwebview.types.WebResourceErrorExt;
|
||||
import com.pichillilorenzo.flutter_inappwebview.types.WebResourceRequestExt;
|
||||
import com.pichillilorenzo.flutter_inappwebview.types.WebResourceResponseExt;
|
||||
|
@ -63,10 +65,10 @@ import io.flutter.plugin.common.MethodChannel;
|
|||
|
||||
public class WebViewChannelDelegate extends ChannelDelegateImpl {
|
||||
static final String LOG_TAG = "WebViewChannelDelegate";
|
||||
|
||||
|
||||
@Nullable
|
||||
private InAppWebViewInterface webView;
|
||||
|
||||
|
||||
public WebViewChannelDelegate(@NonNull InAppWebViewInterface webView, @NonNull MethodChannel channel) {
|
||||
super(channel);
|
||||
this.webView = webView;
|
||||
|
@ -141,8 +143,7 @@ public class WebViewChannelDelegate extends ChannelDelegateImpl {
|
|||
result.success(value);
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
result.success(null);
|
||||
}
|
||||
break;
|
||||
|
@ -210,8 +211,7 @@ public class WebViewChannelDelegate extends ChannelDelegateImpl {
|
|||
if (webView != null) {
|
||||
Map<String, Object> screenshotConfiguration = (Map<String, Object>) call.argument("screenshotConfiguration");
|
||||
webView.takeScreenshot(screenshotConfiguration, result);
|
||||
}
|
||||
else
|
||||
} else
|
||||
result.success(null);
|
||||
break;
|
||||
case setSettings:
|
||||
|
@ -282,8 +282,7 @@ public class WebViewChannelDelegate extends ChannelDelegateImpl {
|
|||
result.success(success);
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
result.success(false);
|
||||
}
|
||||
break;
|
||||
|
@ -568,8 +567,7 @@ public class WebViewChannelDelegate extends ChannelDelegateImpl {
|
|||
result.success(value);
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
result.success(null);
|
||||
}
|
||||
break;
|
||||
|
@ -598,27 +596,33 @@ public class WebViewChannelDelegate extends ChannelDelegateImpl {
|
|||
break;
|
||||
case postWebMessage:
|
||||
if (webView != null && WebViewFeature.isFeatureSupported(WebViewFeature.POST_WEB_MESSAGE)) {
|
||||
Map<String, Object> message = (Map<String, Object>) call.argument("message");
|
||||
WebMessageCompatExt message = WebMessageCompatExt.fromMap((Map<String, Object>) call.argument("message"));
|
||||
String targetOrigin = (String) call.argument("targetOrigin");
|
||||
List<WebMessagePortCompat> compatPorts = new ArrayList<>();
|
||||
List<WebMessagePort> ports = new ArrayList<>();
|
||||
List<Map<String, Object>> portsMap = (List<Map<String, Object>>) message.get("ports");
|
||||
if (portsMap != null) {
|
||||
for (Map<String, Object> portMap : portsMap) {
|
||||
String webMessageChannelId = (String) portMap.get("webMessageChannelId");
|
||||
Integer index = (Integer) portMap.get("index");
|
||||
WebMessageChannel webMessageChannel = webView.getWebMessageChannels().get(webMessageChannelId);
|
||||
List<WebMessagePortCompatExt> portsExt = message.getPorts();
|
||||
if (portsExt != null) {
|
||||
for (WebMessagePortCompatExt portExt : portsExt) {
|
||||
WebMessageChannel webMessageChannel = webView.getWebMessageChannels().get(portExt.getWebMessageChannelId());
|
||||
if (webMessageChannel != null) {
|
||||
if (webView instanceof InAppWebView) {
|
||||
compatPorts.add(webMessageChannel.compatPorts.get(index));
|
||||
compatPorts.add(webMessageChannel.compatPorts.get(portExt.getIndex()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Object data = message.getData();
|
||||
if (webView instanceof InAppWebView) {
|
||||
WebMessageCompat webMessage = new WebMessageCompat((String) message.get("data"), compatPorts.toArray(new WebMessagePortCompat[0]));
|
||||
try {
|
||||
WebViewCompat.postWebMessage((WebView) webView, webMessage, Uri.parse(targetOrigin));
|
||||
if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_ARRAY_BUFFER) && data != null &&
|
||||
message.getType() == WebMessageCompat.TYPE_ARRAY_BUFFER) {
|
||||
WebViewCompat.postWebMessage((WebView) webView,
|
||||
new WebMessageCompat((byte[]) data, compatPorts.toArray(new WebMessagePortCompat[0])),
|
||||
Uri.parse(targetOrigin));
|
||||
} else {
|
||||
WebViewCompat.postWebMessage((WebView) webView,
|
||||
new WebMessageCompat(data != null ? data.toString() : null, compatPorts.toArray(new WebMessagePortCompat[0])),
|
||||
Uri.parse(targetOrigin));
|
||||
}
|
||||
result.success(true);
|
||||
} catch (Exception e) {
|
||||
result.error(LOG_TAG, e.getMessage(), null);
|
||||
|
@ -671,8 +675,7 @@ public class WebViewChannelDelegate extends ChannelDelegateImpl {
|
|||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* Use {@link FindInteractionChannelDelegate#onFindResultReceived} instead.
|
||||
* @deprecated Use {@link FindInteractionChannelDelegate#onFindResultReceived} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public void onFindResultReceived(int activeMatchOrdinal, int numberOfMatches, boolean isDoneCounting) {
|
||||
|
@ -684,7 +687,7 @@ public class WebViewChannelDelegate extends ChannelDelegateImpl {
|
|||
obj.put("isDoneCounting", isDoneCounting);
|
||||
channel.invokeMethod("onFindResultReceived", obj);
|
||||
}
|
||||
|
||||
|
||||
public void onLongPressHitTestResult(HitTestResult hitTestResult) {
|
||||
MethodChannel channel = getChannel();
|
||||
if (channel == null) return;
|
||||
|
@ -762,7 +765,7 @@ public class WebViewChannelDelegate extends ChannelDelegateImpl {
|
|||
return JsAlertResponse.fromMap((Map<String, Object>) obj);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void onJsAlert(String url, String message, Boolean isMainFrame, @NonNull JsAlertCallback callback) {
|
||||
MethodChannel channel = getChannel();
|
||||
if (channel == null) {
|
||||
|
@ -1174,7 +1177,7 @@ public class WebViewChannelDelegate extends ChannelDelegateImpl {
|
|||
return (new LoadResourceWithCustomSchemeCallback()).decodeResult(obj);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
public CustomSchemeResponse onLoadResourceWithCustomScheme(WebResourceRequestExt request) throws InterruptedException {
|
||||
MethodChannel channel = getChannel();
|
||||
|
|
|
@ -11,6 +11,8 @@ import androidx.webkit.WebViewFeature;
|
|||
|
||||
import com.pichillilorenzo.flutter_inappwebview.plugin_scripts_js.JavaScriptBridgeJS;
|
||||
import com.pichillilorenzo.flutter_inappwebview.types.Disposable;
|
||||
import com.pichillilorenzo.flutter_inappwebview.types.WebMessageCompatExt;
|
||||
import com.pichillilorenzo.flutter_inappwebview.types.WebMessagePortCompatExt;
|
||||
import com.pichillilorenzo.flutter_inappwebview.webview.InAppWebViewInterface;
|
||||
import com.pichillilorenzo.flutter_inappwebview.types.WebMessagePort;
|
||||
import com.pichillilorenzo.flutter_inappwebview.webview.in_app_webview.InAppWebView;
|
||||
|
@ -77,7 +79,7 @@ public class WebMessageChannel implements Disposable {
|
|||
@Override
|
||||
public void onMessage(@NonNull WebMessagePortCompat port, @Nullable WebMessageCompat message) {
|
||||
super.onMessage(port, message);
|
||||
webMessageChannel.onMessage(index, message != null ? message.getData() : null);
|
||||
webMessageChannel.onMessage(index, message != null ? WebMessageCompatExt.fromMapWebMessageCompat(message) : null);
|
||||
}
|
||||
});
|
||||
result.success(true);
|
||||
|
@ -89,25 +91,28 @@ public class WebMessageChannel implements Disposable {
|
|||
}
|
||||
}
|
||||
|
||||
public void postMessageForInAppWebView(final Integer index, Map<String, Object> message, @NonNull MethodChannel.Result result) {
|
||||
public void postMessageForInAppWebView(final Integer index, @NonNull WebMessageCompatExt message, @NonNull MethodChannel.Result result) {
|
||||
if (webView != null && compatPorts.size() > 0 &&
|
||||
WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_PORT_POST_MESSAGE)) {
|
||||
WebMessagePortCompat port = compatPorts.get(index);
|
||||
List<WebMessagePortCompat> webMessagePorts = new ArrayList<>();
|
||||
List<Map<String, Object>> portsMap = (List<Map<String, Object>>) message.get("ports");
|
||||
if (portsMap != null) {
|
||||
for (Map<String, Object> portMap : portsMap) {
|
||||
String webMessageChannelId = (String) portMap.get("webMessageChannelId");
|
||||
Integer portIndex = (Integer) portMap.get("index");
|
||||
WebMessageChannel webMessageChannel = webView.getWebMessageChannels().get(webMessageChannelId);
|
||||
List<WebMessagePortCompatExt> portsExt = message.getPorts();
|
||||
if (portsExt != null) {
|
||||
for (WebMessagePortCompatExt portExt : portsExt) {
|
||||
WebMessageChannel webMessageChannel = webView.getWebMessageChannels().get(portExt.getWebMessageChannelId());
|
||||
if (webMessageChannel != null) {
|
||||
webMessagePorts.add(webMessageChannel.compatPorts.get(portIndex));
|
||||
webMessagePorts.add(webMessageChannel.compatPorts.get(portExt.getIndex()));
|
||||
}
|
||||
}
|
||||
}
|
||||
WebMessageCompat webMessage = new WebMessageCompat((String) message.get("data"), webMessagePorts.toArray(new WebMessagePortCompat[0]));
|
||||
Object data = message.getData();
|
||||
try {
|
||||
port.postMessage(webMessage);
|
||||
if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_ARRAY_BUFFER) && data != null &&
|
||||
message.getType() == WebMessageCompat.TYPE_ARRAY_BUFFER) {
|
||||
port.postMessage(new WebMessageCompat((byte[]) data, webMessagePorts.toArray(new WebMessagePortCompat[0])));
|
||||
} else {
|
||||
port.postMessage(new WebMessageCompat(data != null ? data.toString() : null, webMessagePorts.toArray(new WebMessagePortCompat[0])));
|
||||
}
|
||||
result.success(true);
|
||||
} catch (Exception e) {
|
||||
result.error(LOG_TAG, e.getMessage(), null);
|
||||
|
@ -132,7 +137,7 @@ public class WebMessageChannel implements Disposable {
|
|||
}
|
||||
}
|
||||
|
||||
public void onMessage(int index, String message) {
|
||||
public void onMessage(int index, @Nullable WebMessageCompatExt message) {
|
||||
if (channelDelegate != null) {
|
||||
channelDelegate.onMessage(index, message);
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import androidx.annotation.Nullable;
|
|||
|
||||
import com.pichillilorenzo.flutter_inappwebview.types.ChannelDelegateImpl;
|
||||
import com.pichillilorenzo.flutter_inappwebview.types.Disposable;
|
||||
import com.pichillilorenzo.flutter_inappwebview.types.WebMessageCompatExt;
|
||||
import com.pichillilorenzo.flutter_inappwebview.webview.in_app_webview.InAppWebView;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
@ -36,7 +37,7 @@ public class WebMessageChannelChannelDelegate extends ChannelDelegateImpl {
|
|||
case "postMessage":
|
||||
if (webMessageChannel != null && webMessageChannel.webView instanceof InAppWebView) {
|
||||
final Integer index = (Integer) call.argument("index");
|
||||
Map<String, Object> message = (Map<String, Object>) call.argument("message");
|
||||
WebMessageCompatExt message = WebMessageCompatExt.fromMap((Map<String, Object>) call.argument("message"));
|
||||
webMessageChannel.postMessageForInAppWebView(index, message, result);
|
||||
} else {
|
||||
result.success(false);
|
||||
|
@ -55,12 +56,12 @@ public class WebMessageChannelChannelDelegate extends ChannelDelegateImpl {
|
|||
}
|
||||
}
|
||||
|
||||
public void onMessage(int index, String message) {
|
||||
public void onMessage(int index, @Nullable WebMessageCompatExt message) {
|
||||
MethodChannel channel = getChannel();
|
||||
if (channel == null) return;
|
||||
Map<String, Object> obj = new HashMap<>();
|
||||
obj.put("index", index);
|
||||
obj.put("message", message );
|
||||
obj.put("message", message != null ? message.toMap() : null);
|
||||
channel.invokeMethod("onMessage", obj);
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ import androidx.webkit.WebViewFeature;
|
|||
import com.pichillilorenzo.flutter_inappwebview.Util;
|
||||
import com.pichillilorenzo.flutter_inappwebview.plugin_scripts_js.JavaScriptBridgeJS;
|
||||
import com.pichillilorenzo.flutter_inappwebview.types.Disposable;
|
||||
import com.pichillilorenzo.flutter_inappwebview.types.WebMessageCompatExt;
|
||||
import com.pichillilorenzo.flutter_inappwebview.webview.InAppWebViewInterface;
|
||||
import com.pichillilorenzo.flutter_inappwebview.types.PluginScript;
|
||||
import com.pichillilorenzo.flutter_inappwebview.types.UserScriptInjectionTime;
|
||||
|
@ -61,7 +62,7 @@ public class WebMessageListener implements Disposable {
|
|||
boolean isMainFrame, @NonNull JavaScriptReplyProxy javaScriptReplyProxy) {
|
||||
replyProxy = javaScriptReplyProxy;
|
||||
if (channelDelegate != null) {
|
||||
channelDelegate.onPostMessage(message.getData(),
|
||||
channelDelegate.onPostMessage(WebMessageCompatExt.fromMapWebMessageCompat(message),
|
||||
sourceOrigin.toString().equals("null") ? null : sourceOrigin.toString(),
|
||||
isMainFrame);
|
||||
}
|
||||
|
@ -171,9 +172,16 @@ public class WebMessageListener implements Disposable {
|
|||
}
|
||||
}
|
||||
|
||||
public void postMessageForInAppWebView(String message, @NonNull MethodChannel.Result result) {
|
||||
public void postMessageForInAppWebView(WebMessageCompatExt message, @NonNull MethodChannel.Result result) {
|
||||
if (replyProxy != null && WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_LISTENER)) {
|
||||
replyProxy.postMessage(message);
|
||||
Object data = message.getData();
|
||||
if (data != null) {
|
||||
if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_ARRAY_BUFFER) && message.getType() == WebMessageCompat.TYPE_ARRAY_BUFFER) {
|
||||
replyProxy.postMessage((byte[]) data);
|
||||
} else {
|
||||
replyProxy.postMessage(data.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
result.success(true);
|
||||
}
|
||||
|
@ -196,12 +204,14 @@ public class WebMessageListener implements Disposable {
|
|||
if (rule.getHost() != null && rule.getHost().startsWith("[")) {
|
||||
try {
|
||||
IPv6 = Util.normalizeIPv6(rule.getHost().substring(1, rule.getHost().length() - 1));
|
||||
} catch (Exception ignored) {}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
String hostIPv6 = null;
|
||||
try {
|
||||
hostIPv6 = Util.normalizeIPv6(host);
|
||||
} catch (Exception ignored) {}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
boolean schemeAllowed = rule.getScheme().equals(scheme);
|
||||
|
||||
|
|
|
@ -2,10 +2,12 @@ package com.pichillilorenzo.flutter_inappwebview.webview.web_message;
|
|||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.webkit.WebMessageCompat;
|
||||
|
||||
import com.pichillilorenzo.flutter_inappwebview.headless_in_app_webview.HeadlessInAppWebView;
|
||||
import com.pichillilorenzo.flutter_inappwebview.types.ChannelDelegateImpl;
|
||||
import com.pichillilorenzo.flutter_inappwebview.types.Disposable;
|
||||
import com.pichillilorenzo.flutter_inappwebview.types.WebMessageCompatExt;
|
||||
import com.pichillilorenzo.flutter_inappwebview.webview.in_app_webview.InAppWebView;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
@ -28,7 +30,7 @@ public class WebMessageListenerChannelDelegate extends ChannelDelegateImpl {
|
|||
switch (call.method) {
|
||||
case "postMessage":
|
||||
if (webMessageListener != null && webMessageListener.webView instanceof InAppWebView) {
|
||||
String message = (String) call.argument("message");
|
||||
WebMessageCompatExt message = WebMessageCompatExt.fromMap((Map<String, Object>) call.argument("message"));
|
||||
webMessageListener.postMessageForInAppWebView(message, result);
|
||||
} else {
|
||||
result.success(false);
|
||||
|
@ -39,11 +41,11 @@ public class WebMessageListenerChannelDelegate extends ChannelDelegateImpl {
|
|||
}
|
||||
}
|
||||
|
||||
public void onPostMessage(String message, String sourceOrigin, boolean isMainFrame) {
|
||||
public void onPostMessage(@Nullable WebMessageCompatExt message, String sourceOrigin, boolean isMainFrame) {
|
||||
MethodChannel channel = getChannel();
|
||||
if (channel == null) return;
|
||||
Map<String, Object> obj = new HashMap<>();
|
||||
obj.put("message", message);
|
||||
obj.put("message", message != null ? message.toMap() : null);
|
||||
obj.put("sourceOrigin", sourceOrigin);
|
||||
obj.put("isMainFrame", isMainFrame);
|
||||
channel.invokeMethod("onPostMessage", obj);
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
## 1.1.1
|
||||
|
||||
- Added `ExchangeableObject.fromMapForceAllInline`.
|
||||
|
||||
## 1.1.0
|
||||
|
||||
- Added `ExchangeableObject.copyMethod`.
|
||||
|
|
|
@ -2,6 +2,7 @@ class ExchangeableObject {
|
|||
final bool toMapMethod;
|
||||
final bool toJsonMethod;
|
||||
final bool fromMapFactory;
|
||||
final bool fromMapForceAllInline;
|
||||
final bool nullableFromMapFactory;
|
||||
final bool toStringMethod;
|
||||
final bool copyMethod;
|
||||
|
@ -10,6 +11,7 @@ class ExchangeableObject {
|
|||
this.toMapMethod = true,
|
||||
this.toJsonMethod = true,
|
||||
this.fromMapFactory = true,
|
||||
this.fromMapForceAllInline = false,
|
||||
this.nullableFromMapFactory = true,
|
||||
this.toStringMethod = true,
|
||||
this.copyMethod = false
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
name: flutter_inappwebview_internal_annotations
|
||||
description: Internal annotations used by the generator of flutter_inappwebview plugin
|
||||
version: 1.1.0
|
||||
version: 1.1.1
|
||||
homepage: https://github.com/pichillilorenzo/flutter_inappwebview
|
||||
|
||||
environment:
|
||||
sdk: ">=2.14.0 <3.0.0"
|
||||
sdk: ">=2.15.0 <4.0.0"
|
||||
|
||||
dev_dependencies:
|
||||
test: ^1.21.6
|
|
@ -358,7 +358,7 @@ class ExchangeableObjectGenerator
|
|||
constructorParameter.isFinal || fieldElement.isFinal ||
|
||||
!Util.typeIsNullable(constructorParameter.type)) &&
|
||||
!constructorParameter.hasDefaultValue;
|
||||
if (isRequiredParameter || fieldElement.isFinal) {
|
||||
if (isRequiredParameter || fieldElement.isFinal || annotation.read("fromMapForceAllInline").boolValue) {
|
||||
requiredFields.add('$fieldName: $value,');
|
||||
} else {
|
||||
nonRequiredFields.add("instance.$fieldName = $value;");
|
||||
|
|
|
@ -209,11 +209,10 @@ packages:
|
|||
flutter_inappwebview_internal_annotations:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_inappwebview_internal_annotations
|
||||
sha256: "064a8ccbc76217dcd3b0fd6c6ea6f549e69b2849a0233b5bb46af9632c3ce2ff"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
path: "../flutter_inappwebview_internal_annotations"
|
||||
relative: true
|
||||
source: path
|
||||
version: "1.1.1"
|
||||
frontend_server_client:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
@ -12,7 +12,7 @@ dependencies:
|
|||
sdk: flutter
|
||||
build: ^2.4.0
|
||||
source_gen: ^1.3.1
|
||||
flutter_inappwebview_internal_annotations: ^1.1.0
|
||||
flutter_inappwebview_internal_annotations: ^1.1.1
|
||||
|
||||
dev_dependencies:
|
||||
build_runner: ^2.4.2
|
||||
|
|
|
@ -10,7 +10,8 @@ void webMessage() {
|
|||
].contains(defaultTargetPlatform);
|
||||
|
||||
skippableGroup('WebMessage', () {
|
||||
skippableTestWidgets('WebMessageChannel', (WidgetTester tester) async {
|
||||
skippableTestWidgets('WebMessageChannel post String',
|
||||
(WidgetTester tester) async {
|
||||
final Completer<InAppWebViewController> controllerCompleter =
|
||||
Completer<InAppWebViewController>();
|
||||
final Completer webMessageCompleter = Completer<String>();
|
||||
|
@ -61,7 +62,7 @@ void webMessage() {
|
|||
|
||||
await port1.setWebMessageCallback((message) async {
|
||||
await port1
|
||||
.postMessage(WebMessage(data: message! + " and back"));
|
||||
.postMessage(WebMessage(data: message!.data + " and back"));
|
||||
});
|
||||
await controller.postWebMessage(
|
||||
message: WebMessage(data: "capturePort", ports: [port2]),
|
||||
|
@ -78,7 +79,94 @@ void webMessage() {
|
|||
expect(message, 'JavaScript To Native and back');
|
||||
});
|
||||
|
||||
skippableTestWidgets('WebMessageListener', (WidgetTester tester) async {
|
||||
skippableTestWidgets('WebMessageChannel post ArrayBuffer',
|
||||
(WidgetTester tester) async {
|
||||
final Completer<InAppWebViewController> controllerCompleter =
|
||||
Completer<InAppWebViewController>();
|
||||
final Completer webMessageCompleter = Completer<String>();
|
||||
await tester.pumpWidget(
|
||||
Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: InAppWebView(
|
||||
key: GlobalKey(),
|
||||
initialData: InAppWebViewInitialData(data: """
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>WebMessageChannel Test</title>
|
||||
</head>
|
||||
<body>
|
||||
<button id="button" onclick="port.postMessage(stringToBuffer(input.value));" />Send</button>
|
||||
<br />
|
||||
<input id="input" type="text" value="JavaScript To Native" />
|
||||
|
||||
<script>
|
||||
function bufferToString(buffer) {
|
||||
return String.fromCharCode.apply(null, Array.from(new Uint8Array(buffer)));
|
||||
}
|
||||
|
||||
function stringToBuffer(value) {
|
||||
var buffer = new ArrayBuffer(value.length);
|
||||
var view = new Uint8Array(buffer);
|
||||
for (var i = 0, length = value.length; i < length; i++) {
|
||||
view[i] = value.charCodeAt(i);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
var port;
|
||||
window.addEventListener('message', function(event) {
|
||||
if (bufferToString(event.data) == 'capturePort') {
|
||||
if (event.ports[0] != null) {
|
||||
port = event.ports[0];
|
||||
port.onmessage = function (event) {
|
||||
console.log(event.data);
|
||||
};
|
||||
}
|
||||
}
|
||||
}, false);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
"""),
|
||||
onWebViewCreated: (controller) {
|
||||
controllerCompleter.complete(controller);
|
||||
},
|
||||
onConsoleMessage: (controller, consoleMessage) {
|
||||
webMessageCompleter.complete(consoleMessage.message);
|
||||
},
|
||||
onLoadStop: (controller, url) async {
|
||||
var webMessageChannel =
|
||||
await controller.createWebMessageChannel();
|
||||
var port1 = webMessageChannel!.port1;
|
||||
var port2 = webMessageChannel.port2;
|
||||
|
||||
await port1.setWebMessageCallback((message) async {
|
||||
await port1.postMessage(WebMessage(
|
||||
data: utf8.encode(utf8.decode(message!.data) + " and back"),
|
||||
type: WebMessageType.ARRAY_BUFFER));
|
||||
});
|
||||
await controller.postWebMessage(
|
||||
message: WebMessage(
|
||||
data: utf8.encode("capturePort"),
|
||||
type: WebMessageType.ARRAY_BUFFER,
|
||||
ports: [port2]),
|
||||
targetOrigin: WebUri("*"));
|
||||
await controller.evaluateJavascript(
|
||||
source: "document.getElementById('button').click();");
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
await controllerCompleter.future;
|
||||
|
||||
final String message = await webMessageCompleter.future;
|
||||
expect(message, 'JavaScript To Native and back');
|
||||
});
|
||||
|
||||
skippableTestWidgets('WebMessageListener post String',
|
||||
(WidgetTester tester) async {
|
||||
final Completer<InAppWebViewController> controllerCompleter =
|
||||
Completer<InAppWebViewController>();
|
||||
final Completer<void> pageLoaded = Completer<void>();
|
||||
|
@ -97,9 +185,10 @@ void webMessage() {
|
|||
if (isMainFrame &&
|
||||
(sourceOrigin.toString() + '/') ==
|
||||
TEST_URL_EXAMPLE.toString()) {
|
||||
replyProxy.postMessage(message! + " and back");
|
||||
replyProxy.postMessage(
|
||||
WebMessage(data: message!.data + " and back"));
|
||||
} else {
|
||||
replyProxy.postMessage("Nope");
|
||||
replyProxy.postMessage(WebMessage(data: "Nope"));
|
||||
}
|
||||
},
|
||||
));
|
||||
|
@ -130,5 +219,77 @@ void webMessage() {
|
|||
final String message = await webMessageCompleter.future;
|
||||
expect(message, 'JavaScript To Native and back');
|
||||
});
|
||||
|
||||
skippableTestWidgets('WebMessageListener post ArrayBuffer',
|
||||
(WidgetTester tester) async {
|
||||
final Completer<InAppWebViewController> controllerCompleter =
|
||||
Completer<InAppWebViewController>();
|
||||
final Completer<void> pageLoaded = Completer<void>();
|
||||
final Completer webMessageCompleter = Completer<String>();
|
||||
await tester.pumpWidget(
|
||||
Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: InAppWebView(
|
||||
key: GlobalKey(),
|
||||
onWebViewCreated: (controller) async {
|
||||
await controller.addWebMessageListener(WebMessageListener(
|
||||
jsObjectName: "myTestObj",
|
||||
allowedOriginRules: Set.from(["https://*.example.com"]),
|
||||
onPostMessage:
|
||||
(message, sourceOrigin, isMainFrame, replyProxy) {
|
||||
if (isMainFrame &&
|
||||
(sourceOrigin.toString() + '/') ==
|
||||
TEST_URL_EXAMPLE.toString()) {
|
||||
replyProxy.postMessage(WebMessage(
|
||||
data: utf8
|
||||
.encode(utf8.decode(message!.data) + " and back"),
|
||||
type: WebMessageType.ARRAY_BUFFER));
|
||||
} else {
|
||||
replyProxy.postMessage(WebMessage(
|
||||
data: utf8.encode("Nope"),
|
||||
type: WebMessageType.ARRAY_BUFFER));
|
||||
}
|
||||
},
|
||||
));
|
||||
controllerCompleter.complete(controller);
|
||||
},
|
||||
onConsoleMessage: (controller, consoleMessage) {
|
||||
webMessageCompleter.complete(consoleMessage.message);
|
||||
},
|
||||
onLoadStop: (controller, url) async {
|
||||
if (url.toString() == TEST_URL_EXAMPLE.toString()) {
|
||||
pageLoaded.complete();
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
final controller = await controllerCompleter.future;
|
||||
await controller.loadUrl(urlRequest: URLRequest(url: TEST_URL_EXAMPLE));
|
||||
await pageLoaded.future;
|
||||
|
||||
await controller.evaluateJavascript(source: """
|
||||
function bufferToString(buffer) {
|
||||
return String.fromCharCode.apply(null, Array.from(new Uint8Array(buffer)));
|
||||
}
|
||||
|
||||
function stringToBuffer(value) {
|
||||
var buffer = new ArrayBuffer(value.length);
|
||||
var view = new Uint8Array(buffer);
|
||||
for (var i = 0, length = value.length; i < length; i++) {
|
||||
view[i] = value.charCodeAt(i);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
myTestObj.addEventListener('message', function(event) {
|
||||
console.log(bufferToString(event.data));
|
||||
});
|
||||
myTestObj.postMessage(stringToBuffer('JavaScript To Native'));
|
||||
""");
|
||||
|
||||
final String message = await webMessageCompleter.future;
|
||||
expect(message, 'JavaScript To Native and back');
|
||||
});
|
||||
}, skip: shouldSkip);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
part of 'main.dart';
|
||||
|
||||
void apply() {
|
||||
final shouldSkip = kIsWeb
|
||||
? true
|
||||
: ![
|
||||
TargetPlatform.android,
|
||||
].contains(defaultTargetPlatform);
|
||||
|
||||
skippableTestWidgets('apply', (WidgetTester tester) async {
|
||||
await expectLater(
|
||||
ProcessGlobalConfig.instance().apply(
|
||||
settings: ProcessGlobalConfigSettings(
|
||||
dataDirectorySuffix:
|
||||
(await WebViewFeature.isStartupFeatureSupported(
|
||||
WebViewFeature
|
||||
.STARTUP_FEATURE_SET_DATA_DIRECTORY_SUFFIX))
|
||||
? 'suffix_inappwebviewexample'
|
||||
: null,
|
||||
directoryBasePaths:
|
||||
(await WebViewFeature.isStartupFeatureSupported(
|
||||
WebViewFeature
|
||||
.STARTUP_FEATURE_SET_DIRECTORY_BASE_PATHS))
|
||||
? ProcessGlobalConfigDirectoryBasePaths(
|
||||
cacheDirectoryBasePath:
|
||||
(await getApplicationDocumentsDirectory())
|
||||
.absolute
|
||||
.path +
|
||||
'/inappwebviewexample/cache',
|
||||
dataDirectoryBasePath:
|
||||
(await getApplicationDocumentsDirectory())
|
||||
.absolute
|
||||
.path +
|
||||
'/inappwebviewexample/data')
|
||||
: null)),
|
||||
completes);
|
||||
}, skip: shouldSkip);
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import '../constants.dart';
|
||||
import '../env.dart';
|
||||
import '../util.dart';
|
||||
|
||||
part 'apply.dart';
|
||||
|
||||
void main() {
|
||||
final shouldSkip = kIsWeb;
|
||||
|
||||
skippableGroup('Process Global Config', () {
|
||||
apply();
|
||||
}, skip: shouldSkip);
|
||||
}
|
|
@ -2,6 +2,7 @@ import 'package:flutter/foundation.dart';
|
|||
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import 'process_global_config/main.dart' as process_global_config_tests;
|
||||
import 'in_app_webview/main.dart' as in_app_webview_tests;
|
||||
import 'find_interaction_controller/main.dart'
|
||||
as find_interaction_controller_tests;
|
||||
|
@ -34,6 +35,7 @@ void main() {
|
|||
FindInteractionController.debugLoggingSettings.usePrint = true;
|
||||
FindInteractionController.debugLoggingSettings.maxLogMessageLength = 7000;
|
||||
|
||||
process_global_config_tests.main();
|
||||
in_app_webview_tests.main();
|
||||
find_interaction_controller_tests.main();
|
||||
service_worker_controller_tests.main();
|
||||
|
|
|
@ -2887,14 +2887,22 @@ if(window.\(JAVASCRIPT_BRIDGE_NAME)[\(_callHandlerID)] != null) {
|
|||
let body = message.body as! [String: Any?]
|
||||
let webMessageChannelId = body["webMessageChannelId"] as! String
|
||||
let index = body["index"] as! Int64
|
||||
let webMessage = body["message"] as? String
|
||||
var webMessage: WebMessage? = nil
|
||||
if let webMessageMap = body["message"] as? [String : Any?] {
|
||||
webMessage = WebMessage.fromMap(map: webMessageMap)
|
||||
}
|
||||
|
||||
if let webMessageChannel = webMessageChannels[webMessageChannelId] {
|
||||
webMessageChannel.channelDelegate?.onMessage(index: index, message: webMessage)
|
||||
}
|
||||
} else if message.name == "onWebMessageListenerPostMessageReceived" {
|
||||
let body = message.body as! [String: Any?]
|
||||
let jsObjectName = body["jsObjectName"] as! String
|
||||
let messageData = body["message"] as? String
|
||||
var webMessage: WebMessage? = nil
|
||||
if let webMessageMap = body["message"] as? [String : Any?] {
|
||||
webMessage = WebMessage.fromMap(map: webMessageMap)
|
||||
}
|
||||
|
||||
if let webMessageListener = webMessageListeners.first(where: ({($0.jsObjectName == jsObjectName)})) {
|
||||
let isMainFrame = message.frameInfo.isMainFrame
|
||||
|
||||
|
@ -2920,7 +2928,7 @@ if(window.\(JAVASCRIPT_BRIDGE_NAME)[\(_callHandlerID)] != null) {
|
|||
if let scheme = scheme, !scheme.isEmpty, let host = host, !host.isEmpty {
|
||||
sourceOrigin = URL(string: "\(scheme)://\(host)\(port != nil && port != 0 ? ":" + String(port!) : "")")
|
||||
}
|
||||
webMessageListener.channelDelegate?.onPostMessage(message: messageData, sourceOrigin: sourceOrigin, isMainFrame: isMainFrame)
|
||||
webMessageListener.channelDelegate?.onPostMessage(message: webMessage, sourceOrigin: sourceOrigin, isMainFrame: isMainFrame)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3190,11 +3198,11 @@ if(window.\(JAVASCRIPT_BRIDGE_NAME)[\(_callHandlerID)] != null) {
|
|||
}
|
||||
portsString = "[" + portArrayString.joined(separator: ", ") + "]"
|
||||
}
|
||||
let data = message.data?.replacingOccurrences(of: "\'", with: "\\'") ?? "null"
|
||||
|
||||
let url = URL(string: targetOrigin)?.absoluteString ?? "*"
|
||||
let source = """
|
||||
(function() {
|
||||
window.postMessage('\(data)', '\(url)', \(portsString));
|
||||
window.postMessage(\(message.jsData), '\(url)', \(portsString));
|
||||
})();
|
||||
"""
|
||||
evaluateJavascript(source: source, completionHandler: completionHandler)
|
||||
|
|
|
@ -25,8 +25,8 @@ public class WebMessageChannel : FlutterMethodCallDelegate {
|
|||
self.channelDelegate = WebMessageChannelChannelDelegate(webMessageChannel: self, channel: channel)
|
||||
}
|
||||
self.ports = [
|
||||
WebMessagePort(name: "port1", webMessageChannel: self),
|
||||
WebMessagePort(name: "port2", webMessageChannel: self)
|
||||
WebMessagePort(name: "port1", index: 0, webMessageChannelId: self.id, webMessageChannel: self),
|
||||
WebMessagePort(name: "port2", index: 1, webMessageChannelId: self.id, webMessageChannel: self)
|
||||
]
|
||||
}
|
||||
|
||||
|
|
|
@ -39,22 +39,20 @@ public class WebMessageChannelChannelDelegate : ChannelDelegate {
|
|||
if let webView = webMessageChannel?.webView, let ports = webMessageChannel?.ports, ports.count > 0 {
|
||||
let index = arguments!["index"] as! Int
|
||||
let port = ports[index]
|
||||
let message = arguments!["message"] as! [String: Any?]
|
||||
var message = WebMessage.fromMap(map: arguments!["message"] as! [String: Any?])
|
||||
|
||||
var webMessagePorts: [WebMessagePort] = []
|
||||
let portsMap = message["ports"] as? [[String: Any?]]
|
||||
if let portsMap = portsMap {
|
||||
for portMap in portsMap {
|
||||
let webMessageChannelId = portMap["webMessageChannelId"] as! String
|
||||
let index = portMap["index"] as! Int
|
||||
if let webMessageChannel = webView.webMessageChannels[webMessageChannelId] {
|
||||
webMessagePorts.append(webMessageChannel.ports[index])
|
||||
var ports: [WebMessagePort] = []
|
||||
if let notConnectedPorts = message.ports {
|
||||
for notConnectedPort in notConnectedPorts {
|
||||
if let webMessageChannel = webView.webMessageChannels[notConnectedPort.webMessageChannelId] {
|
||||
ports.append(webMessageChannel.ports[Int(notConnectedPort.index)])
|
||||
}
|
||||
}
|
||||
}
|
||||
let webMessage = WebMessage(data: message["data"] as? String, ports: webMessagePorts)
|
||||
message.ports = ports
|
||||
|
||||
do {
|
||||
try port.postMessage(message: webMessage) { (_) in
|
||||
try port.postMessage(message: message) { (_) in
|
||||
result(true)
|
||||
}
|
||||
} catch let error as NSError {
|
||||
|
@ -85,10 +83,10 @@ public class WebMessageChannelChannelDelegate : ChannelDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
public func onMessage(index: Int64, message: String?) {
|
||||
public func onMessage(index: Int64, message: WebMessage?) {
|
||||
let arguments: [String:Any?] = [
|
||||
"index": index,
|
||||
"message": message
|
||||
"message": message?.toMap()
|
||||
]
|
||||
channel?.invokeMethod("onMessage", arguments: arguments)
|
||||
}
|
||||
|
|
|
@ -22,12 +22,13 @@ public class WebMessageListenerChannelDelegate : ChannelDelegate {
|
|||
case "postMessage":
|
||||
if let webView = webMessageListener?.webView, let jsObjectName = webMessageListener?.jsObjectName {
|
||||
let jsObjectNameEscaped = jsObjectName.replacingOccurrences(of: "\'", with: "\\'")
|
||||
let messageEscaped = (arguments!["message"] as! String).replacingOccurrences(of: "\'", with: "\\'")
|
||||
let message = WebMessage.fromMap(map: arguments!["message"] as! [String: Any?])
|
||||
|
||||
let source = """
|
||||
(function() {
|
||||
var webMessageListener = window['\(jsObjectNameEscaped)'];
|
||||
if (webMessageListener != null) {
|
||||
var event = {data: '\(messageEscaped)'};
|
||||
var event = {data: \(message.jsData)};
|
||||
if (webMessageListener.onmessage != null) {
|
||||
webMessageListener.onmessage(event);
|
||||
}
|
||||
|
@ -50,9 +51,9 @@ public class WebMessageListenerChannelDelegate : ChannelDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
public func onPostMessage(message: String?, sourceOrigin: URL?, isMainFrame: Bool) {
|
||||
public func onPostMessage(message: WebMessage?, sourceOrigin: URL?, isMainFrame: Bool) {
|
||||
let arguments: [String:Any?] = [
|
||||
"message": message,
|
||||
"message": message?.toMap(),
|
||||
"sourceOrigin": sourceOrigin?.absoluteString,
|
||||
"isMainFrame": isMainFrame
|
||||
]
|
||||
|
|
|
@ -509,23 +509,21 @@ public class WebViewChannelDelegate : ChannelDelegate {
|
|||
break
|
||||
case .postWebMessage:
|
||||
if let webView = webView {
|
||||
let message = arguments!["message"] as! [String: Any?]
|
||||
var message = WebMessage.fromMap(map: arguments!["message"] as! [String: Any?])
|
||||
let targetOrigin = arguments!["targetOrigin"] as! String
|
||||
|
||||
var ports: [WebMessagePort] = []
|
||||
let portsMap = message["ports"] as? [[String: Any?]]
|
||||
if let portsMap = portsMap {
|
||||
for portMap in portsMap {
|
||||
let webMessageChannelId = portMap["webMessageChannelId"] as! String
|
||||
let index = portMap["index"] as! Int
|
||||
if let webMessageChannel = webView.webMessageChannels[webMessageChannelId] {
|
||||
ports.append(webMessageChannel.ports[index])
|
||||
if let notConnectedPorts = message.ports {
|
||||
for notConnectedPort in notConnectedPorts {
|
||||
if let webMessageChannel = webView.webMessageChannels[notConnectedPort.webMessageChannelId] {
|
||||
ports.append(webMessageChannel.ports[Int(notConnectedPort.index)])
|
||||
}
|
||||
}
|
||||
}
|
||||
let webMessage = WebMessage(data: message["data"] as? String, ports: ports)
|
||||
message.ports = ports
|
||||
|
||||
do {
|
||||
try webView.postWebMessage(message: webMessage, targetOrigin: targetOrigin) { (_) in
|
||||
try webView.postWebMessage(message: message, targetOrigin: targetOrigin) { (_) in
|
||||
result(true)
|
||||
}
|
||||
} catch let error as NSError {
|
||||
|
|
|
@ -13,7 +13,11 @@ function FlutterInAppWebViewWebMessageListener(jsObjectName) {
|
|||
this.listeners = [];
|
||||
this.onmessage = null;
|
||||
}
|
||||
FlutterInAppWebViewWebMessageListener.prototype.postMessage = function(message) {
|
||||
FlutterInAppWebViewWebMessageListener.prototype.postMessage = function(data) {
|
||||
var message = {
|
||||
"data": window.ArrayBuffer != null && data instanceof ArrayBuffer ? Array.from(new Uint8Array(data)) : (data != null ? data.toString() : null),
|
||||
"type": window.ArrayBuffer != null && data instanceof ArrayBuffer ? 1 : 0
|
||||
};
|
||||
window.webkit.messageHandlers['onWebMessageListenerPostMessageReceived'].postMessage({jsObjectName: this.jsObjectName, message: message});
|
||||
};
|
||||
FlutterInAppWebViewWebMessageListener.prototype.addEventListener = function(type, listener) {
|
||||
|
|
|
@ -7,16 +7,53 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
public class WebMessage : NSObject {
|
||||
var data: String?
|
||||
public class WebMessage : NSObject, Disposable {
|
||||
var data: Any?
|
||||
var type: WebMessageType
|
||||
var ports: [WebMessagePort]?
|
||||
|
||||
public init(data: String?, ports: [WebMessagePort]?) {
|
||||
var jsData: String {
|
||||
var jsData: String = "null"
|
||||
if let messageData = data {
|
||||
if type == .arrayBuffer, let messageDataArrayBuffer = messageData as? FlutterStandardTypedData {
|
||||
jsData = "new Uint8Array(\(Array(messageDataArrayBuffer.data))).buffer"
|
||||
} else if let messageDataString = messageData as? String {
|
||||
jsData = "'\(messageDataString.replacingOccurrences(of: "\'", with: "\\'"))'"
|
||||
}
|
||||
}
|
||||
return jsData
|
||||
}
|
||||
|
||||
public init(data: Any?, type: WebMessageType, ports: [WebMessagePort]?) {
|
||||
self.type = type
|
||||
super.init()
|
||||
self.data = data
|
||||
self.ports = ports
|
||||
}
|
||||
|
||||
public static func fromMap(map: [String: Any?]) -> WebMessage {
|
||||
let portMapList = map["ports"] as? [[String: Any?]]
|
||||
var ports: [WebMessagePort]? = nil
|
||||
if let portMapList = portMapList, !portMapList.isEmpty {
|
||||
ports = []
|
||||
portMapList.forEach { (portMap) in
|
||||
ports?.append(WebMessagePort.fromMap(map: portMap))
|
||||
}
|
||||
}
|
||||
|
||||
return WebMessage(
|
||||
data: map["data"] as? Any,
|
||||
type: WebMessageType.init(rawValue: map["type"] as! Int)!,
|
||||
ports: ports)
|
||||
}
|
||||
|
||||
public func toMap () -> [String: Any?] {
|
||||
return [
|
||||
"data": type == .arrayBuffer && data is [UInt8] ? Data(data as! [UInt8]) : data,
|
||||
"type": type.rawValue
|
||||
]
|
||||
}
|
||||
|
||||
public func dispose() {
|
||||
ports?.removeAll()
|
||||
}
|
||||
|
@ -26,3 +63,8 @@ public class WebMessage : NSObject {
|
|||
dispose()
|
||||
}
|
||||
}
|
||||
|
||||
public enum WebMessageType: Int {
|
||||
case string = 0
|
||||
case arrayBuffer = 1
|
||||
}
|
||||
|
|
|
@ -9,13 +9,17 @@ import Foundation
|
|||
|
||||
public class WebMessagePort : NSObject {
|
||||
var name: String
|
||||
var index: Int64
|
||||
var webMessageChannelId: String
|
||||
var webMessageChannel: WebMessageChannel?
|
||||
var isClosed = false
|
||||
var isTransferred = false
|
||||
var isStarted = false
|
||||
|
||||
public init(name: String, webMessageChannel: WebMessageChannel) {
|
||||
public init(name: String, index: Int64, webMessageChannelId: String, webMessageChannel: WebMessageChannel?) {
|
||||
self.name = name
|
||||
self.index = index
|
||||
self.webMessageChannelId = webMessageChannelId
|
||||
super.init()
|
||||
self.webMessageChannel = webMessageChannel
|
||||
}
|
||||
|
@ -35,7 +39,10 @@ public class WebMessagePort : NSObject {
|
|||
window.webkit.messageHandlers["onWebMessagePortMessageReceived"].postMessage({
|
||||
"webMessageChannelId": "\(webMessageChannel.id)",
|
||||
"index": \(String(index)),
|
||||
"message": event.data
|
||||
"message": {
|
||||
"data": window.ArrayBuffer != null && event.data instanceof ArrayBuffer ? Array.from(new Uint8Array(event.data)) : (event.data != null ? event.data.toString() : null),
|
||||
"type": window.ArrayBuffer != null && event.data instanceof ArrayBuffer ? 1 : 0
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -71,12 +78,12 @@ public class WebMessagePort : NSObject {
|
|||
}
|
||||
portsString = "[" + portArrayString.joined(separator: ", ") + "]"
|
||||
}
|
||||
let data = message.data?.replacingOccurrences(of: "\'", with: "\\'") ?? "null"
|
||||
|
||||
let source = """
|
||||
(function() {
|
||||
var webMessageChannel = \(WEB_MESSAGE_CHANNELS_VARIABLE_NAME)["\(webMessageChannel.id)"];
|
||||
if (webMessageChannel != null) {
|
||||
webMessageChannel.\(self.name).postMessage('\(data)', \(portsString));
|
||||
webMessageChannel.\(self.name).postMessage(\(message.jsData), \(portsString));
|
||||
}
|
||||
})();
|
||||
"""
|
||||
|
@ -111,6 +118,23 @@ public class WebMessagePort : NSObject {
|
|||
}
|
||||
}
|
||||
|
||||
public static func fromMap(map: [String: Any?]) -> WebMessagePort {
|
||||
let index = map["index"] as! Int64
|
||||
return WebMessagePort(
|
||||
name: "port\(String(index + 1))",
|
||||
index: index,
|
||||
webMessageChannelId: map["webMessageChannelId"] as! String,
|
||||
webMessageChannel: nil)
|
||||
}
|
||||
|
||||
public func toMap () -> [String: Any?] {
|
||||
return [
|
||||
"name": name,
|
||||
"index": index,
|
||||
"webMessageChannelId": webMessageChannelId
|
||||
]
|
||||
}
|
||||
|
||||
public func dispose() {
|
||||
isClosed = true
|
||||
webMessageChannel = nil
|
||||
|
|
|
@ -9,3 +9,8 @@ export 'webview_asset_loader.dart'
|
|||
ResourcesPathHandler,
|
||||
InternalStoragePathHandler;
|
||||
export 'tracing_controller.dart' show TracingController, TracingSettings;
|
||||
export 'process_global_config.dart'
|
||||
show
|
||||
ProcessGlobalConfig,
|
||||
ProcessGlobalConfigSettings,
|
||||
ProcessGlobalConfigDirectoryBasePaths;
|
||||
|
|
|
@ -0,0 +1,150 @@
|
|||
import 'dart:async';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_inappwebview_internal_annotations/flutter_inappwebview_internal_annotations.dart';
|
||||
import 'webview_feature.dart';
|
||||
import '../in_app_webview/webview.dart';
|
||||
import '../in_app_webview/in_app_webview_controller.dart';
|
||||
import '../cookie_manager.dart';
|
||||
|
||||
part 'process_global_config.g.dart';
|
||||
|
||||
///Process Global Configuration for [WebView].
|
||||
///WebView has some process-global configuration parameters
|
||||
///that cannot be changed once WebView has been loaded.
|
||||
///This class allows apps to set these parameters.
|
||||
///
|
||||
///If it is used, the configuration should be set and apply should
|
||||
///be called prior to loading WebView into the calling process.
|
||||
///Most of the methods in `android.webkit` and `androidx.webkit` packages load WebView,
|
||||
///so the configuration should be applied before calling any of these methods.
|
||||
///
|
||||
///The following code configures the data directory suffix that WebView uses and
|
||||
///then applies the configuration. WebView uses this configuration when it is loaded.
|
||||
///
|
||||
///[apply] can only be called once.
|
||||
///
|
||||
///Only a single thread should access this class at a given time.
|
||||
///
|
||||
///The configuration should be set up as early as possible during application startup,
|
||||
///to ensure that it happens before any other thread can call a method that loads [WebView].
|
||||
///
|
||||
///**Supported Platforms/Implementations**:
|
||||
///- Android native WebView ([Official API - ProcessGlobalConfig](https://developer.android.com/reference/androidx/webkit/ProcessGlobalConfig))
|
||||
class ProcessGlobalConfig {
|
||||
static ProcessGlobalConfig? _instance;
|
||||
static const MethodChannel _channel = const MethodChannel(
|
||||
'com.pichillilorenzo/flutter_inappwebview_processglobalconfig');
|
||||
|
||||
ProcessGlobalConfig._();
|
||||
|
||||
///Gets the [ProcessGlobalConfig] shared instance.
|
||||
static ProcessGlobalConfig instance() {
|
||||
return (_instance != null) ? _instance! : _init();
|
||||
}
|
||||
|
||||
static ProcessGlobalConfig _init() {
|
||||
_channel.setMethodCallHandler((call) async {
|
||||
try {
|
||||
return await _handleMethod(call);
|
||||
} on Error catch (e) {
|
||||
print(e);
|
||||
print(e.stackTrace);
|
||||
}
|
||||
});
|
||||
_instance = ProcessGlobalConfig._();
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
static Future<dynamic> _handleMethod(MethodCall call) async {
|
||||
// ProcessGlobalConfig controller = ProcessGlobalConfig.instance();
|
||||
switch (call.method) {
|
||||
default:
|
||||
throw UnimplementedError("Unimplemented ${call.method} method");
|
||||
}
|
||||
// return null;
|
||||
}
|
||||
|
||||
///Applies the configuration to be used by [WebView] on loading.
|
||||
///This method can only be called once.
|
||||
///
|
||||
///Calling this method will not cause [WebView] to be loaded and will not block the calling thread.
|
||||
///
|
||||
///**Supported Platforms/Implementations**:
|
||||
///- Android native WebView ([Official API - ProcessGlobalConfig.apply](https://developer.android.com/reference/androidx/webkit/ProcessGlobalConfig#apply(androidx.webkit.ProcessGlobalConfig)))
|
||||
Future<void> apply({required ProcessGlobalConfigSettings settings}) async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
args.putIfAbsent("settings", () => settings.toMap());
|
||||
return await _channel.invokeMethod('apply', args);
|
||||
}
|
||||
}
|
||||
|
||||
///Class that represents the settings used to configure the [ProcessGlobalConfig].
|
||||
///
|
||||
///**Supported Platforms/Implementations**:
|
||||
///- Android native WebView
|
||||
@ExchangeableObject(copyMethod: true)
|
||||
class ProcessGlobalConfigSettings_ {
|
||||
///The directory name suffix to be used for the current process.
|
||||
///Must not contain a path separator and should not be empty.
|
||||
///
|
||||
///Define the directory used to store WebView data for the current process.
|
||||
///The provided suffix will be used when constructing data and cache directory paths.
|
||||
///If this API is not called, no suffix will be used.
|
||||
///Each directory can be used by only one process in the application.
|
||||
///If more than one process in an app wishes to use WebView,
|
||||
///only one process can use the default directory,
|
||||
///and other processes must call this API to define a unique suffix.
|
||||
///
|
||||
///This means that different processes in the same application cannot directly share [WebView]-related data,
|
||||
///since the data directories must be distinct.
|
||||
///Applications that use this API may have to explicitly pass data between processes.
|
||||
///For example, login cookies may have to be copied from one process's cookie jar to the other using [CookieManager] if both processes' WebViews are intended to be logged in.
|
||||
///
|
||||
///Most applications should simply ensure that all components of the app that rely
|
||||
///on WebView are in the same process, to avoid needing multiple data directories.
|
||||
///The [InAppWebViewController.disableWebView] method can be used to ensure that the other processes do not use WebView by accident in this case.
|
||||
///
|
||||
///**NOTE**: available only if [WebViewFeature.STARTUP_FEATURE_SET_DATA_DIRECTORY_SUFFIX] feature is supported.
|
||||
String? dataDirectorySuffix;
|
||||
|
||||
///Set the base directories that [WebView] will use for the current process.
|
||||
///If this method is not used, [WebView] uses the default base paths defined by the Android framework.
|
||||
///
|
||||
///WebView will create and use a subdirectory under each of the base paths supplied to this method.
|
||||
///
|
||||
///This method can be used in conjunction with setDataDirectorySuffix.
|
||||
///A different subdirectory is created for each suffix.
|
||||
///
|
||||
///The base paths must be absolute paths.
|
||||
///
|
||||
///The data directory must not be under the Android cache directory,
|
||||
///as Android may delete cache files when disk space is low and WebView may not function properly if this occurs.
|
||||
///Refer to [this link](https://developer.android.com/training/data-storage/app-specific#internal-remove-cache).
|
||||
///
|
||||
///If the specified directories already exist then they must be readable and writable by the current process.
|
||||
///If they do not already exist, [WebView] will attempt to create them during initialization, along with any missing parent directories.
|
||||
///In such a case, the directory in which [WebView] creates missing directories must be readable and writable by the current process.
|
||||
///
|
||||
///**NOTE**: available only if [WebViewFeature.STARTUP_FEATURE_SET_DIRECTORY_BASE_PATHS] feature is supported.
|
||||
ProcessGlobalConfigDirectoryBasePaths_? directoryBasePaths;
|
||||
|
||||
ProcessGlobalConfigSettings_(
|
||||
{this.dataDirectorySuffix, this.directoryBasePaths});
|
||||
}
|
||||
|
||||
///Class that represents the settings used to configure the [ProcessGlobalConfigSettings.directoryBasePaths].
|
||||
///
|
||||
///**Supported Platforms/Implementations**:
|
||||
///- Android native WebView ([Official API - ProxyConfig](https://developer.android.com/reference/androidx/webkit/ProxyConfig))
|
||||
@ExchangeableObject()
|
||||
class ProcessGlobalConfigDirectoryBasePaths_ {
|
||||
///The absolute base path for the WebView data directory.
|
||||
String dataDirectoryBasePath;
|
||||
|
||||
///The absolute base path for the WebView cache directory.
|
||||
String cacheDirectoryBasePath;
|
||||
|
||||
ProcessGlobalConfigDirectoryBasePaths_(
|
||||
{required this.dataDirectoryBasePath,
|
||||
required this.cacheDirectoryBasePath});
|
||||
}
|
|
@ -0,0 +1,142 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'process_global_config.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// ExchangeableObjectGenerator
|
||||
// **************************************************************************
|
||||
|
||||
///Class that represents the settings used to configure the [ProcessGlobalConfig].
|
||||
///
|
||||
///**Supported Platforms/Implementations**:
|
||||
///- Android native WebView
|
||||
class ProcessGlobalConfigSettings {
|
||||
///The directory name suffix to be used for the current process.
|
||||
///Must not contain a path separator and should not be empty.
|
||||
///
|
||||
///Define the directory used to store WebView data for the current process.
|
||||
///The provided suffix will be used when constructing data and cache directory paths.
|
||||
///If this API is not called, no suffix will be used.
|
||||
///Each directory can be used by only one process in the application.
|
||||
///If more than one process in an app wishes to use WebView,
|
||||
///only one process can use the default directory,
|
||||
///and other processes must call this API to define a unique suffix.
|
||||
///
|
||||
///This means that different processes in the same application cannot directly share [WebView]-related data,
|
||||
///since the data directories must be distinct.
|
||||
///Applications that use this API may have to explicitly pass data between processes.
|
||||
///For example, login cookies may have to be copied from one process's cookie jar to the other using [CookieManager] if both processes' WebViews are intended to be logged in.
|
||||
///
|
||||
///Most applications should simply ensure that all components of the app that rely
|
||||
///on WebView are in the same process, to avoid needing multiple data directories.
|
||||
///The [InAppWebViewController.disableWebView] method can be used to ensure that the other processes do not use WebView by accident in this case.
|
||||
///
|
||||
///**NOTE**: available only if [WebViewFeature.STARTUP_FEATURE_SET_DATA_DIRECTORY_SUFFIX] feature is supported.
|
||||
String? dataDirectorySuffix;
|
||||
|
||||
///Set the base directories that [WebView] will use for the current process.
|
||||
///If this method is not used, [WebView] uses the default base paths defined by the Android framework.
|
||||
///
|
||||
///WebView will create and use a subdirectory under each of the base paths supplied to this method.
|
||||
///
|
||||
///This method can be used in conjunction with setDataDirectorySuffix.
|
||||
///A different subdirectory is created for each suffix.
|
||||
///
|
||||
///The base paths must be absolute paths.
|
||||
///
|
||||
///The data directory must not be under the Android cache directory,
|
||||
///as Android may delete cache files when disk space is low and WebView may not function properly if this occurs.
|
||||
///Refer to [this link](https://developer.android.com/training/data-storage/app-specific#internal-remove-cache).
|
||||
///
|
||||
///If the specified directories already exist then they must be readable and writable by the current process.
|
||||
///If they do not already exist, [WebView] will attempt to create them during initialization, along with any missing parent directories.
|
||||
///In such a case, the directory in which [WebView] creates missing directories must be readable and writable by the current process.
|
||||
///
|
||||
///**NOTE**: available only if [WebViewFeature.STARTUP_FEATURE_SET_DIRECTORY_BASE_PATHS] feature is supported.
|
||||
ProcessGlobalConfigDirectoryBasePaths? directoryBasePaths;
|
||||
ProcessGlobalConfigSettings(
|
||||
{this.dataDirectorySuffix, this.directoryBasePaths});
|
||||
|
||||
///Gets a possible [ProcessGlobalConfigSettings] instance from a [Map] value.
|
||||
static ProcessGlobalConfigSettings? fromMap(Map<String, dynamic>? map) {
|
||||
if (map == null) {
|
||||
return null;
|
||||
}
|
||||
final instance = ProcessGlobalConfigSettings(
|
||||
dataDirectorySuffix: map['dataDirectorySuffix'],
|
||||
directoryBasePaths: ProcessGlobalConfigDirectoryBasePaths.fromMap(
|
||||
map['directoryBasePaths']?.cast<String, dynamic>()),
|
||||
);
|
||||
return instance;
|
||||
}
|
||||
|
||||
///Converts instance to a map.
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
"dataDirectorySuffix": dataDirectorySuffix,
|
||||
"directoryBasePaths": directoryBasePaths?.toMap(),
|
||||
};
|
||||
}
|
||||
|
||||
///Converts instance to a map.
|
||||
Map<String, dynamic> toJson() {
|
||||
return toMap();
|
||||
}
|
||||
|
||||
///Returns a copy of ProcessGlobalConfigSettings.
|
||||
ProcessGlobalConfigSettings copy() {
|
||||
return ProcessGlobalConfigSettings.fromMap(toMap()) ??
|
||||
ProcessGlobalConfigSettings();
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ProcessGlobalConfigSettings{dataDirectorySuffix: $dataDirectorySuffix, directoryBasePaths: $directoryBasePaths}';
|
||||
}
|
||||
}
|
||||
|
||||
///Class that represents the settings used to configure the [ProcessGlobalConfigSettings.directoryBasePaths].
|
||||
///
|
||||
///**Supported Platforms/Implementations**:
|
||||
///- Android native WebView ([Official API - ProxyConfig](https://developer.android.com/reference/androidx/webkit/ProxyConfig))
|
||||
class ProcessGlobalConfigDirectoryBasePaths {
|
||||
///The absolute base path for the WebView cache directory.
|
||||
String cacheDirectoryBasePath;
|
||||
|
||||
///The absolute base path for the WebView data directory.
|
||||
String dataDirectoryBasePath;
|
||||
ProcessGlobalConfigDirectoryBasePaths(
|
||||
{required this.cacheDirectoryBasePath,
|
||||
required this.dataDirectoryBasePath});
|
||||
|
||||
///Gets a possible [ProcessGlobalConfigDirectoryBasePaths] instance from a [Map] value.
|
||||
static ProcessGlobalConfigDirectoryBasePaths? fromMap(
|
||||
Map<String, dynamic>? map) {
|
||||
if (map == null) {
|
||||
return null;
|
||||
}
|
||||
final instance = ProcessGlobalConfigDirectoryBasePaths(
|
||||
cacheDirectoryBasePath: map['cacheDirectoryBasePath'],
|
||||
dataDirectoryBasePath: map['dataDirectoryBasePath'],
|
||||
);
|
||||
return instance;
|
||||
}
|
||||
|
||||
///Converts instance to a map.
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
"cacheDirectoryBasePath": cacheDirectoryBasePath,
|
||||
"dataDirectoryBasePath": dataDirectoryBasePath,
|
||||
};
|
||||
}
|
||||
|
||||
///Converts instance to a map.
|
||||
Map<String, dynamic> toJson() {
|
||||
return toMap();
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ProcessGlobalConfigDirectoryBasePaths{cacheDirectoryBasePath: $cacheDirectoryBasePath, dataDirectoryBasePath: $dataDirectoryBasePath}';
|
||||
}
|
||||
}
|
|
@ -139,41 +139,4 @@ class ProxySettings_ {
|
|||
this.bypassSimpleHostnames,
|
||||
this.removeImplicitRules,
|
||||
this.reverseBypassEnabled = false});
|
||||
|
||||
// Map<String, dynamic> toMap() {
|
||||
// return {
|
||||
// "bypassRules": bypassRules,
|
||||
// "directs": directs,
|
||||
// "proxyRules": proxyRules.map((e) => e.toMap()).toList(),
|
||||
// "bypassSimpleHostnames": bypassSimpleHostnames,
|
||||
// "removeImplicitRules": removeImplicitRules,
|
||||
// "reverseBypassEnabled": reverseBypassEnabled
|
||||
// };
|
||||
// }
|
||||
//
|
||||
// static ProxySettings fromMap(Map<String, dynamic> map) {
|
||||
// var settings = ProxySettings();
|
||||
// settings.bypassRules = map["bypassRules"];
|
||||
// settings.directs = map["directs"];
|
||||
// settings.proxyRules = (map["proxyRules"].cast<Map<String, dynamic>>()
|
||||
// as List<Map<String, dynamic>>)
|
||||
// .map((e) => ProxyRule.fromMap(e)) as List<ProxyRule>;
|
||||
// settings.bypassSimpleHostnames = map["bypassSimpleHostnames"];
|
||||
// settings.removeImplicitRules = map["removeImplicitRules"];
|
||||
// settings.reverseBypassEnabled = map["reverseBypassEnabled"];
|
||||
// return settings;
|
||||
// }
|
||||
//
|
||||
// Map<String, dynamic> toJson() {
|
||||
// return this.toMap();
|
||||
// }
|
||||
//
|
||||
// @override
|
||||
// String toString() {
|
||||
// return 'ProxySettings{bypassRules: $bypassRules, directs: $directs, proxyRules: $proxyRules, bypassSimpleHostnames: $bypassSimpleHostnames, removeImplicitRules: $removeImplicitRules, reverseBypassEnabled: $reverseBypassEnabled}';
|
||||
// }
|
||||
//
|
||||
// ProxySettings copy() {
|
||||
// return ProxySettings.fromMap(this.toMap());
|
||||
// }
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import 'dart:async';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_inappwebview/src/cookie_manager.dart';
|
||||
import 'package:flutter_inappwebview_internal_annotations/flutter_inappwebview_internal_annotations.dart';
|
||||
import 'process_global_config.dart';
|
||||
import '../cookie_manager.dart';
|
||||
import '../in_app_webview/in_app_webview_controller.dart';
|
||||
import '../in_app_webview/in_app_webview_settings.dart';
|
||||
import 'proxy_controller.dart';
|
||||
|
@ -29,18 +30,18 @@ class WebViewFeature_ {
|
|||
@ExchangeableObjectMethod(ignore: true)
|
||||
String toNativeValue() => _value;
|
||||
|
||||
///This feature covers [InAppWebViewController.createWebMessageChannel].
|
||||
///Feature for [isFeatureSupported]. This feature covers [InAppWebViewController.createWebMessageChannel].
|
||||
static const CREATE_WEB_MESSAGE_CHANNEL =
|
||||
const WebViewFeature_._internal("CREATE_WEB_MESSAGE_CHANNEL");
|
||||
|
||||
///This feature covers [InAppWebViewSettings.disabledActionModeMenuItems].
|
||||
///Feature for [isFeatureSupported]. This feature covers [InAppWebViewSettings.disabledActionModeMenuItems].
|
||||
static const DISABLED_ACTION_MODE_MENU_ITEMS =
|
||||
const WebViewFeature_._internal("DISABLED_ACTION_MODE_MENU_ITEMS");
|
||||
|
||||
///This feature covers [InAppWebViewSettings.forceDark].
|
||||
///Feature for [isFeatureSupported]. This feature covers [InAppWebViewSettings.forceDark].
|
||||
static const FORCE_DARK = const WebViewFeature_._internal("FORCE_DARK");
|
||||
|
||||
///This feature covers [InAppWebViewSettings.forceDarkStrategy].
|
||||
///Feature for [isFeatureSupported]. This feature covers [InAppWebViewSettings.forceDarkStrategy].
|
||||
static const FORCE_DARK_STRATEGY =
|
||||
const WebViewFeature_._internal("FORCE_DARK_STRATEGY");
|
||||
|
||||
|
@ -59,19 +60,19 @@ class WebViewFeature_ {
|
|||
///
|
||||
static const MULTI_PROCESS = const WebViewFeature_._internal("MULTI_PROCESS");
|
||||
|
||||
///This feature covers [InAppWebViewSettings.offscreenPreRaster].
|
||||
///Feature for [isFeatureSupported]. This feature covers [InAppWebViewSettings.offscreenPreRaster].
|
||||
static const OFF_SCREEN_PRERASTER =
|
||||
const WebViewFeature_._internal("OFF_SCREEN_PRERASTER");
|
||||
|
||||
///This feature covers [InAppWebViewController.postWebMessage].
|
||||
///Feature for [isFeatureSupported]. This feature covers [InAppWebViewController.postWebMessage].
|
||||
static const POST_WEB_MESSAGE =
|
||||
const WebViewFeature_._internal("POST_WEB_MESSAGE");
|
||||
|
||||
///This feature covers [ProxyController.setProxyOverride] and [ProxyController.clearProxyOverride].
|
||||
///Feature for [isFeatureSupported]. This feature covers [ProxyController.setProxyOverride] and [ProxyController.clearProxyOverride].
|
||||
static const PROXY_OVERRIDE =
|
||||
const WebViewFeature_._internal("PROXY_OVERRIDE");
|
||||
|
||||
///This feature covers [ProxySettings.reverseBypassEnabled].
|
||||
///Feature for [isFeatureSupported]. This feature covers [ProxySettings.reverseBypassEnabled].
|
||||
static const PROXY_OVERRIDE_REVERSE_BYPASS =
|
||||
const WebViewFeature_._internal("PROXY_OVERRIDE_REVERSE_BYPASS");
|
||||
|
||||
|
@ -83,11 +84,11 @@ class WebViewFeature_ {
|
|||
static const RECEIVE_WEB_RESOURCE_ERROR =
|
||||
const WebViewFeature_._internal("RECEIVE_WEB_RESOURCE_ERROR");
|
||||
|
||||
///This feature covers [InAppWebViewController.setSafeBrowsingAllowlist].
|
||||
///Feature for [isFeatureSupported]. This feature covers [InAppWebViewController.setSafeBrowsingAllowlist].
|
||||
static const SAFE_BROWSING_ALLOWLIST =
|
||||
const WebViewFeature_._internal("SAFE_BROWSING_ALLOWLIST");
|
||||
|
||||
///This feature covers [InAppWebViewSettings.safeBrowsingEnabled].
|
||||
///Feature for [isFeatureSupported]. This feature covers [InAppWebViewSettings.safeBrowsingEnabled].
|
||||
static const SAFE_BROWSING_ENABLE =
|
||||
const WebViewFeature_._internal("SAFE_BROWSING_ENABLE");
|
||||
|
||||
|
@ -95,7 +96,7 @@ class WebViewFeature_ {
|
|||
static const SAFE_BROWSING_HIT =
|
||||
const WebViewFeature_._internal("SAFE_BROWSING_HIT");
|
||||
|
||||
///This feature covers [InAppWebViewController.getSafeBrowsingPrivacyPolicyUrl].
|
||||
///Feature for [isFeatureSupported]. This feature covers [InAppWebViewController.getSafeBrowsingPrivacyPolicyUrl].
|
||||
static const SAFE_BROWSING_PRIVACY_POLICY_URL =
|
||||
const WebViewFeature_._internal("SAFE_BROWSING_PRIVACY_POLICY_URL");
|
||||
|
||||
|
@ -117,27 +118,27 @@ class WebViewFeature_ {
|
|||
static const SAFE_BROWSING_WHITELIST =
|
||||
const WebViewFeature_._internal("SAFE_BROWSING_WHITELIST");
|
||||
|
||||
///This feature covers [ServiceWorkerController].
|
||||
///Feature for [isFeatureSupported]. This feature covers [ServiceWorkerController].
|
||||
static const SERVICE_WORKER_BASIC_USAGE =
|
||||
const WebViewFeature_._internal("SERVICE_WORKER_BASIC_USAGE");
|
||||
|
||||
///This feature covers [ServiceWorkerController.setBlockNetworkLoads] and [ServiceWorkerController.getBlockNetworkLoads].
|
||||
///Feature for [isFeatureSupported]. This feature covers [ServiceWorkerController.setBlockNetworkLoads] and [ServiceWorkerController.getBlockNetworkLoads].
|
||||
static const SERVICE_WORKER_BLOCK_NETWORK_LOADS =
|
||||
const WebViewFeature_._internal("SERVICE_WORKER_BLOCK_NETWORK_LOADS");
|
||||
|
||||
///This feature covers [ServiceWorkerController.setCacheMode] and [ServiceWorkerController.getCacheMode].
|
||||
///Feature for [isFeatureSupported]. This feature covers [ServiceWorkerController.setCacheMode] and [ServiceWorkerController.getCacheMode].
|
||||
static const SERVICE_WORKER_CACHE_MODE =
|
||||
const WebViewFeature_._internal("SERVICE_WORKER_CACHE_MODE");
|
||||
|
||||
///This feature covers [ServiceWorkerController.setAllowContentAccess] and [ServiceWorkerController.getAllowContentAccess].
|
||||
///Feature for [isFeatureSupported]. This feature covers [ServiceWorkerController.setAllowContentAccess] and [ServiceWorkerController.getAllowContentAccess].
|
||||
static const SERVICE_WORKER_CONTENT_ACCESS =
|
||||
const WebViewFeature_._internal("SERVICE_WORKER_CONTENT_ACCESS");
|
||||
|
||||
///This feature covers [ServiceWorkerController.setAllowFileAccess] and [ServiceWorkerController.getAllowFileAccess].
|
||||
///Feature for [isFeatureSupported]. This feature covers [ServiceWorkerController.setAllowFileAccess] and [ServiceWorkerController.getAllowFileAccess].
|
||||
static const SERVICE_WORKER_FILE_ACCESS =
|
||||
const WebViewFeature_._internal("SERVICE_WORKER_FILE_ACCESS");
|
||||
|
||||
///This feature covers [ServiceWorkerClient.shouldInterceptRequest].
|
||||
///Feature for [isFeatureSupported]. This feature covers [ServiceWorkerClient.shouldInterceptRequest].
|
||||
static const SERVICE_WORKER_SHOULD_INTERCEPT_REQUEST =
|
||||
const WebViewFeature_._internal(
|
||||
"SERVICE_WORKER_SHOULD_INTERCEPT_REQUEST");
|
||||
|
@ -146,7 +147,7 @@ class WebViewFeature_ {
|
|||
static const SHOULD_OVERRIDE_WITH_REDIRECTS =
|
||||
const WebViewFeature_._internal("SHOULD_OVERRIDE_WITH_REDIRECTS");
|
||||
|
||||
///This feature covers [InAppWebViewController.startSafeBrowsing].
|
||||
///Feature for [isFeatureSupported]. This feature covers [InAppWebViewController.startSafeBrowsing].
|
||||
static const START_SAFE_BROWSING =
|
||||
const WebViewFeature_._internal("START_SAFE_BROWSING");
|
||||
|
||||
|
@ -162,7 +163,7 @@ class WebViewFeature_ {
|
|||
static const WEB_MESSAGE_CALLBACK_ON_MESSAGE =
|
||||
const WebViewFeature_._internal("WEB_MESSAGE_CALLBACK_ON_MESSAGE");
|
||||
|
||||
///This feature covers [WebMessageListener].
|
||||
///Feature for [isFeatureSupported]. This feature covers [WebMessageListener].
|
||||
static const WEB_MESSAGE_LISTENER =
|
||||
const WebViewFeature_._internal("WEB_MESSAGE_LISTENER");
|
||||
|
||||
|
@ -198,45 +199,90 @@ class WebViewFeature_ {
|
|||
static const WEB_VIEW_RENDERER_TERMINATE =
|
||||
const WebViewFeature_._internal("WEB_VIEW_RENDERER_TERMINATE");
|
||||
|
||||
///This feature covers [UserScriptInjectionTime.AT_DOCUMENT_START].
|
||||
///Feature for [isFeatureSupported]. This feature covers [UserScriptInjectionTime.AT_DOCUMENT_START].
|
||||
static const DOCUMENT_START_SCRIPT =
|
||||
const WebViewFeature_._internal("DOCUMENT_START_SCRIPT");
|
||||
|
||||
///This feature covers [InAppWebViewSettings.willSuppressErrorPage].
|
||||
///Feature for [isFeatureSupported]. This feature covers [InAppWebViewSettings.willSuppressErrorPage].
|
||||
static const SUPPRESS_ERROR_PAGE =
|
||||
const WebViewFeature_._internal("SUPPRESS_ERROR_PAGE");
|
||||
|
||||
///This feature covers [InAppWebViewSettings.algorithmicDarkeningAllowed].
|
||||
///Feature for [isFeatureSupported]. This feature covers [InAppWebViewSettings.algorithmicDarkeningAllowed].
|
||||
static const ALGORITHMIC_DARKENING =
|
||||
const WebViewFeature_._internal("ALGORITHMIC_DARKENING");
|
||||
|
||||
///This feature covers [InAppWebViewSettings.enterpriseAuthenticationAppLinkPolicyEnabled].
|
||||
///Feature for [isFeatureSupported]. This feature covers [InAppWebViewSettings.enterpriseAuthenticationAppLinkPolicyEnabled].
|
||||
static const ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY =
|
||||
const WebViewFeature_._internal(
|
||||
"ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY");
|
||||
|
||||
///This feature covers [InAppWebViewController.getVariationsHeader].
|
||||
///Feature for [isFeatureSupported]. This feature covers [InAppWebViewController.getVariationsHeader].
|
||||
static const GET_VARIATIONS_HEADER =
|
||||
const WebViewFeature_._internal("GET_VARIATIONS_HEADER");
|
||||
|
||||
///This feature covers cookie attributes of [CookieManager.getCookie] and [CookieManager.getCookies] methods.
|
||||
///Feature for [isFeatureSupported]. This feature covers cookie attributes of [CookieManager.getCookie] and [CookieManager.getCookies] methods.
|
||||
static const GET_COOKIE_INFO =
|
||||
const WebViewFeature_._internal("GET_COOKIE_INFO");
|
||||
|
||||
///This feature covers cookie attributes of [CookieManager.getCookie] and [CookieManager.getCookies] methods.
|
||||
///Feature for [isFeatureSupported]. This feature covers cookie attributes of [CookieManager.getCookie] and [CookieManager.getCookies] methods.
|
||||
static const REQUESTED_WITH_HEADER_ALLOW_LIST =
|
||||
const WebViewFeature_._internal("REQUESTED_WITH_HEADER_ALLOW_LIST");
|
||||
|
||||
///Feature for [isFeatureSupported]. This feature covers [WebMessagePort.postMessage] with `ArrayBuffer` type,
|
||||
///[InAppWebViewController.postWebMessage] with `ArrayBuffer` type, and [JavaScriptReplyProxy.postMessage] with `ArrayBuffer` type.
|
||||
static const WEB_MESSAGE_ARRAY_BUFFER =
|
||||
const WebViewFeature_._internal("WEB_MESSAGE_ARRAY_BUFFER");
|
||||
|
||||
///Feature for [isStartupFeatureSupported]. This feature covers [ProcessGlobalConfigSettings.dataDirectorySuffix].
|
||||
static const STARTUP_FEATURE_SET_DATA_DIRECTORY_SUFFIX =
|
||||
const WebViewFeature_._internal(
|
||||
"STARTUP_FEATURE_SET_DATA_DIRECTORY_SUFFIX");
|
||||
|
||||
///Feature for [isStartupFeatureSupported]. This feature covers [ProcessGlobalConfigSettings.directoryBasePaths].
|
||||
static const STARTUP_FEATURE_SET_DIRECTORY_BASE_PATHS =
|
||||
const WebViewFeature_._internal(
|
||||
"STARTUP_FEATURE_SET_DIRECTORY_BASE_PATHS");
|
||||
|
||||
///Return whether a feature is supported at run-time. On devices running Android version `Build.VERSION_CODES.LOLLIPOP` and higher,
|
||||
///this will check whether a feature is supported, depending on the combination of the desired feature, the Android version of device,
|
||||
///and the WebView APK on the device. If running on a device with a lower API level, this will always return `false`.
|
||||
///
|
||||
///**Note**: This method is different from [isStartupFeatureSupported] and this
|
||||
///method only accepts certain features.
|
||||
///Please verify that the correct feature checking method is used for a particular feature.
|
||||
///
|
||||
///**Note**: If this method returns `false`, it is not safe to invoke the methods
|
||||
///requiring the desired feature.
|
||||
///Furthermore, if this method returns `false` for a particular feature, any callback guarded by that feature will not be invoked.
|
||||
///
|
||||
///**Official Android API**: https://developer.android.com/reference/androidx/webkit/WebViewFeature#isFeatureSupported(java.lang.String)
|
||||
static Future<bool> isFeatureSupported(WebViewFeature_ feature) async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
args.putIfAbsent("feature", () => feature.toNativeValue());
|
||||
return await _channel.invokeMethod('isFeatureSupported', args);
|
||||
}
|
||||
|
||||
///Return whether a startup feature is supported at run-time.
|
||||
///On devices running Android version `Build.VERSION_CODES.LOLLIPOP` and higher,
|
||||
///this will check whether a startup feature is supported,
|
||||
///depending on the combination of the desired feature,
|
||||
///the Android version of device, and the WebView APK on the device.
|
||||
///If running on a device with a lower API level, this will always return `false`.
|
||||
///
|
||||
///**Note**: This method is different from [isFeatureSupported] and this method only accepts startup features.
|
||||
///Please verify that the correct feature checking method is used for a particular feature.
|
||||
///
|
||||
///**Note**: If this method returns `false`, it is not safe to invoke the methods requiring the desired feature.
|
||||
///Furthermore, if this method returns `false` for a particular feature,
|
||||
///any callback guarded by that feature will not be invoked.
|
||||
///
|
||||
///**Official Android API**: https://developer.android.com/reference/androidx/webkit/WebViewFeature#isFeatureSupported(java.lang.String)
|
||||
static Future<bool> isStartupFeatureSupported(
|
||||
WebViewFeature_ startupFeature) async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
args.putIfAbsent("startupFeature", () => startupFeature.toNativeValue());
|
||||
return await _channel.invokeMethod('isStartupFeatureSupported', args);
|
||||
}
|
||||
}
|
||||
|
||||
///Class that represents an Android-specific utility class for checking which WebView Support Library features are supported on the device.
|
||||
|
@ -430,23 +476,23 @@ class AndroidWebViewFeature_ {
|
|||
static const WEB_VIEW_RENDERER_TERMINATE =
|
||||
const AndroidWebViewFeature_._internal("WEB_VIEW_RENDERER_TERMINATE");
|
||||
|
||||
///This feature covers [UserScriptInjectionTime.AT_DOCUMENT_START].
|
||||
///Feature for [isFeatureSupported]. This feature covers [UserScriptInjectionTime.AT_DOCUMENT_START].
|
||||
static const DOCUMENT_START_SCRIPT =
|
||||
const AndroidWebViewFeature_._internal("DOCUMENT_START_SCRIPT");
|
||||
|
||||
///This feature covers [InAppWebViewSettings.willSuppressErrorPage].
|
||||
///Feature for [isFeatureSupported]. This feature covers [InAppWebViewSettings.willSuppressErrorPage].
|
||||
static const SUPPRESS_ERROR_PAGE =
|
||||
const AndroidWebViewFeature_._internal("SUPPRESS_ERROR_PAGE");
|
||||
|
||||
///This feature covers [InAppWebViewSettings.algorithmicDarkeningAllowed].
|
||||
///Feature for [isFeatureSupported]. This feature covers [InAppWebViewSettings.algorithmicDarkeningAllowed].
|
||||
static const ALGORITHMIC_DARKENING =
|
||||
const AndroidWebViewFeature_._internal("ALGORITHMIC_DARKENING");
|
||||
|
||||
///This feature covers [InAppWebViewSettings.requestedWithHeaderMode].
|
||||
///Feature for [isFeatureSupported]. This feature covers [InAppWebViewSettings.requestedWithHeaderMode].
|
||||
static const REQUESTED_WITH_HEADER_CONTROL =
|
||||
const AndroidWebViewFeature_._internal("REQUESTED_WITH_HEADER_CONTROL");
|
||||
|
||||
///This feature covers [InAppWebViewSettings.enterpriseAuthenticationAppLinkPolicyEnabled].
|
||||
///Feature for [isFeatureSupported]. This feature covers [InAppWebViewSettings.enterpriseAuthenticationAppLinkPolicyEnabled].
|
||||
static const ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY =
|
||||
const AndroidWebViewFeature_._internal(
|
||||
"ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY");
|
||||
|
|
|
@ -19,40 +19,40 @@ class WebViewFeature {
|
|||
String value, Function nativeValue) =>
|
||||
WebViewFeature._internal(value, nativeValue());
|
||||
|
||||
///This feature covers [InAppWebViewSettings.algorithmicDarkeningAllowed].
|
||||
///Feature for [isFeatureSupported]. This feature covers [InAppWebViewSettings.algorithmicDarkeningAllowed].
|
||||
static const ALGORITHMIC_DARKENING = WebViewFeature._internal(
|
||||
'ALGORITHMIC_DARKENING', 'ALGORITHMIC_DARKENING');
|
||||
|
||||
///This feature covers [InAppWebViewController.createWebMessageChannel].
|
||||
///Feature for [isFeatureSupported]. This feature covers [InAppWebViewController.createWebMessageChannel].
|
||||
static const CREATE_WEB_MESSAGE_CHANNEL = WebViewFeature._internal(
|
||||
'CREATE_WEB_MESSAGE_CHANNEL', 'CREATE_WEB_MESSAGE_CHANNEL');
|
||||
|
||||
///This feature covers [InAppWebViewSettings.disabledActionModeMenuItems].
|
||||
///Feature for [isFeatureSupported]. This feature covers [InAppWebViewSettings.disabledActionModeMenuItems].
|
||||
static const DISABLED_ACTION_MODE_MENU_ITEMS = WebViewFeature._internal(
|
||||
'DISABLED_ACTION_MODE_MENU_ITEMS', 'DISABLED_ACTION_MODE_MENU_ITEMS');
|
||||
|
||||
///This feature covers [UserScriptInjectionTime.AT_DOCUMENT_START].
|
||||
///Feature for [isFeatureSupported]. This feature covers [UserScriptInjectionTime.AT_DOCUMENT_START].
|
||||
static const DOCUMENT_START_SCRIPT = WebViewFeature._internal(
|
||||
'DOCUMENT_START_SCRIPT', 'DOCUMENT_START_SCRIPT');
|
||||
|
||||
///This feature covers [InAppWebViewSettings.enterpriseAuthenticationAppLinkPolicyEnabled].
|
||||
///Feature for [isFeatureSupported]. This feature covers [InAppWebViewSettings.enterpriseAuthenticationAppLinkPolicyEnabled].
|
||||
static const ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY =
|
||||
WebViewFeature._internal('ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY',
|
||||
'ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY');
|
||||
|
||||
///This feature covers [InAppWebViewSettings.forceDark].
|
||||
///Feature for [isFeatureSupported]. This feature covers [InAppWebViewSettings.forceDark].
|
||||
static const FORCE_DARK =
|
||||
WebViewFeature._internal('FORCE_DARK', 'FORCE_DARK');
|
||||
|
||||
///This feature covers [InAppWebViewSettings.forceDarkStrategy].
|
||||
///Feature for [isFeatureSupported]. This feature covers [InAppWebViewSettings.forceDarkStrategy].
|
||||
static const FORCE_DARK_STRATEGY =
|
||||
WebViewFeature._internal('FORCE_DARK_STRATEGY', 'FORCE_DARK_STRATEGY');
|
||||
|
||||
///This feature covers cookie attributes of [CookieManager.getCookie] and [CookieManager.getCookies] methods.
|
||||
///Feature for [isFeatureSupported]. This feature covers cookie attributes of [CookieManager.getCookie] and [CookieManager.getCookies] methods.
|
||||
static const GET_COOKIE_INFO =
|
||||
WebViewFeature._internal('GET_COOKIE_INFO', 'GET_COOKIE_INFO');
|
||||
|
||||
///This feature covers [InAppWebViewController.getVariationsHeader].
|
||||
///Feature for [isFeatureSupported]. This feature covers [InAppWebViewController.getVariationsHeader].
|
||||
static const GET_VARIATIONS_HEADER = WebViewFeature._internal(
|
||||
'GET_VARIATIONS_HEADER', 'GET_VARIATIONS_HEADER');
|
||||
|
||||
|
@ -72,19 +72,19 @@ class WebViewFeature {
|
|||
static const MULTI_PROCESS =
|
||||
WebViewFeature._internal('MULTI_PROCESS', 'MULTI_PROCESS');
|
||||
|
||||
///This feature covers [InAppWebViewSettings.offscreenPreRaster].
|
||||
///Feature for [isFeatureSupported]. This feature covers [InAppWebViewSettings.offscreenPreRaster].
|
||||
static const OFF_SCREEN_PRERASTER =
|
||||
WebViewFeature._internal('OFF_SCREEN_PRERASTER', 'OFF_SCREEN_PRERASTER');
|
||||
|
||||
///This feature covers [InAppWebViewController.postWebMessage].
|
||||
///Feature for [isFeatureSupported]. This feature covers [InAppWebViewController.postWebMessage].
|
||||
static const POST_WEB_MESSAGE =
|
||||
WebViewFeature._internal('POST_WEB_MESSAGE', 'POST_WEB_MESSAGE');
|
||||
|
||||
///This feature covers [ProxyController.setProxyOverride] and [ProxyController.clearProxyOverride].
|
||||
///Feature for [isFeatureSupported]. This feature covers [ProxyController.setProxyOverride] and [ProxyController.clearProxyOverride].
|
||||
static const PROXY_OVERRIDE =
|
||||
WebViewFeature._internal('PROXY_OVERRIDE', 'PROXY_OVERRIDE');
|
||||
|
||||
///This feature covers [ProxySettings.reverseBypassEnabled].
|
||||
///Feature for [isFeatureSupported]. This feature covers [ProxySettings.reverseBypassEnabled].
|
||||
static const PROXY_OVERRIDE_REVERSE_BYPASS = WebViewFeature._internal(
|
||||
'PROXY_OVERRIDE_REVERSE_BYPASS', 'PROXY_OVERRIDE_REVERSE_BYPASS');
|
||||
|
||||
|
@ -96,15 +96,15 @@ class WebViewFeature {
|
|||
static const RECEIVE_WEB_RESOURCE_ERROR = WebViewFeature._internal(
|
||||
'RECEIVE_WEB_RESOURCE_ERROR', 'RECEIVE_WEB_RESOURCE_ERROR');
|
||||
|
||||
///This feature covers cookie attributes of [CookieManager.getCookie] and [CookieManager.getCookies] methods.
|
||||
///Feature for [isFeatureSupported]. This feature covers cookie attributes of [CookieManager.getCookie] and [CookieManager.getCookies] methods.
|
||||
static const REQUESTED_WITH_HEADER_ALLOW_LIST = WebViewFeature._internal(
|
||||
'REQUESTED_WITH_HEADER_ALLOW_LIST', 'REQUESTED_WITH_HEADER_ALLOW_LIST');
|
||||
|
||||
///This feature covers [InAppWebViewController.setSafeBrowsingAllowlist].
|
||||
///Feature for [isFeatureSupported]. This feature covers [InAppWebViewController.setSafeBrowsingAllowlist].
|
||||
static const SAFE_BROWSING_ALLOWLIST = WebViewFeature._internal(
|
||||
'SAFE_BROWSING_ALLOWLIST', 'SAFE_BROWSING_ALLOWLIST');
|
||||
|
||||
///This feature covers [InAppWebViewSettings.safeBrowsingEnabled].
|
||||
///Feature for [isFeatureSupported]. This feature covers [InAppWebViewSettings.safeBrowsingEnabled].
|
||||
static const SAFE_BROWSING_ENABLE =
|
||||
WebViewFeature._internal('SAFE_BROWSING_ENABLE', 'SAFE_BROWSING_ENABLE');
|
||||
|
||||
|
@ -112,7 +112,7 @@ class WebViewFeature {
|
|||
static const SAFE_BROWSING_HIT =
|
||||
WebViewFeature._internal('SAFE_BROWSING_HIT', 'SAFE_BROWSING_HIT');
|
||||
|
||||
///This feature covers [InAppWebViewController.getSafeBrowsingPrivacyPolicyUrl].
|
||||
///Feature for [isFeatureSupported]. This feature covers [InAppWebViewController.getSafeBrowsingPrivacyPolicyUrl].
|
||||
static const SAFE_BROWSING_PRIVACY_POLICY_URL = WebViewFeature._internal(
|
||||
'SAFE_BROWSING_PRIVACY_POLICY_URL', 'SAFE_BROWSING_PRIVACY_POLICY_URL');
|
||||
|
||||
|
@ -134,28 +134,28 @@ class WebViewFeature {
|
|||
static const SAFE_BROWSING_WHITELIST = WebViewFeature._internal(
|
||||
'SAFE_BROWSING_WHITELIST', 'SAFE_BROWSING_WHITELIST');
|
||||
|
||||
///This feature covers [ServiceWorkerController].
|
||||
///Feature for [isFeatureSupported]. This feature covers [ServiceWorkerController].
|
||||
static const SERVICE_WORKER_BASIC_USAGE = WebViewFeature._internal(
|
||||
'SERVICE_WORKER_BASIC_USAGE', 'SERVICE_WORKER_BASIC_USAGE');
|
||||
|
||||
///This feature covers [ServiceWorkerController.setBlockNetworkLoads] and [ServiceWorkerController.getBlockNetworkLoads].
|
||||
///Feature for [isFeatureSupported]. This feature covers [ServiceWorkerController.setBlockNetworkLoads] and [ServiceWorkerController.getBlockNetworkLoads].
|
||||
static const SERVICE_WORKER_BLOCK_NETWORK_LOADS = WebViewFeature._internal(
|
||||
'SERVICE_WORKER_BLOCK_NETWORK_LOADS',
|
||||
'SERVICE_WORKER_BLOCK_NETWORK_LOADS');
|
||||
|
||||
///This feature covers [ServiceWorkerController.setCacheMode] and [ServiceWorkerController.getCacheMode].
|
||||
///Feature for [isFeatureSupported]. This feature covers [ServiceWorkerController.setCacheMode] and [ServiceWorkerController.getCacheMode].
|
||||
static const SERVICE_WORKER_CACHE_MODE = WebViewFeature._internal(
|
||||
'SERVICE_WORKER_CACHE_MODE', 'SERVICE_WORKER_CACHE_MODE');
|
||||
|
||||
///This feature covers [ServiceWorkerController.setAllowContentAccess] and [ServiceWorkerController.getAllowContentAccess].
|
||||
///Feature for [isFeatureSupported]. This feature covers [ServiceWorkerController.setAllowContentAccess] and [ServiceWorkerController.getAllowContentAccess].
|
||||
static const SERVICE_WORKER_CONTENT_ACCESS = WebViewFeature._internal(
|
||||
'SERVICE_WORKER_CONTENT_ACCESS', 'SERVICE_WORKER_CONTENT_ACCESS');
|
||||
|
||||
///This feature covers [ServiceWorkerController.setAllowFileAccess] and [ServiceWorkerController.getAllowFileAccess].
|
||||
///Feature for [isFeatureSupported]. This feature covers [ServiceWorkerController.setAllowFileAccess] and [ServiceWorkerController.getAllowFileAccess].
|
||||
static const SERVICE_WORKER_FILE_ACCESS = WebViewFeature._internal(
|
||||
'SERVICE_WORKER_FILE_ACCESS', 'SERVICE_WORKER_FILE_ACCESS');
|
||||
|
||||
///This feature covers [ServiceWorkerClient.shouldInterceptRequest].
|
||||
///Feature for [isFeatureSupported]. This feature covers [ServiceWorkerClient.shouldInterceptRequest].
|
||||
static const SERVICE_WORKER_SHOULD_INTERCEPT_REQUEST =
|
||||
WebViewFeature._internal('SERVICE_WORKER_SHOULD_INTERCEPT_REQUEST',
|
||||
'SERVICE_WORKER_SHOULD_INTERCEPT_REQUEST');
|
||||
|
@ -164,11 +164,21 @@ class WebViewFeature {
|
|||
static const SHOULD_OVERRIDE_WITH_REDIRECTS = WebViewFeature._internal(
|
||||
'SHOULD_OVERRIDE_WITH_REDIRECTS', 'SHOULD_OVERRIDE_WITH_REDIRECTS');
|
||||
|
||||
///This feature covers [InAppWebViewController.startSafeBrowsing].
|
||||
///Feature for [isStartupFeatureSupported]. This feature covers [ProcessGlobalConfigSettings.dataDirectorySuffix].
|
||||
static const STARTUP_FEATURE_SET_DATA_DIRECTORY_SUFFIX =
|
||||
WebViewFeature._internal('STARTUP_FEATURE_SET_DATA_DIRECTORY_SUFFIX',
|
||||
'STARTUP_FEATURE_SET_DATA_DIRECTORY_SUFFIX');
|
||||
|
||||
///Feature for [isStartupFeatureSupported]. This feature covers [ProcessGlobalConfigSettings.directoryBasePaths].
|
||||
static const STARTUP_FEATURE_SET_DIRECTORY_BASE_PATHS =
|
||||
WebViewFeature._internal('STARTUP_FEATURE_SET_DIRECTORY_BASE_PATHS',
|
||||
'STARTUP_FEATURE_SET_DIRECTORY_BASE_PATHS');
|
||||
|
||||
///Feature for [isFeatureSupported]. This feature covers [InAppWebViewController.startSafeBrowsing].
|
||||
static const START_SAFE_BROWSING =
|
||||
WebViewFeature._internal('START_SAFE_BROWSING', 'START_SAFE_BROWSING');
|
||||
|
||||
///This feature covers [InAppWebViewSettings.willSuppressErrorPage].
|
||||
///Feature for [isFeatureSupported]. This feature covers [InAppWebViewSettings.willSuppressErrorPage].
|
||||
static const SUPPRESS_ERROR_PAGE =
|
||||
WebViewFeature._internal('SUPPRESS_ERROR_PAGE', 'SUPPRESS_ERROR_PAGE');
|
||||
|
||||
|
@ -180,11 +190,16 @@ class WebViewFeature {
|
|||
static const VISUAL_STATE_CALLBACK = WebViewFeature._internal(
|
||||
'VISUAL_STATE_CALLBACK', 'VISUAL_STATE_CALLBACK');
|
||||
|
||||
///Feature for [isFeatureSupported]. This feature covers [WebMessagePort.postMessage] with `ArrayBuffer` type,
|
||||
///[InAppWebViewController.postWebMessage] with `ArrayBuffer` type, and [JavaScriptReplyProxy.postMessage] with `ArrayBuffer` type.
|
||||
static const WEB_MESSAGE_ARRAY_BUFFER = WebViewFeature._internal(
|
||||
'WEB_MESSAGE_ARRAY_BUFFER', 'WEB_MESSAGE_ARRAY_BUFFER');
|
||||
|
||||
///
|
||||
static const WEB_MESSAGE_CALLBACK_ON_MESSAGE = WebViewFeature._internal(
|
||||
'WEB_MESSAGE_CALLBACK_ON_MESSAGE', 'WEB_MESSAGE_CALLBACK_ON_MESSAGE');
|
||||
|
||||
///This feature covers [WebMessageListener].
|
||||
///Feature for [isFeatureSupported]. This feature covers [WebMessageListener].
|
||||
static const WEB_MESSAGE_LISTENER =
|
||||
WebViewFeature._internal('WEB_MESSAGE_LISTENER', 'WEB_MESSAGE_LISTENER');
|
||||
|
||||
|
@ -262,10 +277,13 @@ class WebViewFeature {
|
|||
WebViewFeature.SERVICE_WORKER_FILE_ACCESS,
|
||||
WebViewFeature.SERVICE_WORKER_SHOULD_INTERCEPT_REQUEST,
|
||||
WebViewFeature.SHOULD_OVERRIDE_WITH_REDIRECTS,
|
||||
WebViewFeature.STARTUP_FEATURE_SET_DATA_DIRECTORY_SUFFIX,
|
||||
WebViewFeature.STARTUP_FEATURE_SET_DIRECTORY_BASE_PATHS,
|
||||
WebViewFeature.START_SAFE_BROWSING,
|
||||
WebViewFeature.SUPPRESS_ERROR_PAGE,
|
||||
WebViewFeature.TRACING_CONTROLLER_BASIC_USAGE,
|
||||
WebViewFeature.VISUAL_STATE_CALLBACK,
|
||||
WebViewFeature.WEB_MESSAGE_ARRAY_BUFFER,
|
||||
WebViewFeature.WEB_MESSAGE_CALLBACK_ON_MESSAGE,
|
||||
WebViewFeature.WEB_MESSAGE_LISTENER,
|
||||
WebViewFeature.WEB_MESSAGE_PORT_CLOSE,
|
||||
|
@ -308,6 +326,14 @@ class WebViewFeature {
|
|||
///this will check whether a feature is supported, depending on the combination of the desired feature, the Android version of device,
|
||||
///and the WebView APK on the device. If running on a device with a lower API level, this will always return `false`.
|
||||
///
|
||||
///**Note**: This method is different from [isStartupFeatureSupported] and this
|
||||
///method only accepts certain features.
|
||||
///Please verify that the correct feature checking method is used for a particular feature.
|
||||
///
|
||||
///**Note**: If this method returns `false`, it is not safe to invoke the methods
|
||||
///requiring the desired feature.
|
||||
///Furthermore, if this method returns `false` for a particular feature, any callback guarded by that feature will not be invoked.
|
||||
///
|
||||
///**Official Android API**: https://developer.android.com/reference/androidx/webkit/WebViewFeature#isFeatureSupported(java.lang.String)
|
||||
static Future<bool> isFeatureSupported(WebViewFeature feature) async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
|
@ -315,6 +341,28 @@ class WebViewFeature {
|
|||
return await _channel.invokeMethod('isFeatureSupported', args);
|
||||
}
|
||||
|
||||
///Return whether a startup feature is supported at run-time.
|
||||
///On devices running Android version `Build.VERSION_CODES.LOLLIPOP` and higher,
|
||||
///this will check whether a startup feature is supported,
|
||||
///depending on the combination of the desired feature,
|
||||
///the Android version of device, and the WebView APK on the device.
|
||||
///If running on a device with a lower API level, this will always return `false`.
|
||||
///
|
||||
///**Note**: This method is different from [isFeatureSupported] and this method only accepts startup features.
|
||||
///Please verify that the correct feature checking method is used for a particular feature.
|
||||
///
|
||||
///**Note**: If this method returns `false`, it is not safe to invoke the methods requiring the desired feature.
|
||||
///Furthermore, if this method returns `false` for a particular feature,
|
||||
///any callback guarded by that feature will not be invoked.
|
||||
///
|
||||
///**Official Android API**: https://developer.android.com/reference/androidx/webkit/WebViewFeature#isFeatureSupported(java.lang.String)
|
||||
static Future<bool> isStartupFeatureSupported(
|
||||
WebViewFeature startupFeature) async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
args.putIfAbsent("startupFeature", () => startupFeature.toNativeValue());
|
||||
return await _channel.invokeMethod('isStartupFeatureSupported', args);
|
||||
}
|
||||
|
||||
///Gets [String] value.
|
||||
String toValue() => _value;
|
||||
|
||||
|
@ -345,7 +393,7 @@ class AndroidWebViewFeature {
|
|||
String value, Function nativeValue) =>
|
||||
AndroidWebViewFeature._internal(value, nativeValue());
|
||||
|
||||
///This feature covers [InAppWebViewSettings.algorithmicDarkeningAllowed].
|
||||
///Feature for [isFeatureSupported]. This feature covers [InAppWebViewSettings.algorithmicDarkeningAllowed].
|
||||
static const ALGORITHMIC_DARKENING = AndroidWebViewFeature._internal(
|
||||
'ALGORITHMIC_DARKENING', 'ALGORITHMIC_DARKENING');
|
||||
|
||||
|
@ -358,11 +406,11 @@ class AndroidWebViewFeature {
|
|||
AndroidWebViewFeature._internal(
|
||||
'DISABLED_ACTION_MODE_MENU_ITEMS', 'DISABLED_ACTION_MODE_MENU_ITEMS');
|
||||
|
||||
///This feature covers [UserScriptInjectionTime.AT_DOCUMENT_START].
|
||||
///Feature for [isFeatureSupported]. This feature covers [UserScriptInjectionTime.AT_DOCUMENT_START].
|
||||
static const DOCUMENT_START_SCRIPT = AndroidWebViewFeature._internal(
|
||||
'DOCUMENT_START_SCRIPT', 'DOCUMENT_START_SCRIPT');
|
||||
|
||||
///This feature covers [InAppWebViewSettings.enterpriseAuthenticationAppLinkPolicyEnabled].
|
||||
///Feature for [isFeatureSupported]. This feature covers [InAppWebViewSettings.enterpriseAuthenticationAppLinkPolicyEnabled].
|
||||
static const ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY =
|
||||
AndroidWebViewFeature._internal(
|
||||
'ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY',
|
||||
|
@ -412,7 +460,7 @@ class AndroidWebViewFeature {
|
|||
static const RECEIVE_WEB_RESOURCE_ERROR = AndroidWebViewFeature._internal(
|
||||
'RECEIVE_WEB_RESOURCE_ERROR', 'RECEIVE_WEB_RESOURCE_ERROR');
|
||||
|
||||
///This feature covers [InAppWebViewSettings.requestedWithHeaderMode].
|
||||
///Feature for [isFeatureSupported]. This feature covers [InAppWebViewSettings.requestedWithHeaderMode].
|
||||
static const REQUESTED_WITH_HEADER_CONTROL = AndroidWebViewFeature._internal(
|
||||
'REQUESTED_WITH_HEADER_CONTROL', 'REQUESTED_WITH_HEADER_CONTROL');
|
||||
|
||||
|
@ -486,7 +534,7 @@ class AndroidWebViewFeature {
|
|||
static const START_SAFE_BROWSING = AndroidWebViewFeature._internal(
|
||||
'START_SAFE_BROWSING', 'START_SAFE_BROWSING');
|
||||
|
||||
///This feature covers [InAppWebViewSettings.willSuppressErrorPage].
|
||||
///Feature for [isFeatureSupported]. This feature covers [InAppWebViewSettings.willSuppressErrorPage].
|
||||
static const SUPPRESS_ERROR_PAGE = AndroidWebViewFeature._internal(
|
||||
'SUPPRESS_ERROR_PAGE', 'SUPPRESS_ERROR_PAGE');
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@ import 'in_app_webview_keep_alive.dart';
|
|||
|
||||
import '../print_job/main.dart';
|
||||
import '../find_interaction/main.dart';
|
||||
import '../android/process_global_config.dart';
|
||||
|
||||
///List of forbidden names for JavaScript handlers.
|
||||
// ignore: non_constant_identifier_names
|
||||
|
@ -3870,6 +3871,24 @@ class InAppWebViewController extends ChannelController {
|
|||
false;
|
||||
}
|
||||
|
||||
///Indicate that the current process does not intend to use WebView,
|
||||
///and that an exception should be thrown if a WebView is created or any other
|
||||
///methods in the `android.webkit` package are used.
|
||||
///
|
||||
///Applications with multiple processes may wish to call this in processes that
|
||||
///are not intended to use WebView to avoid accidentally incurring the memory usage
|
||||
///of initializing WebView in long-lived processes that have no need for it,
|
||||
///and to prevent potential data directory conflicts (see [ProcessGlobalConfigSettings.dataDirectorySuffix]).
|
||||
///
|
||||
///**NOTE for Android**: available only on Android 28+.
|
||||
///
|
||||
///**Supported Platforms/Implementations**:
|
||||
///- Android native WebView ([Official API - WebView.disableWebView](https://developer.android.com/reference/android/webkit/WebView.html#disableWebView()))
|
||||
static Future<void> disableWebView() async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
await _staticChannel.invokeMethod('disableWebView', args);
|
||||
}
|
||||
|
||||
///Returns a Boolean value that indicates whether WebKit natively supports resources with the specified URL scheme.
|
||||
///
|
||||
///[urlScheme] represents the URL scheme associated with the resource.
|
||||
|
|
|
@ -2,5 +2,5 @@ import '../web_message/main.dart';
|
|||
import '../web_uri.dart';
|
||||
|
||||
///The listener for handling [WebMessageListener] events sent by a `postMessage()` on the injected JavaScript object.
|
||||
typedef void OnPostMessageCallback(String? message, WebUri? sourceOrigin,
|
||||
typedef void OnPostMessageCallback(WebMessage? message, WebUri? sourceOrigin,
|
||||
bool isMainFrame, JavaScriptReplyProxy replyProxy);
|
||||
|
|
|
@ -2,4 +2,4 @@ import '../web_message/main.dart';
|
|||
|
||||
///The listener for handling [WebMessagePort] events.
|
||||
///The message callback methods are called on the main thread.
|
||||
typedef void WebMessageCallback(String? message);
|
||||
typedef void WebMessageCallback(WebMessage? message);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
export 'web_message.dart' show WebMessage;
|
||||
export 'web_message.dart' show WebMessage, WebMessageType;
|
||||
export 'web_message_port.dart' show WebMessagePort;
|
||||
export 'web_message_channel.dart' show WebMessageChannel;
|
||||
export 'web_message_listener.dart';
|
||||
|
|
|
@ -1,18 +1,47 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_inappwebview_internal_annotations/flutter_inappwebview_internal_annotations.dart';
|
||||
|
||||
import '../android/webview_feature.dart';
|
||||
import 'web_message_port.dart';
|
||||
|
||||
part 'web_message.g.dart';
|
||||
|
||||
///The Dart representation of the HTML5 PostMessage event.
|
||||
///See https://html.spec.whatwg.org/multipage/comms.html#the-messageevent-interfaces for definition of a MessageEvent in HTML5.
|
||||
@ExchangeableObject()
|
||||
@ExchangeableObject(fromMapForceAllInline: true)
|
||||
class WebMessage_ {
|
||||
///The data of the message.
|
||||
String? data;
|
||||
dynamic data;
|
||||
|
||||
///The payload type of the message.
|
||||
WebMessageType_ type;
|
||||
|
||||
///The ports that are sent with the message.
|
||||
List<WebMessagePort>? ports;
|
||||
|
||||
WebMessage_({this.data, this.ports});
|
||||
@ExchangeableObjectConstructor()
|
||||
WebMessage_({this.data, this.type = WebMessageType_.STRING, this.ports}) {
|
||||
assert(((this.data == null || this.data is String) &&
|
||||
this.type == WebMessageType_.STRING) ||
|
||||
(this.data != null &&
|
||||
this.data is Uint8List &&
|
||||
this.type == WebMessageType_.ARRAY_BUFFER));
|
||||
}
|
||||
}
|
||||
|
||||
///The type corresponding to the [WebMessage].
|
||||
@ExchangeableEnum()
|
||||
class WebMessageType_ {
|
||||
// ignore: unused_field
|
||||
final int _value;
|
||||
|
||||
const WebMessageType_._internal(this._value);
|
||||
|
||||
///Indicates the payload of WebMessageCompat is String.
|
||||
static const STRING = const WebMessageType_._internal(0);
|
||||
|
||||
///Indicates the payload of WebMessageCompat is JavaScript ArrayBuffer.
|
||||
///
|
||||
///**NOTE**: available only if [WebViewFeature.WEB_MESSAGE_ARRAY_BUFFER] feature is supported.
|
||||
static const ARRAY_BUFFER = const WebMessageType_._internal(1);
|
||||
}
|
||||
|
|
|
@ -2,6 +2,84 @@
|
|||
|
||||
part of 'web_message.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// ExchangeableEnumGenerator
|
||||
// **************************************************************************
|
||||
|
||||
///The type corresponding to the [WebMessage].
|
||||
class WebMessageType {
|
||||
final int _value;
|
||||
final int _nativeValue;
|
||||
const WebMessageType._internal(this._value, this._nativeValue);
|
||||
// ignore: unused_element
|
||||
factory WebMessageType._internalMultiPlatform(
|
||||
int value, Function nativeValue) =>
|
||||
WebMessageType._internal(value, nativeValue());
|
||||
|
||||
///Indicates the payload of WebMessageCompat is JavaScript ArrayBuffer.
|
||||
///
|
||||
///**NOTE**: available only if [WebViewFeature.WEB_MESSAGE_ARRAY_BUFFER] feature is supported.
|
||||
static const ARRAY_BUFFER = WebMessageType._internal(1, 1);
|
||||
|
||||
///Indicates the payload of WebMessageCompat is String.
|
||||
static const STRING = WebMessageType._internal(0, 0);
|
||||
|
||||
///Set of all values of [WebMessageType].
|
||||
static final Set<WebMessageType> values = [
|
||||
WebMessageType.ARRAY_BUFFER,
|
||||
WebMessageType.STRING,
|
||||
].toSet();
|
||||
|
||||
///Gets a possible [WebMessageType] instance from [int] value.
|
||||
static WebMessageType? fromValue(int? value) {
|
||||
if (value != null) {
|
||||
try {
|
||||
return WebMessageType.values
|
||||
.firstWhere((element) => element.toValue() == value);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
///Gets a possible [WebMessageType] instance from a native value.
|
||||
static WebMessageType? fromNativeValue(int? value) {
|
||||
if (value != null) {
|
||||
try {
|
||||
return WebMessageType.values
|
||||
.firstWhere((element) => element.toNativeValue() == value);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
///Gets [int] value.
|
||||
int toValue() => _value;
|
||||
|
||||
///Gets [int] native value.
|
||||
int toNativeValue() => _nativeValue;
|
||||
|
||||
@override
|
||||
int get hashCode => _value.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(value) => value == _value;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
switch (_value) {
|
||||
case 1:
|
||||
return 'ARRAY_BUFFER';
|
||||
case 0:
|
||||
return 'STRING';
|
||||
}
|
||||
return _value.toString();
|
||||
}
|
||||
}
|
||||
|
||||
// **************************************************************************
|
||||
// ExchangeableObjectGenerator
|
||||
// **************************************************************************
|
||||
|
@ -10,11 +88,20 @@ part of 'web_message.dart';
|
|||
///See https://html.spec.whatwg.org/multipage/comms.html#the-messageevent-interfaces for definition of a MessageEvent in HTML5.
|
||||
class WebMessage {
|
||||
///The data of the message.
|
||||
String? data;
|
||||
dynamic data;
|
||||
|
||||
///The ports that are sent with the message.
|
||||
List<WebMessagePort>? ports;
|
||||
WebMessage({this.data, this.ports});
|
||||
|
||||
///The payload type of the message.
|
||||
WebMessageType type;
|
||||
WebMessage({this.data, this.type = WebMessageType.STRING, this.ports}) {
|
||||
assert(((this.data == null || this.data is String) &&
|
||||
this.type == WebMessageType.STRING) ||
|
||||
(this.data != null &&
|
||||
this.data is Uint8List &&
|
||||
this.type == WebMessageType.ARRAY_BUFFER));
|
||||
}
|
||||
|
||||
///Gets a possible [WebMessage] instance from a [Map] value.
|
||||
static WebMessage? fromMap(Map<String, dynamic>? map) {
|
||||
|
@ -26,6 +113,7 @@ class WebMessage {
|
|||
ports: map['ports'] != null
|
||||
? List<WebMessagePort>.from(map['ports'].map((e) => e))
|
||||
: null,
|
||||
type: WebMessageType.fromNativeValue(map['type'])!,
|
||||
);
|
||||
return instance;
|
||||
}
|
||||
|
@ -35,6 +123,7 @@ class WebMessage {
|
|||
return {
|
||||
"data": data,
|
||||
"ports": ports?.map((e) => e.toMap()).toList(),
|
||||
"type": type.toNativeValue(),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -45,6 +134,6 @@ class WebMessage {
|
|||
|
||||
@override
|
||||
String toString() {
|
||||
return 'WebMessage{data: $data, ports: $ports}';
|
||||
return 'WebMessage{data: $data, ports: $ports, type: $type}';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_inappwebview/src/util.dart';
|
||||
import 'web_message_port.dart';
|
||||
import 'web_message.dart';
|
||||
|
||||
///The representation of the [HTML5 message channels](https://html.spec.whatwg.org/multipage/web-messaging.html#message-channels).
|
||||
///
|
||||
|
@ -45,7 +46,10 @@ class WebMessageChannel extends ChannelController {
|
|||
int index = call.arguments["index"];
|
||||
var port = index == 0 ? this.port1 : this.port2;
|
||||
if (port.onMessage != null) {
|
||||
String? message = call.arguments["message"];
|
||||
WebMessage? message = call.arguments["message"] != null
|
||||
? WebMessage.fromMap(
|
||||
call.arguments["message"].cast<String, dynamic>())
|
||||
: null;
|
||||
port.onMessage!(message);
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -3,6 +3,7 @@ import '../in_app_webview/in_app_webview_controller.dart';
|
|||
import '../types/main.dart';
|
||||
import '../util.dart';
|
||||
import '../web_uri.dart';
|
||||
import 'web_message.dart';
|
||||
|
||||
///This listener receives messages sent on the JavaScript object which was injected by [InAppWebViewController.addWebMessageListener].
|
||||
///
|
||||
|
@ -56,7 +57,10 @@ class WebMessageListener extends ChannelController {
|
|||
_replyProxy = new JavaScriptReplyProxy(this);
|
||||
}
|
||||
if (onPostMessage != null) {
|
||||
String? message = call.arguments["message"];
|
||||
WebMessage? message = call.arguments["message"] != null
|
||||
? WebMessage.fromMap(
|
||||
call.arguments["message"].cast<String, dynamic>())
|
||||
: null;
|
||||
WebUri? sourceOrigin = call.arguments["sourceOrigin"] != null
|
||||
? WebUri(call.arguments["sourceOrigin"])
|
||||
: null;
|
||||
|
@ -107,10 +111,12 @@ class JavaScriptReplyProxy {
|
|||
|
||||
///Post a [message] to the injected JavaScript object which sent this [JavaScriptReplyProxy].
|
||||
///
|
||||
///If [message] is of type [WebMessageType.ARRAY_BUFFER], be aware that large byte buffers can lead to out-of-memory crashes on low-end devices.
|
||||
///
|
||||
///**Official Android API**: https://developer.android.com/reference/androidx/webkit/JavaScriptReplyProxy#postMessage(java.lang.String)
|
||||
Future<void> postMessage(String message) async {
|
||||
Future<void> postMessage(WebMessage message) async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
args.putIfAbsent('message', () => message);
|
||||
args.putIfAbsent('message', () => message.toMap());
|
||||
await _webMessageListener.channel?.invokeMethod('postMessage', args);
|
||||
}
|
||||
|
||||
|
|
|
@ -2201,14 +2201,22 @@ if(window.\(JAVASCRIPT_BRIDGE_NAME)[\(_callHandlerID)] != null) {
|
|||
let body = message.body as! [String: Any?]
|
||||
let webMessageChannelId = body["webMessageChannelId"] as! String
|
||||
let index = body["index"] as! Int64
|
||||
let webMessage = body["message"] as? String
|
||||
var webMessage: WebMessage? = nil
|
||||
if let webMessageMap = body["message"] as? [String : Any?] {
|
||||
webMessage = WebMessage.fromMap(map: webMessageMap)
|
||||
}
|
||||
|
||||
if let webMessageChannel = webMessageChannels[webMessageChannelId] {
|
||||
webMessageChannel.channelDelegate?.onMessage(index: index, message: webMessage)
|
||||
}
|
||||
} else if message.name == "onWebMessageListenerPostMessageReceived" {
|
||||
let body = message.body as! [String: Any?]
|
||||
let jsObjectName = body["jsObjectName"] as! String
|
||||
let messageData = body["message"] as? String
|
||||
var webMessage: WebMessage? = nil
|
||||
if let webMessageMap = body["message"] as? [String : Any?] {
|
||||
webMessage = WebMessage.fromMap(map: webMessageMap)
|
||||
}
|
||||
|
||||
if let webMessageListener = webMessageListeners.first(where: ({($0.jsObjectName == jsObjectName)})) {
|
||||
let isMainFrame = message.frameInfo.isMainFrame
|
||||
|
||||
|
@ -2225,7 +2233,7 @@ if(window.\(JAVASCRIPT_BRIDGE_NAME)[\(_callHandlerID)] != null) {
|
|||
if !scheme.isEmpty, !host.isEmpty {
|
||||
sourceOrigin = URL(string: "\(scheme)://\(host)\(port != 0 ? ":" + String(port) : "")")
|
||||
}
|
||||
webMessageListener.channelDelegate?.onPostMessage(message: messageData, sourceOrigin: sourceOrigin, isMainFrame: isMainFrame)
|
||||
webMessageListener.channelDelegate?.onPostMessage(message: webMessage, sourceOrigin: sourceOrigin, isMainFrame: isMainFrame)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2514,11 +2522,11 @@ if(window.\(JAVASCRIPT_BRIDGE_NAME)[\(_callHandlerID)] != null) {
|
|||
}
|
||||
portsString = "[" + portArrayString.joined(separator: ", ") + "]"
|
||||
}
|
||||
let data = message.data?.replacingOccurrences(of: "\'", with: "\\'") ?? "null"
|
||||
|
||||
let url = URL(string: targetOrigin)?.absoluteString ?? "*"
|
||||
let source = """
|
||||
(function() {
|
||||
window.postMessage('\(data)', '\(url)', \(portsString));
|
||||
window.postMessage(\(message.jsData), '\(url)', \(portsString));
|
||||
})();
|
||||
"""
|
||||
evaluateJavascript(source: source, completionHandler: completionHandler)
|
||||
|
|
|
@ -26,8 +26,8 @@ public class WebMessageChannel : FlutterMethodCallDelegate {
|
|||
self.channelDelegate = WebMessageChannelChannelDelegate(webMessageChannel: self, channel: channel)
|
||||
}
|
||||
self.ports = [
|
||||
WebMessagePort(name: "port1", webMessageChannel: self),
|
||||
WebMessagePort(name: "port2", webMessageChannel: self)
|
||||
WebMessagePort(name: "port1", index: 0, webMessageChannelId: self.id, webMessageChannel: self),
|
||||
WebMessagePort(name: "port2", index: 1, webMessageChannelId: self.id, webMessageChannel: self)
|
||||
]
|
||||
}
|
||||
|
||||
|
|
|
@ -40,22 +40,20 @@ public class WebMessageChannelChannelDelegate : ChannelDelegate {
|
|||
if let webView = webMessageChannel?.webView, let ports = webMessageChannel?.ports, ports.count > 0 {
|
||||
let index = arguments!["index"] as! Int
|
||||
let port = ports[index]
|
||||
let message = arguments!["message"] as! [String: Any?]
|
||||
var message = WebMessage.fromMap(map: arguments!["message"] as! [String: Any?])
|
||||
|
||||
var webMessagePorts: [WebMessagePort] = []
|
||||
let portsMap = message["ports"] as? [[String: Any?]]
|
||||
if let portsMap = portsMap {
|
||||
for portMap in portsMap {
|
||||
let webMessageChannelId = portMap["webMessageChannelId"] as! String
|
||||
let index = portMap["index"] as! Int
|
||||
if let webMessageChannel = webView.webMessageChannels[webMessageChannelId] {
|
||||
webMessagePorts.append(webMessageChannel.ports[index])
|
||||
var ports: [WebMessagePort] = []
|
||||
if let notConnectedPorts = message.ports {
|
||||
for notConnectedPort in notConnectedPorts {
|
||||
if let webMessageChannel = webView.webMessageChannels[notConnectedPort.webMessageChannelId] {
|
||||
ports.append(webMessageChannel.ports[Int(notConnectedPort.index)])
|
||||
}
|
||||
}
|
||||
}
|
||||
let webMessage = WebMessage(data: message["data"] as? String, ports: webMessagePorts)
|
||||
message.ports = ports
|
||||
|
||||
do {
|
||||
try port.postMessage(message: webMessage) { (_) in
|
||||
try port.postMessage(message: message) { (_) in
|
||||
result(true)
|
||||
}
|
||||
} catch let error as NSError {
|
||||
|
@ -86,10 +84,10 @@ public class WebMessageChannelChannelDelegate : ChannelDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
public func onMessage(index: Int64, message: String?) {
|
||||
public func onMessage(index: Int64, message: WebMessage?) {
|
||||
let arguments: [String:Any?] = [
|
||||
"index": index,
|
||||
"message": message
|
||||
"message": message?.toMap()
|
||||
]
|
||||
channel?.invokeMethod("onMessage", arguments: arguments)
|
||||
}
|
||||
|
|
|
@ -23,12 +23,13 @@ public class WebMessageListenerChannelDelegate : ChannelDelegate {
|
|||
case "postMessage":
|
||||
if let webView = webMessageListener?.webView, let jsObjectName = webMessageListener?.jsObjectName {
|
||||
let jsObjectNameEscaped = jsObjectName.replacingOccurrences(of: "\'", with: "\\'")
|
||||
let messageEscaped = (arguments!["message"] as! String).replacingOccurrences(of: "\'", with: "\\'")
|
||||
let message = WebMessage.fromMap(map: arguments!["message"] as! [String: Any?])
|
||||
|
||||
let source = """
|
||||
(function() {
|
||||
var webMessageListener = window['\(jsObjectNameEscaped)'];
|
||||
if (webMessageListener != null) {
|
||||
var event = {data: '\(messageEscaped)'};
|
||||
var event = {data: \(message.jsData)};
|
||||
if (webMessageListener.onmessage != null) {
|
||||
webMessageListener.onmessage(event);
|
||||
}
|
||||
|
@ -51,9 +52,9 @@ public class WebMessageListenerChannelDelegate : ChannelDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
public func onPostMessage(message: String?, sourceOrigin: URL?, isMainFrame: Bool) {
|
||||
public func onPostMessage(message: WebMessage?, sourceOrigin: URL?, isMainFrame: Bool) {
|
||||
let arguments: [String:Any?] = [
|
||||
"message": message,
|
||||
"message": message?.toMap(),
|
||||
"sourceOrigin": sourceOrigin?.absoluteString,
|
||||
"isMainFrame": isMainFrame
|
||||
]
|
||||
|
|
|
@ -480,23 +480,21 @@ public class WebViewChannelDelegate : ChannelDelegate {
|
|||
break
|
||||
case .postWebMessage:
|
||||
if let webView = webView {
|
||||
let message = arguments!["message"] as! [String: Any?]
|
||||
var message = WebMessage.fromMap(map: arguments!["message"] as! [String: Any?])
|
||||
let targetOrigin = arguments!["targetOrigin"] as! String
|
||||
|
||||
var ports: [WebMessagePort] = []
|
||||
let portsMap = message["ports"] as? [[String: Any?]]
|
||||
if let portsMap = portsMap {
|
||||
for portMap in portsMap {
|
||||
let webMessageChannelId = portMap["webMessageChannelId"] as! String
|
||||
let index = portMap["index"] as! Int
|
||||
if let webMessageChannel = webView.webMessageChannels[webMessageChannelId] {
|
||||
ports.append(webMessageChannel.ports[index])
|
||||
if let notConnectedPorts = message.ports {
|
||||
for notConnectedPort in notConnectedPorts {
|
||||
if let webMessageChannel = webView.webMessageChannels[notConnectedPort.webMessageChannelId] {
|
||||
ports.append(webMessageChannel.ports[Int(notConnectedPort.index)])
|
||||
}
|
||||
}
|
||||
}
|
||||
let webMessage = WebMessage(data: message["data"] as? String, ports: ports)
|
||||
message.ports = ports
|
||||
|
||||
do {
|
||||
try webView.postWebMessage(message: webMessage, targetOrigin: targetOrigin) { (_) in
|
||||
try webView.postWebMessage(message: message, targetOrigin: targetOrigin) { (_) in
|
||||
result(true)
|
||||
}
|
||||
} catch let error as NSError {
|
||||
|
|
|
@ -13,7 +13,11 @@ function FlutterInAppWebViewWebMessageListener(jsObjectName) {
|
|||
this.listeners = [];
|
||||
this.onmessage = null;
|
||||
}
|
||||
FlutterInAppWebViewWebMessageListener.prototype.postMessage = function(message) {
|
||||
FlutterInAppWebViewWebMessageListener.prototype.postMessage = function(data) {
|
||||
var message = {
|
||||
"data": window.ArrayBuffer != null && data instanceof ArrayBuffer ? Array.from(new Uint8Array(data)) : (data != null ? data.toString() : null),
|
||||
"type": window.ArrayBuffer != null && data instanceof ArrayBuffer ? 1 : 0
|
||||
};
|
||||
window.webkit.messageHandlers['onWebMessageListenerPostMessageReceived'].postMessage({jsObjectName: this.jsObjectName, message: message});
|
||||
};
|
||||
FlutterInAppWebViewWebMessageListener.prototype.addEventListener = function(type, listener) {
|
||||
|
|
|
@ -6,17 +6,55 @@
|
|||
//
|
||||
|
||||
import Foundation
|
||||
import FlutterMacOS
|
||||
|
||||
public class WebMessage : NSObject {
|
||||
var data: String?
|
||||
public class WebMessage : NSObject, Disposable {
|
||||
var data: Any?
|
||||
var type: WebMessageType
|
||||
var ports: [WebMessagePort]?
|
||||
|
||||
public init(data: String?, ports: [WebMessagePort]?) {
|
||||
var jsData: String {
|
||||
var jsData: String = "null"
|
||||
if let messageData = data {
|
||||
if type == .arrayBuffer, let messageDataArrayBuffer = messageData as? FlutterStandardTypedData {
|
||||
jsData = "new Uint8Array(\(Array(messageDataArrayBuffer.data))).buffer"
|
||||
} else if let messageDataString = messageData as? String {
|
||||
jsData = "'\(messageDataString.replacingOccurrences(of: "\'", with: "\\'"))'"
|
||||
}
|
||||
}
|
||||
return jsData
|
||||
}
|
||||
|
||||
public init(data: Any?, type: WebMessageType, ports: [WebMessagePort]?) {
|
||||
self.type = type
|
||||
super.init()
|
||||
self.data = data
|
||||
self.ports = ports
|
||||
}
|
||||
|
||||
public static func fromMap(map: [String: Any?]) -> WebMessage {
|
||||
let portMapList = map["ports"] as? [[String: Any?]]
|
||||
var ports: [WebMessagePort]? = nil
|
||||
if let portMapList = portMapList, !portMapList.isEmpty {
|
||||
ports = []
|
||||
portMapList.forEach { (portMap) in
|
||||
ports?.append(WebMessagePort.fromMap(map: portMap))
|
||||
}
|
||||
}
|
||||
|
||||
return WebMessage(
|
||||
data: map["data"] as? Any,
|
||||
type: WebMessageType.init(rawValue: map["type"] as! Int)!,
|
||||
ports: ports)
|
||||
}
|
||||
|
||||
public func toMap () -> [String: Any?] {
|
||||
return [
|
||||
"data": type == .arrayBuffer && data is [UInt8] ? Data(data as! [UInt8]) : data,
|
||||
"type": type.rawValue
|
||||
]
|
||||
}
|
||||
|
||||
public func dispose() {
|
||||
ports?.removeAll()
|
||||
}
|
||||
|
@ -26,3 +64,8 @@ public class WebMessage : NSObject {
|
|||
dispose()
|
||||
}
|
||||
}
|
||||
|
||||
public enum WebMessageType: Int {
|
||||
case string = 0
|
||||
case arrayBuffer = 1
|
||||
}
|
||||
|
|
|
@ -9,13 +9,17 @@ import Foundation
|
|||
|
||||
public class WebMessagePort : NSObject {
|
||||
var name: String
|
||||
var index: Int64
|
||||
var webMessageChannelId: String
|
||||
var webMessageChannel: WebMessageChannel?
|
||||
var isClosed = false
|
||||
var isTransferred = false
|
||||
var isStarted = false
|
||||
|
||||
public init(name: String, webMessageChannel: WebMessageChannel) {
|
||||
public init(name: String, index: Int64, webMessageChannelId: String, webMessageChannel: WebMessageChannel?) {
|
||||
self.name = name
|
||||
self.index = index
|
||||
self.webMessageChannelId = webMessageChannelId
|
||||
super.init()
|
||||
self.webMessageChannel = webMessageChannel
|
||||
}
|
||||
|
@ -35,7 +39,10 @@ public class WebMessagePort : NSObject {
|
|||
window.webkit.messageHandlers["onWebMessagePortMessageReceived"].postMessage({
|
||||
"webMessageChannelId": "\(webMessageChannel.id)",
|
||||
"index": \(String(index)),
|
||||
"message": event.data
|
||||
"message": {
|
||||
"data": window.ArrayBuffer != null && event.data instanceof ArrayBuffer ? Array.from(new Uint8Array(event.data)) : (event.data != null ? event.data.toString() : null),
|
||||
"type": window.ArrayBuffer != null && event.data instanceof ArrayBuffer ? 1 : 0
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -71,12 +78,12 @@ public class WebMessagePort : NSObject {
|
|||
}
|
||||
portsString = "[" + portArrayString.joined(separator: ", ") + "]"
|
||||
}
|
||||
let data = message.data?.replacingOccurrences(of: "\'", with: "\\'") ?? "null"
|
||||
|
||||
let source = """
|
||||
(function() {
|
||||
var webMessageChannel = \(WEB_MESSAGE_CHANNELS_VARIABLE_NAME)["\(webMessageChannel.id)"];
|
||||
if (webMessageChannel != null) {
|
||||
webMessageChannel.\(self.name).postMessage('\(data)', \(portsString));
|
||||
webMessageChannel.\(self.name).postMessage(\(message.jsData), \(portsString));
|
||||
}
|
||||
})();
|
||||
"""
|
||||
|
@ -111,6 +118,23 @@ public class WebMessagePort : NSObject {
|
|||
}
|
||||
}
|
||||
|
||||
public static func fromMap(map: [String: Any?]) -> WebMessagePort {
|
||||
let index = map["index"] as! Int64
|
||||
return WebMessagePort(
|
||||
name: "port\(String(index + 1))",
|
||||
index: index,
|
||||
webMessageChannelId: map["webMessageChannelId"] as! String,
|
||||
webMessageChannel: nil)
|
||||
}
|
||||
|
||||
public func toMap () -> [String: Any?] {
|
||||
return [
|
||||
"name": name,
|
||||
"index": index,
|
||||
"webMessageChannelId": webMessageChannelId
|
||||
]
|
||||
}
|
||||
|
||||
public func dispose() {
|
||||
isClosed = true
|
||||
webMessageChannel = nil
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
name: flutter_inappwebview
|
||||
description: A Flutter plugin that allows you to add an inline webview, to use an headless webview, and to open an in-app browser window.
|
||||
version: 6.0.0-beta.27
|
||||
version: 6.0.0-beta.28
|
||||
homepage: https://inappwebview.dev/
|
||||
repository: https://github.com/pichillilorenzo/flutter_inappwebview
|
||||
issue_tracker: https://github.com/pichillilorenzo/flutter_inappwebview/issues
|
||||
|
@ -21,7 +21,7 @@ dependencies:
|
|||
flutter_web_plugins:
|
||||
sdk: flutter
|
||||
js: ^0.6.4
|
||||
flutter_inappwebview_internal_annotations: ^1.1.0
|
||||
flutter_inappwebview_internal_annotations: ^1.1.1
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
|
Loading…
Reference in New Issue