Fixed ajax and fetch interceptor when the data/body sent is not a string, fix #724, updated nodejs test server
This commit is contained in:
parent
bcc5fc68ba
commit
6f356be623
@ -9,6 +9,12 @@
|
|||||||
- Fixed wrong mapping of `NavigationAction` class on Android for `androidHasGesture` and `androidIsRedirect` properties
|
- Fixed wrong mapping of `NavigationAction` class on Android for `androidHasGesture` and `androidIsRedirect` properties
|
||||||
- Fixed "Pull to refresh creating problem in some webpages on Android" [#719](https://github.com/pichillilorenzo/flutter_inappwebview/issues/719)
|
- Fixed "Pull to refresh creating problem in some webpages on Android" [#719](https://github.com/pichillilorenzo/flutter_inappwebview/issues/719)
|
||||||
- Fixed iOS sometimes `scrollView.contentSize` doesn't fit all the `frame.size` available
|
- Fixed iOS sometimes `scrollView.contentSize` doesn't fit all the `frame.size` available
|
||||||
|
- Fixed ajax and fetch interceptor when the data/body sent is not a string
|
||||||
|
- Fixed "InAppLocalhostServer - Error: type 'List<dynamic>' is not a subtype of type 'List<int>' in type cast" [#724](https://github.com/pichillilorenzo/flutter_inappwebview/issues/724)
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
- `FetchRequest.body` is a dynamic type now
|
||||||
|
|
||||||
## 5.1.0+4
|
## 5.1.0+4
|
||||||
|
|
||||||
|
@ -274,4 +274,17 @@ public class Util {
|
|||||||
public static String replaceAll(String s, String oldString, String newString) {
|
public static String replaceAll(String s, String oldString, String newString) {
|
||||||
return TextUtils.join(newString, s.split(Pattern.quote(oldString)));
|
return TextUtils.join(newString, s.split(Pattern.quote(oldString)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void log(String tag, String message) {
|
||||||
|
// Split by line, then ensure each line can fit into Log's maximum length.
|
||||||
|
for (int i = 0, length = message.length(); i < length; i++) {
|
||||||
|
int newline = message.indexOf('\n', i);
|
||||||
|
newline = newline != -1 ? newline : length;
|
||||||
|
do {
|
||||||
|
int end = Math.min(newline, i + 4000);
|
||||||
|
Log.d(tag, message.substring(i, end));
|
||||||
|
i = end;
|
||||||
|
} while (i < newline);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -183,46 +183,63 @@ public class InterceptAjaxRequestJS {
|
|||||||
" this.addEventListener('error', handleEvent);" +
|
" this.addEventListener('error', handleEvent);" +
|
||||||
" this.addEventListener('abort', handleEvent);" +
|
" this.addEventListener('abort', handleEvent);" +
|
||||||
" this.addEventListener('timeout', handleEvent);" +
|
" this.addEventListener('timeout', handleEvent);" +
|
||||||
" var ajaxRequest = {" +
|
" " + JavaScriptBridgeJS.JAVASCRIPT_UTIL_VAR_NAME + ".convertBodyRequest(data).then(function(data) {" +
|
||||||
" data: data," +
|
" var ajaxRequest = {" +
|
||||||
" method: this._flutter_inappwebview_method," +
|
" data: data," +
|
||||||
" url: this._flutter_inappwebview_url," +
|
" method: self._flutter_inappwebview_method," +
|
||||||
" isAsync: this._flutter_inappwebview_isAsync," +
|
" url: self._flutter_inappwebview_url," +
|
||||||
" user: this._flutter_inappwebview_user," +
|
" isAsync: self._flutter_inappwebview_isAsync," +
|
||||||
" password: this._flutter_inappwebview_password," +
|
" user: self._flutter_inappwebview_user," +
|
||||||
" withCredentials: this.withCredentials," +
|
" password: self._flutter_inappwebview_password," +
|
||||||
" headers: this._flutter_inappwebview_request_headers," +
|
" withCredentials: self.withCredentials," +
|
||||||
" responseType: this.responseType" +
|
" headers: self._flutter_inappwebview_request_headers," +
|
||||||
" };" +
|
" responseType: self.responseType" +
|
||||||
" window." + JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_NAME + ".callHandler('shouldInterceptAjaxRequest', ajaxRequest).then(function(result) {" +
|
" };" +
|
||||||
" if (result != null) {" +
|
" window." + JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_NAME + ".callHandler('shouldInterceptAjaxRequest', ajaxRequest).then(function(result) {" +
|
||||||
" switch (result.action) {" +
|
" if (result != null) {" +
|
||||||
" case 0:" +
|
" switch (result.action) {" +
|
||||||
" self.abort();" +
|
" case 0:" +
|
||||||
" return;" +
|
" self.abort();" +
|
||||||
" };" +
|
" return;" +
|
||||||
" data = result.data;" +
|
" };" +
|
||||||
" self.withCredentials = result.withCredentials;" +
|
" if (result.data != null && !" + JavaScriptBridgeJS.JAVASCRIPT_UTIL_VAR_NAME + ".isString(result.data) && result.data.length > 0) {" +
|
||||||
" if (result.responseType != null) {" +
|
" var bodyString = " + JavaScriptBridgeJS.JAVASCRIPT_UTIL_VAR_NAME + ".arrayBufferToString(result.data);" +
|
||||||
" self.responseType = result.responseType;" +
|
" if (" + JavaScriptBridgeJS.JAVASCRIPT_UTIL_VAR_NAME + ".isBodyFormData(bodyString)) {" +
|
||||||
" };" +
|
" var formDataContentType = " + JavaScriptBridgeJS.JAVASCRIPT_UTIL_VAR_NAME + ".getFormDataContentType(bodyString);" +
|
||||||
" for (var header in result.headers) {" +
|
" if (result.headers != null) {" +
|
||||||
" var value = result.headers[header];" +
|
" result.headers['Content-Type'] = result.headers['Content-Type'] == null ? formDataContentType : result.headers['Content-Type'];" +
|
||||||
" var flutter_inappwebview_value = self._flutter_inappwebview_request_headers[header];" +
|
" } else {" +
|
||||||
" if (flutter_inappwebview_value == null) {" +
|
" result.headers = { 'Content-Type': formDataContentType };" +
|
||||||
" self._flutter_inappwebview_request_headers[header] = value;" +
|
" }" +
|
||||||
" } else {" +
|
" }" +
|
||||||
" self._flutter_inappwebview_request_headers[header] += ', ' + value;" +
|
" }" +
|
||||||
|
" if (" + JavaScriptBridgeJS.JAVASCRIPT_UTIL_VAR_NAME + ".isString(result.data) || result.data == null) {" +
|
||||||
|
" data = result.data;" +
|
||||||
|
" } else if (result.data.length > 0) {" +
|
||||||
|
" data = new Uint8Array(result.data);" +
|
||||||
|
" }" +
|
||||||
|
" self.withCredentials = result.withCredentials;" +
|
||||||
|
" if (result.responseType != null) {" +
|
||||||
|
" self.responseType = result.responseType;" +
|
||||||
|
" };" +
|
||||||
|
" for (var header in result.headers) {" +
|
||||||
|
" var value = result.headers[header];" +
|
||||||
|
" var flutter_inappwebview_value = self._flutter_inappwebview_request_headers[header];" +
|
||||||
|
" if (flutter_inappwebview_value == null) {" +
|
||||||
|
" self._flutter_inappwebview_request_headers[header] = value;" +
|
||||||
|
" } else {" +
|
||||||
|
" self._flutter_inappwebview_request_headers[header] += ', ' + value;" +
|
||||||
|
" }" +
|
||||||
|
" setRequestHeader.call(self, header, value);" +
|
||||||
|
" };" +
|
||||||
|
" if ((self._flutter_inappwebview_method != result.method && result.method != null) || (self._flutter_inappwebview_url != result.url && result.url != null)) {" +
|
||||||
|
" self.abort();" +
|
||||||
|
" self.open(result.method, result.url, result.isAsync, result.user, result.password);" +
|
||||||
|
" return;" +
|
||||||
" }" +
|
" }" +
|
||||||
" setRequestHeader.call(self, header, value);" +
|
|
||||||
" };" +
|
|
||||||
" if ((self._flutter_inappwebview_method != result.method && result.method != null) || (self._flutter_inappwebview_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);" +
|
||||||
" send.call(self, data);" +
|
" });" +
|
||||||
" });" +
|
" });" +
|
||||||
" } else {" +
|
" } else {" +
|
||||||
" send.call(this, data);" +
|
" send.call(this, data);" +
|
||||||
|
@ -21,68 +21,6 @@ public class InterceptFetchRequestJS {
|
|||||||
" if (fetch == null) {" +
|
" if (fetch == null) {" +
|
||||||
" return;" +
|
" return;" +
|
||||||
" }" +
|
" }" +
|
||||||
" function convertHeadersToJson(headers) {" +
|
|
||||||
" var headersObj = {};" +
|
|
||||||
" for (var header of headers.keys()) {" +
|
|
||||||
" var value = headers.get(header);" +
|
|
||||||
" headersObj[header] = value;" +
|
|
||||||
" }" +
|
|
||||||
" return headersObj;" +
|
|
||||||
" }" +
|
|
||||||
" function convertJsonToHeaders(headersJson) {" +
|
|
||||||
" return new Headers(headersJson);" +
|
|
||||||
" }" +
|
|
||||||
" function convertBodyToArray(body) {" +
|
|
||||||
" return new Response(body).arrayBuffer().then(function(arrayBuffer) {" +
|
|
||||||
" var arr = Array.from(new Uint8Array(arrayBuffer));" +
|
|
||||||
" return arr;" +
|
|
||||||
" })" +
|
|
||||||
" }" +
|
|
||||||
" function convertArrayIntBodyToUint8Array(arrayIntBody) {" +
|
|
||||||
" return new Uint8Array(arrayIntBody);" +
|
|
||||||
" }" +
|
|
||||||
" function convertCredentialsToJson(credentials) {" +
|
|
||||||
" var credentialsObj = {};" +
|
|
||||||
" if (window.FederatedCredential != null && credentials instanceof FederatedCredential) {" +
|
|
||||||
" credentialsObj.type = credentials.type;" +
|
|
||||||
" credentialsObj.id = credentials.id;" +
|
|
||||||
" credentialsObj.name = credentials.name;" +
|
|
||||||
" credentialsObj.protocol = credentials.protocol;" +
|
|
||||||
" credentialsObj.provider = credentials.provider;" +
|
|
||||||
" credentialsObj.iconURL = credentials.iconURL;" +
|
|
||||||
" } else if (window.PasswordCredential != null && credentials instanceof PasswordCredential) {" +
|
|
||||||
" credentialsObj.type = credentials.type;" +
|
|
||||||
" credentialsObj.id = credentials.id;" +
|
|
||||||
" credentialsObj.name = credentials.name;" +
|
|
||||||
" credentialsObj.password = credentials.password;" +
|
|
||||||
" credentialsObj.iconURL = credentials.iconURL;" +
|
|
||||||
" } else {" +
|
|
||||||
" credentialsObj.type = 'default';" +
|
|
||||||
" credentialsObj.value = credentials;" +
|
|
||||||
" }" +
|
|
||||||
" }" +
|
|
||||||
" function convertJsonToCredential(credentialsJson) {" +
|
|
||||||
" var credentials;" +
|
|
||||||
" if (window.FederatedCredential != null && credentialsJson.type === 'federated') {" +
|
|
||||||
" credentials = new FederatedCredential({" +
|
|
||||||
" id: credentialsJson.id," +
|
|
||||||
" name: credentialsJson.name," +
|
|
||||||
" protocol: credentialsJson.protocol," +
|
|
||||||
" provider: credentialsJson.provider," +
|
|
||||||
" iconURL: credentialsJson.iconURL" +
|
|
||||||
" });" +
|
|
||||||
" } else if (window.PasswordCredential != null && credentialsJson.type === 'password') {" +
|
|
||||||
" credentials = new PasswordCredential({" +
|
|
||||||
" id: credentialsJson.id," +
|
|
||||||
" name: credentialsJson.name," +
|
|
||||||
" password: credentialsJson.password," +
|
|
||||||
" iconURL: credentialsJson.iconURL" +
|
|
||||||
" });" +
|
|
||||||
" } else {" +
|
|
||||||
" credentials = credentialsJson;" +
|
|
||||||
" }" +
|
|
||||||
" return credentials;" +
|
|
||||||
" }" +
|
|
||||||
" window.fetch = async function(resource, init) {" +
|
" window.fetch = async function(resource, init) {" +
|
||||||
" var w = (window.top == null || window.top === window) ? window : window.top;" +
|
" var w = (window.top == null || window.top === window) ? window : window.top;" +
|
||||||
" if (w." + FLAG_VARIABLE_FOR_SHOULD_INTERCEPT_FETCH_REQUEST_JS_SOURCE + " == null || w." + FLAG_VARIABLE_FOR_SHOULD_INTERCEPT_FETCH_REQUEST_JS_SOURCE + " == true) {" +
|
" if (w." + FLAG_VARIABLE_FOR_SHOULD_INTERCEPT_FETCH_REQUEST_JS_SOURCE + " == null || w." + FLAG_VARIABLE_FOR_SHOULD_INTERCEPT_FETCH_REQUEST_JS_SOURCE + " == true) {" +
|
||||||
@ -114,7 +52,7 @@ public class InterceptFetchRequestJS {
|
|||||||
" fetchRequest.integrity = resource.integrity;" +
|
" fetchRequest.integrity = resource.integrity;" +
|
||||||
" fetchRequest.keepalive = resource.keepalive;" +
|
" fetchRequest.keepalive = resource.keepalive;" +
|
||||||
" } else {" +
|
" } else {" +
|
||||||
" fetchRequest.url = resource;" +
|
" fetchRequest.url = resource != null ? resource.toString() : null;" +
|
||||||
" if (init != null) {" +
|
" if (init != null) {" +
|
||||||
" fetchRequest.method = init.method;" +
|
" fetchRequest.method = init.method;" +
|
||||||
" fetchRequest.headers = init.headers;" +
|
" fetchRequest.headers = init.headers;" +
|
||||||
@ -130,10 +68,10 @@ public class InterceptFetchRequestJS {
|
|||||||
" }" +
|
" }" +
|
||||||
" }" +
|
" }" +
|
||||||
" if (fetchRequest.headers instanceof Headers) {" +
|
" if (fetchRequest.headers instanceof Headers) {" +
|
||||||
" fetchRequest.headers = convertHeadersToJson(fetchRequest.headers);" +
|
" fetchRequest.headers = " + JavaScriptBridgeJS.JAVASCRIPT_UTIL_VAR_NAME + ".convertHeadersToJson(fetchRequest.headers);" +
|
||||||
" }" +
|
" }" +
|
||||||
" fetchRequest.credentials = convertCredentialsToJson(fetchRequest.credentials);" +
|
" fetchRequest.credentials = " + JavaScriptBridgeJS.JAVASCRIPT_UTIL_VAR_NAME + ".convertCredentialsToJson(fetchRequest.credentials);" +
|
||||||
" return convertBodyToArray(fetchRequest.body).then(function(body) {" +
|
" return " + JavaScriptBridgeJS.JAVASCRIPT_UTIL_VAR_NAME + ".convertBodyRequest(fetchRequest.body).then(function(body) {" +
|
||||||
" fetchRequest.body = body;" +
|
" fetchRequest.body = body;" +
|
||||||
" return window." + JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_NAME + ".callHandler('shouldInterceptFetchRequest', fetchRequest).then(function(result) {" +
|
" return window." + JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_NAME + ".callHandler('shouldInterceptFetchRequest', fetchRequest).then(function(result) {" +
|
||||||
" if (result != null) {" +
|
" if (result != null) {" +
|
||||||
@ -150,7 +88,18 @@ public class InterceptFetchRequestJS {
|
|||||||
" controller.abort();" +
|
" controller.abort();" +
|
||||||
" break;" +
|
" break;" +
|
||||||
" }" +
|
" }" +
|
||||||
" resource = (result.url != null) ? result.url : resource;" +
|
" if (result.body != null && !" + JavaScriptBridgeJS.JAVASCRIPT_UTIL_VAR_NAME + ".isString(result.body) && result.body.length > 0) {" +
|
||||||
|
" var bodyString = " + JavaScriptBridgeJS.JAVASCRIPT_UTIL_VAR_NAME + ".arrayBufferToString(result.body);" +
|
||||||
|
" if (" + JavaScriptBridgeJS.JAVASCRIPT_UTIL_VAR_NAME + ".isBodyFormData(bodyString)) {" +
|
||||||
|
" var formDataContentType = " + JavaScriptBridgeJS.JAVASCRIPT_UTIL_VAR_NAME + ".getFormDataContentType(bodyString);" +
|
||||||
|
" if (result.headers != null) {" +
|
||||||
|
" result.headers['Content-Type'] = result.headers['Content-Type'] == null ? formDataContentType : result.headers['Content-Type'];" +
|
||||||
|
" } else {" +
|
||||||
|
" result.headers = { 'Content-Type': formDataContentType };" +
|
||||||
|
" }" +
|
||||||
|
" }" +
|
||||||
|
" }" +
|
||||||
|
" resource = result.url;" +
|
||||||
" if (init == null) {" +
|
" if (init == null) {" +
|
||||||
" init = {};" +
|
" init = {};" +
|
||||||
" }" +
|
" }" +
|
||||||
@ -158,16 +107,18 @@ public class InterceptFetchRequestJS {
|
|||||||
" init.method = result.method;" +
|
" init.method = result.method;" +
|
||||||
" }" +
|
" }" +
|
||||||
" if (result.headers != null && Object.keys(result.headers).length > 0) {" +
|
" if (result.headers != null && Object.keys(result.headers).length > 0) {" +
|
||||||
" init.headers = convertJsonToHeaders(result.headers);" +
|
" init.headers = " + JavaScriptBridgeJS.JAVASCRIPT_UTIL_VAR_NAME + ".convertJsonToHeaders(result.headers);" +
|
||||||
" }" +
|
" }" +
|
||||||
" if (result.body != null && result.body.length > 0) {" +
|
" if (" + JavaScriptBridgeJS.JAVASCRIPT_UTIL_VAR_NAME + ".isString(result.body) || result.body == null) {" +
|
||||||
" init.body = convertArrayIntBodyToUint8Array(result.body);" +
|
" init.body = result.body;" +
|
||||||
|
" } else if (result.body.length > 0) {" +
|
||||||
|
" init.body = new Uint8Array(result.body);" +
|
||||||
" }" +
|
" }" +
|
||||||
" if (result.mode != null && result.mode.length > 0) {" +
|
" if (result.mode != null && result.mode.length > 0) {" +
|
||||||
" init.mode = result.mode;" +
|
" init.mode = result.mode;" +
|
||||||
" }" +
|
" }" +
|
||||||
" if (result.credentials != null) {" +
|
" if (result.credentials != null) {" +
|
||||||
" init.credentials = convertJsonToCredential(result.credentials);" +
|
" init.credentials = " + JavaScriptBridgeJS.JAVASCRIPT_UTIL_VAR_NAME + ".convertJsonToCredential(result.credentials);" +
|
||||||
" }" +
|
" }" +
|
||||||
" if (result.cache != null && result.cache.length > 0) {" +
|
" if (result.cache != null && result.cache.length > 0) {" +
|
||||||
" init.cache = result.cache;" +
|
" init.cache = result.cache;" +
|
||||||
|
@ -14,6 +14,203 @@ public class JavaScriptBridgeJS {
|
|||||||
true
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
|
public static final String JAVASCRIPT_UTIL_VAR_NAME = "window." + JAVASCRIPT_BRIDGE_NAME + "._Util";
|
||||||
|
|
||||||
|
public static final String UTIL_JS_SOURCE = JAVASCRIPT_UTIL_VAR_NAME + " = {" +
|
||||||
|
" support: {" +
|
||||||
|
" searchParams: 'URLSearchParams' in window," +
|
||||||
|
" iterable: 'Symbol' in window && 'iterator' in Symbol," +
|
||||||
|
" blob:" +
|
||||||
|
" 'FileReader' in window &&" +
|
||||||
|
" 'Blob' in window &&" +
|
||||||
|
" (function() {" +
|
||||||
|
" try {" +
|
||||||
|
" new Blob();" +
|
||||||
|
" return true;" +
|
||||||
|
" } catch (e) {" +
|
||||||
|
" return false;" +
|
||||||
|
" }" +
|
||||||
|
" })()," +
|
||||||
|
" formData: 'FormData' in window," +
|
||||||
|
" arrayBuffer: 'ArrayBuffer' in window" +
|
||||||
|
" }," +
|
||||||
|
" isDataView: function(obj) {" +
|
||||||
|
" return obj && DataView.prototype.isPrototypeOf(obj);" +
|
||||||
|
" }," +
|
||||||
|
" fileReaderReady: function(reader) {" +
|
||||||
|
" return new Promise(function(resolve, reject) {" +
|
||||||
|
" reader.onload = function() {" +
|
||||||
|
" resolve(reader.result);" +
|
||||||
|
" };" +
|
||||||
|
" reader.onerror = function() {" +
|
||||||
|
" reject(reader.error);" +
|
||||||
|
" };" +
|
||||||
|
" });" +
|
||||||
|
" }," +
|
||||||
|
" readBlobAsArrayBuffer: function(blob) {" +
|
||||||
|
" var reader = new FileReader();" +
|
||||||
|
" var promise = " + JAVASCRIPT_UTIL_VAR_NAME + ".fileReaderReady(reader);" +
|
||||||
|
" reader.readAsArrayBuffer(blob);" +
|
||||||
|
" return promise;" +
|
||||||
|
" }," +
|
||||||
|
" convertBodyToArrayBuffer: function(body) {" +
|
||||||
|
" var viewClasses = [" +
|
||||||
|
" '[object Int8Array]'," +
|
||||||
|
" '[object Uint8Array]'," +
|
||||||
|
" '[object Uint8ClampedArray]'," +
|
||||||
|
" '[object Int16Array]'," +
|
||||||
|
" '[object Uint16Array]'," +
|
||||||
|
" '[object Int32Array]'," +
|
||||||
|
" '[object Uint32Array]'," +
|
||||||
|
" '[object Float32Array]'," +
|
||||||
|
" '[object Float64Array]'" +
|
||||||
|
" ];" +
|
||||||
|
" var isArrayBufferView = null;" +
|
||||||
|
" if (" + JAVASCRIPT_UTIL_VAR_NAME + ".support.arrayBuffer) {" +
|
||||||
|
" isArrayBufferView =" +
|
||||||
|
" ArrayBuffer.isView ||" +
|
||||||
|
" function(obj) {" +
|
||||||
|
" return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1;" +
|
||||||
|
" };" +
|
||||||
|
" }" +
|
||||||
|
" var bodyUsed = false;" +
|
||||||
|
" this._bodyInit = body;" +
|
||||||
|
" if (!body) {" +
|
||||||
|
" this._bodyText = '';" +
|
||||||
|
" } else if (typeof body === 'string') {" +
|
||||||
|
" this._bodyText = body;" +
|
||||||
|
" } else if (" + JAVASCRIPT_UTIL_VAR_NAME + ".support.blob && Blob.prototype.isPrototypeOf(body)) {" +
|
||||||
|
" this._bodyBlob = body;" +
|
||||||
|
" } else if (" + JAVASCRIPT_UTIL_VAR_NAME + ".support.formData && FormData.prototype.isPrototypeOf(body)) {" +
|
||||||
|
" this._bodyFormData = body;" +
|
||||||
|
" } else if (" + JAVASCRIPT_UTIL_VAR_NAME + ".support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) {" +
|
||||||
|
" this._bodyText = body.toString();" +
|
||||||
|
" } else if (" + JAVASCRIPT_UTIL_VAR_NAME + ".support.arrayBuffer && " + JAVASCRIPT_UTIL_VAR_NAME + ".support.blob && " + JAVASCRIPT_UTIL_VAR_NAME + ".isDataView(body)) {" +
|
||||||
|
" this._bodyArrayBuffer = bufferClone(body.buffer);" +
|
||||||
|
" this._bodyInit = new Blob([this._bodyArrayBuffer]);" +
|
||||||
|
" } else if (" + JAVASCRIPT_UTIL_VAR_NAME + ".support.arrayBuffer && (ArrayBuffer.prototype.isPrototypeOf(body) || isArrayBufferView(body))) {" +
|
||||||
|
" this._bodyArrayBuffer = bufferClone(body);" +
|
||||||
|
" } else {" +
|
||||||
|
" this._bodyText = body = Object.prototype.toString.call(body);" +
|
||||||
|
" }" +
|
||||||
|
" this.blob = function () {" +
|
||||||
|
" if (bodyUsed) {" +
|
||||||
|
" return Promise.reject(new TypeError('Already read'));" +
|
||||||
|
" }" +
|
||||||
|
" bodyUsed = true;" +
|
||||||
|
" if (this._bodyBlob) {" +
|
||||||
|
" return Promise.resolve(this._bodyBlob);" +
|
||||||
|
" } else if (this._bodyArrayBuffer) {" +
|
||||||
|
" return Promise.resolve(new Blob([this._bodyArrayBuffer]));" +
|
||||||
|
" } else if (this._bodyFormData) {" +
|
||||||
|
" throw new Error('could not read FormData body as blob');" +
|
||||||
|
" } else {" +
|
||||||
|
" return Promise.resolve(new Blob([this._bodyText]));" +
|
||||||
|
" }" +
|
||||||
|
" };" +
|
||||||
|
" if (this._bodyArrayBuffer) {" +
|
||||||
|
" if (bodyUsed) {" +
|
||||||
|
" return Promise.reject(new TypeError('Already read'));" +
|
||||||
|
" }" +
|
||||||
|
" bodyUsed = true;" +
|
||||||
|
" if (ArrayBuffer.isView(this._bodyArrayBuffer)) {" +
|
||||||
|
" return Promise.resolve(" +
|
||||||
|
" this._bodyArrayBuffer.buffer.slice(" +
|
||||||
|
" this._bodyArrayBuffer.byteOffset," +
|
||||||
|
" this._bodyArrayBuffer.byteOffset + this._bodyArrayBuffer.byteLength" +
|
||||||
|
" )" +
|
||||||
|
" );" +
|
||||||
|
" } else {" +
|
||||||
|
" return Promise.resolve(this._bodyArrayBuffer);" +
|
||||||
|
" }" +
|
||||||
|
" }" +
|
||||||
|
" return this.blob().then(" + JAVASCRIPT_UTIL_VAR_NAME + ".readBlobAsArrayBuffer);" +
|
||||||
|
" }," +
|
||||||
|
" isString: function(variable) {" +
|
||||||
|
" return typeof variable === 'string' || variable instanceof String;" +
|
||||||
|
" }," +
|
||||||
|
" convertBodyRequest: function(body) {" +
|
||||||
|
" if (body == null) {" +
|
||||||
|
" return new Promise((resolve, reject) => resolve(null));" +
|
||||||
|
" }" +
|
||||||
|
" if (" + JAVASCRIPT_UTIL_VAR_NAME + ".isString(body) || (" + JAVASCRIPT_UTIL_VAR_NAME + ".support.searchParams && body instanceof URLSearchParams)) {" +
|
||||||
|
" return new Promise((resolve, reject) => resolve(body.toString()));" +
|
||||||
|
" }" +
|
||||||
|
" if (window.Response != null) {" +
|
||||||
|
" return new Response(body).arrayBuffer().then(function(arrayBuffer) {" +
|
||||||
|
" return Array.from(new Uint8Array(arrayBuffer));" +
|
||||||
|
" });" +
|
||||||
|
" }" +
|
||||||
|
" return " + JAVASCRIPT_UTIL_VAR_NAME + ".convertBodyToArrayBuffer(body).then(function(arrayBuffer) {" +
|
||||||
|
" return Array.from(new Uint8Array(arrayBuffer));" +
|
||||||
|
" });" +
|
||||||
|
" }," +
|
||||||
|
" arrayBufferToString: function(arrayBuffer) {" +
|
||||||
|
" return String.fromCharCode.apply(String, arrayBuffer);" +
|
||||||
|
" }," +
|
||||||
|
" isBodyFormData: function(bodyString) {" +
|
||||||
|
" return bodyString.indexOf('------WebKitFormBoundary') >= 0;" +
|
||||||
|
" }," +
|
||||||
|
" getFormDataContentType: function(bodyString) {" +
|
||||||
|
" var boundary = bodyString.substr(2, 40);" +
|
||||||
|
" return 'multipart/form-data; boundary=' + boundary;" +
|
||||||
|
" }," +
|
||||||
|
" convertHeadersToJson: function(headers) {" +
|
||||||
|
" var headersObj = {};" +
|
||||||
|
" for (var header of headers.keys()) {" +
|
||||||
|
" var value = headers.get(header);" +
|
||||||
|
" headersObj[header] = value;" +
|
||||||
|
" }" +
|
||||||
|
" return headersObj;" +
|
||||||
|
" }," +
|
||||||
|
" convertJsonToHeaders: function(headersJson) {" +
|
||||||
|
" return new Headers(headersJson);" +
|
||||||
|
" }," +
|
||||||
|
" convertCredentialsToJson: function(credentials) {" +
|
||||||
|
" var credentialsObj = {};" +
|
||||||
|
" if (window.FederatedCredential != null && credentials instanceof FederatedCredential) {" +
|
||||||
|
" credentialsObj.type = credentials.type;" +
|
||||||
|
" credentialsObj.id = credentials.id;" +
|
||||||
|
" credentialsObj.name = credentials.name;" +
|
||||||
|
" credentialsObj.protocol = credentials.protocol;" +
|
||||||
|
" credentialsObj.provider = credentials.provider;" +
|
||||||
|
" credentialsObj.iconURL = credentials.iconURL;" +
|
||||||
|
" } else if (window.PasswordCredential != null && credentials instanceof PasswordCredential) {" +
|
||||||
|
" credentialsObj.type = credentials.type;" +
|
||||||
|
" credentialsObj.id = credentials.id;" +
|
||||||
|
" credentialsObj.name = credentials.name;" +
|
||||||
|
" credentialsObj.password = credentials.password;" +
|
||||||
|
" credentialsObj.iconURL = credentials.iconURL;" +
|
||||||
|
" } else {" +
|
||||||
|
" credentialsObj.type = 'default';" +
|
||||||
|
" credentialsObj.value = credentials;" +
|
||||||
|
" }" +
|
||||||
|
" return credentialsObj;" +
|
||||||
|
" }," +
|
||||||
|
" convertJsonToCredential: function(credentialsJson) {" +
|
||||||
|
" var credentials;" +
|
||||||
|
" if (window.FederatedCredential != null && credentialsJson.type === 'federated') {" +
|
||||||
|
" credentials = new FederatedCredential({" +
|
||||||
|
" id: credentialsJson.id," +
|
||||||
|
" name: credentialsJson.name," +
|
||||||
|
" protocol: credentialsJson.protocol," +
|
||||||
|
" provider: credentialsJson.provider," +
|
||||||
|
" iconURL: credentialsJson.iconURL" +
|
||||||
|
" });" +
|
||||||
|
" } else if (window.PasswordCredential != null && credentialsJson.type === 'password') {" +
|
||||||
|
" credentials = new PasswordCredential({" +
|
||||||
|
" id: credentialsJson.id," +
|
||||||
|
" name: credentialsJson.name," +
|
||||||
|
" password: credentialsJson.password," +
|
||||||
|
" iconURL: credentialsJson.iconURL" +
|
||||||
|
" });" +
|
||||||
|
" } else {" +
|
||||||
|
" credentials = credentialsJson.value == null ? undefined : credentialsJson.value;" +
|
||||||
|
" }" +
|
||||||
|
" return credentials;" +
|
||||||
|
" }" +
|
||||||
|
"};";
|
||||||
|
|
||||||
public static final String JAVASCRIPT_BRIDGE_JS_SOURCE = "if (window.top == null || window.top === window) {" +
|
public static final String JAVASCRIPT_BRIDGE_JS_SOURCE = "if (window.top == null || window.top === window) {" +
|
||||||
" window." + JAVASCRIPT_BRIDGE_NAME + ".callHandler = function() {" +
|
" window." + JAVASCRIPT_BRIDGE_NAME + ".callHandler = function() {" +
|
||||||
" var _callHandlerID = setTimeout(function(){});" +
|
" var _callHandlerID = setTimeout(function(){});" +
|
||||||
@ -30,8 +227,9 @@ public class JavaScriptBridgeJS {
|
|||||||
" return new Promise(function(resolve, reject) {" +
|
" return new Promise(function(resolve, reject) {" +
|
||||||
" window.top." + JAVASCRIPT_BRIDGE_NAME + "[_callHandlerID] = resolve;" +
|
" window.top." + JAVASCRIPT_BRIDGE_NAME + "[_callHandlerID] = resolve;" +
|
||||||
" });" +
|
" });" +
|
||||||
" };"+
|
" };" +
|
||||||
"}";
|
"}" +
|
||||||
|
UTIL_JS_SOURCE;
|
||||||
|
|
||||||
public static final String PLATFORM_READY_JS_SOURCE = "(function() {" +
|
public static final String PLATFORM_READY_JS_SOURCE = "(function() {" +
|
||||||
" if ((window.top == null || window.top === window) && window." + JAVASCRIPT_BRIDGE_NAME + "._platformReady == null) {" +
|
" if ((window.top == null || window.top === window) && window." + JAVASCRIPT_BRIDGE_NAME + "._platformReady == null) {" +
|
||||||
|
@ -1706,20 +1706,21 @@ void main() {
|
|||||||
skip: !Platform.isAndroid,
|
skip: !Platform.isAndroid,
|
||||||
);
|
);
|
||||||
|
|
||||||
testWidgets('intercept ajax request', (WidgetTester tester) async {
|
group('intercept ajax request', () {
|
||||||
final Completer controllerCompleter = Completer<InAppWebViewController>();
|
testWidgets('send string data', (WidgetTester tester) async {
|
||||||
final Completer shouldInterceptAjaxPostRequestCompleter =
|
final Completer controllerCompleter = Completer<InAppWebViewController>();
|
||||||
Completer<void>();
|
final Completer shouldInterceptAjaxPostRequestCompleter =
|
||||||
final Completer<Map<String, dynamic>> onAjaxReadyStateChangeCompleter =
|
Completer<void>();
|
||||||
Completer<Map<String, dynamic>>();
|
final Completer<Map<String, dynamic>> onAjaxReadyStateChangeCompleter =
|
||||||
final Completer<Map<String, dynamic>> onAjaxProgressCompleter =
|
Completer<Map<String, dynamic>>();
|
||||||
Completer<Map<String, dynamic>>();
|
final Completer<Map<String, dynamic>> onAjaxProgressCompleter =
|
||||||
await tester.pumpWidget(
|
Completer<Map<String, dynamic>>();
|
||||||
Directionality(
|
await tester.pumpWidget(
|
||||||
textDirection: TextDirection.ltr,
|
Directionality(
|
||||||
child: InAppWebView(
|
textDirection: TextDirection.ltr,
|
||||||
key: GlobalKey(),
|
child: InAppWebView(
|
||||||
initialData: InAppWebViewInitialData(data: """
|
key: GlobalKey(),
|
||||||
|
initialData: InAppWebViewInitialData(data: """
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
@ -1736,117 +1737,347 @@ void main() {
|
|||||||
xhttp.open("POST", "http://${environment["NODE_SERVER_IP"]}:8082/test-ajax-post");
|
xhttp.open("POST", "http://${environment["NODE_SERVER_IP"]}:8082/test-ajax-post");
|
||||||
xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
|
xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
|
||||||
xhttp.send("firstname=Foo&lastname=Bar");
|
xhttp.send("firstname=Foo&lastname=Bar");
|
||||||
|
|
||||||
var xhttp2 = new XMLHttpRequest();
|
|
||||||
xhttp2.open("GET", "http://${environment["NODE_SERVER_IP"]}:8082/test-download-file");
|
|
||||||
xhttp2.send();
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
"""),
|
"""),
|
||||||
initialOptions: InAppWebViewGroupOptions(
|
initialOptions: InAppWebViewGroupOptions(
|
||||||
crossPlatform: InAppWebViewOptions(
|
crossPlatform: InAppWebViewOptions(
|
||||||
clearCache: true,
|
clearCache: true,
|
||||||
useShouldInterceptAjaxRequest: true,
|
useShouldInterceptAjaxRequest: true,
|
||||||
)),
|
)),
|
||||||
onWebViewCreated: (controller) {
|
onWebViewCreated: (controller) {
|
||||||
controllerCompleter.complete(controller);
|
controllerCompleter.complete(controller);
|
||||||
},
|
},
|
||||||
shouldInterceptAjaxRequest: (controller, ajaxRequest) async {
|
shouldInterceptAjaxRequest: (controller, ajaxRequest) async {
|
||||||
if (ajaxRequest.url!.toString().endsWith("/test-ajax-post")) {
|
expect(ajaxRequest.data, "firstname=Foo&lastname=Bar");
|
||||||
|
|
||||||
ajaxRequest.responseType = 'json';
|
ajaxRequest.responseType = 'json';
|
||||||
ajaxRequest.data = "firstname=Foo2&lastname=Bar2";
|
ajaxRequest.data = "firstname=Foo2&lastname=Bar2";
|
||||||
shouldInterceptAjaxPostRequestCompleter.complete(controller);
|
shouldInterceptAjaxPostRequestCompleter.complete(controller);
|
||||||
}
|
return ajaxRequest;
|
||||||
return ajaxRequest;
|
},
|
||||||
},
|
onAjaxReadyStateChange: (controller, ajaxRequest) async {
|
||||||
onAjaxReadyStateChange: (controller, ajaxRequest) async {
|
if (ajaxRequest.readyState == AjaxRequestReadyState.DONE &&
|
||||||
if (ajaxRequest.readyState == AjaxRequestReadyState.DONE &&
|
ajaxRequest.status == 200) {
|
||||||
ajaxRequest.status == 200 &&
|
Map<String, dynamic> res = ajaxRequest.response;
|
||||||
ajaxRequest.url!.toString().endsWith("/test-ajax-post")) {
|
onAjaxReadyStateChangeCompleter.complete(res);
|
||||||
Map<String, dynamic> res = ajaxRequest.response;
|
}
|
||||||
onAjaxReadyStateChangeCompleter.complete(res);
|
return AjaxRequestAction.PROCEED;
|
||||||
}
|
},
|
||||||
return AjaxRequestAction.PROCEED;
|
onAjaxProgress: (controller, ajaxRequest) async {
|
||||||
},
|
if (ajaxRequest.event!.type == AjaxRequestEventType.LOAD) {
|
||||||
onAjaxProgress: (controller, ajaxRequest) async {
|
Map<String, dynamic> res = ajaxRequest.response;
|
||||||
if (ajaxRequest.event!.type == AjaxRequestEventType.LOAD &&
|
onAjaxProgressCompleter.complete(res);
|
||||||
ajaxRequest.url!.toString().endsWith("/test-ajax-post")) {
|
}
|
||||||
Map<String, dynamic> res = ajaxRequest.response;
|
return AjaxRequestAction.PROCEED;
|
||||||
onAjaxProgressCompleter.complete(res);
|
},
|
||||||
}
|
),
|
||||||
return AjaxRequestAction.PROCEED;
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
);
|
|
||||||
|
|
||||||
await shouldInterceptAjaxPostRequestCompleter.future;
|
await shouldInterceptAjaxPostRequestCompleter.future;
|
||||||
final Map<String, dynamic> onAjaxReadyStateChangeValue =
|
final Map<String, dynamic> onAjaxReadyStateChangeValue =
|
||||||
await onAjaxReadyStateChangeCompleter.future;
|
await onAjaxReadyStateChangeCompleter.future;
|
||||||
final Map<String, dynamic> onAjaxProgressValue =
|
final Map<String, dynamic> onAjaxProgressValue =
|
||||||
await onAjaxProgressCompleter.future;
|
await onAjaxProgressCompleter.future;
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
mapEquals(onAjaxReadyStateChangeValue,
|
mapEquals(onAjaxReadyStateChangeValue,
|
||||||
{'firstname': 'Foo2', 'lastname': 'Bar2'}),
|
{'firstname': 'Foo2', 'lastname': 'Bar2'}),
|
||||||
true);
|
true);
|
||||||
expect(
|
expect(
|
||||||
mapEquals(
|
mapEquals(
|
||||||
onAjaxProgressValue, {'firstname': 'Foo2', 'lastname': 'Bar2'}),
|
onAjaxProgressValue, {'firstname': 'Foo2', 'lastname': 'Bar2'}),
|
||||||
true);
|
true);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('send json data', (WidgetTester tester) async {
|
||||||
|
final Completer controllerCompleter = Completer<InAppWebViewController>();
|
||||||
|
final Completer shouldInterceptAjaxPostRequestCompleter =
|
||||||
|
Completer<void>();
|
||||||
|
final Completer<Map<String, dynamic>> onAjaxReadyStateChangeCompleter =
|
||||||
|
Completer<Map<String, dynamic>>();
|
||||||
|
final Completer<Map<String, dynamic>> onAjaxProgressCompleter =
|
||||||
|
Completer<Map<String, dynamic>>();
|
||||||
|
await tester.pumpWidget(
|
||||||
|
Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: InAppWebView(
|
||||||
|
key: GlobalKey(),
|
||||||
|
initialData: InAppWebViewInitialData(data: """
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||||
|
<title>InAppWebViewAjaxTest</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>InAppWebViewAjaxTest</h1>
|
||||||
|
<script>
|
||||||
|
window.addEventListener('flutterInAppWebViewPlatformReady', function(event) {
|
||||||
|
var jsonData = {
|
||||||
|
firstname: 'Foo',
|
||||||
|
lastname: 'Bar'
|
||||||
|
};
|
||||||
|
var xhttp = new XMLHttpRequest();
|
||||||
|
xhttp.open("POST", "http://${environment["NODE_SERVER_IP"]}:8082/test-ajax-post");
|
||||||
|
xhttp.setRequestHeader("Content-type", "application/json");
|
||||||
|
xhttp.send(JSON.stringify(jsonData));
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""),
|
||||||
|
initialOptions: InAppWebViewGroupOptions(
|
||||||
|
crossPlatform: InAppWebViewOptions(
|
||||||
|
clearCache: true,
|
||||||
|
useShouldInterceptAjaxRequest: true,
|
||||||
|
)),
|
||||||
|
onWebViewCreated: (controller) {
|
||||||
|
controllerCompleter.complete(controller);
|
||||||
|
},
|
||||||
|
shouldInterceptAjaxRequest: (controller, ajaxRequest) async {
|
||||||
|
expect(ajaxRequest.data, '{"firstname":"Foo","lastname":"Bar"}');
|
||||||
|
|
||||||
|
ajaxRequest.responseType = 'json';
|
||||||
|
ajaxRequest.data = "{'firstname': 'Foo2', 'lastname': 'Bar2'}";
|
||||||
|
shouldInterceptAjaxPostRequestCompleter.complete(controller);
|
||||||
|
return ajaxRequest;
|
||||||
|
},
|
||||||
|
onAjaxReadyStateChange: (controller, ajaxRequest) async {
|
||||||
|
if (ajaxRequest.readyState == AjaxRequestReadyState.DONE &&
|
||||||
|
ajaxRequest.status == 200) {
|
||||||
|
Map<String, dynamic> res = ajaxRequest.response;
|
||||||
|
onAjaxReadyStateChangeCompleter.complete(res);
|
||||||
|
}
|
||||||
|
return AjaxRequestAction.PROCEED;
|
||||||
|
},
|
||||||
|
onAjaxProgress: (controller, ajaxRequest) async {
|
||||||
|
if (ajaxRequest.event!.type == AjaxRequestEventType.LOAD) {
|
||||||
|
Map<String, dynamic> res = ajaxRequest.response;
|
||||||
|
onAjaxProgressCompleter.complete(res);
|
||||||
|
}
|
||||||
|
return AjaxRequestAction.PROCEED;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await shouldInterceptAjaxPostRequestCompleter.future;
|
||||||
|
final Map<String, dynamic> onAjaxReadyStateChangeValue =
|
||||||
|
await onAjaxReadyStateChangeCompleter.future;
|
||||||
|
final Map<String, dynamic> onAjaxProgressValue =
|
||||||
|
await onAjaxProgressCompleter.future;
|
||||||
|
|
||||||
|
expect(
|
||||||
|
mapEquals(onAjaxReadyStateChangeValue,
|
||||||
|
{'firstname': 'Foo2', 'lastname': 'Bar2'}),
|
||||||
|
true);
|
||||||
|
expect(
|
||||||
|
mapEquals(
|
||||||
|
onAjaxProgressValue, {'firstname': 'Foo2', 'lastname': 'Bar2'}),
|
||||||
|
true);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('send URLSearchParams data', (WidgetTester tester) async {
|
||||||
|
final Completer controllerCompleter = Completer<InAppWebViewController>();
|
||||||
|
final Completer shouldInterceptAjaxPostRequestCompleter =
|
||||||
|
Completer<void>();
|
||||||
|
final Completer<Map<String, dynamic>> onAjaxReadyStateChangeCompleter =
|
||||||
|
Completer<Map<String, dynamic>>();
|
||||||
|
final Completer<Map<String, dynamic>> onAjaxProgressCompleter =
|
||||||
|
Completer<Map<String, dynamic>>();
|
||||||
|
await tester.pumpWidget(
|
||||||
|
Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: InAppWebView(
|
||||||
|
key: GlobalKey(),
|
||||||
|
initialData: InAppWebViewInitialData(data: """
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||||
|
<title>InAppWebViewAjaxTest</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>InAppWebViewAjaxTest</h1>
|
||||||
|
<script>
|
||||||
|
window.addEventListener('flutterInAppWebViewPlatformReady', function(event) {
|
||||||
|
var paramsString = "firstname=Foo&lastname=Bar";
|
||||||
|
var searchParams = new URLSearchParams(paramsString);
|
||||||
|
var xhttp = new XMLHttpRequest();
|
||||||
|
xhttp.open("POST", "http://${environment["NODE_SERVER_IP"]}:8082/test-ajax-post");
|
||||||
|
xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
|
||||||
|
xhttp.send(searchParams);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""),
|
||||||
|
initialOptions: InAppWebViewGroupOptions(
|
||||||
|
crossPlatform: InAppWebViewOptions(
|
||||||
|
clearCache: true,
|
||||||
|
useShouldInterceptAjaxRequest: true,
|
||||||
|
)),
|
||||||
|
onWebViewCreated: (controller) {
|
||||||
|
controllerCompleter.complete(controller);
|
||||||
|
},
|
||||||
|
shouldInterceptAjaxRequest: (controller, ajaxRequest) async {
|
||||||
|
expect(ajaxRequest.data, "firstname=Foo&lastname=Bar");
|
||||||
|
|
||||||
|
ajaxRequest.responseType = 'json';
|
||||||
|
ajaxRequest.data = "firstname=Foo2&lastname=Bar2";
|
||||||
|
shouldInterceptAjaxPostRequestCompleter.complete(controller);
|
||||||
|
return ajaxRequest;
|
||||||
|
},
|
||||||
|
onAjaxReadyStateChange: (controller, ajaxRequest) async {
|
||||||
|
if (ajaxRequest.readyState == AjaxRequestReadyState.DONE &&
|
||||||
|
ajaxRequest.status == 200) {
|
||||||
|
Map<String, dynamic> res = ajaxRequest.response;
|
||||||
|
onAjaxReadyStateChangeCompleter.complete(res);
|
||||||
|
}
|
||||||
|
return AjaxRequestAction.PROCEED;
|
||||||
|
},
|
||||||
|
onAjaxProgress: (controller, ajaxRequest) async {
|
||||||
|
if (ajaxRequest.event!.type == AjaxRequestEventType.LOAD) {
|
||||||
|
Map<String, dynamic> res = ajaxRequest.response;
|
||||||
|
onAjaxProgressCompleter.complete(res);
|
||||||
|
}
|
||||||
|
return AjaxRequestAction.PROCEED;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await shouldInterceptAjaxPostRequestCompleter.future;
|
||||||
|
final Map<String, dynamic> onAjaxReadyStateChangeValue =
|
||||||
|
await onAjaxReadyStateChangeCompleter.future;
|
||||||
|
final Map<String, dynamic> onAjaxProgressValue =
|
||||||
|
await onAjaxProgressCompleter.future;
|
||||||
|
|
||||||
|
expect(
|
||||||
|
mapEquals(onAjaxReadyStateChangeValue,
|
||||||
|
{'firstname': 'Foo2', 'lastname': 'Bar2'}),
|
||||||
|
true);
|
||||||
|
expect(
|
||||||
|
mapEquals(
|
||||||
|
onAjaxProgressValue, {'firstname': 'Foo2', 'lastname': 'Bar2'}),
|
||||||
|
true);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('send FormData', (WidgetTester tester) async {
|
||||||
|
final Completer controllerCompleter = Completer<InAppWebViewController>();
|
||||||
|
final Completer shouldInterceptAjaxPostRequestCompleter =
|
||||||
|
Completer<void>();
|
||||||
|
final Completer<Map<String, dynamic>> onAjaxReadyStateChangeCompleter =
|
||||||
|
Completer<Map<String, dynamic>>();
|
||||||
|
final Completer<Map<String, dynamic>> onAjaxProgressCompleter =
|
||||||
|
Completer<Map<String, dynamic>>();
|
||||||
|
await tester.pumpWidget(
|
||||||
|
Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: InAppWebView(
|
||||||
|
key: GlobalKey(),
|
||||||
|
initialData: InAppWebViewInitialData(data: """
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||||
|
<title>InAppWebViewAjaxTest</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>InAppWebViewAjaxTest</h1>
|
||||||
|
<script>
|
||||||
|
window.addEventListener('flutterInAppWebViewPlatformReady', function(event) {
|
||||||
|
var formData = new FormData();
|
||||||
|
formData.append('firstname', 'Foo');
|
||||||
|
formData.append('lastname', 'Bar');
|
||||||
|
var xhttp = new XMLHttpRequest();
|
||||||
|
xhttp.open("POST", "http://${environment["NODE_SERVER_IP"]}:8082/test-ajax-post");
|
||||||
|
xhttp.send(formData);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""),
|
||||||
|
initialOptions: InAppWebViewGroupOptions(
|
||||||
|
crossPlatform: InAppWebViewOptions(
|
||||||
|
clearCache: true,
|
||||||
|
useShouldInterceptAjaxRequest: true,
|
||||||
|
)),
|
||||||
|
onWebViewCreated: (controller) {
|
||||||
|
controllerCompleter.complete(controller);
|
||||||
|
},
|
||||||
|
shouldInterceptAjaxRequest: (controller, ajaxRequest) async {
|
||||||
|
expect(ajaxRequest.data, isNotNull);
|
||||||
|
|
||||||
|
var body = ajaxRequest.data.cast<int>();
|
||||||
|
var bodyString = String.fromCharCodes(body);
|
||||||
|
expect(bodyString.indexOf("WebKitFormBoundary") >= 0, true);
|
||||||
|
|
||||||
|
ajaxRequest.data = utf8.encode(bodyString.replaceFirst("Foo", "Foo2").replaceFirst("Bar", "Bar2"));
|
||||||
|
ajaxRequest.responseType = 'json';
|
||||||
|
shouldInterceptAjaxPostRequestCompleter.complete(controller);
|
||||||
|
return ajaxRequest;
|
||||||
|
},
|
||||||
|
onAjaxReadyStateChange: (controller, ajaxRequest) async {
|
||||||
|
if (ajaxRequest.readyState == AjaxRequestReadyState.DONE &&
|
||||||
|
ajaxRequest.status == 200) {
|
||||||
|
Map<String, dynamic> res = ajaxRequest.response;
|
||||||
|
onAjaxReadyStateChangeCompleter.complete(res);
|
||||||
|
}
|
||||||
|
return AjaxRequestAction.PROCEED;
|
||||||
|
},
|
||||||
|
onAjaxProgress: (controller, ajaxRequest) async {
|
||||||
|
if (ajaxRequest.event!.type == AjaxRequestEventType.LOAD) {
|
||||||
|
Map<String, dynamic> res = ajaxRequest.response;
|
||||||
|
onAjaxProgressCompleter.complete(res);
|
||||||
|
}
|
||||||
|
return AjaxRequestAction.PROCEED;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await shouldInterceptAjaxPostRequestCompleter.future;
|
||||||
|
final Map<String, dynamic> onAjaxReadyStateChangeValue =
|
||||||
|
await onAjaxReadyStateChangeCompleter.future;
|
||||||
|
final Map<String, dynamic> onAjaxProgressValue =
|
||||||
|
await onAjaxProgressCompleter.future;
|
||||||
|
|
||||||
|
expect(
|
||||||
|
mapEquals(onAjaxReadyStateChangeValue,
|
||||||
|
{'firstname': 'Foo2', 'lastname': 'Bar2'}),
|
||||||
|
true);
|
||||||
|
expect(
|
||||||
|
mapEquals(
|
||||||
|
onAjaxProgressValue, {'firstname': 'Foo2', 'lastname': 'Bar2'}),
|
||||||
|
true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Content Blocker', (WidgetTester tester) async {
|
group('intercept fetch request', () {
|
||||||
final Completer controllerCompleter = Completer<InAppWebViewController>();
|
testWidgets('send string data', (WidgetTester tester) async {
|
||||||
final Completer<void> pageLoaded = Completer<void>();
|
final Completer controllerCompleter = Completer<InAppWebViewController>();
|
||||||
await tester.pumpWidget(
|
final Completer<String> fetchGetCompleter = Completer<String>();
|
||||||
Directionality(
|
final Completer<Map<String, dynamic>> fetchPostCompleter =
|
||||||
textDirection: TextDirection.ltr,
|
Completer<Map<String, dynamic>>();
|
||||||
child: InAppWebView(
|
final Completer<void> shouldInterceptFetchPostRequestCompleter =
|
||||||
key: GlobalKey(),
|
Completer<void>();
|
||||||
initialUrlRequest:
|
await tester.pumpWidget(
|
||||||
URLRequest(url: Uri.parse('https://flutter.dev/')),
|
Directionality(
|
||||||
onWebViewCreated: (controller) {
|
textDirection: TextDirection.ltr,
|
||||||
controllerCompleter.complete(controller);
|
child: InAppWebView(
|
||||||
},
|
key: GlobalKey(),
|
||||||
initialOptions: InAppWebViewGroupOptions(
|
initialData: InAppWebViewInitialData(data: """
|
||||||
crossPlatform:
|
|
||||||
InAppWebViewOptions(clearCache: true, contentBlockers: [
|
|
||||||
ContentBlocker(
|
|
||||||
trigger:
|
|
||||||
ContentBlockerTrigger(urlFilter: ".*", resourceType: [
|
|
||||||
ContentBlockerTriggerResourceType.IMAGE,
|
|
||||||
ContentBlockerTriggerResourceType.STYLE_SHEET
|
|
||||||
], ifTopUrl: [
|
|
||||||
"https://flutter.dev/"
|
|
||||||
]),
|
|
||||||
action: ContentBlockerAction(
|
|
||||||
type: ContentBlockerActionType.BLOCK))
|
|
||||||
])),
|
|
||||||
onLoadStop: (controller, url) {
|
|
||||||
pageLoaded.complete();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
await expectLater(pageLoaded.future, completes);
|
|
||||||
});
|
|
||||||
|
|
||||||
testWidgets('intercept fetch request', (WidgetTester tester) async {
|
|
||||||
final Completer controllerCompleter = Completer<InAppWebViewController>();
|
|
||||||
final Completer<String> fetchGetCompleter = Completer<String>();
|
|
||||||
final Completer<Map<String, dynamic>> fetchPostCompleter =
|
|
||||||
Completer<Map<String, dynamic>>();
|
|
||||||
final Completer<void> shouldInterceptFetchPostRequestCompleter =
|
|
||||||
Completer<void>();
|
|
||||||
await tester.pumpWidget(
|
|
||||||
Directionality(
|
|
||||||
textDirection: TextDirection.ltr,
|
|
||||||
child: InAppWebView(
|
|
||||||
key: GlobalKey(),
|
|
||||||
initialData: InAppWebViewInitialData(data: """
|
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
@ -1859,21 +2090,12 @@ void main() {
|
|||||||
<h1>InAppWebViewFetchTest</h1>
|
<h1>InAppWebViewFetchTest</h1>
|
||||||
<script>
|
<script>
|
||||||
window.addEventListener('flutterInAppWebViewPlatformReady', function(event) {
|
window.addEventListener('flutterInAppWebViewPlatformReady', function(event) {
|
||||||
fetch(new Request("http://${environment["NODE_SERVER_IP"]}:8082/test-download-file")).then(function(response) {
|
|
||||||
window.flutter_inappwebview.callHandler('fetchGet', response.status);
|
|
||||||
}).catch(function(error) {
|
|
||||||
window.flutter_inappwebview.callHandler('fetchGet', "ERROR: " + error);
|
|
||||||
});
|
|
||||||
|
|
||||||
fetch("http://${environment["NODE_SERVER_IP"]}:8082/test-ajax-post", {
|
fetch("http://${environment["NODE_SERVER_IP"]}:8082/test-ajax-post", {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: JSON.stringify({
|
|
||||||
firstname: 'Foo',
|
|
||||||
lastname: 'Bar'
|
|
||||||
}),
|
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/x-www-form-urlencoded'
|
||||||
}
|
},
|
||||||
|
body: "firstname=Foo&lastname=Bar"
|
||||||
}).then(function(response) {
|
}).then(function(response) {
|
||||||
response.json().then(function(value) {
|
response.json().then(function(value) {
|
||||||
window.flutter_inappwebview.callHandler('fetchPost', value);
|
window.flutter_inappwebview.callHandler('fetchPost', value);
|
||||||
@ -1888,52 +2110,338 @@ void main() {
|
|||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
"""),
|
"""),
|
||||||
initialOptions: InAppWebViewGroupOptions(
|
initialOptions: InAppWebViewGroupOptions(
|
||||||
crossPlatform: InAppWebViewOptions(
|
crossPlatform: InAppWebViewOptions(
|
||||||
clearCache: true,
|
clearCache: true,
|
||||||
useShouldInterceptFetchRequest: true,
|
useShouldInterceptFetchRequest: true,
|
||||||
)),
|
)),
|
||||||
|
onWebViewCreated: (controller) {
|
||||||
|
controllerCompleter.complete(controller);
|
||||||
|
|
||||||
|
controller.addJavaScriptHandler(
|
||||||
|
handlerName: "fetchPost",
|
||||||
|
callback: (args) {
|
||||||
|
fetchPostCompleter
|
||||||
|
.complete(args[0] as Map<String, dynamic>);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
shouldInterceptFetchRequest: (controller, fetchRequest) async {
|
||||||
|
expect(fetchRequest.body, "firstname=Foo&lastname=Bar");
|
||||||
|
|
||||||
|
fetchRequest.body = "firstname=Foo2&lastname=Bar2";
|
||||||
|
shouldInterceptFetchPostRequestCompleter.complete();
|
||||||
|
return fetchRequest;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
var fetchGetCompleterValue = await fetchGetCompleter.future;
|
||||||
|
expect(fetchGetCompleterValue, '200');
|
||||||
|
|
||||||
|
await shouldInterceptFetchPostRequestCompleter.future;
|
||||||
|
var fetchPostCompleterValue = await fetchPostCompleter.future;
|
||||||
|
|
||||||
|
expect(
|
||||||
|
mapEquals(fetchPostCompleterValue,
|
||||||
|
{'firstname': 'Foo2', 'lastname': 'Bar2'}),
|
||||||
|
true);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('send json data', (WidgetTester tester) async {
|
||||||
|
final Completer controllerCompleter = Completer<InAppWebViewController>();
|
||||||
|
final Completer<String> fetchGetCompleter = Completer<String>();
|
||||||
|
final Completer<Map<String, dynamic>> fetchPostCompleter =
|
||||||
|
Completer<Map<String, dynamic>>();
|
||||||
|
final Completer<void> shouldInterceptFetchPostRequestCompleter =
|
||||||
|
Completer<void>();
|
||||||
|
await tester.pumpWidget(
|
||||||
|
Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: InAppWebView(
|
||||||
|
key: GlobalKey(),
|
||||||
|
initialData: InAppWebViewInitialData(data: """
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||||
|
<title>InAppWebViewFetchTest</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>InAppWebViewFetchTest</h1>
|
||||||
|
<script>
|
||||||
|
window.addEventListener('flutterInAppWebViewPlatformReady', function(event) {
|
||||||
|
var jsonData = {
|
||||||
|
firstname: 'Foo',
|
||||||
|
lastname: 'Bar'
|
||||||
|
};
|
||||||
|
fetch("http://${environment["NODE_SERVER_IP"]}:8082/test-ajax-post", {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify(jsonData)
|
||||||
|
}).then(function(response) {
|
||||||
|
response.json().then(function(value) {
|
||||||
|
window.flutter_inappwebview.callHandler('fetchPost', value);
|
||||||
|
}).catch(function(error) {
|
||||||
|
window.flutter_inappwebview.callHandler('fetchPost', "ERROR: " + error);
|
||||||
|
});
|
||||||
|
}).catch(function(error) {
|
||||||
|
window.flutter_inappwebview.callHandler('fetchPost', "ERROR: " + error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""),
|
||||||
|
initialOptions: InAppWebViewGroupOptions(
|
||||||
|
crossPlatform: InAppWebViewOptions(
|
||||||
|
clearCache: true,
|
||||||
|
useShouldInterceptFetchRequest: true,
|
||||||
|
)),
|
||||||
|
onWebViewCreated: (controller) {
|
||||||
|
controllerCompleter.complete(controller);
|
||||||
|
|
||||||
|
controller.addJavaScriptHandler(
|
||||||
|
handlerName: "fetchPost",
|
||||||
|
callback: (args) {
|
||||||
|
fetchPostCompleter
|
||||||
|
.complete(args[0] as Map<String, dynamic>);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
shouldInterceptFetchRequest: (controller, fetchRequest) async {
|
||||||
|
expect(fetchRequest.body, '{"firstname":"Foo","lastname":"Bar"}');
|
||||||
|
|
||||||
|
fetchRequest.body = "{'firstname': 'Foo2', 'lastname': 'Bar2'}";
|
||||||
|
shouldInterceptFetchPostRequestCompleter.complete();
|
||||||
|
return fetchRequest;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
var fetchGetCompleterValue = await fetchGetCompleter.future;
|
||||||
|
expect(fetchGetCompleterValue, '200');
|
||||||
|
|
||||||
|
await shouldInterceptFetchPostRequestCompleter.future;
|
||||||
|
var fetchPostCompleterValue = await fetchPostCompleter.future;
|
||||||
|
|
||||||
|
expect(
|
||||||
|
mapEquals(fetchPostCompleterValue,
|
||||||
|
{'firstname': 'Foo2', 'lastname': 'Bar2'}),
|
||||||
|
true);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('send URLSearchParams data', (WidgetTester tester) async {
|
||||||
|
final Completer controllerCompleter = Completer<InAppWebViewController>();
|
||||||
|
final Completer<String> fetchGetCompleter = Completer<String>();
|
||||||
|
final Completer<Map<String, dynamic>> fetchPostCompleter =
|
||||||
|
Completer<Map<String, dynamic>>();
|
||||||
|
final Completer<void> shouldInterceptFetchPostRequestCompleter =
|
||||||
|
Completer<void>();
|
||||||
|
await tester.pumpWidget(
|
||||||
|
Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: InAppWebView(
|
||||||
|
key: GlobalKey(),
|
||||||
|
initialData: InAppWebViewInitialData(data: """
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||||
|
<title>InAppWebViewFetchTest</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>InAppWebViewFetchTest</h1>
|
||||||
|
<script>
|
||||||
|
window.addEventListener('flutterInAppWebViewPlatformReady', function(event) {
|
||||||
|
var paramsString = "firstname=Foo&lastname=Bar";
|
||||||
|
var searchParams = new URLSearchParams(paramsString);
|
||||||
|
fetch("http://${environment["NODE_SERVER_IP"]}:8082/test-ajax-post", {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded'
|
||||||
|
},
|
||||||
|
body: searchParams
|
||||||
|
}).then(function(response) {
|
||||||
|
response.json().then(function(value) {
|
||||||
|
window.flutter_inappwebview.callHandler('fetchPost', value);
|
||||||
|
}).catch(function(error) {
|
||||||
|
window.flutter_inappwebview.callHandler('fetchPost', "ERROR: " + error);
|
||||||
|
});
|
||||||
|
}).catch(function(error) {
|
||||||
|
window.flutter_inappwebview.callHandler('fetchPost', "ERROR: " + error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""),
|
||||||
|
initialOptions: InAppWebViewGroupOptions(
|
||||||
|
crossPlatform: InAppWebViewOptions(
|
||||||
|
clearCache: true,
|
||||||
|
useShouldInterceptFetchRequest: true,
|
||||||
|
)),
|
||||||
|
onWebViewCreated: (controller) {
|
||||||
|
controllerCompleter.complete(controller);
|
||||||
|
|
||||||
|
controller.addJavaScriptHandler(
|
||||||
|
handlerName: "fetchPost",
|
||||||
|
callback: (args) {
|
||||||
|
fetchPostCompleter
|
||||||
|
.complete(args[0] as Map<String, dynamic>);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
shouldInterceptFetchRequest: (controller, fetchRequest) async {
|
||||||
|
expect(fetchRequest.body, "firstname=Foo&lastname=Bar");
|
||||||
|
|
||||||
|
fetchRequest.body = "firstname=Foo2&lastname=Bar2";
|
||||||
|
shouldInterceptFetchPostRequestCompleter.complete();
|
||||||
|
return fetchRequest;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
var fetchGetCompleterValue = await fetchGetCompleter.future;
|
||||||
|
expect(fetchGetCompleterValue, '200');
|
||||||
|
|
||||||
|
await shouldInterceptFetchPostRequestCompleter.future;
|
||||||
|
var fetchPostCompleterValue = await fetchPostCompleter.future;
|
||||||
|
|
||||||
|
expect(
|
||||||
|
mapEquals(fetchPostCompleterValue,
|
||||||
|
{'firstname': 'Foo2', 'lastname': 'Bar2'}),
|
||||||
|
true);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('send FormData', (WidgetTester tester) async {
|
||||||
|
final Completer controllerCompleter = Completer<InAppWebViewController>();
|
||||||
|
final Completer<String> fetchGetCompleter = Completer<String>();
|
||||||
|
final Completer<Map<String, dynamic>> fetchPostCompleter =
|
||||||
|
Completer<Map<String, dynamic>>();
|
||||||
|
final Completer<void> shouldInterceptFetchPostRequestCompleter =
|
||||||
|
Completer<void>();
|
||||||
|
await tester.pumpWidget(
|
||||||
|
Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: InAppWebView(
|
||||||
|
key: GlobalKey(),
|
||||||
|
initialData: InAppWebViewInitialData(data: """
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||||
|
<title>InAppWebViewFetchTest</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>InAppWebViewFetchTest</h1>
|
||||||
|
<script>
|
||||||
|
window.addEventListener('flutterInAppWebViewPlatformReady', function(event) {
|
||||||
|
var formData = new FormData();
|
||||||
|
formData.append('firstname', 'Foo');
|
||||||
|
formData.append('lastname', 'Bar');
|
||||||
|
fetch("http://${environment["NODE_SERVER_IP"]}:8082/test-ajax-post", {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData
|
||||||
|
}).then(function(response) {
|
||||||
|
response.json().then(function(value) {
|
||||||
|
window.flutter_inappwebview.callHandler('fetchPost', value);
|
||||||
|
}).catch(function(error) {
|
||||||
|
window.flutter_inappwebview.callHandler('fetchPost', "ERROR: " + error);
|
||||||
|
});
|
||||||
|
}).catch(function(error) {
|
||||||
|
window.flutter_inappwebview.callHandler('fetchPost', "ERROR: " + error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""),
|
||||||
|
initialOptions: InAppWebViewGroupOptions(
|
||||||
|
crossPlatform: InAppWebViewOptions(
|
||||||
|
clearCache: true,
|
||||||
|
useShouldInterceptFetchRequest: true,
|
||||||
|
)),
|
||||||
|
onWebViewCreated: (controller) {
|
||||||
|
controllerCompleter.complete(controller);
|
||||||
|
|
||||||
|
controller.addJavaScriptHandler(
|
||||||
|
handlerName: "fetchPost",
|
||||||
|
callback: (args) {
|
||||||
|
fetchPostCompleter
|
||||||
|
.complete(args[0] as Map<String, dynamic>);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
shouldInterceptFetchRequest: (controller, fetchRequest) async {
|
||||||
|
expect(fetchRequest.body, isNotNull);
|
||||||
|
|
||||||
|
var body = fetchRequest.body.cast<int>();
|
||||||
|
var bodyString = String.fromCharCodes(body);
|
||||||
|
expect(bodyString.indexOf("WebKitFormBoundary") >= 0, true);
|
||||||
|
|
||||||
|
fetchRequest.body = utf8.encode(bodyString.replaceFirst("Foo", "Foo2").replaceFirst("Bar", "Bar2"));
|
||||||
|
shouldInterceptFetchPostRequestCompleter.complete();
|
||||||
|
return fetchRequest;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
var fetchGetCompleterValue = await fetchGetCompleter.future;
|
||||||
|
expect(fetchGetCompleterValue, '200');
|
||||||
|
|
||||||
|
await shouldInterceptFetchPostRequestCompleter.future;
|
||||||
|
var fetchPostCompleterValue = await fetchPostCompleter.future;
|
||||||
|
|
||||||
|
expect(
|
||||||
|
mapEquals(fetchPostCompleterValue,
|
||||||
|
{'firstname': 'Foo2', 'lastname': 'Bar2'}),
|
||||||
|
true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('Content Blocker', (WidgetTester tester) async {
|
||||||
|
final Completer controllerCompleter = Completer<InAppWebViewController>();
|
||||||
|
final Completer<void> pageLoaded = Completer<void>();
|
||||||
|
await tester.pumpWidget(
|
||||||
|
Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: InAppWebView(
|
||||||
|
key: GlobalKey(),
|
||||||
|
initialUrlRequest:
|
||||||
|
URLRequest(url: Uri.parse('https://flutter.dev/')),
|
||||||
onWebViewCreated: (controller) {
|
onWebViewCreated: (controller) {
|
||||||
controllerCompleter.complete(controller);
|
controllerCompleter.complete(controller);
|
||||||
|
|
||||||
controller.addJavaScriptHandler(
|
|
||||||
handlerName: "fetchGet",
|
|
||||||
callback: (args) {
|
|
||||||
fetchGetCompleter.complete(args[0].toString());
|
|
||||||
});
|
|
||||||
|
|
||||||
controller.addJavaScriptHandler(
|
|
||||||
handlerName: "fetchPost",
|
|
||||||
callback: (args) {
|
|
||||||
fetchPostCompleter
|
|
||||||
.complete(args[0] as Map<String, dynamic>);
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
shouldInterceptFetchRequest: (controller, fetchRequest) async {
|
initialOptions: InAppWebViewGroupOptions(
|
||||||
if (fetchRequest.url!.toString().endsWith("/test-ajax-post")) {
|
crossPlatform:
|
||||||
fetchRequest.body = utf8.encode("""{
|
InAppWebViewOptions(clearCache: true, contentBlockers: [
|
||||||
"firstname": "Foo2",
|
ContentBlocker(
|
||||||
"lastname": "Bar2"
|
trigger:
|
||||||
}
|
ContentBlockerTrigger(urlFilter: ".*", resourceType: [
|
||||||
""") as Uint8List;
|
ContentBlockerTriggerResourceType.IMAGE,
|
||||||
shouldInterceptFetchPostRequestCompleter.complete();
|
ContentBlockerTriggerResourceType.STYLE_SHEET
|
||||||
}
|
], ifTopUrl: [
|
||||||
return fetchRequest;
|
"https://flutter.dev/"
|
||||||
|
]),
|
||||||
|
action: ContentBlockerAction(
|
||||||
|
type: ContentBlockerActionType.BLOCK))
|
||||||
|
])),
|
||||||
|
onLoadStop: (controller, url) {
|
||||||
|
pageLoaded.complete();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
await expectLater(pageLoaded.future, completes);
|
||||||
var fetchGetCompleterValue = await fetchGetCompleter.future;
|
|
||||||
expect(fetchGetCompleterValue, '200');
|
|
||||||
|
|
||||||
await shouldInterceptFetchPostRequestCompleter.future;
|
|
||||||
var fetchPostCompleterValue = await fetchPostCompleter.future;
|
|
||||||
|
|
||||||
expect(
|
|
||||||
mapEquals(fetchPostCompleterValue,
|
|
||||||
{'firstname': 'Foo2', 'lastname': 'Bar2'}),
|
|
||||||
true);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Http Auth Credential Database', (WidgetTester tester) async {
|
testWidgets('Http Auth Credential Database', (WidgetTester tester) async {
|
||||||
@ -5001,4 +5509,37 @@ setTimeout(function() {
|
|||||||
expect(chromeSafariBrowser.isOpened(), false);
|
expect(chromeSafariBrowser.isOpened(), false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
group('InAppLocalhostServer', () {
|
||||||
|
final InAppLocalhostServer localhostServer = InAppLocalhostServer();
|
||||||
|
|
||||||
|
setUpAll(() async {
|
||||||
|
localhostServer.start();
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('load asset file', (WidgetTester tester) async {
|
||||||
|
final Completer controllerCompleter = Completer<InAppWebViewController>();
|
||||||
|
await tester.pumpWidget(
|
||||||
|
Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: InAppWebView(
|
||||||
|
key: GlobalKey(),
|
||||||
|
initialUrlRequest:
|
||||||
|
URLRequest(url: Uri.parse('http://localhost:8080/test_assets/index.html')),
|
||||||
|
onWebViewCreated: (controller) {
|
||||||
|
controllerCompleter.complete(controller);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
final InAppWebViewController controller =
|
||||||
|
await controllerCompleter.future;
|
||||||
|
final String? currentUrl = (await controller.getUrl())?.toString();
|
||||||
|
expect(currentUrl, 'http://localhost:8080/test_assets/index.html');
|
||||||
|
});
|
||||||
|
|
||||||
|
tearDownAll(() async {
|
||||||
|
localhostServer.close();
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
@ -183,46 +183,65 @@ let INTERCEPT_AJAX_REQUEST_JS_SOURCE = """
|
|||||||
this.addEventListener('error', handleEvent);
|
this.addEventListener('error', handleEvent);
|
||||||
this.addEventListener('abort', handleEvent);
|
this.addEventListener('abort', handleEvent);
|
||||||
this.addEventListener('timeout', handleEvent);
|
this.addEventListener('timeout', handleEvent);
|
||||||
var ajaxRequest = {
|
\(JAVASCRIPT_UTIL_VAR_NAME).convertBodyRequest(data).then(function(data) {
|
||||||
data: data,
|
var ajaxRequest = {
|
||||||
method: this._flutter_inappwebview_method,
|
data: data,
|
||||||
url: this._flutter_inappwebview_url,
|
method: self._flutter_inappwebview_method,
|
||||||
isAsync: this._flutter_inappwebview_isAsync,
|
url: self._flutter_inappwebview_url,
|
||||||
user: this._flutter_inappwebview_user,
|
isAsync: self._flutter_inappwebview_isAsync,
|
||||||
password: this._flutter_inappwebview_password,
|
user: self._flutter_inappwebview_user,
|
||||||
withCredentials: this.withCredentials,
|
password: self._flutter_inappwebview_password,
|
||||||
headers: this._flutter_inappwebview_request_headers,
|
withCredentials: self.withCredentials,
|
||||||
responseType: this.responseType
|
headers: self._flutter_inappwebview_request_headers,
|
||||||
};
|
responseType: self.responseType
|
||||||
window.\(JAVASCRIPT_BRIDGE_NAME).callHandler('shouldInterceptAjaxRequest', ajaxRequest).then(function(result) {
|
|
||||||
if (result != null) {
|
|
||||||
switch (result.action) {
|
|
||||||
case 0:
|
|
||||||
self.abort();
|
|
||||||
return;
|
|
||||||
};
|
};
|
||||||
data = result.data;
|
window.\(JAVASCRIPT_BRIDGE_NAME).callHandler('shouldInterceptAjaxRequest', ajaxRequest).then(function(result) {
|
||||||
self.withCredentials = result.withCredentials;
|
if (result != null) {
|
||||||
if (result.responseType != null) {
|
switch (result.action) {
|
||||||
self.responseType = result.responseType;
|
case 0:
|
||||||
};
|
self.abort();
|
||||||
for (var header in result.headers) {
|
return;
|
||||||
var value = result.headers[header];
|
};
|
||||||
var flutter_inappwebview_value = self._flutter_inappwebview_request_headers[header];
|
if (result.data != null && !\(JAVASCRIPT_UTIL_VAR_NAME).isString(result.data) && result.data.length > 0) {
|
||||||
if (flutter_inappwebview_value == null) {
|
var bodyString = \(JAVASCRIPT_UTIL_VAR_NAME).arrayBufferToString(result.data);
|
||||||
self._flutter_inappwebview_request_headers[header] = value;
|
if (\(JAVASCRIPT_UTIL_VAR_NAME).isBodyFormData(bodyString)) {
|
||||||
} else {
|
var formDataContentType = \(JAVASCRIPT_UTIL_VAR_NAME).getFormDataContentType(bodyString);
|
||||||
self._flutter_inappwebview_request_headers[header] += ', ' + value;
|
if (result.headers != null) {
|
||||||
|
result.headers['Content-Type'] = result.headers['Content-Type'] == null ? formDataContentType : result.headers['Content-Type'];
|
||||||
|
} else {
|
||||||
|
result.headers = { 'Content-Type': formDataContentType };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (\(JAVASCRIPT_UTIL_VAR_NAME).isString(result.data) || result.data == null) {
|
||||||
|
data = result.data;
|
||||||
|
} else if (result.data.length > 0) {
|
||||||
|
data = new Uint8Array(result.data);
|
||||||
|
}
|
||||||
|
self.withCredentials = result.withCredentials;
|
||||||
|
if (result.responseType != null) {
|
||||||
|
self.responseType = result.responseType;
|
||||||
|
};
|
||||||
|
if (result.headers != null) {
|
||||||
|
for (var header in result.headers) {
|
||||||
|
var value = result.headers[header];
|
||||||
|
var flutter_inappwebview_value = self._flutter_inappwebview_request_headers[header];
|
||||||
|
if (flutter_inappwebview_value == null) {
|
||||||
|
self._flutter_inappwebview_request_headers[header] = value;
|
||||||
|
} else {
|
||||||
|
self._flutter_inappwebview_request_headers[header] += ', ' + value;
|
||||||
|
}
|
||||||
|
setRequestHeader.call(self, header, value);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if ((self._flutter_inappwebview_method != result.method && result.method != null) || (self._flutter_inappwebview_url != result.url && result.url != null)) {
|
||||||
|
self.abort();
|
||||||
|
self.open(result.method, result.url, result.isAsync, result.user, result.password);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
setRequestHeader.call(self, header, value);
|
send.call(self, data);
|
||||||
};
|
});
|
||||||
if ((self._flutter_inappwebview_method != result.method && result.method != null) || (self._flutter_inappwebview_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);
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
send.call(this, data);
|
send.call(this, data);
|
||||||
|
@ -24,68 +24,6 @@ let INTERCEPT_FETCH_REQUEST_JS_SOURCE = """
|
|||||||
if (fetch == null) {
|
if (fetch == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
function convertHeadersToJson(headers) {
|
|
||||||
var headersObj = {};
|
|
||||||
for (var header of headers.keys()) {
|
|
||||||
var value = headers.get(header);
|
|
||||||
headersObj[header] = value;
|
|
||||||
}
|
|
||||||
return headersObj;
|
|
||||||
}
|
|
||||||
function convertJsonToHeaders(headersJson) {
|
|
||||||
return new Headers(headersJson);
|
|
||||||
}
|
|
||||||
function convertBodyToArray(body) {
|
|
||||||
return new Response(body).arrayBuffer().then(function(arrayBuffer) {
|
|
||||||
var arr = Array.from(new Uint8Array(arrayBuffer));
|
|
||||||
return arr;
|
|
||||||
})
|
|
||||||
}
|
|
||||||
function convertArrayIntBodyToUint8Array(arrayIntBody) {
|
|
||||||
return new Uint8Array(arrayIntBody);
|
|
||||||
}
|
|
||||||
function convertCredentialsToJson(credentials) {
|
|
||||||
var credentialsObj = {};
|
|
||||||
if (window.FederatedCredential != null && credentials instanceof FederatedCredential) {
|
|
||||||
credentialsObj.type = credentials.type;
|
|
||||||
credentialsObj.id = credentials.id;
|
|
||||||
credentialsObj.name = credentials.name;
|
|
||||||
credentialsObj.protocol = credentials.protocol;
|
|
||||||
credentialsObj.provider = credentials.provider;
|
|
||||||
credentialsObj.iconURL = credentials.iconURL;
|
|
||||||
} else if (window.PasswordCredential != null && credentials instanceof PasswordCredential) {
|
|
||||||
credentialsObj.type = credentials.type;
|
|
||||||
credentialsObj.id = credentials.id;
|
|
||||||
credentialsObj.name = credentials.name;
|
|
||||||
credentialsObj.password = credentials.password;
|
|
||||||
credentialsObj.iconURL = credentials.iconURL;
|
|
||||||
} else {
|
|
||||||
credentialsObj.type = 'default';
|
|
||||||
credentialsObj.value = credentials;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function convertJsonToCredential(credentialsJson) {
|
|
||||||
var credentials;
|
|
||||||
if (window.FederatedCredential != null && credentialsJson.type === 'federated') {
|
|
||||||
credentials = new FederatedCredential({
|
|
||||||
id: credentialsJson.id,
|
|
||||||
name: credentialsJson.name,
|
|
||||||
protocol: credentialsJson.protocol,
|
|
||||||
provider: credentialsJson.provider,
|
|
||||||
iconURL: credentialsJson.iconURL
|
|
||||||
});
|
|
||||||
} else if (window.PasswordCredential != null && credentialsJson.type === 'password') {
|
|
||||||
credentials = new PasswordCredential({
|
|
||||||
id: credentialsJson.id,
|
|
||||||
name: credentialsJson.name,
|
|
||||||
password: credentialsJson.password,
|
|
||||||
iconURL: credentialsJson.iconURL
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
credentials = credentialsJson;
|
|
||||||
}
|
|
||||||
return credentials;
|
|
||||||
}
|
|
||||||
window.fetch = async function(resource, init) {
|
window.fetch = async function(resource, init) {
|
||||||
if (\(FLAG_VARIABLE_FOR_SHOULD_INTERCEPT_FETCH_REQUEST_JS_SOURCE) == null || \(FLAG_VARIABLE_FOR_SHOULD_INTERCEPT_FETCH_REQUEST_JS_SOURCE) == true) {
|
if (\(FLAG_VARIABLE_FOR_SHOULD_INTERCEPT_FETCH_REQUEST_JS_SOURCE) == null || \(FLAG_VARIABLE_FOR_SHOULD_INTERCEPT_FETCH_REQUEST_JS_SOURCE) == true) {
|
||||||
var fetchRequest = {
|
var fetchRequest = {
|
||||||
@ -116,7 +54,7 @@ let INTERCEPT_FETCH_REQUEST_JS_SOURCE = """
|
|||||||
fetchRequest.integrity = resource.integrity;
|
fetchRequest.integrity = resource.integrity;
|
||||||
fetchRequest.keepalive = resource.keepalive;
|
fetchRequest.keepalive = resource.keepalive;
|
||||||
} else {
|
} else {
|
||||||
fetchRequest.url = resource;
|
fetchRequest.url = resource != null ? resource.toString() : null;
|
||||||
if (init != null) {
|
if (init != null) {
|
||||||
fetchRequest.method = init.method;
|
fetchRequest.method = init.method;
|
||||||
fetchRequest.headers = init.headers;
|
fetchRequest.headers = init.headers;
|
||||||
@ -132,10 +70,10 @@ let INTERCEPT_FETCH_REQUEST_JS_SOURCE = """
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (fetchRequest.headers instanceof Headers) {
|
if (fetchRequest.headers instanceof Headers) {
|
||||||
fetchRequest.headers = convertHeadersToJson(fetchRequest.headers);
|
fetchRequest.headers = \(JAVASCRIPT_UTIL_VAR_NAME).convertHeadersToJson(fetchRequest.headers);
|
||||||
}
|
}
|
||||||
fetchRequest.credentials = convertCredentialsToJson(fetchRequest.credentials);
|
fetchRequest.credentials = \(JAVASCRIPT_UTIL_VAR_NAME).convertCredentialsToJson(fetchRequest.credentials);
|
||||||
return convertBodyToArray(fetchRequest.body).then(function(body) {
|
return \(JAVASCRIPT_UTIL_VAR_NAME).convertBodyRequest(fetchRequest.body).then(function(body) {
|
||||||
fetchRequest.body = body;
|
fetchRequest.body = body;
|
||||||
return window.\(JAVASCRIPT_BRIDGE_NAME).callHandler('shouldInterceptFetchRequest', fetchRequest).then(function(result) {
|
return window.\(JAVASCRIPT_BRIDGE_NAME).callHandler('shouldInterceptFetchRequest', fetchRequest).then(function(result) {
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
@ -152,7 +90,18 @@ let INTERCEPT_FETCH_REQUEST_JS_SOURCE = """
|
|||||||
controller.abort();
|
controller.abort();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
resource = (result.url != null) ? result.url : resource;
|
if (result.body != null && !\(JAVASCRIPT_UTIL_VAR_NAME).isString(result.body) && result.body.length > 0) {
|
||||||
|
var bodyString = \(JAVASCRIPT_UTIL_VAR_NAME).arrayBufferToString(result.body);
|
||||||
|
if (\(JAVASCRIPT_UTIL_VAR_NAME).isBodyFormData(bodyString)) {
|
||||||
|
var formDataContentType = \(JAVASCRIPT_UTIL_VAR_NAME).getFormDataContentType(bodyString);
|
||||||
|
if (result.headers != null) {
|
||||||
|
result.headers['Content-Type'] = result.headers['Content-Type'] == null ? formDataContentType : result.headers['Content-Type'];
|
||||||
|
} else {
|
||||||
|
result.headers = { 'Content-Type': formDataContentType };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resource = result.url;
|
||||||
if (init == null) {
|
if (init == null) {
|
||||||
init = {};
|
init = {};
|
||||||
}
|
}
|
||||||
@ -160,16 +109,18 @@ let INTERCEPT_FETCH_REQUEST_JS_SOURCE = """
|
|||||||
init.method = result.method;
|
init.method = result.method;
|
||||||
}
|
}
|
||||||
if (result.headers != null && Object.keys(result.headers).length > 0) {
|
if (result.headers != null && Object.keys(result.headers).length > 0) {
|
||||||
init.headers = convertJsonToHeaders(result.headers);
|
init.headers = \(JAVASCRIPT_UTIL_VAR_NAME).convertJsonToHeaders(result.headers);
|
||||||
}
|
}
|
||||||
if (result.body != null && result.body.length > 0) {
|
if (\(JAVASCRIPT_UTIL_VAR_NAME).isString(result.body) || result.body == null) {
|
||||||
init.body = convertArrayIntBodyToUint8Array(result.body);
|
init.body = result.body;
|
||||||
|
} else if (result.body.length > 0) {
|
||||||
|
init.body = new Uint8Array(result.body);
|
||||||
}
|
}
|
||||||
if (result.mode != null && result.mode.length > 0) {
|
if (result.mode != null && result.mode.length > 0) {
|
||||||
init.mode = result.mode;
|
init.mode = result.mode;
|
||||||
}
|
}
|
||||||
if (result.credentials != null) {
|
if (result.credentials != null) {
|
||||||
init.credentials = convertJsonToCredential(result.credentials);
|
init.credentials = \(JAVASCRIPT_UTIL_VAR_NAME).convertJsonToCredential(result.credentials);
|
||||||
}
|
}
|
||||||
if (result.cache != null && result.cache.length > 0) {
|
if (result.cache != null && result.cache.length > 0) {
|
||||||
init.cache = result.cache;
|
init.cache = result.cache;
|
||||||
|
@ -30,6 +30,213 @@ window.\(JAVASCRIPT_BRIDGE_NAME).callHandler = function() {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
\(WEB_MESSAGE_LISTENER_JS_SOURCE)
|
\(WEB_MESSAGE_LISTENER_JS_SOURCE)
|
||||||
|
\(UTIL_JS_SOURCE)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
let PLATFORM_READY_JS_SOURCE = "window.dispatchEvent(new Event('flutterInAppWebViewPlatformReady'));";
|
let PLATFORM_READY_JS_SOURCE = "window.dispatchEvent(new Event('flutterInAppWebViewPlatformReady'));";
|
||||||
|
|
||||||
|
let JAVASCRIPT_UTIL_VAR_NAME = "window.\(JAVASCRIPT_BRIDGE_NAME)._Util"
|
||||||
|
|
||||||
|
/*
|
||||||
|
https://github.com/github/fetch/blob/master/fetch.js
|
||||||
|
*/
|
||||||
|
let UTIL_JS_SOURCE = """
|
||||||
|
\(JAVASCRIPT_UTIL_VAR_NAME) = {
|
||||||
|
support: {
|
||||||
|
searchParams: 'URLSearchParams' in window,
|
||||||
|
iterable: 'Symbol' in window && 'iterator' in Symbol,
|
||||||
|
blob:
|
||||||
|
'FileReader' in window &&
|
||||||
|
'Blob' in window &&
|
||||||
|
(function() {
|
||||||
|
try {
|
||||||
|
new Blob();
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
})(),
|
||||||
|
formData: 'FormData' in window,
|
||||||
|
arrayBuffer: 'ArrayBuffer' in window
|
||||||
|
},
|
||||||
|
isDataView: function(obj) {
|
||||||
|
return obj && DataView.prototype.isPrototypeOf(obj);
|
||||||
|
},
|
||||||
|
fileReaderReady: function(reader) {
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
reader.onload = function() {
|
||||||
|
resolve(reader.result);
|
||||||
|
};
|
||||||
|
reader.onerror = function() {
|
||||||
|
reject(reader.error);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
|
readBlobAsArrayBuffer: function(blob) {
|
||||||
|
var reader = new FileReader();
|
||||||
|
var promise = \(JAVASCRIPT_UTIL_VAR_NAME).fileReaderReady(reader);
|
||||||
|
reader.readAsArrayBuffer(blob);
|
||||||
|
return promise;
|
||||||
|
},
|
||||||
|
convertBodyToArrayBuffer: function(body) {
|
||||||
|
var viewClasses = [
|
||||||
|
'[object Int8Array]',
|
||||||
|
'[object Uint8Array]',
|
||||||
|
'[object Uint8ClampedArray]',
|
||||||
|
'[object Int16Array]',
|
||||||
|
'[object Uint16Array]',
|
||||||
|
'[object Int32Array]',
|
||||||
|
'[object Uint32Array]',
|
||||||
|
'[object Float32Array]',
|
||||||
|
'[object Float64Array]'
|
||||||
|
];
|
||||||
|
var isArrayBufferView = null;
|
||||||
|
if (\(JAVASCRIPT_UTIL_VAR_NAME).support.arrayBuffer) {
|
||||||
|
isArrayBufferView =
|
||||||
|
ArrayBuffer.isView ||
|
||||||
|
function(obj) {
|
||||||
|
return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var bodyUsed = false;
|
||||||
|
|
||||||
|
this._bodyInit = body;
|
||||||
|
if (!body) {
|
||||||
|
this._bodyText = '';
|
||||||
|
} else if (typeof body === 'string') {
|
||||||
|
this._bodyText = body;
|
||||||
|
} else if (\(JAVASCRIPT_UTIL_VAR_NAME).support.blob && Blob.prototype.isPrototypeOf(body)) {
|
||||||
|
this._bodyBlob = body;
|
||||||
|
} else if (\(JAVASCRIPT_UTIL_VAR_NAME).support.formData && FormData.prototype.isPrototypeOf(body)) {
|
||||||
|
this._bodyFormData = body;
|
||||||
|
} else if (\(JAVASCRIPT_UTIL_VAR_NAME).support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) {
|
||||||
|
this._bodyText = body.toString();
|
||||||
|
} else if (\(JAVASCRIPT_UTIL_VAR_NAME).support.arrayBuffer && \(JAVASCRIPT_UTIL_VAR_NAME).support.blob && \(JAVASCRIPT_UTIL_VAR_NAME).isDataView(body)) {
|
||||||
|
this._bodyArrayBuffer = bufferClone(body.buffer);
|
||||||
|
this._bodyInit = new Blob([this._bodyArrayBuffer]);
|
||||||
|
} else if (\(JAVASCRIPT_UTIL_VAR_NAME).support.arrayBuffer && (ArrayBuffer.prototype.isPrototypeOf(body) || isArrayBufferView(body))) {
|
||||||
|
this._bodyArrayBuffer = bufferClone(body);
|
||||||
|
} else {
|
||||||
|
this._bodyText = body = Object.prototype.toString.call(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.blob = function () {
|
||||||
|
if (bodyUsed) {
|
||||||
|
return Promise.reject(new TypeError('Already read'));
|
||||||
|
}
|
||||||
|
bodyUsed = true;
|
||||||
|
if (this._bodyBlob) {
|
||||||
|
return Promise.resolve(this._bodyBlob);
|
||||||
|
} else if (this._bodyArrayBuffer) {
|
||||||
|
return Promise.resolve(new Blob([this._bodyArrayBuffer]));
|
||||||
|
} else if (this._bodyFormData) {
|
||||||
|
throw new Error('could not read FormData body as blob');
|
||||||
|
} else {
|
||||||
|
return Promise.resolve(new Blob([this._bodyText]));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this._bodyArrayBuffer) {
|
||||||
|
if (bodyUsed) {
|
||||||
|
return Promise.reject(new TypeError('Already read'));
|
||||||
|
}
|
||||||
|
bodyUsed = true;
|
||||||
|
if (ArrayBuffer.isView(this._bodyArrayBuffer)) {
|
||||||
|
return Promise.resolve(
|
||||||
|
this._bodyArrayBuffer.buffer.slice(
|
||||||
|
this._bodyArrayBuffer.byteOffset,
|
||||||
|
this._bodyArrayBuffer.byteOffset + this._bodyArrayBuffer.byteLength
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Promise.resolve(this._bodyArrayBuffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this.blob().then(\(JAVASCRIPT_UTIL_VAR_NAME).readBlobAsArrayBuffer);
|
||||||
|
},
|
||||||
|
isString: function(variable) {
|
||||||
|
return typeof variable === 'string' || variable instanceof String;
|
||||||
|
},
|
||||||
|
convertBodyRequest: function(body) {
|
||||||
|
if (body == null) {
|
||||||
|
return new Promise((resolve, reject) => resolve(null));
|
||||||
|
}
|
||||||
|
if (\(JAVASCRIPT_UTIL_VAR_NAME).isString(body) || (\(JAVASCRIPT_UTIL_VAR_NAME).support.searchParams && body instanceof URLSearchParams)) {
|
||||||
|
return new Promise((resolve, reject) => resolve(body.toString()));
|
||||||
|
}
|
||||||
|
if (window.Response != null) {
|
||||||
|
return new Response(body).arrayBuffer().then(function(arrayBuffer) {
|
||||||
|
return Array.from(new Uint8Array(arrayBuffer));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return \(JAVASCRIPT_UTIL_VAR_NAME).convertBodyToArrayBuffer(body).then(function(arrayBuffer) {
|
||||||
|
return Array.from(new Uint8Array(arrayBuffer));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
arrayBufferToString: function(arrayBuffer) {
|
||||||
|
return String.fromCharCode.apply(String, arrayBuffer);
|
||||||
|
},
|
||||||
|
isBodyFormData: function(bodyString) {
|
||||||
|
return bodyString.indexOf('------WebKitFormBoundary') >= 0;
|
||||||
|
},
|
||||||
|
getFormDataContentType: function(bodyString) {
|
||||||
|
var boundary = bodyString.substr(2, 40);
|
||||||
|
return 'multipart/form-data; boundary=' + boundary;
|
||||||
|
},
|
||||||
|
convertHeadersToJson: function(headers) {
|
||||||
|
var headersObj = {};
|
||||||
|
for (var header of headers.keys()) {
|
||||||
|
var value = headers.get(header);
|
||||||
|
headersObj[header] = value;
|
||||||
|
}
|
||||||
|
return headersObj;
|
||||||
|
},
|
||||||
|
convertJsonToHeaders: function(headersJson) {
|
||||||
|
return new Headers(headersJson);
|
||||||
|
},
|
||||||
|
convertCredentialsToJson: function(credentials) {
|
||||||
|
var credentialsObj = {};
|
||||||
|
if (window.FederatedCredential != null && credentials instanceof FederatedCredential) {
|
||||||
|
credentialsObj.type = credentials.type;
|
||||||
|
credentialsObj.id = credentials.id;
|
||||||
|
credentialsObj.name = credentials.name;
|
||||||
|
credentialsObj.protocol = credentials.protocol;
|
||||||
|
credentialsObj.provider = credentials.provider;
|
||||||
|
credentialsObj.iconURL = credentials.iconURL;
|
||||||
|
} else if (window.PasswordCredential != null && credentials instanceof PasswordCredential) {
|
||||||
|
credentialsObj.type = credentials.type;
|
||||||
|
credentialsObj.id = credentials.id;
|
||||||
|
credentialsObj.name = credentials.name;
|
||||||
|
credentialsObj.password = credentials.password;
|
||||||
|
credentialsObj.iconURL = credentials.iconURL;
|
||||||
|
} else {
|
||||||
|
credentialsObj.type = 'default';
|
||||||
|
credentialsObj.value = credentials;
|
||||||
|
}
|
||||||
|
return credentialsObj;
|
||||||
|
},
|
||||||
|
convertJsonToCredential: function(credentialsJson) {
|
||||||
|
var credentials;
|
||||||
|
if (window.FederatedCredential != null && credentialsJson.type === 'federated') {
|
||||||
|
credentials = new FederatedCredential({
|
||||||
|
id: credentialsJson.id,
|
||||||
|
name: credentialsJson.name,
|
||||||
|
protocol: credentialsJson.protocol,
|
||||||
|
provider: credentialsJson.provider,
|
||||||
|
iconURL: credentialsJson.iconURL
|
||||||
|
});
|
||||||
|
} else if (window.PasswordCredential != null && credentialsJson.type === 'password') {
|
||||||
|
credentials = new PasswordCredential({
|
||||||
|
id: credentialsJson.id,
|
||||||
|
name: credentialsJson.name,
|
||||||
|
password: credentialsJson.password,
|
||||||
|
iconURL: credentialsJson.iconURL
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
credentials = credentialsJson.value == null ? undefined : credentialsJson.value;
|
||||||
|
}
|
||||||
|
return credentials;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
"""
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:flutter/services.dart' show rootBundle;
|
import 'package:flutter/services.dart' show rootBundle;
|
||||||
|
|
||||||
@ -39,7 +40,8 @@ class InAppLocalhostServer {
|
|||||||
this._server = server;
|
this._server = server;
|
||||||
|
|
||||||
server.listen((HttpRequest request) async {
|
server.listen((HttpRequest request) async {
|
||||||
var body = [] as List<int>;
|
Uint8List body = Uint8List(0);
|
||||||
|
|
||||||
var path = request.requestedUri.path;
|
var path = request.requestedUri.path;
|
||||||
path = (path.startsWith('/')) ? path.substring(1) : path;
|
path = (path.startsWith('/')) ? path.substring(1) : path;
|
||||||
path += (path.endsWith('/')) ? 'index.html' : '';
|
path += (path.endsWith('/')) ? 'index.html' : '';
|
||||||
|
@ -2873,7 +2873,7 @@ class AjaxRequestHeaders {
|
|||||||
///Class that represents a JavaScript [XMLHttpRequest](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest) object.
|
///Class that represents a JavaScript [XMLHttpRequest](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest) object.
|
||||||
class AjaxRequest {
|
class AjaxRequest {
|
||||||
///Data passed as a parameter to the `XMLHttpRequest.send()` method.
|
///Data passed as a parameter to the `XMLHttpRequest.send()` method.
|
||||||
dynamic data;
|
dynamic? data;
|
||||||
|
|
||||||
///The HTTP request method of the `XMLHttpRequest` request.
|
///The HTTP request method of the `XMLHttpRequest` request.
|
||||||
String? method;
|
String? method;
|
||||||
@ -3225,7 +3225,7 @@ class FetchRequest {
|
|||||||
Map<String, dynamic>? headers;
|
Map<String, dynamic>? headers;
|
||||||
|
|
||||||
///Body of the request.
|
///Body of the request.
|
||||||
Uint8List? body;
|
dynamic? body;
|
||||||
|
|
||||||
///The mode used by the request.
|
///The mode used by the request.
|
||||||
String? mode;
|
String? mode;
|
||||||
@ -3291,9 +3291,7 @@ class FetchRequest {
|
|||||||
url: map["url"] != null ? Uri.parse(map["url"]) : null,
|
url: map["url"] != null ? Uri.parse(map["url"]) : null,
|
||||||
method: map["method"],
|
method: map["method"],
|
||||||
headers: map["headers"]?.cast<String, dynamic>(),
|
headers: map["headers"]?.cast<String, dynamic>(),
|
||||||
body: map["body"] != null
|
body: map["body"],
|
||||||
? Uint8List.fromList(map["body"].cast<int>())
|
|
||||||
: null,
|
|
||||||
mode: map["mode"],
|
mode: map["mode"],
|
||||||
credentials: credentials,
|
credentials: credentials,
|
||||||
cache: map["cache"],
|
cache: map["cache"],
|
||||||
|
@ -9,6 +9,8 @@ const appHttps = express()
|
|||||||
const appAuthBasic = express()
|
const appAuthBasic = express()
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
|
const bodyParser = require('body-parser');
|
||||||
|
const multiparty = require('multiparty');
|
||||||
|
|
||||||
var options = {
|
var options = {
|
||||||
key: fs.readFileSync('server-key.pem'),
|
key: fs.readFileSync('server-key.pem'),
|
||||||
@ -117,14 +119,11 @@ appAuthBasic.get('/test-index', (req, res) => {
|
|||||||
appAuthBasic.listen(8081);
|
appAuthBasic.listen(8081);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Parse URL-encoded bodies (as sent by HTML forms)
|
|
||||||
app.use(express.urlencoded());
|
|
||||||
|
|
||||||
app.use(cors());
|
app.use(cors());
|
||||||
|
|
||||||
|
app.use(bodyParser.urlencoded({extended: false}));
|
||||||
// Parse JSON bodies (as sent by API clients)
|
// Parse JSON bodies (as sent by API clients)
|
||||||
app.use(express.json());
|
app.use(bodyParser.json());
|
||||||
|
|
||||||
app.use(express.static(__dirname + '/public'));
|
app.use(express.static(__dirname + '/public'));
|
||||||
|
|
||||||
@ -162,14 +161,27 @@ app.post("/test-post", (req, res) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
app.post("/test-ajax-post", (req, res) => {
|
app.post("/test-ajax-post", (req, res) => {
|
||||||
console.log(JSON.stringify(req.headers))
|
console.log(JSON.stringify(req.headers));
|
||||||
console.log(JSON.stringify(req.body))
|
if (req.headers["content-type"].indexOf("multipart/form-data;") === 0) {
|
||||||
res.set("Content-Type", "application/json")
|
const form = new multiparty.Form();
|
||||||
res.send(JSON.stringify({
|
form.parse(req, function(err, fields, files) {
|
||||||
"firstname": req.body.firstname,
|
console.log(fields);
|
||||||
"lastname": req.body.lastname,
|
res.set("Content-Type", "application/json")
|
||||||
}))
|
res.send(JSON.stringify({
|
||||||
res.end()
|
"firstname": fields.firstname[0],
|
||||||
|
"lastname": fields.lastname[0],
|
||||||
|
}));
|
||||||
|
res.end();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log(req.body);
|
||||||
|
res.set("Content-Type", "application/json")
|
||||||
|
res.send(JSON.stringify({
|
||||||
|
"firstname": req.body.firstname,
|
||||||
|
"lastname": req.body.lastname,
|
||||||
|
}));
|
||||||
|
res.end();
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
app.get("/test-download-file", (req, res) => {
|
app.get("/test-download-file", (req, res) => {
|
||||||
@ -184,4 +196,4 @@ app.get("/test-download-file", (req, res) => {
|
|||||||
res.end();
|
res.end();
|
||||||
})
|
})
|
||||||
|
|
||||||
app.listen(8082)
|
app.listen(8082)
|
||||||
|
@ -252,6 +252,45 @@
|
|||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||||
},
|
},
|
||||||
|
"multiparty": {
|
||||||
|
"version": "4.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/multiparty/-/multiparty-4.2.2.tgz",
|
||||||
|
"integrity": "sha512-NtZLjlvsjcoGrzojtwQwn/Tm90aWJ6XXtPppYF4WmOk/6ncdwMMKggFY2NlRRN9yiCEIVxpOfPWahVEG2HAG8Q==",
|
||||||
|
"requires": {
|
||||||
|
"http-errors": "~1.8.0",
|
||||||
|
"safe-buffer": "5.2.1",
|
||||||
|
"uid-safe": "2.1.5"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"http-errors": {
|
||||||
|
"version": "1.8.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.0.tgz",
|
||||||
|
"integrity": "sha512-4I8r0C5JDhT5VkvI47QktDW75rNlGVsUf/8hzjCC/wkWI/jdTRmBb9aI7erSG82r1bjKY3F6k28WnsVxB1C73A==",
|
||||||
|
"requires": {
|
||||||
|
"depd": "~1.1.2",
|
||||||
|
"inherits": "2.0.4",
|
||||||
|
"setprototypeof": "1.2.0",
|
||||||
|
"statuses": ">= 1.5.0 < 2",
|
||||||
|
"toidentifier": "1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"inherits": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||||
|
},
|
||||||
|
"safe-buffer": {
|
||||||
|
"version": "5.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||||
|
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
|
||||||
|
},
|
||||||
|
"setprototypeof": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"negotiator": {
|
"negotiator": {
|
||||||
"version": "0.6.2",
|
"version": "0.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
|
||||||
@ -294,6 +333,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
|
||||||
"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
|
"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
|
||||||
},
|
},
|
||||||
|
"random-bytes": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs="
|
||||||
|
},
|
||||||
"range-parser": {
|
"range-parser": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
||||||
@ -382,6 +426,14 @@
|
|||||||
"mime-types": "~2.1.24"
|
"mime-types": "~2.1.24"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"uid-safe": {
|
||||||
|
"version": "2.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz",
|
||||||
|
"integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==",
|
||||||
|
"requires": {
|
||||||
|
"random-bytes": "~1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"unpipe": {
|
"unpipe": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
||||||
|
@ -10,8 +10,10 @@
|
|||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"basic-auth": "latest",
|
"basic-auth": "latest",
|
||||||
|
"body-parser": "^1.19.0",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"express": "latest",
|
"express": "latest",
|
||||||
"https": "latest"
|
"https": "latest",
|
||||||
|
"multiparty": "^4.2.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user