diff --git a/tests/test_download.py b/tests/test_download.py new file mode 100644 index 00000000..fa2d4ef5 --- /dev/null +++ b/tests/test_download.py @@ -0,0 +1,70 @@ +import pytest + +import webview + +from .util import run_test, assert_js + +import time + +html = """ + +
+ + + + +""" + +@pytest.fixture +def window(): + return webview.create_window('Download test', html=html) + +# skip this test by default since it's a manual test that requires user interaction +@pytest.mark.skip +def test_download_attribute(window): + webview.settings['ALLOW_DOWNLOADS'] = True + run_test(webview, window, download_test) + +def get_result(): + res = input("Did the system prompt you for a download with name test_download.txt or download a file by that name? Y/N").upper() + return (res == 'Y') + +def download_test(window): + # this should not cause the browser to navigate away but instead should trigger a download + # since the type is text the browser should support it and if it navigates to it it will display it instead + window.evaluate_js("document.getElementById('start_download').click();") + time.sleep(0.5) # make sure it has executed + + # if it loaded on the page and navigated away then it will fail this + elements = window.dom.get_elements('#start_download') + assert len(elements) == 1 + + # wait for download prompt to be dismissed and focus to come back to window + while not window.evaluate_js("checkFocus()"): + time.sleep(0.5) + + # ask user if the test passed + assert window.evaluate_js("verify_result();") + + + + + diff --git a/webview/platforms/cocoa.py b/webview/platforms/cocoa.py index 689aca75..961867e6 100644 --- a/webview/platforms/cocoa.py +++ b/webview/platforms/cocoa.py @@ -146,6 +146,23 @@ def userContentController_didReceiveScriptMessage_(self, controller, message): body['params'] = None js_bridge_call(self.window, body['funcName'], body['params'], body['id']) + class DownloadDelegate(AppKit.NSObject): + # Download delegate to handle links with download attribute set + def download_decideDestinationUsingResponse_suggestedFilename_completionHandler_(self, download, decideDestinationUsingResponse, suggestedFilename, completionHandler): + save_dlg = AppKit.NSSavePanel.savePanel() + directory = Foundation.NSSearchPathForDirectoriesInDomains( + Foundation.NSDownloadsDirectory, + Foundation.NSUserDomainMask, + True)[0] + save_dlg.setDirectoryURL_(Foundation.NSURL.fileURLWithPath_(directory)) + save_dlg.setNameFieldStringValue_(suggestedFilename) + if save_dlg.runModal() == AppKit.NSFileHandlingPanelOKButton: + filename = save_dlg.filename() + url = Foundation.NSURL.fileURLWithPath_(filename) + completionHandler(url) + else: + completionHandler(None) + class BrowserDelegate(AppKit.NSObject): # Display a JavaScript alert panel containing the specified message @@ -229,6 +246,11 @@ def webView_decidePolicyForNavigationAction_decisionHandler_( if not handler.__block_signature__: handler.__block_signature__ = BrowserView.pyobjc_method_signature(b'v@i') + # Handle links with the download attribute set to recommend a file name + if action.shouldPerformDownload() and webview_settings['ALLOW_DOWNLOADS']: + handler(getattr(WebKit, 'WKNavigationActionPolicyDownload', 2)) + return + """ Disable back navigation on pressing the Delete key: """ # Check if the requested navigation action is Back/Forward if action.navigationType() == getattr(WebKit, 'WKNavigationTypeBackForward', 2): @@ -241,6 +263,9 @@ def webView_decidePolicyForNavigationAction_decisionHandler_( # Normal navigation, allow handler(getattr(WebKit, 'WKNavigationActionPolicyAllow', 1)) + def webView_navigationAction_didBecomeDownload_(self, webview, navigationAction, download): + download.setDelegate_(BrowserView.DownloadDelegate.alloc().init().retain()) + def webView_decidePolicyForNavigationResponse_decisionHandler_(self, webview, navigationResponse, decisionHandler): if navigationResponse.canShowMIMEType(): decisionHandler(WebKit.WKNavigationResponsePolicyAllow)