Added shouldInterceptAjaxRequest, onAjaxReadyStateChange, onAjaxProgressEvent and shouldInterceptFetchRequest events, added useShouldInterceptAjaxRequest and useShouldInterceptFetchRequest webview options, updated node.js server test, added injectJavascriptFileFromAsset and injectCSSFileFromAsset methods, renamed some methods

This commit is contained in:
Lorenzo Pichilli 2019-11-05 03:44:22 +01:00
parent 2811fce463
commit 18919576e0
28 changed files with 1264 additions and 482 deletions

View File

@ -1,6 +1,5 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="example/lib/main.dart" type="FlutterRunConfigurationType" factoryName="Flutter" singleton="false">
<option name="additionalArgs" value="--debug" />
<option name="filePath" value="$PROJECT_DIR$/example/lib/main.dart" />
<method v="2" />
</configuration>

View File

@ -15,35 +15,33 @@
</component>
<component name="ChangeListManager">
<list default="true" id="9b41f7a2-a71e-4923-91fb-249d7815b3e7" name="Default" comment="">
<change afterPath="$PROJECT_DIR$/.idea/codeStyles/codeStyleConfig.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/lib/t_rex_runner/t-rex.css" afterDir="false" />
<change afterPath="$PROJECT_DIR$/nodejs_server_test_auth_basic_and_ssl/assets/flutter_logo.svg" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/runConfigurations/example_lib_main_dart.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/runConfigurations/example_lib_main_dart.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/CHANGELOG.md" beforeDir="false" afterPath="$PROJECT_DIR$/CHANGELOG.md" afterDir="false" />
<change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/ContentBlocker/ContentBlockerHandler.java" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/ContentBlocker/ContentBlockerHandler.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/FlutterWebView.java" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/FlutterWebView.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowser.java" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowser.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserActivity.java" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserActivity.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppWebView/InAppWebView.java" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppWebView/InAppWebView.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppWebView/InAppWebViewClient.java" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppWebView/InAppWebViewClient.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppWebView/InAppWebViewOptions.java" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppWebView/InAppWebViewOptions.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/JavaScriptBridgeInterface.java" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/JavaScriptBridgeInterface.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/example/assets/index.html" beforeDir="false" afterPath="$PROJECT_DIR$/example/assets/index.html" afterDir="false" />
<change beforePath="$PROJECT_DIR$/example/ios/Podfile" beforeDir="false" afterPath="$PROJECT_DIR$/example/ios/Podfile" afterDir="false" />
<change beforePath="$PROJECT_DIR$/example/ios/Flutter/flutter_export_environment.sh" beforeDir="false" afterPath="$PROJECT_DIR$/example/ios/Flutter/flutter_export_environment.sh" afterDir="false" />
<change beforePath="$PROJECT_DIR$/example/ios/Runner.xcodeproj/project.pbxproj" beforeDir="false" afterPath="$PROJECT_DIR$/example/ios/Runner.xcodeproj/project.pbxproj" afterDir="false" />
<change beforePath="$PROJECT_DIR$/example/lib/chrome_safari_example.screen.dart" beforeDir="false" afterPath="$PROJECT_DIR$/example/lib/chrome_safari_example.screen.dart" afterDir="false" />
<change beforePath="$PROJECT_DIR$/example/lib/inline_example.screen.dart" beforeDir="false" afterPath="$PROJECT_DIR$/example/lib/inline_example.screen.dart" afterDir="false" />
<change beforePath="$PROJECT_DIR$/example/lib/test.dart" beforeDir="false" afterPath="$PROJECT_DIR$/example/lib/test.dart" afterDir="false" />
<change beforePath="$PROJECT_DIR$/example/lib/webview_example.screen.dart" beforeDir="false" afterPath="$PROJECT_DIR$/example/lib/webview_example.screen.dart" afterDir="false" />
<change beforePath="$PROJECT_DIR$/example/pubspec.yaml" beforeDir="false" afterPath="$PROJECT_DIR$/example/pubspec.yaml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ios/Classes/CustomeSchemeHandler.swift" beforeDir="false" afterPath="$PROJECT_DIR$/ios/Classes/CustomeSchemeHandler.swift" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ios/Classes/FlutterWebViewController.swift" beforeDir="false" afterPath="$PROJECT_DIR$/ios/Classes/FlutterWebViewController.swift" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ios/Classes/InAppWebView.swift" beforeDir="false" afterPath="$PROJECT_DIR$/ios/Classes/InAppWebView.swift" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ios/Classes/InAppWebViewOptions.swift" beforeDir="false" afterPath="$PROJECT_DIR$/ios/Classes/InAppWebViewOptions.swift" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ios/Classes/SwiftFlutterPlugin.swift" beforeDir="false" afterPath="$PROJECT_DIR$/ios/Classes/SwiftFlutterPlugin.swift" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ios/flutter_inappbrowser.podspec" beforeDir="false" afterPath="$PROJECT_DIR$/ios/flutter_inappbrowser.podspec" afterDir="false" />
<change beforePath="$PROJECT_DIR$/lib/src/chrome_safari_browser.dart" beforeDir="false" afterPath="$PROJECT_DIR$/lib/src/chrome_safari_browser.dart" afterDir="false" />
<change beforePath="$PROJECT_DIR$/lib/src/content_blocker.dart" beforeDir="false" afterPath="$PROJECT_DIR$/lib/src/content_blocker.dart" afterDir="false" />
<change beforePath="$PROJECT_DIR$/lib/src/in_app_browser.dart" beforeDir="false" afterPath="$PROJECT_DIR$/lib/src/in_app_browser.dart" afterDir="false" />
<change beforePath="$PROJECT_DIR$/lib/src/in_app_localhost_server.dart" beforeDir="false" afterPath="$PROJECT_DIR$/lib/src/in_app_localhost_server.dart" afterDir="false" />
<change beforePath="$PROJECT_DIR$/lib/src/in_app_webview.dart" beforeDir="false" afterPath="$PROJECT_DIR$/lib/src/in_app_webview.dart" afterDir="false" />
<change beforePath="$PROJECT_DIR$/lib/src/types.dart" beforeDir="false" afterPath="$PROJECT_DIR$/lib/src/types.dart" afterDir="false" />
<change beforePath="$PROJECT_DIR$/lib/src/webview_options.dart" beforeDir="false" afterPath="$PROJECT_DIR$/lib/src/webview_options.dart" afterDir="false" />
<change beforePath="$PROJECT_DIR$/nodejs_server_test_auth_basic_and_ssl/index.js" beforeDir="false" afterPath="$PROJECT_DIR$/nodejs_server_test_auth_basic_and_ssl/index.js" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pubspec.yaml" beforeDir="false" afterPath="$PROJECT_DIR$/pubspec.yaml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/nodejs_server_test_auth_basic_and_ssl/package-lock.json" beforeDir="false" afterPath="$PROJECT_DIR$/nodejs_server_test_auth_basic_and_ssl/package-lock.json" afterDir="false" />
<change beforePath="$PROJECT_DIR$/nodejs_server_test_auth_basic_and_ssl/package.json" beforeDir="false" afterPath="$PROJECT_DIR$/nodejs_server_test_auth_basic_and_ssl/package.json" afterDir="false" />
</list>
<ignored path="$PROJECT_DIR$/.dart_tool/" />
<ignored path="$PROJECT_DIR$/.idea/" />
@ -66,8 +64,8 @@
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/lib/src/in_app_webview.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="251">
<caret line="265" column="89" selection-start-line="261" selection-start-column="4" selection-end-line="265" selection-end-column="89" />
<state relative-caret-position="429">
<caret line="628" column="43" selection-start-line="628" selection-start-column="16" selection-end-line="628" selection-end-column="43" />
<folding>
<element signature="e#0#17#0" expanded="true" />
</folding>
@ -76,10 +74,10 @@
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/example/lib/inline_example.screen.dart">
<entry file="file://$PROJECT_DIR$/lib/src/types.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="28">
<caret line="94" column="32" selection-start-line="94" selection-start-column="16" selection-end-line="94" selection-end-column="32" />
<state relative-caret-position="-319">
<caret line="776" column="32" selection-start-line="776" selection-start-column="32" selection-end-line="776" selection-end-column="32" />
<folding>
<element signature="e#0#20#0" expanded="true" />
</folding>
@ -88,26 +86,65 @@
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/example/pubspec.yaml">
<entry file="file://$PROJECT_DIR$/lib/src/webview_options.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="131">
<caret line="24" column="27" selection-start-line="24" selection-start-column="27" selection-end-line="24" selection-end-column="27" />
<state relative-caret-position="371">
<caret line="94" column="37" selection-start-line="94" selection-start-column="7" selection-end-line="94" selection-end-column="37" />
<folding>
<element signature="e#0#17#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/example/assets/index.html">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="423">
<caret line="80" column="26" selection-start-line="78" selection-start-column="12" selection-end-line="80" selection-end-column="26" />
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/nodejs_server_test_auth_basic_and_ssl/index.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-11">
<caret line="61" selection-start-line="61" selection-end-line="61" />
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="true">
<entry file="file://$PROJECT_DIR$/lib/src/webview_options.dart">
<entry file="file://$PROJECT_DIR$/example/lib/inline_example.screen.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="364">
<caret line="81" column="43" selection-start-line="81" selection-start-column="43" selection-end-line="81" selection-end-column="43" />
<state relative-caret-position="164">
<caret line="89" column="55" selection-start-line="89" selection-start-column="55" selection-end-line="89" selection-end-column="55" />
<folding>
<element signature="e#0#17#0" expanded="true" />
<element signature="e#0#20#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/CHANGELOG.md">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="435">
<caret line="29" selection-start-line="29" selection-end-line="29" />
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/example/pubspec.yaml">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="116">
<caret line="23" column="28" selection-start-line="23" selection-start-column="28" selection-end-line="23" selection-end-column="28" />
</state>
</provider>
</entry>
</file>
</leaf>
</component>
<component name="FileTemplateManagerImpl">
@ -120,23 +157,6 @@
</component>
<component name="FindInProjectRecents">
<findStrings>
<find>toggleErrorInformationPopup</find>
<find>error-information-popup-container</find>
<find>offline-spri</find>
<find>sprite</find>
<find>icon-offline</find>
<find>ICON</find>
<find>loadImages</find>
<find>RESOURCE_TEMPLATE_ID</find>
<find>adjustDimensions</find>
<find>outerContainerEl</find>
<find>ontouch</find>
<find>fromVa</find>
<find>ContentBlockerActionType</find>
<find>getOptions</find>
<find>iOS</find>
<find>Ios</find>
<find>initialOptions</find>
<find>initialOpti</find>
<find>Platform</find>
<find>Platform.i</find>
@ -150,6 +170,23 @@
<find>List&lt;</find>
<find>debuggingEnabled</find>
<find>initialOp</find>
<find>injectScriptFile</find>
<find>injectScriptUrlFile</find>
<find>[InAppWebView] window</find>
<find>javaScriptEnabled</find>
<find>onLoadRes</find>
<find>onLoadResource</find>
<find>onLoadResource&quot;</find>
<find>javaScriptHandlersMap</find>
<find>javaScriptHandlers</find>
<find>onAjaxSend</find>
<find>Action</find>
<find>AjaxRequestEventType</find>
<find>onAjax</find>
<find>onAjaxReadyStateChange</find>
<find>onAjaxEvent</find>
<find>shouldInterceptAjaxRequest</find>
<find>shouldInterceptFetchRequest</find>
</findStrings>
<replaceStrings>
<replace>activity.getPreferences(0)</replace>
@ -160,6 +197,8 @@
<replace>ChannelManager</replace>
<replace>Function</replace>
<replace>?.toMap() ?? {});</replace>
<replace>javascriptEnabled</replace>
<replace>[InAppWebView]\</replace>
</replaceStrings>
<dirStrings>
<dir>$PROJECT_DIR$/example/android</dir>
@ -199,31 +238,31 @@
<option value="$PROJECT_DIR$/example/lib/main.dart" />
<option value="$PROJECT_DIR$/lib/src/credentials_database.dart" />
<option value="$PROJECT_DIR$/lib/flutter_inappbrowser.dart" />
<option value="$PROJECT_DIR$/ios/flutter_inappbrowser.podspec" />
<option value="$PROJECT_DIR$/lib/src/channel_manager.dart" />
<option value="$PROJECT_DIR$/lib/src/cookie_manager.dart" />
<option value="$PROJECT_DIR$/lib/src/http_auth_credentials_database.dart" />
<option value="$PROJECT_DIR$/android/build.gradle" />
<option value="$PROJECT_DIR$/nodejs_server_test_auth_basic_and_ssl/index.js" />
<option value="$PROJECT_DIR$/example/assets/index.html" />
<option value="$PROJECT_DIR$/lib/t_rex_runner/index.js" />
<option value="$PROJECT_DIR$/lib/t_rex_runner/index.css" />
<option value="$PROJECT_DIR$/lib/t_rex_runner/index.html" />
<option value="$PROJECT_DIR$/lib/t_rex_runner/t-rex.html" />
<option value="$PROJECT_DIR$/pubspec.yaml" />
<option value="$PROJECT_DIR$/lib/t_rex_runner/t-rex.css" />
<option value="$PROJECT_DIR$/CHANGELOG.md" />
<option value="$PROJECT_DIR$/example/lib/test.dart" />
<option value="$PROJECT_DIR$/example/lib/webview_example.screen.dart" />
<option value="$PROJECT_DIR$/lib/src/content_blocker.dart" />
<option value="$PROJECT_DIR$/lib/src/types.dart" />
<option value="$PROJECT_DIR$/lib/src/in_app_webview.dart" />
<option value="$PROJECT_DIR$/lib/src/in_app_browser.dart" />
<option value="$PROJECT_DIR$/lib/src/chrome_safari_browser.dart" />
<option value="$PROJECT_DIR$/example/lib/chrome_safari_example.screen.dart" />
<option value="$PROJECT_DIR$/example/pubspec.yaml" />
<option value="$PROJECT_DIR$/example/lib/inline_example.screen.dart" />
<option value="$PROJECT_DIR$/ios/flutter_inappbrowser.podspec" />
<option value="$PROJECT_DIR$/lib/src/types.dart" />
<option value="$PROJECT_DIR$/nodejs_server_test_auth_basic_and_ssl/index.js" />
<option value="$PROJECT_DIR$/lib/src/in_app_webview.dart" />
<option value="$PROJECT_DIR$/example/assets/index.html" />
<option value="$PROJECT_DIR$/lib/src/webview_options.dart" />
<option value="$PROJECT_DIR$/CHANGELOG.md" />
<option value="$PROJECT_DIR$/example/lib/inline_example.screen.dart" />
</list>
</option>
</component>
@ -238,6 +277,77 @@
<foldersAlwaysOnTop value="true" />
</navigator>
<panes>
<pane id="ProjectPane">
<subPane>
<expand>
<path>
<item name="flutter_inappbrowser" type="b2602c69:ProjectViewProjectNode" />
<item name="flutter_inappbrowser" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="flutter_inappbrowser" type="b2602c69:ProjectViewProjectNode" />
<item name="flutter_inappbrowser" type="462c0819:PsiDirectoryNode" />
<item name="example" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="flutter_inappbrowser" type="b2602c69:ProjectViewProjectNode" />
<item name="flutter_inappbrowser" type="462c0819:PsiDirectoryNode" />
<item name="example" type="462c0819:PsiDirectoryNode" />
<item name="assets" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="flutter_inappbrowser" type="b2602c69:ProjectViewProjectNode" />
<item name="flutter_inappbrowser" type="462c0819:PsiDirectoryNode" />
<item name="ios" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="flutter_inappbrowser" type="b2602c69:ProjectViewProjectNode" />
<item name="flutter_inappbrowser" type="462c0819:PsiDirectoryNode" />
<item name="lib" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="flutter_inappbrowser" type="b2602c69:ProjectViewProjectNode" />
<item name="flutter_inappbrowser" type="462c0819:PsiDirectoryNode" />
<item name="lib" type="462c0819:PsiDirectoryNode" />
<item name="src" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="flutter_inappbrowser" type="b2602c69:ProjectViewProjectNode" />
<item name="flutter_inappbrowser" type="462c0819:PsiDirectoryNode" />
<item name="lib" type="462c0819:PsiDirectoryNode" />
<item name="t_rex_runner" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="flutter_inappbrowser" type="b2602c69:ProjectViewProjectNode" />
<item name="flutter_inappbrowser" type="462c0819:PsiDirectoryNode" />
<item name="nodejs_server_test_auth_basic_and_ssl" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="flutter_inappbrowser" type="b2602c69:ProjectViewProjectNode" />
<item name="flutter_inappbrowser" type="462c0819:PsiDirectoryNode" />
<item name="nodejs_server_test_auth_basic_and_ssl" type="462c0819:PsiDirectoryNode" />
<item name="assets" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="flutter_inappbrowser" type="b2602c69:ProjectViewProjectNode" />
<item name="External Libraries" type="cb654da1:ExternalLibrariesNode" />
</path>
</expand>
<select />
</subPane>
</pane>
<pane id="PackagesPane" />
<pane id="Scope">
<subPane subId="Project Files">
<expand>
<path>
<item name="Root" type="cbb8eebc:String" user="Root" />
<item name="flutter_inappbrowser" type="cbb8eebc:String" user="flutter_inappbrowser" />
</path>
</expand>
<select />
</subPane>
</pane>
<pane id="AndroidView">
<subPane>
<expand>
@ -267,61 +377,6 @@
<select />
</subPane>
</pane>
<pane id="PackagesPane" />
<pane id="ProjectPane">
<subPane>
<expand>
<path>
<item name="flutter_inappbrowser" type="b2602c69:ProjectViewProjectNode" />
<item name="flutter_inappbrowser" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="flutter_inappbrowser" type="b2602c69:ProjectViewProjectNode" />
<item name="flutter_inappbrowser" type="462c0819:PsiDirectoryNode" />
<item name="example" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="flutter_inappbrowser" type="b2602c69:ProjectViewProjectNode" />
<item name="flutter_inappbrowser" type="462c0819:PsiDirectoryNode" />
<item name="example" type="462c0819:PsiDirectoryNode" />
<item name="lib" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="flutter_inappbrowser" type="b2602c69:ProjectViewProjectNode" />
<item name="flutter_inappbrowser" type="462c0819:PsiDirectoryNode" />
<item name="lib" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="flutter_inappbrowser" type="b2602c69:ProjectViewProjectNode" />
<item name="flutter_inappbrowser" type="462c0819:PsiDirectoryNode" />
<item name="lib" type="462c0819:PsiDirectoryNode" />
<item name="src" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="flutter_inappbrowser" type="b2602c69:ProjectViewProjectNode" />
<item name="flutter_inappbrowser" type="462c0819:PsiDirectoryNode" />
<item name="lib" type="462c0819:PsiDirectoryNode" />
<item name="t_rex_runner" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="flutter_inappbrowser" type="b2602c69:ProjectViewProjectNode" />
<item name="External Libraries" type="cb654da1:ExternalLibrariesNode" />
</path>
</expand>
<select />
</subPane>
</pane>
<pane id="Scope">
<subPane subId="Project Files">
<expand>
<path>
<item name="Root" type="cbb8eebc:String" user="Root" />
<item name="flutter_inappbrowser" type="cbb8eebc:String" user="flutter_inappbrowser" />
</path>
</expand>
<select />
</subPane>
</pane>
</panes>
</component>
<component name="PropertiesComponent">
@ -341,13 +396,6 @@
<property name="show.migrate.to.gradle.popup" value="false" />
</component>
<component name="RecentsManager">
<key name="MoveFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$/lib" />
<recent name="$PROJECT_DIR$/example/assets" />
<recent name="$PROJECT_DIR$/lib/src" />
<recent name="$PROJECT_DIR$/lib/in_app_browser.dart" />
<recent name="$PROJECT_DIR$/example/assets/images" />
</key>
<key name="CopyFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$/example/assets" />
<recent name="$PROJECT_DIR$" />
@ -355,6 +403,13 @@
<recent name="$PROJECT_DIR$/example/assets/images" />
<recent name="$PROJECT_DIR$/android/libs" />
</key>
<key name="MoveFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$/nodejs_server_test_auth_basic_and_ssl/assets" />
<recent name="$PROJECT_DIR$/lib" />
<recent name="$PROJECT_DIR$/example/assets" />
<recent name="$PROJECT_DIR$/lib/src" />
<recent name="$PROJECT_DIR$/lib/in_app_browser.dart" />
</key>
</component>
<component name="RunDashboard">
<option name="ruleStates">
@ -486,7 +541,7 @@
<window_info id="Resources Explorer" order="8" />
<window_info anchor="bottom" id="Message" order="0" />
<window_info anchor="bottom" id="Find" order="1" weight="0.32642487" />
<window_info active="true" anchor="bottom" id="Run" order="2" sideWeight="0.49574015" visible="true" weight="0.5160622" />
<window_info anchor="bottom" id="Run" order="2" sideWeight="0.49574015" weight="0.5181347" />
<window_info anchor="bottom" id="Debug" order="3" weight="0.34196892" />
<window_info anchor="bottom" id="Cvs" order="4" weight="0.25" />
<window_info anchor="bottom" id="Inspection" order="5" weight="0.4" />
@ -494,7 +549,7 @@
<window_info anchor="bottom" id="Android Profiler" order="7" show_stripe_button="false" />
<window_info anchor="bottom" id="Event Log" order="8" sideWeight="0.50692225" side_tool="true" weight="0.38445595" />
<window_info anchor="bottom" id="Version Control" order="9" weight="0.32953367" />
<window_info anchor="bottom" id="Terminal" order="10" sideWeight="0.49533224" weight="0.34507772" />
<window_info active="true" anchor="bottom" id="Terminal" order="10" sideWeight="0.49533224" visible="true" weight="0.37823835" />
<window_info anchor="bottom" id="Logcat" order="11" weight="0.32953367" />
<window_info anchor="bottom" id="Messages" order="12" weight="0.3253886" />
<window_info anchor="bottom" id="Dependency Viewer" order="13" weight="0.32800853" />
@ -524,25 +579,11 @@
</ignored-roots>
</component>
<component name="editorHistoryManager">
<entry file="file://$PROJECT_DIR$/lib/flutter_inappbrowser.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="195">
<caret line="32" column="47" selection-start-line="32" selection-start-column="47" selection-end-line="32" selection-end-column="47" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/example/assets/client1-crt.pem" />
<entry file="file://$PROJECT_DIR$/example/assets/client1-crt.crt" />
<entry file="file://$PROJECT_DIR$/pubspec.lock">
<provider selected="true" editor-type-id="text-editor" />
</entry>
<entry file="file://$PROJECT_DIR$/ios/flutter_inappbrowser.podspec">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="315">
<caret line="21" column="25" selection-start-line="21" selection-start-column="25" selection-end-line="21" selection-end-column="25" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/.gitignore">
<provider selected="true" editor-type-id="text-editor" />
</entry>
@ -607,13 +648,6 @@
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/nodejs_server_test_auth_basic_and_ssl/index.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="733">
<caret line="111" column="34" selection-start-line="111" selection-start-column="34" selection-end-line="111" selection-end-column="34" />
</state>
</provider>
</entry>
<entry file="file://$USER_HOME$/flutter/bin/cache/pkg/sky_engine/lib/io/file.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-1206">
@ -638,13 +672,6 @@
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/example/assets/index.html">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="240">
<caret line="34" column="87" selection-start-line="34" selection-start-column="87" selection-end-line="34" selection-end-column="87" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/lib/t_rex_runner/assets/novas-coisas.gif" />
<entry file="file://$PROJECT_DIR$/lib/t_rex_runner/assets/t-rex-runner-19janil.gif" />
<entry file="file://$PROJECT_DIR$/lib/t_rex_runner/assets/t-rex-runner-bot.gif" />
@ -679,23 +706,6 @@
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/example/lib/test.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="827">
<caret line="402" column="63" selection-start-line="402" selection-start-column="63" selection-end-line="402" selection-end-column="63" />
<folding>
<element signature="e#0#16406#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/CHANGELOG.md">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="408">
<caret line="29" column="8" selection-start-line="29" selection-start-column="8" selection-end-line="29" selection-end-column="8" />
</state>
</provider>
</entry>
<entry file="file://$USER_HOME$/flutter/bin/cache/dart-sdk/lib/collection/linked_hash_map.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="137">
@ -740,9 +750,25 @@
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/example/build/app/outputs/apk/debug/output.json">
<provider selected="true" editor-type-id="text-editor" />
</entry>
<entry file="file://$PROJECT_DIR$/flutter_inappbrowser.iml">
<provider selected="true" editor-type-id="text-editor" />
</entry>
<entry file="file://$PROJECT_DIR$/example/lib/test.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="298">
<caret line="127" column="53" selection-start-line="127" selection-start-column="37" selection-end-line="127" selection-end-column="53" />
<folding>
<element signature="e#12489#12509#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/lib/src/in_app_browser.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="239">
<state relative-caret-position="896">
<caret line="287" column="5" selection-start-line="276" selection-start-column="4" selection-end-line="287" selection-end-column="5" />
<folding>
<element signature="e#0#20#0" expanded="true" />
@ -750,30 +776,38 @@
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/lib/src/types.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="215">
<caret line="658" column="30" selection-start-line="658" selection-start-column="6" selection-end-line="658" selection-end-column="30" />
<folding>
<element signature="e#0#20#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/example/build/app/outputs/apk/debug/output.json">
<provider selected="true" editor-type-id="text-editor" />
</entry>
<entry file="file://$PROJECT_DIR$/example/pubspec.yaml">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="131">
<caret line="24" column="27" selection-start-line="24" selection-start-column="27" selection-end-line="24" selection-end-column="27" />
<state relative-caret-position="116">
<caret line="23" column="28" selection-start-line="23" selection-start-column="28" selection-end-line="23" selection-end-column="28" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/example/lib/inline_example.screen.dart">
<entry file="file://$PROJECT_DIR$/ios/flutter_inappbrowser.podspec">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="28">
<caret line="94" column="32" selection-start-line="94" selection-start-column="16" selection-end-line="94" selection-end-column="32" />
<state relative-caret-position="300">
<caret line="20" column="25" selection-start-line="20" selection-start-column="25" selection-end-line="20" selection-end-column="25" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/nodejs_server_test_auth_basic_and_ssl/index.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-11">
<caret line="61" selection-start-line="61" selection-end-line="61" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/example/assets/index.html">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="423">
<caret line="80" column="26" selection-start-line="78" selection-start-column="12" selection-end-line="80" selection-end-column="26" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/lib/src/types.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-319">
<caret line="776" column="32" selection-start-line="776" selection-start-column="32" selection-end-line="776" selection-end-column="32" />
<folding>
<element signature="e#0#20#0" expanded="true" />
</folding>
@ -782,8 +816,8 @@
</entry>
<entry file="file://$PROJECT_DIR$/lib/src/in_app_webview.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="251">
<caret line="265" column="89" selection-start-line="261" selection-start-column="4" selection-end-line="265" selection-end-column="89" />
<state relative-caret-position="429">
<caret line="628" column="43" selection-start-line="628" selection-start-column="16" selection-end-line="628" selection-end-column="43" />
<folding>
<element signature="e#0#17#0" expanded="true" />
</folding>
@ -792,14 +826,31 @@
</entry>
<entry file="file://$PROJECT_DIR$/lib/src/webview_options.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="364">
<caret line="81" column="43" selection-start-line="81" selection-start-column="43" selection-end-line="81" selection-end-column="43" />
<state relative-caret-position="371">
<caret line="94" column="37" selection-start-line="94" selection-start-column="7" selection-end-line="94" selection-end-column="37" />
<folding>
<element signature="e#0#17#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/CHANGELOG.md">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="435">
<caret line="29" selection-start-line="29" selection-end-line="29" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/example/lib/inline_example.screen.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="164">
<caret line="89" column="55" selection-start-line="89" selection-start-column="55" selection-end-line="89" selection-end-column="55" />
<folding>
<element signature="e#0#20#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
</component>
<component name="masterDetails">
<states>

View File

@ -26,7 +26,8 @@
- Added `HttpAuthCredentialDatabase` class
- Added `onReceivedServerTrustAuthRequest` and `onReceivedClientCertRequest` events to manage SSL requests
- Added `onFindResultReceived` event, `findAllAsync`, `findNext` and `clearMatches` methods
- Added `getHtml` method
- Added `getHtml`, `injectJavascriptFileFromAsset` and `injectCSSFileFromAsset` methods
- Added `shouldInterceptAjaxRequest`, `onAjaxReadyStateChange`, `onAjaxProgressEvent` and `shouldInterceptFetchRequest` events with `useShouldInterceptAjaxRequest` and `useShouldInterceptFetchRequest` webview options
- Fun: added `getTRexRunnerHtml` and `getTRexRunnerCss` methods to get html (with javascript) and css to recreate the Chromium's t-rex runner game
### BREAKING CHANGES
@ -37,6 +38,10 @@
- Updated `CookieManager` class
- WebView options are now available with the new corresponding classes: `InAppWebViewOptions`, `AndroidInAppWebViewOptions`, `iOSInAppWebViewOptions`, `InAppBrowserOptions`, `AndroidInAppBrowserOptions`, `iOSInAppBrowserOptions`, `AndroidChromeCustomTabsOptions` and `iOSSafariOptions`
- Renamed `getFavicon` to `getFavicons`, now it returns a list of all favicons (`List<Favicon>`) found
- Renamed `injectScriptFile` to `injectJavascriptFileFromUrl`
- Renamed `injectScriptCode` to `evaluateJavascript`
- Renamed `injectStyleCode` to `injectCSSCode`
- Renamed `injectStyleFile` to `injectCSSFileFromUrl`
## 1.2.1

View File

@ -21,6 +21,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.regex.Matcher;
import io.flutter.plugin.common.MethodChannel;
import okhttp3.Request;
import okhttp3.Response;
@ -145,7 +146,7 @@ public class ContentBlockerHandler {
@Override
public void run() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
webView.evaluateJavascript(jsScript, null);
webView.evaluateJavascript(jsScript, (MethodChannel.Result) null);
} else {
webView.loadUrl("javascript:" + jsScript);
}

View File

@ -1,11 +1,9 @@
package com.pichillilorenzo.flutter_inappbrowser;
import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.hardware.display.DisplayManager;
import android.os.Build;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.webkit.WebChromeClient;
@ -15,21 +13,20 @@ import android.webkit.WebViewClient;
import com.pichillilorenzo.flutter_inappbrowser.InAppWebView.DisplayListenerProxy;
import com.pichillilorenzo.flutter_inappbrowser.InAppWebView.InAppWebView;
import com.pichillilorenzo.flutter_inappbrowser.InAppWebView.InAppWebViewOptions;
import com.pichillilorenzo.flutter_inappbrowser.InAppWebView.InputAwareWebView;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import static io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import static io.flutter.plugin.common.MethodChannel.Result;
import io.flutter.plugin.common.PluginRegistry.Registrar;
import io.flutter.plugin.platform.PlatformView;
import static io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import static io.flutter.plugin.common.MethodChannel.Result;
public class FlutterWebView implements PlatformView, MethodCallHandler {
static final String LOG_TAG = "FlutterWebView";
@ -135,33 +132,33 @@ public class FlutterWebView implements PlatformView, MethodCallHandler {
else
result.success(false);
break;
case "injectScriptCode":
case "evaluateJavascript":
if (webView != null) {
String source = (String) call.argument("source");
webView.injectScriptCode(source, result);
webView.evaluateJavascript(source, result);
}
else {
result.success("");
}
break;
case "injectScriptFile":
case "injectJavascriptFileFromUrl":
if (webView != null) {
String urlFile = (String) call.argument("urlFile");
webView.injectScriptFile(urlFile);
webView.injectJavascriptFileFromUrl(urlFile);
}
result.success(true);
break;
case "injectStyleCode":
case "injectCSSCode":
if (webView != null) {
String source = (String) call.argument("source");
webView.injectStyleCode(source);
webView.injectCSSCode(source);
}
result.success(true);
break;
case "injectStyleFile":
case "injectCSSFileFromUrl":
if (webView != null) {
String urlFile = (String) call.argument("urlFile");
webView.injectStyleFile(urlFile);
webView.injectCSSFileFromUrl(urlFile);
}
result.success(true);
break;
@ -312,13 +309,13 @@ public class FlutterWebView implements PlatformView, MethodCallHandler {
@Override
public void onInputConnectionLocked() {
if (webView.inAppBrowserActivity == null)
if (webView != null && webView.inAppBrowserActivity == null)
webView.lockInputConnection();
}
@Override
public void onInputConnectionUnlocked() {
if (webView.inAppBrowserActivity == null)
if (webView != null && webView.inAppBrowserActivity == null)
webView.unlockInputConnection();
}

View File

@ -192,23 +192,23 @@ public class InAppBrowser implements MethodChannel.MethodCallHandler {
case "close":
close(activity, uuid, result);
break;
case "injectScriptCode":
case "evaluateJavascript":
source = (String) call.argument("source");
injectScriptCode(uuid, source, result);
evaluateJavascript(uuid, source, result);
break;
case "injectScriptFile":
case "injectJavascriptFileFromUrl":
urlFile = (String) call.argument("urlFile");
injectScriptFile(uuid, urlFile);
injectJavascriptFileFromUrl(uuid, urlFile);
result.success(true);
break;
case "injectStyleCode":
case "injectCSSCode":
source = (String) call.argument("source");
injectStyleCode(uuid, source);
injectCSSCode(uuid, source);
result.success(true);
break;
case "injectStyleFile":
case "injectCSSFileFromUrl":
urlFile = (String) call.argument("urlFile");
injectStyleFile(uuid, urlFile);
injectCSSFileFromUrl(uuid, urlFile);
result.success(true);
break;
case "show":
@ -314,33 +314,33 @@ public class InAppBrowser implements MethodChannel.MethodCallHandler {
}
private void injectScriptCode(String uuid, String source, final Result result) {
private void evaluateJavascript(String uuid, String source, final Result result) {
final InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
if (inAppBrowserActivity != null) {
inAppBrowserActivity.injectScriptCode(source, result);
inAppBrowserActivity.evaluateJavascript(source, result);
} else {
Log.d(LOG_TAG, "webView is null");
}
}
private void injectScriptFile(String uuid, String urlFile) {
private void injectJavascriptFileFromUrl(String uuid, String urlFile) {
final InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
if (inAppBrowserActivity != null) {
inAppBrowserActivity.injectScriptFile(urlFile);
inAppBrowserActivity.injectJavascriptFileFromUrl(urlFile);
}
}
private void injectStyleCode(String uuid, String source) {
private void injectCSSCode(String uuid, String source) {
final InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
if (inAppBrowserActivity != null) {
inAppBrowserActivity.injectStyleCode(source);
inAppBrowserActivity.injectCSSCode(source);
}
}
private void injectStyleFile(String uuid, String urlFile) {
private void injectCSSFileFromUrl(String uuid, String urlFile) {
final InAppBrowserActivity inAppBrowserActivity = webViewActivities.get(uuid);
if (inAppBrowserActivity != null) {
inAppBrowserActivity.injectStyleFile(urlFile);
inAppBrowserActivity.injectCSSFileFromUrl(urlFile);
}
}

View File

@ -434,26 +434,26 @@ public class InAppBrowserActivity extends AppCompatActivity {
return optionsMap;
}
public void injectScriptCode(String source, MethodChannel.Result result) {
public void evaluateJavascript(String source, MethodChannel.Result result) {
if (webView != null)
webView.injectScriptCode(source, result);
webView.evaluateJavascript(source, result);
else
result.success("");
}
public void injectScriptFile(String urlFile) {
public void injectJavascriptFileFromUrl(String urlFile) {
if (webView != null)
webView.injectScriptFile(urlFile);
webView.injectJavascriptFileFromUrl(urlFile);
}
public void injectStyleCode(String source) {
public void injectCSSCode(String source) {
if (webView != null)
webView.injectStyleCode(source);
webView.injectCSSCode(source);
}
public void injectStyleFile(String urlFile) {
public void injectCSSFileFromUrl(String urlFile) {
if (webView != null)
webView.injectStyleFile(urlFile);
webView.injectCSSFileFromUrl(urlFile);
}
public HashMap<String, Object> getCopyBackForwardList() {

View File

@ -59,7 +59,7 @@ final public class InAppWebView extends InputAwareWebView {
int okHttpClientCacheSize = 10 * 1024 * 1024; // 10MB
public ContentBlockerHandler contentBlockerHandler = new ContentBlockerHandler();
static final String consoleLogJS = "(function() {" +
static final String consoleLogJS = "(function(console) {" +
" var oldLogs = {" +
" 'log': console.log," +
" 'debug': console.debug," +
@ -83,12 +83,12 @@ final public class InAppWebView extends InputAwareWebView {
" }" +
" })(k);" +
" }" +
"})();";
"})(window.console);";
static final String resourceObserverJS = "(function() {" +
" var observer = new PerformanceObserver(function(list) {" +
" list.getEntries().forEach(function(entry) {" +
" window." + JavaScriptBridgeInterface.name + "._resourceLoaded(JSON.stringify(entry));" +
" window." + JavaScriptBridgeInterface.name + ".callHandler('onLoadResource', entry);" +
" });" +
" });" +
" observer.observe({entryTypes: ['resource']});" +
@ -96,6 +96,228 @@ final public class InAppWebView extends InputAwareWebView {
static final String platformReadyJS = "window.dispatchEvent(new Event('flutterInAppBrowserPlatformReady'));";
static final String interceptAjaxRequestsJS = "(function(ajax) {" +
" var send = ajax.prototype.send;" +
" var open = ajax.prototype.open;" +
" var setRequestHeader = ajax.prototype.setRequestHeader;" +
" ajax.prototype._flutter_inappbrowser_url = null;" +
" ajax.prototype._flutter_inappbrowser_method = null;" +
" ajax.prototype._flutter_inappbrowser_isAsync = null;" +
" ajax.prototype._flutter_inappbrowser_user = null;" +
" ajax.prototype._flutter_inappbrowser_password = null;" +
" ajax.prototype._flutter_inappbrowser_password = null;" +
" ajax.prototype._flutter_inappbrowser_request_headers = {};" +
" ajax.prototype.open = function(method, url, isAsync, user, password) {" +
" isAsync = (isAsync != null) ? isAsync : true;" +
" this._flutter_inappbrowser_url = url;" +
" this._flutter_inappbrowser_method = method;" +
" this._flutter_inappbrowser_isAsync = isAsync;" +
" this._flutter_inappbrowser_user = user;" +
" this._flutter_inappbrowser_password = password;" +
" open.call(this, method, url, isAsync, user, password);" +
" };" +
" ajax.prototype.setRequestHeader = function(header, value) {" +
" this._flutter_inappbrowser_request_headers[header] = value;" +
" setRequestHeader.call(this, header, value);" +
" };" +
" function handleEvent(e) {" +
" var self = this;" +
" var headers = this.getAllResponseHeaders();" +
" var responseHeaders = {};" +
" if (headers != null) {" +
" var arr = headers.trim().split(/[\\r\\n]+/);" +
" arr.forEach(function (line) {" +
" var parts = line.split(': ');" +
" var header = parts.shift();" +
" var value = parts.join(': ');" +
" responseHeaders[header] = value;" +
" });" +
" }" +
" var ajaxRequest = {" +
" method: this._flutter_inappbrowser_method," +
" url: this._flutter_inappbrowser_url," +
" isAsync: this._flutter_inappbrowser_isAsync," +
" user: this._flutter_inappbrowser_user," +
" password: this._flutter_inappbrowser_password," +
" withCredentials: this.withCredentials," +
" headers: this._flutter_inappbrowser_request_headers," +
" readyState: this.readyState," +
" status: this.status," +
" responseURL: this.responseURL," +
" responseType: this.responseType," +
" responseText: this.responseText," +
" statusText: this.statusText," +
" responseHeaders, responseHeaders," +
" event: {" +
" type: e.type," +
" loaded: e.loaded," +
" lengthComputable: e.lengthComputable" +
" }" +
" };" +
" window." + JavaScriptBridgeInterface.name + ".callHandler('onAjaxProgressEvent', ajaxRequest).then(function(result) {" +
" if (result != null) {" +
" switch (result.action) {" +
" case 0:" +
" self.abort();" +
" return;" +
" };" +
" }" +
" });" +
" };" +
" ajax.prototype.send = function(data) {" +
" var self = this;" +
" var onreadystatechange = this.onreadystatechange;" +
" this.onreadystatechange = function() {" +
" var headers = this.getAllResponseHeaders();" +
" var responseHeaders = {};" +
" if (headers != null) {" +
" var arr = headers.trim().split(/[\\r\\n]+/);" +
" arr.forEach(function (line) {" +
" var parts = line.split(': ');" +
" var header = parts.shift();" +
" var value = parts.join(': ');" +
" responseHeaders[header] = value;" +
" });" +
" }" +
" var ajaxRequest = {" +
" method: this._flutter_inappbrowser_method," +
" url: this._flutter_inappbrowser_url," +
" isAsync: this._flutter_inappbrowser_isAsync," +
" user: this._flutter_inappbrowser_user," +
" password: this._flutter_inappbrowser_password," +
" withCredentials: this.withCredentials," +
" headers: this._flutter_inappbrowser_request_headers," +
" readyState: this.readyState," +
" status: this.status," +
" responseURL: this.responseURL," +
" responseType: this.responseType," +
" responseText: this.responseText," +
" statusText: this.statusText," +
" responseHeaders: responseHeaders" +
" };" +
" window." + JavaScriptBridgeInterface.name + ".callHandler('onAjaxReadyStateChange', ajaxRequest).then(function(result) {" +
" if (result != null) {" +
" switch (result.action) {" +
" case 0:" +
" self.abort();" +
" return;" +
" };" +
" }" +
" if (onreadystatechange != null) {" +
" onreadystatechange();" +
" }" +
" });" +
" };" +
" this.addEventListener('loadstart', handleEvent);" +
" this.addEventListener('load', handleEvent);" +
" this.addEventListener('loadend', handleEvent);" +
" this.addEventListener('progress', handleEvent);" +
" this.addEventListener('error', handleEvent);" +
" this.addEventListener('abort', handleEvent);" +
" var ajaxRequest = {" +
" data: data," +
" method: this._flutter_inappbrowser_method," +
" url: this._flutter_inappbrowser_url," +
" isAsync: this._flutter_inappbrowser_isAsync," +
" user: this._flutter_inappbrowser_user," +
" password: this._flutter_inappbrowser_password," +
" withCredentials: this.withCredentials," +
" headers: this._flutter_inappbrowser_request_headers" +
" };" +
" window." + JavaScriptBridgeInterface.name + ".callHandler('shouldInterceptAjaxRequest', ajaxRequest).then(function(result) {" +
" if (result != null) {" +
" switch (result.action) {" +
" case 0:" +
" self.abort();" +
" return;" +
" };" +
" data = result.data;" +
" self.withCredentials = result.withCredentials;" +
" for (var header in result.headers) {" +
" var value = result.headers[header];" +
" self.setRequestHeader(header, value);" +
" };" +
" if ((self._flutter_inappbrowser_method != result.method && result.method != null) || (self._flutter_inappbrowser_url != result.url && result.url != null)) {" +
" self.abort();" +
" self.open(result.method, result.url, result.isAsync, result.user, result.password);" +
" return;" +
" }" +
" }" +
" send.call(self, data);" +
" });" +
" };" +
"})(window.XMLHttpRequest);";
static final String interceptFetchRequestsJS = "(function(fetch) {" +
" if (fetch == null) {" +
" return;" +
" }" +
" window.fetch = function(resource, init) {" +
" var fetchRequest = {" +
" url: null," +
" method: null," +
" headers: null," +
" body: null," +
" mode: null," +
" credentials: null," +
" cache: null," +
" redirect: null," +
" referrer: null," +
" referrerPolicy: null," +
" integrity: null," +
" keepalive: null" +
" };" +
" if (resource instanceof Request) {" +
" fetchRequest.url = resource.url;" +
" fetchRequest.method = resource.method;" +
" fetchRequest.headers = resource.headers;" +
" fetchRequest.body = resource.body;" +
" fetchRequest.mode = resource.mode;" +
" fetchRequest.credentials = resource.credentials;" +
" fetchRequest.cache = resource.cache;" +
" fetchRequest.redirect = resource.redirect;" +
" fetchRequest.referrer = resource.referrer;" +
" fetchRequest.referrerPolicy = resource.referrerPolicy;" +
" fetchRequest.integrity = resource.integrity;" +
" fetchRequest.keepalive = resource.keepalive;" +
" } else {" +
" fetchRequest.url = resource;" +
" if (init != null) {" +
" fetchRequest.method = init.method;" +
" fetchRequest.headers = init.headers;" +
" fetchRequest.body = init.body;" +
" fetchRequest.mode = init.mode;" +
" fetchRequest.credentials = init.credentials;" +
" fetchRequest.cache = init.cache;" +
" fetchRequest.redirect = init.redirect;" +
" fetchRequest.referrer = init.referrer;" +
" fetchRequest.referrerPolicy = init.referrerPolicy;" +
" fetchRequest.integrity = init.integrity;" +
" fetchRequest.keepalive = init.keepalive;" +
" }" +
" }" +
" return window." + JavaScriptBridgeInterface.name + ".callHandler('shouldInterceptFetchRequest', fetchRequest).then(function(result) {" +
" if (result != null) {" +
" switch (result.action) {" +
" case 0:" +
" var controller = new AbortController();" +
" if (init != null) {" +
" init.signal = controller.signal;" +
" } else {" +
" init = {" +
" signal: controller.signal" +
" };" +
" }" +
" controller.abort();" +
" break;" +
" }" +
" }" +
" return fetch(resource, init);" +
" });" +
" };" +
"})(window.fetch);";
public InAppWebView(Context context) {
super(context);
}
@ -624,22 +846,22 @@ final public class InAppWebView extends InputAwareWebView {
});
}
public void injectScriptCode(String source, MethodChannel.Result result) {
public void evaluateJavascript(String source, MethodChannel.Result result) {
String jsWrapper = "(function(){return JSON.stringify(eval(%s));})();";
injectDeferredObject(source, jsWrapper, result);
}
public void injectScriptFile(String urlFile) {
public void injectJavascriptFileFromUrl(String urlFile) {
String jsWrapper = "(function(d) { var c = d.createElement('script'); c.src = %s; d.body.appendChild(c); })(document);";
injectDeferredObject(urlFile, jsWrapper, null);
}
public void injectStyleCode(String source) {
public void injectCSSCode(String source) {
String jsWrapper = "(function(d) { var c = d.createElement('style'); c.innerHTML = %s; d.body.appendChild(c); })(document);";
injectDeferredObject(source, jsWrapper, null);
}
public void injectStyleFile(String urlFile) {
public void injectCSSFileFromUrl(String urlFile) {
String jsWrapper = "(function(d) { var c = d.createElement('link'); c.rel='stylesheet'; c.type='text/css'; c.href = %s; d.head.appendChild(c); })(document);";
injectDeferredObject(urlFile, jsWrapper, null);
}

View File

@ -7,7 +7,6 @@ import android.net.http.SslCertificate;
import android.net.http.SslError;
import android.os.Build;
import android.os.Bundle;
import android.util.Base64;
import android.util.Log;
import android.webkit.ClientCertRequest;
import android.webkit.CookieManager;
@ -15,7 +14,6 @@ import android.webkit.CookieSyncManager;
import android.webkit.HttpAuthHandler;
import android.webkit.SafeBrowsingResponse;
import android.webkit.SslErrorHandler;
import android.webkit.ValueCallback;
import android.webkit.WebResourceRequest;
import android.webkit.WebResourceResponse;
import android.webkit.WebView;
@ -61,60 +59,7 @@ public class InAppWebViewClient extends WebViewClient {
this.inAppBrowserActivity = (InAppBrowserActivity) obj;
else if (obj instanceof FlutterWebView)
this.flutterWebView = (FlutterWebView) obj;
prepareStatusCodeMapping();
}
private void prepareStatusCodeMapping() {
statusCodeMapping.put(100, "Continue");
statusCodeMapping.put(101, "Switching Protocols");
statusCodeMapping.put(200, "OK");
statusCodeMapping.put(201, "Created");
statusCodeMapping.put(202, "Accepted");
statusCodeMapping.put(203, "Non-Authoritative Information");
statusCodeMapping.put(204, "No Content");
statusCodeMapping.put(205, "Reset Content");
statusCodeMapping.put(206, "Partial Content");
statusCodeMapping.put(300, "Multiple Choices");
statusCodeMapping.put(301, "Moved Permanently");
statusCodeMapping.put(302, "Found");
statusCodeMapping.put(303, "See Other");
statusCodeMapping.put(304, "Not Modified");
statusCodeMapping.put(307, "Temporary Redirect");
statusCodeMapping.put(308, "Permanent Redirect");
statusCodeMapping.put(400, "Bad Request");
statusCodeMapping.put(401, "Unauthorized");
statusCodeMapping.put(403, "Forbidden");
statusCodeMapping.put(404, "Not Found");
statusCodeMapping.put(405, "Method Not Allowed");
statusCodeMapping.put(406, "Not Acceptable");
statusCodeMapping.put(407, "Proxy Authentication Required");
statusCodeMapping.put(408, "Request Timeout");
statusCodeMapping.put(409, "Conflict");
statusCodeMapping.put(410, "Gone");
statusCodeMapping.put(411, "Length Required");
statusCodeMapping.put(412, "Precondition Failed");
statusCodeMapping.put(413, "Payload Too Large");
statusCodeMapping.put(414, "URI Too Long");
statusCodeMapping.put(415, "Unsupported Media Type");
statusCodeMapping.put(416, "Range Not Satisfiable");
statusCodeMapping.put(417, "Expectation Failed");
statusCodeMapping.put(418, "I'm a teapot");
statusCodeMapping.put(422, "Unprocessable Entity");
statusCodeMapping.put(425, "Too Early");
statusCodeMapping.put(426, "Upgrade Required");
statusCodeMapping.put(428, "Precondition Required");
statusCodeMapping.put(429, "Too Many Requests");
statusCodeMapping.put(431, "Request Header Fields Too Large");
statusCodeMapping.put(451, "Unavailable For Legal Reasons");
statusCodeMapping.put(500, "Internal Server Error");
statusCodeMapping.put(501, "Not Implemented");
statusCodeMapping.put(502, "Bad Gateway");
statusCodeMapping.put(503, "Service Unavailable");
statusCodeMapping.put(504, "Gateway Timeout");
statusCodeMapping.put(505, "HTTP Version Not Supported");
statusCodeMapping.put(511, "Network Authentication Required");
}
@Override
public boolean shouldOverrideUrlLoading(WebView webView, String url) {
@ -183,21 +128,22 @@ public class InAppWebViewClient extends WebViewClient {
return super.shouldOverrideUrlLoading(webView, url);
}
/*
* onPageStarted fires the LOAD_START_EVENT
*
* @param view
* @param url
* @param favicon
*/
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
InAppWebView webView = (InAppWebView) view;
if (webView.options.useOnLoadResource)
webView.loadUrl("javascript:" + webView.resourceObserverJS.replaceAll("[\r\n]+", ""));
webView.loadUrl("javascript:" + InAppWebView.consoleLogJS.replaceAll("[\r\n]+", ""));
webView.loadUrl("javascript:" + JavaScriptBridgeInterface.flutterInAppBroserJSClass.replaceAll("[\r\n]+", ""));
if (webView.options.useShouldInterceptAjaxRequest) {
webView.loadUrl("javascript:" + InAppWebView.interceptAjaxRequestsJS.replaceAll("[\r\n]+", ""));
}
if (webView.options.useShouldInterceptFetchRequest) {
webView.loadUrl("javascript:" + InAppWebView.interceptFetchRequestsJS.replaceAll("[\r\n]+", ""));
}
if (webView.options.useOnLoadResource) {
webView.loadUrl("javascript:" + InAppWebView.resourceObserverJS.replaceAll("[\r\n]+", ""));
}
super.onPageStarted(view, url, favicon);
@ -215,8 +161,8 @@ public class InAppWebViewClient extends WebViewClient {
}
public void onPageFinished(final WebView view, String url) {
InAppWebView webView = (InAppWebView) view;
public void onPageFinished(WebView view, String url) {
final InAppWebView webView = (InAppWebView) view;
super.onPageFinished(view, url);
@ -236,18 +182,9 @@ public class InAppWebViewClient extends WebViewClient {
view.requestFocus();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
view.evaluateJavascript(InAppWebView.consoleLogJS, null);
view.evaluateJavascript(JavaScriptBridgeInterface.flutterInAppBroserJSClass, new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
view.evaluateJavascript(InAppWebView.platformReadyJS, null);
}
});
webView.evaluateJavascript(InAppWebView.platformReadyJS, (MethodChannel.Result) null);
} else {
view.loadUrl("javascript:" + InAppWebView.consoleLogJS);
view.loadUrl("javascript:" + JavaScriptBridgeInterface.flutterInAppBroserJSClass);
view.loadUrl("javascript:" + InAppWebView.platformReadyJS);
webView.loadUrl("javascript:" + InAppWebView.platformReadyJS.replaceAll("[\r\n]+", ""));
}
Map<String, Object> obj = new HashMap<>();

View File

@ -29,6 +29,8 @@ public class InAppWebViewOptions extends Options {
public List<String> resourceCustomSchemes = new ArrayList<>();
public List<Map<String, Map<String, Object>>> contentBlockers = new ArrayList<>();
public Integer preferredContentMode = PreferredContentModeOptionType.RECOMMENDED.toValue();
public Boolean useShouldInterceptAjaxRequest = false;
public Boolean useShouldInterceptFetchRequest = false;
public boolean clearSessionCache = false;
public boolean builtInZoomControls = false;

View File

@ -61,7 +61,7 @@ public class JavaScriptBridgeInterface {
return;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
webView.evaluateJavascript("window." + name + "[" + _callHandlerID + "](" + json + "); delete window." + name + "[" + _callHandlerID + "];", null);
webView.evaluateJavascript("window." + name + "[" + _callHandlerID + "](" + json + "); delete window." + name + "[" + _callHandlerID + "];", (MethodChannel.Result) null);
}
else {
webView.loadUrl("javascript:window." + name + "[" + _callHandlerID + "](" + json + "); delete window." + name + "[" + _callHandlerID + "];");
@ -82,42 +82,6 @@ public class JavaScriptBridgeInterface {
});
}
@JavascriptInterface
public void _resourceLoaded(String json) {
try {
JSONObject jsonObject = new JSONObject(json);
final Map<String, Object> obj = new HashMap<>();
if (inAppBrowserActivity != null)
obj.put("uuid", inAppBrowserActivity.uuid);
String initiatorType = jsonObject.getString("initiatorType");
String url = jsonObject.getString("name");
Double startTime = jsonObject.getDouble("startTime");
Double duration = jsonObject.getDouble("duration");
obj.put("initiatorType", initiatorType);
obj.put("url", url);
obj.put("startTime", startTime);
obj.put("duration", duration);
// java.lang.RuntimeException: Methods marked with @UiThread must be executed on the main thread.
// https://github.com/pichillilorenzo/flutter_inappbrowser/issues/98
final Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
getChannel().invokeMethod("onLoadResource", obj);
}
});
} catch (final JSONException e) {
Log.e(LOG_TAG, "Json parsing error: " + e.getMessage());
}
}
private MethodChannel getChannel() {
return (inAppBrowserActivity != null) ? InAppBrowserFlutterPlugin.inAppBrowser.channel : flutterWebView.channel;
}

View File

@ -55,23 +55,43 @@
console.log("ready");
window.flutter_inappbrowser.callHandler('handlerFoo').then(function(result) {
console.log(result, typeof result);
console.log(JSON.stringify(result), result.bar);
//console.log(result, typeof result);
//console.log(JSON.stringify(result), result.bar);
});
window.flutter_inappbrowser.callHandler('handlerFooWithArgs', 1, true, ['bar', 5], {foo: 'baz'}).then(function(result) {
console.log(result, typeof result);
console.log(JSON.stringify(result));
//console.log(result, typeof result);
//console.log(JSON.stringify(result));
});
});
$(document).ready(function() {
console.log("jQuery ready");
var xhttp = new XMLHttpRequest();
xhttp.addEventListener("load", function() {
console.log(this.responseText);
});
xhttp.open("POST", "http://192.168.1.20:8082/test-ajax-post");
xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhttp.send("name=Lorenzo");
var xhttp2 = new XMLHttpRequest();
xhttp2.open("GET", "http://192.168.1.20:8082/test-download-file");
xhttp2.send();
fetch(new Request("http://192.168.1.20:8082/test-download-file")).then(function(response) {
console.log(response);
}).catch(function(error) {
console.error("ERROR: " + error);
});
/*
alert("Alert Popup");
console.log(confirm("Press a button!"));
console.log(prompt("Please enter your name", "Lorenzo"));
*/
console.log("jQuery ready");
/*
if ("geolocation" in navigator) {
console.log("Geolocation API enabled");

View File

@ -2,9 +2,10 @@
# This is a generated file; do not edit or check into version control.
export "FLUTTER_ROOT=/Users/lorenzopichilli/flutter"
export "FLUTTER_APPLICATION_PATH=/Users/lorenzopichilli/Desktop/flutter_inappbrowser/example"
export "FLUTTER_TARGET=lib/main.dart"
export "FLUTTER_TARGET=/Users/lorenzopichilli/Desktop/flutter_inappbrowser/example/lib/main.dart"
export "FLUTTER_BUILD_DIR=build"
export "SYMROOT=${SOURCE_ROOT}/../build/ios"
export "FLUTTER_FRAMEWORK_DIR=/Users/lorenzopichilli/flutter/bin/cache/artifacts/engine/ios"
export "FLUTTER_BUILD_NAME=1.0.0"
export "FLUTTER_BUILD_NUMBER=1"
export "TRACK_WIDGET_CREATION=true"

View File

@ -268,7 +268,6 @@
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh",
"${PODS_ROOT}/../.symlinks/flutter/ios/Flutter.framework",
"${BUILT_PRODUCTS_DIR}/WKWebViewWithURLProtocol/WKWebViewWithURLProtocol.framework",
"${BUILT_PRODUCTS_DIR}/flutter_downloader/flutter_downloader.framework",
"${BUILT_PRODUCTS_DIR}/flutter_inappbrowser/flutter_inappbrowser.framework",
"${BUILT_PRODUCTS_DIR}/path_provider/path_provider.framework",
@ -276,7 +275,6 @@
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/WKWebViewWithURLProtocol.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_downloader.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_inappbrowser.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/path_provider.framework",

View File

@ -93,11 +93,13 @@ class _InlineExampleScreenState extends State<InlineExampleScreen> {
initialOptions: InAppWebViewWidgetOptions(
inAppWebViewOptions: InAppWebViewOptions(
debuggingEnabled: true,
//clearCache: true,
clearCache: true,
useShouldOverrideUrlLoading: true,
useOnTargetBlank: true,
//useOnLoadResource: true,
useOnLoadResource: true,
useOnDownloadStart: true,
useShouldInterceptAjaxRequest: true,
useShouldInterceptFetchRequest: true,
//preferredContentMode: InAppWebViewUserPreferredContentMode.DESKTOP,
resourceCustomSchemes: ["my-special-custom-scheme"],
contentBlockers: [
@ -286,6 +288,29 @@ class _InlineExampleScreenState extends State<InlineExampleScreen> {
onFindResultReceived: (InAppWebViewController controller, int activeMatchOrdinal, int numberOfMatches, bool isDoneCounting) async {
print("Current highlighted: $activeMatchOrdinal, Number of matches found: $numberOfMatches, find operation completed: $isDoneCounting");
},
shouldInterceptAjaxRequest: (InAppWebViewController controller, AjaxRequest ajaxRequest) async {
print("AJAX REQUEST: ${ajaxRequest.method} - ${ajaxRequest.url}, DATA: ${ajaxRequest.data}");
// ajaxRequest.method = "GET";
// ajaxRequest.url = "http://192.168.1.20:8082/test-download-file";
// ajaxRequest.headers = {
// "Custom-Header": "Custom-Value"
// };
// return ajaxRequest;
return null;
},
onAjaxReadyStateChange: (InAppWebViewController controller, AjaxRequest ajaxRequest) async {
print("AJAX READY STATE CHANGE: ${ajaxRequest.method} - ${ajaxRequest.url}, ${ajaxRequest.status}, ${ajaxRequest.readyState}, ${ajaxRequest.responseType}, ${ajaxRequest.responseText}, ${ajaxRequest.responseHeaders}");
return null;
},
onAjaxProgressEvent: (InAppWebViewController controller, AjaxRequest ajaxRequest) async {
print("AJAX EVENT: ${ajaxRequest.method} - ${ajaxRequest.url}, ${ajaxRequest.event.type}, LOADED: ${ajaxRequest.event.loaded}, ${ajaxRequest.responseHeaders}");
return null;
},
shouldInterceptFetchRequest: (InAppWebViewController controller, FetchRequest fetchRequest) async {
print("FETCH REQUEST: ${fetchRequest.method} - ${fetchRequest.url}");
fetchRequest.action = FetchRequestAction.ABORT;
return fetchRequest;
},
),
),
),

View File

@ -161,33 +161,33 @@ public class FlutterWebViewController: NSObject, FlutterPlatformView {
result(false)
}
break
case "injectScriptCode":
case "evaluateJavascript":
if webView != nil {
let source = (arguments!["source"] as? String)!
webView!.injectScriptCode(source: source, result: result)
webView!.evaluateJavascript(source: source, result: result)
}
else {
result("")
}
break
case "injectScriptFile":
case "injectJavascriptFileFromUrl":
if webView != nil {
let urlFile = (arguments!["urlFile"] as? String)!
webView!.injectScriptFile(urlFile: urlFile)
webView!.injectJavascriptFileFromUrl(urlFile: urlFile)
}
result(true)
break
case "injectStyleCode":
case "injectCSSCode":
if webView != nil {
let source = (arguments!["source"] as? String)!
webView!.injectStyleCode(source: source)
webView!.injectCSSCode(source: source)
}
result(true)
break
case "injectStyleFile":
case "injectCSSFileFromUrl":
if webView != nil {
let urlFile = (arguments!["urlFile"] as? String)!
webView!.injectStyleFile(urlFile: urlFile)
webView!.injectCSSFileFromUrl(urlFile: urlFile)
}
result(true)
break

View File

@ -8,7 +8,6 @@
import Flutter
import Foundation
import WebKit
import WKWebViewWithURLProtocol
func currentTimeInMilliSeconds() -> Int64 {
let currentDate = Date()
@ -27,9 +26,11 @@ func convertToDictionary(text: String) -> [String: Any]? {
return nil
}
let JAVASCRIPT_BRIDGE_NAME = "flutter_inappbrowser"
// the message needs to be concatenated with '' in order to have the same behavior like on Android
let consoleLogJS = """
(function() {
(function(console) {
var oldLogs = {
'consoleLog': console.log,
'consoleDebug': console.debug,
@ -54,22 +55,20 @@ let consoleLogJS = """
}
})(k);
}
})();
})(window.console);
"""
let resourceObserverJS = """
(function() {
var observer = new PerformanceObserver(function(list) {
list.getEntries().forEach(function(entry) {
window.webkit.messageHandlers['resourceLoaded'].postMessage(JSON.stringify(entry));
window.\(JAVASCRIPT_BRIDGE_NAME).callHandler("onLoadResource", entry);
});
});
observer.observe({entryTypes: ['resource']});
})();
"""
let JAVASCRIPT_BRIDGE_NAME = "flutter_inappbrowser"
let javaScriptBridgeJS = """
window.\(JAVASCRIPT_BRIDGE_NAME) = {};
window.\(JAVASCRIPT_BRIDGE_NAME).callHandler = function() {
@ -233,6 +232,231 @@ function wkwebview_FindNext(forward) {
}
"""
let interceptAjaxRequestsJS = """
(function(ajax) {
var send = ajax.prototype.send;
var open = ajax.prototype.open;
var setRequestHeader = ajax.prototype.setRequestHeader;
ajax.prototype._flutter_inappbrowser_url = null;
ajax.prototype._flutter_inappbrowser_method = null;
ajax.prototype._flutter_inappbrowser_isAsync = null;
ajax.prototype._flutter_inappbrowser_user = null;
ajax.prototype._flutter_inappbrowser_password = null;
ajax.prototype._flutter_inappbrowser_password = null;
ajax.prototype._flutter_inappbrowser_request_headers = {};
ajax.prototype.open = function(method, url, isAsync, user, password) {
isAsync = (isAsync != null) ? isAsync : true;
this._flutter_inappbrowser_url = url;
this._flutter_inappbrowser_method = method;
this._flutter_inappbrowser_isAsync = isAsync;
this._flutter_inappbrowser_user = user;
this._flutter_inappbrowser_password = password;
open.call(this, method, url, isAsync, user, password);
};
ajax.prototype.setRequestHeader = function(header, value) {
this._flutter_inappbrowser_request_headers[header] = value;
setRequestHeader.call(this, header, value);
};
function handleEvent(e) {
var self = this;
var headers = this.getAllResponseHeaders();
var responseHeaders = {};
if (headers != null) {
var arr = headers.trim().split(/[\\r\\n]+/);
arr.forEach(function (line) {
var parts = line.split(': ');
var header = parts.shift();
var value = parts.join(': ');
responseHeaders[header] = value;
});
}
var ajaxRequest = {
method: this._flutter_inappbrowser_method,
url: this._flutter_inappbrowser_url,
isAsync: this._flutter_inappbrowser_isAsync,
user: this._flutter_inappbrowser_user,
password: this._flutter_inappbrowser_password,
withCredentials: this.withCredentials,
headers: this._flutter_inappbrowser_request_headers,
readyState: this.readyState,
status: this.status,
responseURL: this.responseURL,
responseType: this.responseType,
responseText: this.responseText,
statusText: this.statusText,
responseHeaders, responseHeaders,
event: {
type: e.type,
loaded: e.loaded,
lengthComputable: e.lengthComputable
}
};
window.\(JAVASCRIPT_BRIDGE_NAME).callHandler('onAjaxProgressEvent', ajaxRequest).then(function(result) {
if (result != null) {
switch (result.action) {
case 0:
self.abort();
return;
};
}
});
};
ajax.prototype.send = function(data) {
var self = this;
var onreadystatechange = this.onreadystatechange;
this.onreadystatechange = function() {
var headers = this.getAllResponseHeaders();
var responseHeaders = {};
if (headers != null) {
var arr = headers.trim().split(/[\\r\\n]+/);
arr.forEach(function (line) {
var parts = line.split(': ');
var header = parts.shift();
var value = parts.join(': ');
responseHeaders[header] = value;
});
}
var ajaxRequest = {
method: this._flutter_inappbrowser_method,
url: this._flutter_inappbrowser_url,
isAsync: this._flutter_inappbrowser_isAsync,
user: this._flutter_inappbrowser_user,
password: this._flutter_inappbrowser_password,
withCredentials: this.withCredentials,
headers: this._flutter_inappbrowser_request_headers,
readyState: this.readyState,
status: this.status,
responseURL: this.responseURL,
responseType: this.responseType,
responseText: this.responseText,
statusText: this.statusText,
responseHeaders: responseHeaders
};
window.\(JAVASCRIPT_BRIDGE_NAME).callHandler('onAjaxReadyStateChange', ajaxRequest).then(function(result) {
if (result != null) {
switch (result.action) {
case 0:
self.abort();
return;
};
}
if (onreadystatechange != null) {
onreadystatechange();
}
});
};
this.addEventListener('loadstart', handleEvent);
this.addEventListener('load', handleEvent);
this.addEventListener('loadend', handleEvent);
this.addEventListener('progress', handleEvent);
this.addEventListener('error', handleEvent);
this.addEventListener('abort', handleEvent);
var ajaxRequest = {
data: data,
method: this._flutter_inappbrowser_method,
url: this._flutter_inappbrowser_url,
isAsync: this._flutter_inappbrowser_isAsync,
user: this._flutter_inappbrowser_user,
password: this._flutter_inappbrowser_password,
withCredentials: this.withCredentials,
headers: this._flutter_inappbrowser_request_headers
};
window.\(JAVASCRIPT_BRIDGE_NAME).callHandler('shouldInterceptAjaxRequest', ajaxRequest).then(function(result) {
if (result != null) {
switch (result.action) {
case 0:
self.abort();
return;
};
data = result.data;
self.withCredentials = result.withCredentials;
for (var header in result.headers) {
var value = result.headers[header];
self.setRequestHeader(header, value);
};
if ((self._flutter_inappbrowser_method != result.method && result.method != null) || (self._flutter_inappbrowser_url != result.url && result.url != null)) {
self.abort();
self.open(result.method, result.url, result.isAsync, result.user, result.password);
return;
}
}
send.call(self, data);
});
};
})(window.XMLHttpRequest);
"""
let interceptFetchRequestsJS = """
(function(fetch) {
if (fetch == null) {
return;
}
window.fetch = function(resource, init) {
var fetchRequest = {
url: null,
method: null,
headers: null,
body: null,
mode: null,
credentials: null,
cache: null,
redirect: null,
referrer: null,
referrerPolicy: null,
integrity: null,
keepalive: null
};
if (resource instanceof Request) {
fetchRequest.url = resource.url;
fetchRequest.method = resource.method;
fetchRequest.headers = resource.headers;
fetchRequest.body = resource.body;
fetchRequest.mode = resource.mode;
fetchRequest.credentials = resource.credentials;
fetchRequest.cache = resource.cache;
fetchRequest.redirect = resource.redirect;
fetchRequest.referrer = resource.referrer;
fetchRequest.referrerPolicy = resource.referrerPolicy;
fetchRequest.integrity = resource.integrity;
fetchRequest.keepalive = resource.keepalive;
} else {
fetchRequest.url = resource;
if (init != null) {
fetchRequest.method = init.method;
fetchRequest.headers = init.headers;
fetchRequest.body = init.body;
fetchRequest.mode = init.mode;
fetchRequest.credentials = init.credentials;
fetchRequest.cache = init.cache;
fetchRequest.redirect = init.redirect;
fetchRequest.referrer = init.referrer;
fetchRequest.referrerPolicy = init.referrerPolicy;
fetchRequest.integrity = init.integrity;
fetchRequest.keepalive = init.keepalive;
}
}
return window.\(JAVASCRIPT_BRIDGE_NAME).callHandler('shouldInterceptFetchRequest', fetchRequest).then(function(result) {
if (result != null) {
switch (result.action) {
case 0:
var controller = new AbortController();
if (init != null) {
init.signal = controller.signal;
} else {
init = {
signal: controller.signal
};
}
controller.abort();
break;
}
}
return fetch(resource, init);
});
};
})(window.fetch);
"""
public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavigationDelegate, WKScriptMessageHandler {
var IABController: InAppBrowserWebViewController?
@ -308,14 +532,26 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
configuration.userContentController.addUserScript(javaScriptBridgeJSScript)
configuration.userContentController.add(self, name: "callHandler")
let resourceObserverJSScript = WKUserScript(source: resourceObserverJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
configuration.userContentController.addUserScript(resourceObserverJSScript)
configuration.userContentController.add(self, name: "resourceLoaded")
if (options?.useOnLoadResource)! {
let resourceObserverJSScript = WKUserScript(source: resourceObserverJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
configuration.userContentController.addUserScript(resourceObserverJSScript)
}
let findTextHighlightJSScript = WKUserScript(source: findTextHighlightJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
configuration.userContentController.addUserScript(findTextHighlightJSScript)
configuration.userContentController.add(self, name: "findResultReceived")
if (options?.useShouldInterceptAjaxRequest)! {
let interceptAjaxRequestsJSScript = WKUserScript(source: interceptAjaxRequestsJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
configuration.userContentController.addUserScript(interceptAjaxRequestsJSScript)
}
if (options?.useShouldInterceptFetchRequest)! {
let interceptFetchRequestsJSScript = WKUserScript(source: interceptFetchRequestsJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
configuration.userContentController.addUserScript(interceptFetchRequestsJSScript)
}
//keyboardDisplayRequiresUserAction = browserOptions?.keyboardDisplayRequiresUserAction
configuration.suppressesIncrementalRendering = (options?.suppressesIncrementalRendering)!
@ -548,6 +784,18 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
evaluateJavaScript(jscript, completionHandler: nil)
}
if newOptionsMap["useOnLoadResource"] != nil && options?.useOnLoadResource != newOptions.useOnLoadResource && newOptions.useOnLoadResource {
evaluateJavaScript(resourceObserverJS, completionHandler: nil)
}
if newOptionsMap["useShouldInterceptAjaxRequest"] != nil && options?.useShouldInterceptAjaxRequest != newOptions.useShouldInterceptAjaxRequest && newOptions.useShouldInterceptAjaxRequest {
evaluateJavaScript(interceptAjaxRequestsJS, completionHandler: nil)
}
if newOptionsMap["useShouldInterceptFetchRequest"] != nil && options?.useShouldInterceptFetchRequest != newOptions.useShouldInterceptFetchRequest && newOptions.useShouldInterceptFetchRequest {
evaluateJavaScript(interceptFetchRequestsJS, completionHandler: nil)
}
if newOptionsMap["mediaPlaybackRequiresUserGesture"] != nil && options?.mediaPlaybackRequiresUserGesture != newOptions.mediaPlaybackRequiresUserGesture {
if #available(iOS 10.0, *) {
configuration.mediaTypesRequiringUserActionForPlayback = (newOptions.mediaPlaybackRequiresUserGesture) ? .all : []
@ -734,22 +982,22 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
}
}
public func injectScriptCode(source: String, result: FlutterResult?) {
public func evaluateJavascript(source: String, result: FlutterResult?) {
let jsWrapper = "(function(){return JSON.stringify(eval(%@));})();"
injectDeferredObject(source: source, withWrapper: jsWrapper, result: result)
}
public func injectScriptFile(urlFile: String) {
public func injectJavascriptFileFromUrl(urlFile: String) {
let jsWrapper = "(function(d) { var c = d.createElement('script'); c.src = %@; d.body.appendChild(c); })(document);"
injectDeferredObject(source: urlFile, withWrapper: jsWrapper, result: nil)
}
public func injectStyleCode(source: String) {
public func injectCSSCode(source: String) {
let jsWrapper = "(function(d) { var c = d.createElement('style'); c.innerHTML = %@; d.body.appendChild(c); })(document);"
injectDeferredObject(source: source, withWrapper: jsWrapper, result: nil)
}
public func injectStyleFile(urlFile: String) {
public func injectCSSFileFromUrl(urlFile: String) {
let jsWrapper = "(function(d) { var c = d.createElement('link'); c.rel='stylesheet', c.type='text/css'; c.href = %@; d.body.appendChild(c); })(document);"
injectDeferredObject(source: urlFile, withWrapper: jsWrapper, result: nil)
}
@ -1333,21 +1581,6 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
}
}
public func onLoadResource(initiatorType: String, url: String, startTime: Double, duration: Double) {
var arguments: [String : Any] = [
"initiatorType": initiatorType,
"url": url,
"startTime": startTime,
"duration": duration
]
if IABController != nil {
arguments["uuid"] = IABController!.uuid
}
if let channel = getChannel() {
channel.invokeMethod("onLoadResource", arguments: arguments)
}
}
public func onFindResultReceived(activeMatchOrdinal: Int, numberOfMatches: Int, isDoneCounting: Bool) {
var arguments: [String : Any] = [
"activeMatchOrdinal": activeMatchOrdinal,
@ -1561,22 +1794,6 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
}
onConsoleMessage(sourceURL: "", lineNumber: 1, message: message.body as! String, messageLevel: messageLevel)
}
else if message.name == "resourceLoaded" && (options?.useOnLoadResource)! {
if let resource = convertToDictionary(text: message.body as! String) {
// escape special chars
let resourceName = (resource["name"] as! String).addingPercentEncoding(withAllowedCharacters:NSCharacterSet.urlQueryAllowed)
let url = URL(string: resourceName!)!
if !UIApplication.shared.canOpenURL(url) {
return
}
let initiatorType = resource["initiatorType"] as! String
let startTime = resource["startTime"] as! Double
let duration = resource["duration"] as! Double
self.onLoadResource(initiatorType: initiatorType, url: url.absoluteString, startTime: startTime, duration: duration)
}
}
else if message.name == "callHandler" {
let body = message.body as! [String: Any]
let handlerName = body["handlerName"] as! String

View File

@ -26,6 +26,8 @@ public class InAppWebViewOptions: Options {
var resourceCustomSchemes: [String] = []
var contentBlockers: [[String: [String : Any]]] = []
var minimumFontSize = 0;
var useShouldInterceptAjaxRequest = false
var useShouldInterceptFetchRequest = false
var disallowOverScroll = false
var enableViewportScale = false

View File

@ -195,19 +195,19 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin {
result(false)
}
break
case "injectScriptCode":
self.injectScriptCode(uuid: uuid, arguments: arguments!, result: result)
case "evaluateJavascript":
self.evaluateJavascript(uuid: uuid, arguments: arguments!, result: result)
break
case "injectScriptFile":
self.injectScriptFile(uuid: uuid, arguments: arguments!)
case "injectJavascriptFileFromUrl":
self.injectJavascriptFileFromUrl(uuid: uuid, arguments: arguments!)
result(true)
break
case "injectStyleCode":
self.injectStyleCode(uuid: uuid, arguments: arguments!)
case "injectCSSCode":
self.injectCSSCode(uuid: uuid, arguments: arguments!)
result(true)
break
case "injectStyleFile":
self.injectStyleFile(uuid: uuid, arguments: arguments!)
case "injectCSSFileFromUrl":
self.injectCSSFileFromUrl(uuid: uuid, arguments: arguments!)
result(true)
break
case "takeScreenshot":
@ -665,30 +665,30 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin {
}
}
public func injectScriptCode(uuid: String, arguments: NSDictionary, result: @escaping FlutterResult) {
public func evaluateJavascript(uuid: String, arguments: NSDictionary, result: @escaping FlutterResult) {
if let webViewController = self.webViewControllers[uuid] {
webViewController!.webView.injectScriptCode(source: arguments["source"] as! String, result: result)
webViewController!.webView.evaluateJavascript(source: arguments["source"] as! String, result: result)
}
else {
result(FlutterError(code: "InAppBrowserFlutterPlugin", message: "webView is null", details: nil))
}
}
public func injectScriptFile(uuid: String, arguments: NSDictionary) {
public func injectJavascriptFileFromUrl(uuid: String, arguments: NSDictionary) {
if let webViewController = self.webViewControllers[uuid] {
webViewController!.webView.injectScriptFile(urlFile: arguments["urlFile"] as! String)
webViewController!.webView.injectJavascriptFileFromUrl(urlFile: arguments["urlFile"] as! String)
}
}
public func injectStyleCode(uuid: String, arguments: NSDictionary) {
public func injectCSSCode(uuid: String, arguments: NSDictionary) {
if let webViewController = self.webViewControllers[uuid] {
webViewController!.webView.injectStyleCode(source: arguments["source"] as! String)
webViewController!.webView.injectCSSCode(source: arguments["source"] as! String)
}
}
public func injectStyleFile(uuid: String, arguments: NSDictionary) {
public func injectCSSFileFromUrl(uuid: String, arguments: NSDictionary) {
if let webViewController = self.webViewControllers[uuid] {
webViewController!.webView.injectStyleFile(urlFile: arguments["urlFile"] as! String)
webViewController!.webView.injectCSSFileFromUrl(urlFile: arguments["urlFile"] as! String)
}
}

View File

@ -19,5 +19,4 @@ A new Flutter plugin.
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS[sdk=iphonesimulator*]' => 'x86_64' }
s.swift_version = '5.0'
s.dependency "WKWebViewWithURLProtocol"
end

View File

@ -16,30 +16,7 @@ import 'types.dart';
import 'in_app_browser.dart';
import 'webview_options.dart';
/*
* TODO: injectJavaScriptBeforeLoad
*/
///Initial [data] as a content for an [InAppWebView] instance, using [baseUrl] as the base URL for it.
///The [mimeType] property specifies the format of the data.
///The [encoding] property specifies the encoding of the data.
class InAppWebViewInitialData {
String data;
String mimeType;
String encoding;
String baseUrl;
InAppWebViewInitialData(this.data, {this.mimeType = "text/html", this.encoding = "utf8", this.baseUrl = "about:blank"});
Map<String, String> toMap() {
return {
"data": data,
"mimeType": mimeType,
"encoding": encoding,
"baseUrl": baseUrl
};
}
}
const javaScriptHandlersNameForbidden = ["onLoadResource", "shouldInterceptAjaxRequest", "onAjaxReadyStateChange", "onAjaxEvent"];
///InAppWebView Widget class.
///
@ -64,7 +41,7 @@ class InAppWebViewInitialData {
/// - __builtInZoomControls__: Set to `true` if the WebView should use its built-in zoom mechanisms. The default value is `false`.
/// - __displayZoomControls__: Set to `true` if the WebView should display on-screen zoom controls when using the built-in zoom mechanisms. The default value is `false`.
/// - __supportZoom__: Set to `false` if the WebView should not support zooming using its on-screen zoom controls and gestures. The default value is `true`.
/// - __databaseEnabled__: Set to `true` if you want the database storage API is enabled. The default value is `false`.
/// - __databaseEnabled__: Set to `true` if you want injectScriptFilethe database storage API is enabled. The default value is `false`.
/// - __domStorageEnabled__: Set to `true` if you want the DOM storage API is enabled. The default value is `false`.
/// - __useWideViewPort__: Set to `true` if the WebView should enable support for the "viewport" HTML meta tag or should use a wide viewport. When the value of the setting is false, the layout width is always set to the width of the WebView control in device-independent (CSS) pixels. When the value is true and the page contains the viewport meta tag, the value of the width specified in the tag is used. If the page does not contain the tag or does not provide a width, then a wide viewport will be used. The default value is `true`.
/// - __safeBrowsingEnabled__: Set to `true` if you want the Safe Browsing is enabled. Safe Browsing allows WebView to protect against malware and phishing attacks by verifying the links. The default value is `true`.
@ -198,6 +175,18 @@ class InAppWebView extends StatefulWidget {
///[isDoneCounting] whether the find operation has actually completed.
final void Function(InAppWebViewController controller, int activeMatchOrdinal, int numberOfMatches, bool isDoneCounting) onFindResultReceived;
///
final Future<AjaxRequest> Function(InAppWebViewController controller, AjaxRequest ajaxRequest) shouldInterceptAjaxRequest;
///
final Future<AjaxRequest> Function(InAppWebViewController controller, AjaxRequest ajaxRequest) onAjaxReadyStateChange;
///
final Future<AjaxRequest> Function(InAppWebViewController controller, AjaxRequest ajaxRequest) onAjaxProgressEvent;
///
final Future<FetchRequest> Function(InAppWebViewController controller, FetchRequest fetchRequest) shouldInterceptFetchRequest;
///Initial url that will be loaded.
final String initialUrl;
///Initial asset file that will be loaded. See [InAppWebView.loadFile()] for explanation.
@ -245,6 +234,10 @@ class InAppWebView extends StatefulWidget {
this.onReceivedServerTrustAuthRequest,
this.onReceivedClientCertRequest,
this.onFindResultReceived,
this.shouldInterceptAjaxRequest,
this.onAjaxReadyStateChange,
this.onAjaxProgressEvent,
this.shouldInterceptFetchRequest,
this.gestureRecognizers,
}) : super(key: key);
@ -399,19 +392,6 @@ class InAppWebViewController {
else if (_inAppBrowser != null)
_inAppBrowser.shouldOverrideUrlLoading(url);
break;
case "onLoadResource":
String initiatorType = call.arguments["initiatorType"];
String url = call.arguments["url"];
double startTime = call.arguments["startTime"];
double duration = call.arguments["duration"];
var response = new LoadedResource(initiatorType, url, startTime, duration);
if (_widget != null && _widget.onLoadResource != null)
_widget.onLoadResource(this, response);
else if (_inAppBrowser != null)
_inAppBrowser.onLoadResource(response);
break;
case "onConsoleMessage":
String sourceURL = call.arguments["sourceURL"];
int lineNumber = call.arguments["lineNumber"];
@ -555,6 +535,122 @@ class InAppWebViewController {
String handlerName = call.arguments["handlerName"];
// decode args to json
List<dynamic> args = jsonDecode(call.arguments["args"]);
switch(handlerName) {
case "onLoadResource":
Map<dynamic, dynamic> argMap = args[0];
String initiatorType = argMap["initiatorType"];
String url = argMap["name"];
double startTime = argMap["startTime"] is int ? argMap["startTime"].toDouble() : argMap["startTime"];
double duration = argMap["duration"] is int ? argMap["duration"].toDouble() : argMap["duration"];
var response = new LoadedResource(initiatorType, url, startTime, duration);
if (_widget != null && _widget.onLoadResource != null)
_widget.onLoadResource(this, response);
else if (_inAppBrowser != null)
_inAppBrowser.onLoadResource(response);
return null;
case "shouldInterceptAjaxRequest":
Map<dynamic, dynamic> argMap = args[0];
dynamic data = argMap["data"];
String method = argMap["method"];
String url = argMap["url"];
bool isAsync = argMap["isAsync"];
String user = argMap["user"];
String password = argMap["password"];
bool withCredentials = argMap["withCredentials"];
Map<dynamic, dynamic> headers = argMap["headers"];
var request = new AjaxRequest(data: data, method: method, url: url, isAsync: isAsync, user: user, password: password, withCredentials: withCredentials, headers: headers);
if (_widget != null && _widget.shouldInterceptAjaxRequest != null)
return jsonEncode(await _widget.shouldInterceptAjaxRequest(this, request));
//else if (_inAppBrowser != null)
// return jsonEncode(await _inAppBrowser.shouldInterceptAjaxRequest(request));
return null;
case "onAjaxReadyStateChange":
Map<dynamic, dynamic> argMap = args[0];
dynamic data = argMap["data"];
String method = argMap["method"];
String url = argMap["url"];
bool isAsync = argMap["isAsync"];
String user = argMap["user"];
String password = argMap["password"];
bool withCredentials = argMap["withCredentials"];
Map<dynamic, dynamic> headers = argMap["headers"];
int readyState = argMap["readyState"];
int status = argMap["status"];
String responseURL = argMap["responseURL"];
String responseType = argMap["responseType"];
String responseText = argMap["responseText"];
String statusText = argMap["statusText"];
Map<dynamic, dynamic> responseHeaders = argMap["responseHeaders"];
var request = new AjaxRequest(data: data, method: method, url: url, isAsync: isAsync, user: user, password: password,
withCredentials: withCredentials, headers: headers, readyState: AjaxRequestReadyState.fromValue(readyState), status: status, responseURL: responseURL,
responseType: responseType, responseText: responseText, statusText: statusText, responseHeaders: responseHeaders);
if (_widget != null && _widget.onAjaxReadyStateChange != null)
return jsonEncode(await _widget.onAjaxReadyStateChange(this, request));
//else if (_inAppBrowser != null)
// return jsonEncode(await _inAppBrowser.onAjaxReadyStateChange(request));
return null;
case "onAjaxProgressEvent":
Map<dynamic, dynamic> argMap = args[0];
dynamic data = argMap["data"];
String method = argMap["method"];
String url = argMap["url"];
bool isAsync = argMap["isAsync"];
String user = argMap["user"];
String password = argMap["password"];
bool withCredentials = argMap["withCredentials"];
Map<dynamic, dynamic> headers = argMap["headers"];
int readyState = argMap["readyState"];
int status = argMap["status"];
String responseURL = argMap["responseURL"];
String responseType = argMap["responseType"];
String responseText = argMap["responseText"];
String statusText = argMap["statusText"];
Map<dynamic, dynamic> responseHeaders = argMap["responseHeaders"];
Map<dynamic, dynamic> eventMap = argMap["event"];
AjaxRequestEvent event = AjaxRequestEvent(lengthComputable: eventMap["lengthComputable"], loaded: eventMap["loaded"], type: AjaxRequestEventType.fromValue(eventMap["type"]));
var request = new AjaxRequest(data: data, method: method, url: url, isAsync: isAsync, user: user, password: password,
withCredentials: withCredentials, headers: headers, readyState: AjaxRequestReadyState.fromValue(readyState), status: status, responseURL: responseURL,
responseType: responseType, responseText: responseText, statusText: statusText, responseHeaders: responseHeaders, event: event);
if (_widget != null && _widget.onAjaxProgressEvent != null)
return jsonEncode(await _widget.onAjaxProgressEvent(this, request));
//else if (_inAppBrowser != null)
// return jsonEncode(await _inAppBrowser.onAjaxProgressEvent(request));
return null;
case "shouldInterceptFetchRequest":
Map<dynamic, dynamic> argMap = args[0];
String url = argMap["url"];
String method = argMap["method"];
Map<dynamic, dynamic> headers = argMap["headers"];
dynamic body = argMap["body"];
String mode = argMap["mode"];
String credentials = argMap["credentials"];
String cache = argMap["cache"];
String redirect = argMap["redirect"];
String referrer = argMap["referrer"];
String referrerPolicy = argMap["referrerPolicy"];
String integrity = argMap["integrity"];
bool keepalive = argMap["keepalive"];
var request = new FetchRequest(url: url, method: method, headers: headers, body: body, mode: mode, credentials: credentials,
cache: cache, redirect: redirect, referrer: referrer, referrerPolicy: referrerPolicy, integrity: integrity, keepalive: keepalive);
if (_widget != null && _widget.shouldInterceptFetchRequest != null)
return jsonEncode(await _widget.shouldInterceptFetchRequest(this, request));
//else if (_inAppBrowser != null)
// return jsonEncode(await _inAppBrowser.shouldInterceptFetchRequest(request));
return null;
}
if (javaScriptHandlersMap.containsKey(handlerName)) {
// convert result to json
try {
@ -609,7 +705,7 @@ class InAppWebViewController {
var html = "";
InAppWebViewWidgetOptions options = await getOptions();
if (options != null && options.inAppWebViewOptions.javaScriptEnabled == true) {
html = await injectScriptCode("window.document.getElementsByTagName('html')[0].outerHTML;");
html = await evaluateJavascript("window.document.getElementsByTagName('html')[0].outerHTML;");
if (html.isNotEmpty)
return html;
}
@ -828,7 +924,7 @@ class InAppWebViewController {
await _channel.invokeMethod('loadFile', args);
}
///Reloads the [InAppWebView] window.
///Reloads the [InAppWebView].
Future<void> reload() async {
Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null && _inAppBrowser != null) {
@ -838,7 +934,7 @@ class InAppWebViewController {
await _channel.invokeMethod('reload', args);
}
///Goes back in the history of the [InAppWebView] window.
///Goes back in the history of the [InAppWebView].
Future<void> goBack() async {
Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null && _inAppBrowser != null) {
@ -858,7 +954,7 @@ class InAppWebViewController {
return await _channel.invokeMethod('canGoBack', args);
}
///Goes forward in the history of the [InAppWebView] window.
///Goes forward in the history of the [InAppWebView].
Future<void> goForward() async {
Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null && _inAppBrowser != null) {
@ -929,41 +1025,47 @@ class InAppWebViewController {
await _channel.invokeMethod('stopLoading', args);
}
///Injects JavaScript code into the [InAppWebView] window and returns the result of the evaluation.
Future<String> injectScriptCode(String source) async {
///Evaluates JavaScript code into the [InAppWebView] and returns the result of the evaluation.
Future<String> evaluateJavascript(String source) async {
Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null && _inAppBrowser != null) {
_inAppBrowser.throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
args.putIfAbsent('source', () => source);
return await _channel.invokeMethod('injectScriptCode', args);
return await _channel.invokeMethod('evaluateJavascript', args);
}
///Injects a JavaScript file into the [InAppWebView] window.
Future<void> injectScriptFile(String urlFile) async {
///Injects an external JavaScript file into the [InAppWebView] from a defined url.
Future<void> injectJavascriptFileFromUrl(String urlFile) async {
Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null && _inAppBrowser != null) {
_inAppBrowser.throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
args.putIfAbsent('urlFile', () => urlFile);
await _channel.invokeMethod('injectScriptFile', args);
await _channel.invokeMethod('injectJavascriptFileFromUrl', args);
}
///Injects a JavaScript file into the [InAppWebView] from the flutter assets directory.
Future<void> injectJavascriptFileFromAsset(String assetFilePath) async {
String source = await rootBundle.loadString(assetFilePath);
await evaluateJavascript(source);
}
///Injects CSS into the [InAppWebView] window.
Future<void> injectStyleCode(String source) async {
///Injects CSS into the [InAppWebView].
Future<void> injectCSSCode(String source) async {
Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null && _inAppBrowser != null) {
_inAppBrowser.throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
args.putIfAbsent('source', () => source);
await _channel.invokeMethod('injectStyleCode', args);
await _channel.invokeMethod('injectCSSCode', args);
}
///Injects a CSS file into the [InAppWebView] window.
Future<void> injectStyleFile(String urlFile) async {
///Injects an external CSS file into the [InAppWebView] from a defined url.
Future<void> injectCSSFileFromUrl(String urlFile) async {
Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null && _inAppBrowser != null) {
_inAppBrowser.throwIsNotOpened();
@ -973,6 +1075,12 @@ class InAppWebViewController {
await _channel.invokeMethod('injectStyleFile', args);
}
///Injects a CSS file into the [InAppWebView] from the flutter assets directory.
Future<void> injectCSSFileFromAsset(String assetFilePath) async {
String source = await rootBundle.loadString(assetFilePath);
await injectCSSCode(source);
}
///Adds a JavaScript message handler [callback] ([JavaScriptHandlerCallback]) that listen to post messages sent from JavaScript by the handler with name [handlerName].
///
///The Android implementation uses [addJavascriptInterface](https://developer.android.com/reference/android/webkit/WebView#addJavascriptInterface(java.lang.Object,%20java.lang.String)).
@ -1020,6 +1128,7 @@ class InAppWebViewController {
/// """);
///```
void addJavaScriptHandler(String handlerName, JavaScriptHandlerCallback callback) {
assert(!javaScriptHandlersNameForbidden.contains(handlerName));
this.javaScriptHandlersMap[handlerName] = (callback);
}

View File

@ -58,6 +58,27 @@ class LoadedResource {
}
///Initial [data] as a content for an [InAppWebView] instance, using [baseUrl] as the base URL for it.
///The [mimeType] property specifies the format of the data.
///The [encoding] property specifies the encoding of the data.
class InAppWebViewInitialData {
String data;
String mimeType;
String encoding;
String baseUrl;
InAppWebViewInitialData(this.data, {this.mimeType = "text/html", this.encoding = "utf8", this.baseUrl = "about:blank"});
Map<String, String> toMap() {
return {
"data": data,
"mimeType": mimeType,
"encoding": encoding,
"baseUrl": baseUrl
};
}
}
/*
///Public class representing a resource request of the WebView.
///It is used by the event [shouldInterceptRequest()].
@ -671,4 +692,163 @@ class ChromeSafariBrowserClassOptions {
IosSafariOptions iosSafariOptions;
ChromeSafariBrowserClassOptions({this.androidChromeCustomTabsOptions, this.iosSafariOptions});
}
///
class AjaxRequestAction {
final int _value;
const AjaxRequestAction._internal(this._value);
toValue() => _value;
static const ABORT = const AjaxRequestAction._internal(0);
static const PROCEED = const AjaxRequestAction._internal(1);
}
///
class AjaxRequestEventType {
final String _value;
const AjaxRequestEventType._internal(this._value);
static AjaxRequestEventType fromValue(String value) {
return (["loadstart", "load", "loadend", "progress", "error", "abort"].contains(value)) ? AjaxRequestEventType._internal(value) : null;
}
toValue() => _value;
String toString() => _value;
static const LOADSTART = const AjaxRequestEventType._internal("loadstart");
static const LOAD = const AjaxRequestEventType._internal("load");
static const LOADEND = const AjaxRequestEventType._internal("loadend");
static const PROGRESS = const AjaxRequestEventType._internal("progress");
static const ERROR = const AjaxRequestEventType._internal("error");
static const ABORT = const AjaxRequestEventType._internal("abort");
}
///
class AjaxRequestEvent {
AjaxRequestEventType type;
int loaded;
bool lengthComputable;
AjaxRequestEvent({this.type, this.loaded, this.lengthComputable});
}
///
class AjaxRequestReadyState {
final int _value;
const AjaxRequestReadyState._internal(this._value);
static AjaxRequestReadyState fromValue(int value) {
if (value != null && value >= 0 && value <= 4)
return AjaxRequestReadyState._internal(value);
return null;
}
toValue() => _value;
String toString() => _value.toString();
static const UNSENT = const AjaxRequestReadyState._internal(0);
static const OPENED = const AjaxRequestReadyState._internal(1);
static const HEADERS_RECEIVED = const AjaxRequestReadyState._internal(2);
static const LOADING = const AjaxRequestReadyState._internal(3);
static const DONE = const AjaxRequestReadyState._internal(4);
}
///
class AjaxRequest {
dynamic data;
String method;
String url;
bool isAsync;
String user;
String password;
bool withCredentials;
Map<dynamic, dynamic> headers;
AjaxRequestReadyState readyState;
int status;
String responseURL;
String responseType;
String responseText;
String statusText;
Map<dynamic, dynamic> responseHeaders;
AjaxRequestEvent event;
AjaxRequestAction action;
AjaxRequest({this.data, this.method, this.url, this.isAsync, this.user, this.password,
this.withCredentials, this.headers, this.readyState, this.status, this.responseURL, this.responseType,
this.responseText, this.statusText, this.responseHeaders, this.event, this.action = AjaxRequestAction.PROCEED});
Map<String, dynamic> toMap() {
return {
"data": data,
"method": method,
"url": url,
"isAsync": isAsync,
"user": user,
"password": password,
"withCredentials": withCredentials,
"headers": headers,
"readyState": readyState?.toValue(),
"status": status,
"responseURL": responseURL,
"responseType": responseType,
"responseText": responseText,
"statusText": statusText,
"responseHeaders": responseHeaders,
"action": action?.toValue()
};
}
Map<String, dynamic> toJson() {
return this.toMap();
}
}
///
class FetchRequestAction {
final int _value;
const FetchRequestAction._internal(this._value);
toValue() => _value;
static const ABORT = const FetchRequestAction._internal(0);
static const PROCEED = const FetchRequestAction._internal(1);
}
///
class FetchRequest {
String url;
String method;
Map<dynamic, dynamic> headers;
dynamic body;
String mode;
String credentials;
String cache;
String redirect;
String referrer;
String referrerPolicy;
String integrity;
bool keepalive;
FetchRequestAction action;
FetchRequest({this.url, this.method, this.headers, this.body, this.mode, this.credentials,
this.cache, this.redirect, this.referrer, this.referrerPolicy, this.integrity, this.keepalive,
this.action = FetchRequestAction.PROCEED});
Map<String, dynamic> toMap() {
return {
"url": url,
"method": method,
"headers": headers,
"body": body,
"mode": mode,
"credentials": credentials,
"cache": cache,
"redirect": redirect,
"referrer": referrer,
"referrerPolicy": referrerPolicy,
"integrity": integrity,
"keepalive": keepalive,
"action": action?.toValue()
};
}
Map<String, dynamic> toJson() {
return this.toMap();
}
}

View File

@ -54,11 +54,14 @@ class InAppWebViewOptions implements WebViewOptions, BrowserOptions, AndroidOpti
List<String> resourceCustomSchemes;
List<ContentBlocker> contentBlockers;
InAppWebViewUserPreferredContentMode preferredContentMode;
bool useShouldInterceptAjaxRequest;
bool useShouldInterceptFetchRequest;
InAppWebViewOptions({this.useShouldOverrideUrlLoading = false, this.useOnLoadResource = false, this.useOnDownloadStart = false, this.useOnTargetBlank = false,
this.clearCache = false, this.userAgent = "", this.javaScriptEnabled = true, this.debuggingEnabled = false, this.javaScriptCanOpenWindowsAutomatically = false,
this.mediaPlaybackRequiresUserGesture = true, this.textZoom = 100, this.minimumFontSize, this.verticalScrollBarEnabled = true, this.horizontalScrollBarEnabled = true,
this.resourceCustomSchemes = const [], this.contentBlockers = const [], this.preferredContentMode = InAppWebViewUserPreferredContentMode.RECOMMENDED}) {
this.resourceCustomSchemes = const [], this.contentBlockers = const [], this.preferredContentMode = InAppWebViewUserPreferredContentMode.RECOMMENDED,
this.useShouldInterceptAjaxRequest = false, this.useShouldInterceptFetchRequest = false}) {
if (this.minimumFontSize == null)
this.minimumFontSize = Platform.isAndroid ? 8 : 0;
assert(!this.resourceCustomSchemes.contains("http") && !this.resourceCustomSchemes.contains("https"));
@ -87,7 +90,9 @@ class InAppWebViewOptions implements WebViewOptions, BrowserOptions, AndroidOpti
"horizontalScrollBarEnabled": horizontalScrollBarEnabled,
"resourceCustomSchemes": resourceCustomSchemes,
"contentBlockers": contentBlockersMapList,
"preferredContentMode": preferredContentMode?.toValue()
"preferredContentMode": preferredContentMode?.toValue(),
"useShouldInterceptAjaxRequest": useShouldInterceptAjaxRequest,
"useShouldInterceptFetchRequest": useShouldInterceptFetchRequest
};
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 83 KiB

View File

@ -1,12 +1,14 @@
// Example of the server https is taken from here: https://engineering.circle.com/https-authorized-certs-with-node-js-315e548354a2
// Conversion of client1-crt.pem to certificate.pfx: https://stackoverflow.com/a/38408666/4637638
const express = require('express')
var https = require('https')
const https = require('https')
const cors = require('cors')
const auth = require('basic-auth')
const app = express()
const appHttps = express()
const appAuthBasic = express()
const fs = require('fs')
const path = require('path')
var options = {
key: fs.readFileSync('server-key.pem'),
@ -17,6 +19,7 @@ var options = {
};
appHttps.get('/', (req, res) => {
console.log(JSON.stringify(req.headers))
const cert = req.connection.getPeerCertificate()
// The `req.client.authorized` flag will be true if the certificate is valid and was issued by a CA we white-listed
@ -48,7 +51,8 @@ appHttps.get('/', (req, res) => {
})
appHttps.get('/fakeResource', (req, res) => {
res.set("Content-Type", "text/javascript")
console.log(JSON.stringify(req.headers))
res.set("Content-Type", "text/javascript")
res.send(`alert("HI");`)
res.end()
})
@ -70,6 +74,7 @@ appAuthBasic.use((req, res, next) => {
})
appAuthBasic.get("/", (req, res) => {
console.log(JSON.stringify(req.headers))
res.send(`
<html>
<head>
@ -87,10 +92,13 @@ appAuthBasic.listen(8081)
// Parse URL-encoded bodies (as sent by HTML forms)
app.use(express.urlencoded());
app.use(cors());
// Parse JSON bodies (as sent by API clients)
app.use(express.json());
app.get("/", (req, res) => {
console.log(JSON.stringify(req.headers))
res.send(`
<html>
<head>
@ -104,6 +112,7 @@ app.get("/", (req, res) => {
})
app.post("/test-post", (req, res) => {
console.log(JSON.stringify(req.headers))
res.send(`
<html>
<head>
@ -115,4 +124,27 @@ app.post("/test-post", (req, res) => {
`);
res.end()
})
app.post("/test-ajax-post", (req, res) => {
console.log(JSON.stringify(req.headers))
res.set("Content-Type", "application/json")
res.send(JSON.stringify({
"name": req.body.name,
"key2": "value2"
}))
res.end()
})
app.get("/test-download-file", (req, res) => {
console.log(JSON.stringify(req.headers))
const filePath = path.join(__dirname, 'assets', 'flutter_logo.png');
const stat = fs.statSync(filePath);
const file = fs.readFileSync(filePath, 'binary');
res.setHeader('Content-Length', stat.size);
res.setHeader('Content-Type', 'image/png');
res.setHeader('Content-Disposition', 'attachment; filename=flutter_logo.png');
res.write(file, 'binary');
res.end();
})
app.listen(8082)

View File

@ -71,6 +71,15 @@
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
},
"cors": {
"version": "2.8.5",
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
"requires": {
"object-assign": "^4",
"vary": "^1"
}
},
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
@ -248,6 +257,11 @@
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
},
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
},
"on-finished": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",

View File

@ -10,6 +10,7 @@
"license": "ISC",
"dependencies": {
"basic-auth": "latest",
"cors": "^2.8.5",
"express": "latest",
"https": "latest"
}