diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 78d181ed4..f0c31aa76 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -1448,8 +1448,18 @@ class CommandDispatcher: download_manager.get_mhtml(tab, target) else: qnam = tab.networkaccessmanager() - download_manager.get(self._current_url(), user_agent=user_agent, - qnam=qnam, target=target) + + suggested_fn = downloads.suggested_fn_from_title( + self._current_url().path(), tab.title() + ) + + download_manager.get( + self._current_url(), + user_agent=user_agent, + qnam=qnam, + target=target, + suggested_fn=suggested_fn + ) @cmdutils.register(instance='command-dispatcher', scope='window') def view_source(self): diff --git a/qutebrowser/browser/downloads.py b/qutebrowser/browser/downloads.py index c7cb5ad8e..438dfc528 100644 --- a/qutebrowser/browser/downloads.py +++ b/qutebrowser/browser/downloads.py @@ -182,6 +182,28 @@ def transform_path(path): return path +def suggested_fn_from_title(url_path, title=None): + """Suggest a filename depending on the URL extension and page title. + + Args: + url_path: a string with the URL path + title: the page title string + + Return: + The download filename based on the title, or None if the extension is + not found in the whitelist (or if there is no page title). + """ + ext_whitelist = [".html", ".htm", ".php", ""] + _, ext = os.path.splitext(url_path) + if ext.lower() in ext_whitelist and title: + suggested_fn = utils.sanitize_filename(title) + if not suggested_fn.lower().endswith((".html", ".htm")): + suggested_fn += ".html" + else: + suggested_fn = None + return suggested_fn + + class NoFilenameError(Exception): """Raised when we can't find out a filename in DownloadTarget.""" diff --git a/qutebrowser/browser/qtnetworkdownloads.py b/qutebrowser/browser/qtnetworkdownloads.py index 71039dc2d..512991bbc 100644 --- a/qutebrowser/browser/qtnetworkdownloads.py +++ b/qutebrowser/browser/qtnetworkdownloads.py @@ -412,7 +412,8 @@ class DownloadManager(downloads.AbstractDownloadManager): mhtml.start_download_checked, tab=tab)) message.global_bridge.ask(question, blocking=False) - def get_request(self, request, *, target=None, **kwargs): + def get_request(self, request, *, target=None, + suggested_fn=None, **kwargs): """Start a download with a QNetworkRequest. Args: @@ -428,7 +429,9 @@ class DownloadManager(downloads.AbstractDownloadManager): request.setAttribute(QNetworkRequest.CacheLoadControlAttribute, QNetworkRequest.AlwaysNetwork) - if request.url().scheme().lower() != 'data': + if suggested_fn is not None: + pass + elif request.url().scheme().lower() != 'data': suggested_fn = urlutils.filename_from_url(request.url()) else: # We might be downloading a binary blob embedded on a page or even diff --git a/tests/end2end/data/downloads/download with no title.html b/tests/end2end/data/downloads/download with no title.html new file mode 100644 index 000000000..da4352e59 --- /dev/null +++ b/tests/end2end/data/downloads/download with no title.html @@ -0,0 +1,8 @@ + + +
+ + + + + diff --git a/tests/end2end/data/downloads/qutebrowser.png b/tests/end2end/data/downloads/qutebrowser.png new file mode 100644 index 000000000..e8bbb6b56 Binary files /dev/null and b/tests/end2end/data/downloads/qutebrowser.png differ diff --git a/tests/end2end/features/downloads.feature b/tests/end2end/features/downloads.feature index 07e062610..366414016 100644 --- a/tests/end2end/features/downloads.feature +++ b/tests/end2end/features/downloads.feature @@ -22,6 +22,20 @@ Feature: Downloading things from a website. And I wait until the download is finished Then the downloaded file download.bin should exist + Scenario: Using :download with no URL + When I set storage -> prompt-download-directory to false + And I open data/downloads/downloads.html + And I run :download + And I wait until the download is finished + Then the downloaded file Simple downloads.html should exist + + Scenario: Using :download with no URL on an image + When I set storage -> prompt-download-directory to false + And I open data/downloads/qutebrowser.png + And I run :download + And I wait until the download is finished + Then the downloaded file qutebrowser.png should exist + Scenario: Using hints When I set storage -> prompt-download-directory to false And I open data/downloads/downloads.html @@ -637,7 +651,7 @@ Feature: Downloading things from a website. @qtwebengine_skip: We can't get the UA from the page there Scenario: user-agent when using :download When I open user-agent - And I run :download + And I run :download --dest user-agent And I wait until the download is finished Then the downloaded file user-agent should contain Safari/ diff --git a/tests/unit/browser/webkit/test_downloads.py b/tests/unit/browser/webkit/test_downloads.py index 72b533cc7..5a214f638 100644 --- a/tests/unit/browser/webkit/test_downloads.py +++ b/tests/unit/browser/webkit/test_downloads.py @@ -30,6 +30,42 @@ def test_download_model(qapp, qtmodeltester, config_stub, cookiejar_and_cache): qtmodeltester.check(model) +@pytest.mark.parametrize('url, title, out', [ + ('http://qutebrowser.org/INSTALL.html', + 'Installing qutebrowser | qutebrowser', + 'Installing qutebrowser _ qutebrowser.html'), + ('http://qutebrowser.org/INSTALL.html', + 'Installing qutebrowser | qutebrowser.html', + 'Installing qutebrowser _ qutebrowser.html'), + ('http://qutebrowser.org/INSTALL.HTML', + 'Installing qutebrowser | qutebrowser', + 'Installing qutebrowser _ qutebrowser.html'), + ('http://qutebrowser.org/INSTALL.html', + 'Installing qutebrowser | qutebrowser.HTML', + 'Installing qutebrowser _ qutebrowser.HTML'), + ('http://qutebrowser.org/', + 'qutebrowser | qutebrowser', + 'qutebrowser _ qutebrowser.html'), + ('https://github.com/qutebrowser/qutebrowser/releases', + 'Releases · qutebrowser/qutebrowser', + 'Releases · qutebrowser_qutebrowser.html'), + ('http://qutebrowser.org/index.php', + 'qutebrowser | qutebrowser', + 'qutebrowser _ qutebrowser.html'), + ('http://qutebrowser.org/index.php', + 'qutebrowser | qutebrowser - index.php', + 'qutebrowser _ qutebrowser - index.php.html'), + ('https://qutebrowser.org/img/cheatsheet-big.png', + 'cheatsheet-big.png (3342×2060)', + None), + ('http://qutebrowser.org/page-with-no-title.html', + '', + None), +]) +def test_page_titles(url, title, out): + assert downloads.suggested_fn_from_title(url, title) == out + + class TestDownloadTarget: def test_base(self):