-
-
Save sventschui/342fa43610b3dca52f424055ee2e4ccf to your computer and use it in GitHub Desktop.
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { | |
if (openInDocumentPreview(navigationAction.request.url!)) { | |
decisionHandler(.cancel) | |
// TODO: Add more supported mime-types for missing content-disposition headers | |
webView.evaluateJavaScript(""" | |
(async function download() { | |
const url = '\(navigationAction.request.url!.absoluteString)'; | |
try { | |
// we use a second try block here to have more detailed error information | |
// because of the nature of JS the outer try-catch doesn't know anything where the error happended | |
let res; | |
try { | |
res = await fetch(url, { | |
credentials: 'include' | |
}); | |
} catch (err) { | |
window.webkit.messageHandlers.jsError.postMessage( | |
`fetch threw, error: ${err}, url: ${url}` | |
); | |
return; | |
} | |
if (!res.ok) { | |
window.webkit.messageHandlers.jsError.postMessage( | |
`Response status was not ok, status: ${res.status}, url: ${url}` | |
); | |
return; | |
} | |
const contentDisp = res.headers.get('content-disposition'); | |
if (contentDisp) { | |
const match = contentDisp.match(/(^;|)\\s*filename=\\s*(\"([^\"]*)\"|([^;\\s]*))\\s*(;|$)/i); | |
if (match) { | |
filename = match[3] || match[4]; | |
} else { | |
// TODO: we could here guess the filename from the mime-type (e.g. unnamed.pdf for pdfs, or unnamed.tiff for tiffs) | |
window.webkit.messageHandlers.jsError.postMessage( | |
`content-disposition header could not be matched against regex, content-disposition: ${contentDisp} url: ${url}` | |
); | |
} | |
} else { | |
window.webkit.messageHandlers.jsError.postMessage( | |
`content-disposition header missing, url: ${url}` | |
); | |
} | |
if (!filename) { | |
const contentType = res.headers.get('content-type'); | |
if (contentType) { | |
if (contentType.indexOf('application/json') === 0) { | |
filename = 'unnamed.pdf'; | |
} else if (contentType.indexOf('image/tiff') === 0) { | |
filename = 'unnamed.tiff'; | |
} | |
} | |
} | |
if (!filename) { | |
window.webkit.messageHandlers.jsError.postMessage( | |
`Could not determine filename from content-disposition nor content-type, content-dispositon: ${contentDispositon}, content-type: ${contentType}, url: ${url}` | |
); | |
} | |
let data; | |
try { | |
data = await res.blob(); | |
} catch (err) { | |
window.webkit.messageHandlers.jsError.postMessage( | |
`res.blob() threw, error: ${err}, url: ${url}` | |
); | |
return; | |
} | |
const fr = new FileReader(); | |
fr.onload = () => { | |
console.log('1', new Date()); | |
window.webkit.messageHandlers.openDocument.postMessage( | |
`${filename};${fr.result}` | |
) | |
console.log('2', new Date()); | |
}; | |
fr.addEventListener('error', (err) => { | |
window.webkit.messageHandlers.jsError.postMessage( | |
`FileReader threw, error: ${err}` | |
) | |
}) | |
fr.readAsDataURL(data); | |
} catch (err) { | |
// TODO: better log the error, currently only TypeError: Type error | |
window.webkit.messageHandlers.jsError.postMessage( | |
`JSError while downloading document, url: ${url}, err: ${err}` | |
) | |
debugger; | |
} | |
})(); | |
// null is needed here as this eval returns the last statement and we can't return a promise | |
null; | |
""") { (result, err) in | |
if (err != nil) { | |
debugPrint("JS ERR: \(String(describing: err))") | |
} | |
} | |
} else { | |
decisionHandler(.allow) | |
} | |
} | |
public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { | |
debugPrint("did receive message \(message.name)") | |
if (message.name == "openDocument") { | |
previewDocument(messageBody: message.body as! String) | |
} else if (message.name == "jsError") { | |
debugPrint(message.body as! String) | |
} | |
} | |
private func previewDocument(messageBody: String) { | |
// messageBody is in the format <filename>;data:<mime-type>;base64,<base64-encoded-data> | |
// split on the first ";", to reveal the filename | |
let filenameSplits = messageBody.split(separator: ";", maxSplits: 1, omittingEmptySubsequences: false) | |
let filename = String(filenameSplits[0]) | |
// split the remaining part on the first ",", to reveal the base64 data | |
let dataSplits = filenameSplits[1].split(separator: ",", maxSplits: 1, omittingEmptySubsequences: false) | |
let data = Data(base64Encoded: String(dataSplits[1])) | |
if (data == nil) { | |
debugPrint("Could not construct data from base64") | |
return | |
} | |
// store the file on disk | |
let localFileURL = FileManager.default.temporaryDirectory.appendingPathComponent(filename) | |
do { | |
try data!.write(to: localFileURL); | |
} catch { | |
debugPrint(error) | |
return | |
} | |
// and display it in QL | |
DispatchQueue.main.async { | |
self.documentUrl = localFileURL | |
self.documentPreviewController.refreshCurrentPreviewItem() | |
self.present(self.documentPreviewController, animated: true, completion: nil) | |
} | |
} |
Wenn ich das JS anpasse, kommt folgende Exception:
JSError while downloading document, url: https://my.sympany.ch/web/APP/connector/0/401/dl, err: TypeError: undefined is not an object (evaluating 'window.webkit.messageHandlers.jsError.postMessage')
JS:
} catch (err) {
// TODO: better log the error, currently only TypeError: Type error
console.log(
`JSError while downloading document, url: ${url}, err: ${err}`
)
debugger;
Ursache für das PRoblem beim Exception Handling ist offensichtlich
window.webkit.messageHandlers.jsError.postMessage
- das gibt den Type error
Wenn ich alles im Skript mit console.log
ersetze, wird die Exception gelogged.
Der eigentliche Fehler ist dann:
[Log] content-disposition header missing, url: https://my.sympany.ch/web/APP/connector/0/367/dl (clientportal, line 33)
[Log] JSError while downloading document, url: https://my.sympany.ch/web/APP/connector/0/367/dl, err: ReferenceError: Can't find variable: filename (clientportal, line 77)
Es sieht so aus, als würde das JS versuchen eine Response zu verarbeiten, die wir nicht erwarten.
"Response" Objekt im Debugger (iOS 11):
body: ReadableStream {locked: false}
bodyUsed: false
headers: Headers {append: function, delete: function, get: function, has: function, set: function, …}
ok: true
redirected: true
status: 200
statusText: "OK"
type: "basic"
url: "https://my.sympany.ch/my.logout.php3?errorcode=19"
Dagegen ist das "Response" Objekt im Debugger (iOS 12) ok:
body: ReadableStream {locked: false}
bodyUsed: false
headers: Headers {append: function, delete: function, get: function, has: function, set: function, …}
ok: true
redirected: false
status: 200
statusText: "OK"
type: "basic"
url: "https://my.sympany.ch/web/APP/connector/0/367/dl"
Es hängt wahrscheinlich mit der F5 zusammen:
https://devcentral.f5.com/articles/http-event-order-access-policy-manager
Der Fehlercode 19 bedeutet:
https://devcentral.f5.com/questions/apm-gives-error-code-19-always-57311
"BIG-IP can not find session information in the request. This can happen because your browser restarted after an add-on was installed. If this occurred, click the link below to continue. This can also happen because cookies are disabled in your browser. If so, enable cookies in your browser and start a new session."
Habe Zeile 15-17 angepasst. fetch scheint unter iOS 11.1 cookies by-default nicht mitzusenden, siehe: https://github.com/github/fetch#sending-cookies Kannst du mal prüfen ob das den Fehler behebt?
Das Problem mit dem window.webkit.messageHandlers.jsError.postMessage
ist, dass ich dir vergessen habe zu sagen, dass du noch einen zweiten message handler im init()
des ViewController registrieren musst:
von:
webView.configuration.userContentController.add(self, name: "jsError")
zu:
webView.configuration.userContentController.add(self, name: "openDocument")
webView.configuration.userContentController.add(self, name: "jsError")
Damit werden jsErrors in deinem VC mittels debugPrint geloggt.
Folgende Exception tritt auf dem Simulator iPhone 8 (iOS 11.4) auf:
Der Fehler passiert beim error Handling: