Commit 16de8b19 authored by Lorenzo Pichilli's avatar Lorenzo Pichilli

added onNavigationStateChange, updated onAjaxProgress and onAjaxReadyStateChange return type

parent 18919576
This diff is collapsed.
......@@ -27,7 +27,8 @@
- Added `onReceivedServerTrustAuthRequest` and `onReceivedClientCertRequest` events to manage SSL requests
- Added `onFindResultReceived` event, `findAllAsync`, `findNext` and `clearMatches` methods
- Added `getHtml`, `injectJavascriptFileFromAsset` and `injectCSSFileFromAsset` methods
- Added `shouldInterceptAjaxRequest`, `onAjaxReadyStateChange`, `onAjaxProgressEvent` and `shouldInterceptFetchRequest` events with `useShouldInterceptAjaxRequest` and `useShouldInterceptFetchRequest` webview options
- Added `shouldInterceptAjaxRequest`, `onAjaxReadyStateChange`, `onAjaxProgress` and `shouldInterceptFetchRequest` events with `useShouldInterceptAjaxRequest` and `useShouldInterceptFetchRequest` webview options
- Added `onNavigationStateChange` event
- Fun: added `getTRexRunnerHtml` and `getTRexRunnerCss` methods to get html (with javascript) and css to recreate the Chromium's t-rex runner game
### BREAKING CHANGES
......
......@@ -154,9 +154,9 @@ final public class InAppWebView extends InputAwareWebView {
" lengthComputable: e.lengthComputable" +
" }" +
" };" +
" window." + JavaScriptBridgeInterface.name + ".callHandler('onAjaxProgressEvent', ajaxRequest).then(function(result) {" +
" window." + JavaScriptBridgeInterface.name + ".callHandler('onAjaxProgress', ajaxRequest).then(function(result) {" +
" if (result != null) {" +
" switch (result.action) {" +
" switch (result) {" +
" case 0:" +
" self.abort();" +
" return;" +
......@@ -197,7 +197,7 @@ final public class InAppWebView extends InputAwareWebView {
" };" +
" window." + JavaScriptBridgeInterface.name + ".callHandler('onAjaxReadyStateChange', ajaxRequest).then(function(result) {" +
" if (result != null) {" +
" switch (result.action) {" +
" switch (result) {" +
" case 0:" +
" self.abort();" +
" return;" +
......
......@@ -49,9 +49,9 @@ public class InAppWebViewClient extends WebViewClient {
private FlutterWebView flutterWebView;
private InAppBrowserActivity inAppBrowserActivity;
Map<Integer, String> statusCodeMapping = new HashMap<Integer, String>();
long startPageTime = 0;
private static int previousAuthRequestFailureCount = 0;
private static List<Credential> credentialsProposed = null;
private String onPageStartedURL = "";
public InAppWebViewClient(Object obj) {
super();
......@@ -145,9 +145,9 @@ public class InAppWebViewClient extends WebViewClient {
webView.loadUrl("javascript:" + InAppWebView.resourceObserverJS.replaceAll("[\r\n]+", ""));
}
onPageStartedURL = url;
super.onPageStarted(view, url, favicon);
startPageTime = System.currentTimeMillis();
webView.isLoading = true;
if (inAppBrowserActivity != null && inAppBrowserActivity.searchView != null && !url.equals(inAppBrowserActivity.searchView.getQuery().toString())) {
inAppBrowserActivity.searchView.setQuery(url, false);
......@@ -194,6 +194,21 @@ public class InAppWebViewClient extends WebViewClient {
getChannel().invokeMethod("onLoadStop", obj);
}
@Override
public void doUpdateVisitedHistory (WebView view, String url, boolean isReload) {
super.doUpdateVisitedHistory(view, url, isReload);
if (!isReload && !url.equals(onPageStartedURL)) {
onPageStartedURL = "";
Map<String, Object> obj = new HashMap<>();
if (inAppBrowserActivity != null)
obj.put("uuid", inAppBrowserActivity.uuid);
obj.put("url", url);
getChannel().invokeMethod("onNavigationStateChange", obj);
}
}
@Override
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
super.onReceivedError(view, errorCode, description, failingUrl);
......
......@@ -31,6 +31,9 @@
<option value="1">option 1</option>
<option value="2">option 2</option>
</select>
<button onclick="testHistoryPush1()">History Push 1</button>
<button onclick="testHistoryPush2()">History Push 2</button>
<button onclick="testLocationHref()">Location Href</button>
<p>
<img src="https://via.placeholder.com/100x50" alt="placeholder 100x50">
</p>
......@@ -51,6 +54,27 @@
</div>
<script>
var state = { 'page_id': 1, 'user_id': 5 };
function testHistoryPush1() {
var randomNumber = 100 * Math.random();
var title = 'Hello World ' + randomNumber;
var url = 'hello-foo-' + randomNumber + '.html';
history.pushState(state, title, url);
}
function testHistoryPush2() {
var randomNumber = 100 * Math.random();
var title = 'Hello World ' + randomNumber;
var url = 'hello-bar-' + randomNumber + '.html';
history.replaceState(state, title, url);
}
function testLocationHref() {
var randomNumber = 100 * Math.random();
window.location = "#foo-" + randomNumber;
}
window.addEventListener("flutterInAppBrowserPlatformReady", function(event) {
console.log("ready");
......
......@@ -300,17 +300,23 @@ class _InlineExampleScreenState extends State<InlineExampleScreen> {
},
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;
return AjaxRequestAction.ABORT;
},
onAjaxProgressEvent: (InAppWebViewController controller, AjaxRequest ajaxRequest) async {
onAjaxProgress: (InAppWebViewController controller, AjaxRequest ajaxRequest) async {
print("AJAX EVENT: ${ajaxRequest.method} - ${ajaxRequest.url}, ${ajaxRequest.event.type}, LOADED: ${ajaxRequest.event.loaded}, ${ajaxRequest.responseHeaders}");
return null;
return AjaxRequestAction.ABORT;
},
shouldInterceptFetchRequest: (InAppWebViewController controller, FetchRequest fetchRequest) async {
print("FETCH REQUEST: ${fetchRequest.method} - ${fetchRequest.url}");
fetchRequest.action = FetchRequestAction.ABORT;
return fetchRequest;
},
onNavigationStateChange: (InAppWebViewController controller, String url) async {
print("NAVIGATION STATE CHANGE: ${url}");
setState(() {
this.url = url;
});
},
),
),
),
......
......@@ -124,7 +124,7 @@ function wkwebview_FindAllAsyncForElement(element, keyword) {
value.substr(idx + keyword.length)
);
window.webkit.messageHandlers["findResultReceived"].postMessage(
window.webkit.messageHandlers["onFindResultReceived"].postMessage(
JSON.stringify({
activeMatchOrdinal: wkwebview_CurrentHighlight,
numberOfMatches: wkwebview_SearchResultCount,
......@@ -154,7 +154,7 @@ function wkwebview_FindAllAsync(keyword) {
wkwebview_ClearMatches();
wkwebview_FindAllAsyncForElement(document.body, keyword.toLowerCase());
wkwebview_IsDoneCounting = true;
window.webkit.messageHandlers["findResultReceived"].postMessage(
window.webkit.messageHandlers["onFindResultReceived"].postMessage(
JSON.stringify({
activeMatchOrdinal: wkwebview_CurrentHighlight,
numberOfMatches: wkwebview_SearchResultCount,
......@@ -221,7 +221,7 @@ function wkwebview_FindNext(forward) {
block: "center"
});
window.webkit.messageHandlers["findResultReceived"].postMessage(
window.webkit.messageHandlers["onFindResultReceived"].postMessage(
JSON.stringify({
activeMatchOrdinal: wkwebview_CurrentHighlight,
numberOfMatches: wkwebview_SearchResultCount,
......@@ -291,9 +291,9 @@ let interceptAjaxRequestsJS = """
lengthComputable: e.lengthComputable
}
};
window.\(JAVASCRIPT_BRIDGE_NAME).callHandler('onAjaxProgressEvent', ajaxRequest).then(function(result) {
window.\(JAVASCRIPT_BRIDGE_NAME).callHandler('onAjaxProgress', ajaxRequest).then(function(result) {
if (result != null) {
switch (result.action) {
switch (result) {
case 0:
self.abort();
return;
......@@ -334,7 +334,7 @@ let interceptAjaxRequestsJS = """
};
window.\(JAVASCRIPT_BRIDGE_NAME).callHandler('onAjaxReadyStateChange', ajaxRequest).then(function(result) {
if (result != null) {
switch (result.action) {
switch (result) {
case 0:
self.abort();
return;
......@@ -457,6 +457,35 @@ let interceptFetchRequestsJS = """
})(window.fetch);
"""
let interceptNavigationStateChangeJS = """
(function(window, document, history) {
history.pushState = (function(f) {
return function pushState(){
var ret = f.apply(this, arguments);
window.dispatchEvent(new Event('pushstate'));
window.dispatchEvent(new Event('locationchange'));
return ret;
};
})(history.pushState);
history.replaceState = ( function(f) {
return function replaceState(){
var ret = f.apply(this, arguments);
window.dispatchEvent(new Event('replacestate'));
window.dispatchEvent(new Event('locationchange'));
return ret;
};
})(history.replaceState);
window.addEventListener('popstate',function() {
window.dispatchEvent(new Event('locationchange'));
});
window.addEventListener('locationchange', function() {
window.webkit.messageHandlers["onNavigationStateChange"].postMessage(JSON.stringify({
url: document.location.href
}));
});
})(window, window.document, window.history);
"""
public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavigationDelegate, WKScriptMessageHandler {
var IABController: InAppBrowserWebViewController?
......@@ -539,7 +568,12 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
let findTextHighlightJSScript = WKUserScript(source: findTextHighlightJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
configuration.userContentController.addUserScript(findTextHighlightJSScript)
configuration.userContentController.add(self, name: "findResultReceived")
configuration.userContentController.add(self, name: "onFindResultReceived")
let interceptNavigationStateChangeJSScript = WKUserScript(source: interceptNavigationStateChangeJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
configuration.userContentController.addUserScript(interceptNavigationStateChangeJSScript)
configuration.userContentController.add(self, name: "onNavigationStateChange")
if (options?.useShouldInterceptAjaxRequest)! {
let interceptAjaxRequestsJSScript = WKUserScript(source: interceptAjaxRequestsJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
......@@ -659,7 +693,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
override public func observeValue(forKeyPath keyPath: String?, of object: Any?,
change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "estimatedProgress" {
if keyPath == #keyPath(WKWebView.estimatedProgress) {
let progress = Int(estimatedProgress * 100)
onProgressChanged(progress: progress)
}
......@@ -1595,6 +1629,18 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
}
}
public func onNavigationStateChange(url: String) {
var arguments: [String : Any] = [
"url": url
]
if IABController != nil {
arguments["uuid"] = IABController!.uuid
}
if let channel = getChannel() {
channel.invokeMethod("onNavigationStateChange", arguments: arguments)
}
}
public func onScrollChanged(x: Int, y: Int) {
var arguments: [String: Any] = ["x": x, "y": y]
if IABController != nil {
......@@ -1800,7 +1846,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
let _callHandlerID = body["_callHandlerID"] as! Int64
let args = body["args"] as! String
onCallJsHandler(handlerName: handlerName, _callHandlerID: _callHandlerID, args: args)
} else if message.name == "findResultReceived" {
} else if message.name == "onFindResultReceived" {
if let resource = convertToDictionary(text: message.body as! String) {
let activeMatchOrdinal = resource["activeMatchOrdinal"] as! Int
let numberOfMatches = resource["numberOfMatches"] as! Int
......@@ -1808,6 +1854,13 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
self.onFindResultReceived(activeMatchOrdinal: activeMatchOrdinal, numberOfMatches: numberOfMatches, isDoneCounting: isDoneCounting)
}
} else if message.name == "onNavigationStateChange" {
if let resource = convertToDictionary(text: message.body as! String) {
let url = resource["url"] as! String
self.onNavigationStateChange(url: url)
}
}
}
......
......@@ -483,6 +483,36 @@ class InAppBrowser {
}
///
Future<AjaxRequest> shouldInterceptAjaxRequest(AjaxRequest ajaxRequest) {
}
///
Future<AjaxRequestAction> onAjaxReadyStateChange(AjaxRequest ajaxRequest) {
}
///
Future<AjaxRequestAction> onAjaxProgress(AjaxRequest ajaxRequest) {
}
///
Future<FetchRequest> shouldInterceptFetchRequest(FetchRequest fetchRequest) {
}
///Event fired when the navigation state of the [InAppWebView] changes throught the usage of
///javascript **[History API](https://developer.mozilla.org/en-US/docs/Web/API/History_API)** functions (`pushState()`, `replaceState()`) and `onpopstate` event.
///
///Also, the event is fired when the javascript `window.location` changes without reloading the webview (for example appending or modifying an hash to the url).
///
///[url] represents the new url.
void onNavigationStateChange(String url) {
}
void throwIsAlreadyOpened({String message = ''}) {
if (this.isOpened()) {
throw Exception(['Error: ${ (message.isEmpty) ? '' : message + ' '}The browser is already opened.']);
......
......@@ -179,14 +179,22 @@ class InAppWebView extends StatefulWidget {
final Future<AjaxRequest> Function(InAppWebViewController controller, AjaxRequest ajaxRequest) shouldInterceptAjaxRequest;
///
final Future<AjaxRequest> Function(InAppWebViewController controller, AjaxRequest ajaxRequest) onAjaxReadyStateChange;
final Future<AjaxRequestAction> Function(InAppWebViewController controller, AjaxRequest ajaxRequest) onAjaxReadyStateChange;
///
final Future<AjaxRequest> Function(InAppWebViewController controller, AjaxRequest ajaxRequest) onAjaxProgressEvent;
final Future<AjaxRequestAction> Function(InAppWebViewController controller, AjaxRequest ajaxRequest) onAjaxProgress;
///
final Future<FetchRequest> Function(InAppWebViewController controller, FetchRequest fetchRequest) shouldInterceptFetchRequest;
///Event fired when the navigation state of the [InAppWebView] changes throught the usage of
///javascript **[History API](https://developer.mozilla.org/en-US/docs/Web/API/History_API)** functions (`pushState()`, `replaceState()`) and `onpopstate` event.
///
///Also, the event is fired when the javascript `window.location` changes without reloading the webview (for example appending or modifying an hash to the url).
///
///[url] represents the new url.
final void Function(InAppWebViewController controller, String url) onNavigationStateChange;
///Initial url that will be loaded.
final String initialUrl;
///Initial asset file that will be loaded. See [InAppWebView.loadFile()] for explanation.
......@@ -236,8 +244,9 @@ class InAppWebView extends StatefulWidget {
this.onFindResultReceived,
this.shouldInterceptAjaxRequest,
this.onAjaxReadyStateChange,
this.onAjaxProgressEvent,
this.onAjaxProgress,
this.shouldInterceptFetchRequest,
this.onNavigationStateChange,
this.gestureRecognizers,
}) : super(key: key);
......@@ -531,6 +540,13 @@ class InAppWebViewController {
else if (_inAppBrowser != null)
_inAppBrowser.onFindResultReceived(activeMatchOrdinal, numberOfMatches, isDoneCounting);
break;
case "onNavigationStateChange":
String url = call.arguments["url"];
if (_widget != null && _widget.onNavigationStateChange != null)
_widget.onNavigationStateChange(this, url);
else if (_inAppBrowser != null)
_inAppBrowser.onNavigationStateChange(url);
break;
case "onCallJsHandler":
String handlerName = call.arguments["handlerName"];
// decode args to json
......@@ -566,8 +582,8 @@ class InAppWebViewController {
if (_widget != null && _widget.shouldInterceptAjaxRequest != null)
return jsonEncode(await _widget.shouldInterceptAjaxRequest(this, request));
//else if (_inAppBrowser != null)
// return jsonEncode(await _inAppBrowser.shouldInterceptAjaxRequest(request));
else if (_inAppBrowser != null)
return jsonEncode(await _inAppBrowser.shouldInterceptAjaxRequest(request));
return null;
case "onAjaxReadyStateChange":
Map<dynamic, dynamic> argMap = args[0];
......@@ -593,10 +609,10 @@ class InAppWebViewController {
if (_widget != null && _widget.onAjaxReadyStateChange != null)
return jsonEncode(await _widget.onAjaxReadyStateChange(this, request));
//else if (_inAppBrowser != null)
// return jsonEncode(await _inAppBrowser.onAjaxReadyStateChange(request));
else if (_inAppBrowser != null)
return jsonEncode(await _inAppBrowser.onAjaxReadyStateChange(request));
return null;
case "onAjaxProgressEvent":
case "onAjaxProgress":
Map<dynamic, dynamic> argMap = args[0];
dynamic data = argMap["data"];
String method = argMap["method"];
......@@ -621,10 +637,10 @@ class InAppWebViewController {
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));
if (_widget != null && _widget.onAjaxProgress != null)
return jsonEncode(await _widget.onAjaxProgress(this, request));
else if (_inAppBrowser != null)
return jsonEncode(await _inAppBrowser.onAjaxProgress(request));
return null;
case "shouldInterceptFetchRequest":
Map<dynamic, dynamic> argMap = args[0];
......@@ -646,8 +662,8 @@ class InAppWebViewController {
if (_widget != null && _widget.shouldInterceptFetchRequest != null)
return jsonEncode(await _widget.shouldInterceptFetchRequest(this, request));
//else if (_inAppBrowser != null)
// return jsonEncode(await _inAppBrowser.shouldInterceptFetchRequest(request));
else if (_inAppBrowser != null)
return jsonEncode(await _inAppBrowser.shouldInterceptFetchRequest(request));
return null;
}
......
......@@ -702,6 +702,16 @@ class AjaxRequestAction {
static const ABORT = const AjaxRequestAction._internal(0);
static const PROCEED = const AjaxRequestAction._internal(1);
Map<String, dynamic> toMap() {
return {
"action": _value,
};
}
Map<String, dynamic> toJson() {
return this.toMap();
}
}
///
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment