diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/types/UserContentController.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/types/UserContentController.java index 21c6847e..8e79652d 100644 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/types/UserContentController.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/types/UserContentController.java @@ -2,7 +2,6 @@ package com.pichillilorenzo.flutter_inappwebview.types; import android.annotation.SuppressLint; import android.text.TextUtils; -import android.util.Log; import android.webkit.WebView; import androidx.annotation.NonNull; @@ -37,10 +36,13 @@ public class UserContentController implements Disposable { private final Map scriptHandlerMap = new HashMap<>(); + @Nullable + private ScriptHandler contentWorldsCreatorScript; + @NonNull private final Map> userOnlyScripts = new HashMap>() {{ - put(UserScriptInjectionTime.AT_DOCUMENT_START, new LinkedHashSet()); - put(UserScriptInjectionTime.AT_DOCUMENT_END, new LinkedHashSet()); + put(UserScriptInjectionTime.AT_DOCUMENT_START, new LinkedHashSet()); + put(UserScriptInjectionTime.AT_DOCUMENT_END, new LinkedHashSet()); }}; @NonNull private final Map> pluginScripts = new HashMap>() {{ @@ -171,15 +173,35 @@ public class UserContentController implements Disposable { return new LinkedHashSet<>(this.userOnlyScripts.get(injectionTime)); } + private void updateContentWorldsCreatorScript() { + String source = generateContentWorldsCreatorCode(); + if (WebViewFeature.isFeatureSupported(WebViewFeature.DOCUMENT_START_SCRIPT)) { + if (contentWorldsCreatorScript != null) { + contentWorldsCreatorScript.remove(); + } + if (!source.isEmpty() && webView != null) { + contentWorldsCreatorScript = WebViewCompat.addDocumentStartJavaScript( + webView, + source, + new HashSet() {{ + add("*"); + }} + ); + } + } + } + public boolean addUserOnlyScript(UserScript userOnlyScript) { ContentWorld contentWorld = userOnlyScript.getContentWorld(); if (contentWorld != null) { contentWorlds.add(contentWorld); } - if (webView != null && WebViewFeature.isFeatureSupported(WebViewFeature.DOCUMENT_START_SCRIPT)) { + this.updateContentWorldsCreatorScript(); + if (webView != null && userOnlyScript.getInjectionTime() == UserScriptInjectionTime.AT_DOCUMENT_START + && WebViewFeature.isFeatureSupported(WebViewFeature.DOCUMENT_START_SCRIPT)) { ScriptHandler scriptHandler = WebViewCompat.addDocumentStartJavaScript( webView, - userOnlyScript.getSource(), + wrapSourceCodeInContentWorld(userOnlyScript.getContentWorld(), userOnlyScript.getSource()), userOnlyScript.getAllowedOriginRules() ); this.scriptHandlerMap.put(userOnlyScript, scriptHandler); @@ -200,6 +222,7 @@ public class UserContentController implements Disposable { scriptHandler.remove(); this.scriptHandlerMap.remove(userOnlyScript); } + this.updateContentWorldsCreatorScript(); } return this.userOnlyScripts.get(userOnlyScript.getInjectionTime()).remove(userOnlyScript); } @@ -243,10 +266,12 @@ public class UserContentController implements Disposable { if (contentWorld != null) { contentWorlds.add(contentWorld); } - if (webView != null && WebViewFeature.isFeatureSupported(WebViewFeature.DOCUMENT_START_SCRIPT)) { + this.updateContentWorldsCreatorScript(); + if (webView != null && pluginScript.getInjectionTime() == UserScriptInjectionTime.AT_DOCUMENT_START + && WebViewFeature.isFeatureSupported(WebViewFeature.DOCUMENT_START_SCRIPT)) { ScriptHandler scriptHandler = WebViewCompat.addDocumentStartJavaScript( webView, - pluginScript.getSource(), + wrapSourceCodeInContentWorld(pluginScript.getContentWorld(), pluginScript.getSource()), pluginScript.getAllowedOriginRules() ); this.scriptHandlerMap.put(pluginScript, scriptHandler); @@ -267,6 +292,7 @@ public class UserContentController implements Disposable { scriptHandler.remove(); this.scriptHandlerMap.remove(pluginScript); } + this.updateContentWorldsCreatorScript(); } return this.pluginScripts.get(pluginScript.getInjectionTime()).remove(pluginScript); } @@ -383,9 +409,31 @@ public class UserContentController implements Disposable { "}"; private static final String CONTENT_WORLDS_GENERATOR_JS_SOURCE = "(function() {" + - " var contentWorldNames = [" + PluginScriptsUtil.VAR_CONTENT_WORLD_NAME_ARRAY + "];" + - " for (var contentWorldName of contentWorldNames) {" + - " var iframeId = '" + JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_NAME + "_' + contentWorldName;" + + " var interval = setInterval(function() {" + + " if (document.body == null) {return;}" + + " var contentWorldNames = [" + PluginScriptsUtil.VAR_CONTENT_WORLD_NAME_ARRAY + "];" + + " for (var contentWorldName of contentWorldNames) {" + + " var iframeId = '" + JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_NAME + "_' + contentWorldName;" + + " var iframe = document.getElementById(iframeId);" + + " if (iframe == null) {" + + " iframe = document.createElement('iframe');" + + " iframe.id = iframeId;" + + " iframe.style = 'display: none; z-index: 0; position: absolute; width: 0px; height: 0px';" + + " document.body.append(iframe);" + + " }" + + " var script = iframe.contentWindow.document.createElement('script');" + + " script.id = '" + JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_NAME + "_plugin_scripts';" + + " script.innerHTML = " + PluginScriptsUtil.VAR_JSON_SOURCE_ENCODED + ";" + + " iframe.contentWindow.document.body.append(script);" + + " }" + + " clearInterval(interval);" + + " });" + + "})();"; + + private static final String CONTENT_WORLD_WRAPPER_JS_SOURCE = "(function() {" + + " var interval = setInterval(function() {" + + " if (document.body == null) {return;}" + + " var iframeId = '" + JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_NAME + "_" + PluginScriptsUtil.VAR_CONTENT_WORLD_NAME + "';" + " var iframe = document.getElementById(iframeId);" + " if (iframe == null) {" + " iframe = document.createElement('iframe');" + @@ -393,24 +441,14 @@ public class UserContentController implements Disposable { " iframe.style = 'display: none; z-index: 0; position: absolute; width: 0px; height: 0px';" + " document.body.append(iframe);" + " }" + + " if (iframe.contentWindow.document.querySelector('#" + JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_NAME + "_plugin_scripts') == null) {" + + " return;" + + " }" + " var script = iframe.contentWindow.document.createElement('script');" + - " script.innerHTML = "+ PluginScriptsUtil.VAR_JSON_SOURCE_ENCODED + ";" + + " script.innerHTML = " + PluginScriptsUtil.VAR_JSON_SOURCE_ENCODED + ";" + " iframe.contentWindow.document.body.append(script);" + - " }" + - "})();"; - - private static final String CONTENT_WORLD_WRAPPER_JS_SOURCE = "(function() {" + - " var iframeId = '" + JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_NAME + "_" + PluginScriptsUtil.VAR_CONTENT_WORLD_NAME + "';" + - " var iframe = document.getElementById(iframeId);" + - " if (iframe == null) {" + - " iframe = document.createElement('iframe');" + - " iframe.id = iframeId;" + - " iframe.style = 'display: none; z-index: 0; position: absolute; width: 0px; height: 0px';" + - " document.body.append(iframe);" + - " }" + - " var script = iframe.contentWindow.document.createElement('script');" + - " script.innerHTML = "+ PluginScriptsUtil.VAR_JSON_SOURCE_ENCODED + ";" + - " iframe.contentWindow.document.body.append(script);" + + " clearInterval(interval);" + + " });" + "})();"; private static final String DOCUMENT_READY_WRAPPER_JS_SOURCE = "if (document.readyState === 'interactive' || document.readyState === 'complete') { " + @@ -419,6 +457,11 @@ public class UserContentController implements Disposable { @Override public void dispose() { + if (WebViewFeature.isFeatureSupported(WebViewFeature.DOCUMENT_START_SCRIPT) && contentWorldsCreatorScript != null) { + contentWorldsCreatorScript.remove(); + } + removeAllUserOnlyScripts(); + removeAllPluginScripts(); webView = null; } } diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/InAppWebView.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/InAppWebView.java index 76e56855..f8a6f1ce 100755 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/InAppWebView.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/InAppWebView.java @@ -1,5 +1,8 @@ package com.pichillilorenzo.flutter_inappwebview.webview.in_app_webview; +import static android.content.Context.INPUT_METHOD_SERVICE; +import static com.pichillilorenzo.flutter_inappwebview.types.PreferredContentModeOptionType.fromValue; + import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; import android.annotation.SuppressLint; @@ -56,18 +59,13 @@ import androidx.webkit.WebViewCompat; import androidx.webkit.WebViewFeature; import com.pichillilorenzo.flutter_inappwebview.InAppWebViewFlutterPlugin; -import com.pichillilorenzo.flutter_inappwebview.find_interaction.FindInteractionController; -import com.pichillilorenzo.flutter_inappwebview.print_job.PrintJobController; -import com.pichillilorenzo.flutter_inappwebview.print_job.PrintJobManager; -import com.pichillilorenzo.flutter_inappwebview.print_job.PrintJobSettings; -import com.pichillilorenzo.flutter_inappwebview.types.HitTestResult; -import com.pichillilorenzo.flutter_inappwebview.webview.JavaScriptBridgeInterface; import com.pichillilorenzo.flutter_inappwebview.R; import com.pichillilorenzo.flutter_inappwebview.Util; import com.pichillilorenzo.flutter_inappwebview.content_blocker.ContentBlocker; import com.pichillilorenzo.flutter_inappwebview.content_blocker.ContentBlockerAction; import com.pichillilorenzo.flutter_inappwebview.content_blocker.ContentBlockerHandler; import com.pichillilorenzo.flutter_inappwebview.content_blocker.ContentBlockerTrigger; +import com.pichillilorenzo.flutter_inappwebview.find_interaction.FindInteractionController; import com.pichillilorenzo.flutter_inappwebview.in_app_browser.InAppBrowserDelegate; import com.pichillilorenzo.flutter_inappwebview.plugin_scripts_js.ConsoleLogJS; import com.pichillilorenzo.flutter_inappwebview.plugin_scripts_js.InterceptAjaxRequestJS; @@ -79,16 +77,20 @@ import com.pichillilorenzo.flutter_inappwebview.plugin_scripts_js.OnWindowFocusE import com.pichillilorenzo.flutter_inappwebview.plugin_scripts_js.PluginScriptsUtil; import com.pichillilorenzo.flutter_inappwebview.plugin_scripts_js.PrintJS; import com.pichillilorenzo.flutter_inappwebview.plugin_scripts_js.PromisePolyfillJS; +import com.pichillilorenzo.flutter_inappwebview.print_job.PrintJobController; +import com.pichillilorenzo.flutter_inappwebview.print_job.PrintJobManager; +import com.pichillilorenzo.flutter_inappwebview.print_job.PrintJobSettings; import com.pichillilorenzo.flutter_inappwebview.pull_to_refresh.PullToRefreshLayout; import com.pichillilorenzo.flutter_inappwebview.types.ContentWorld; import com.pichillilorenzo.flutter_inappwebview.types.DownloadStartRequest; -import com.pichillilorenzo.flutter_inappwebview.webview.ContextMenuSettings; -import com.pichillilorenzo.flutter_inappwebview.webview.InAppWebViewInterface; import com.pichillilorenzo.flutter_inappwebview.types.PluginScript; import com.pichillilorenzo.flutter_inappwebview.types.PreferredContentModeOptionType; import com.pichillilorenzo.flutter_inappwebview.types.URLRequest; import com.pichillilorenzo.flutter_inappwebview.types.UserContentController; import com.pichillilorenzo.flutter_inappwebview.types.UserScript; +import com.pichillilorenzo.flutter_inappwebview.webview.ContextMenuSettings; +import com.pichillilorenzo.flutter_inappwebview.webview.InAppWebViewInterface; +import com.pichillilorenzo.flutter_inappwebview.webview.JavaScriptBridgeInterface; import com.pichillilorenzo.flutter_inappwebview.webview.WebViewChannelDelegate; import com.pichillilorenzo.flutter_inappwebview.webview.web_message.WebMessageChannel; import com.pichillilorenzo.flutter_inappwebview.webview.web_message.WebMessageListener; @@ -99,20 +101,15 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; -import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.UUID; import java.util.regex.Pattern; import io.flutter.plugin.common.MethodChannel; import okhttp3.OkHttpClient; -import static android.content.Context.INPUT_METHOD_SERVICE; -import static com.pichillilorenzo.flutter_inappwebview.types.PreferredContentModeOptionType.fromValue; - final public class InAppWebView extends InputAwareWebView implements InAppWebViewInterface { protected static final String LOG_TAG = "InAppWebView"; public static final String METHOD_CHANNEL_NAME_PREFIX = "com.pichillilorenzo/flutter_inappwebview_";