diff --git a/qutebrowser/browser/downloads.py b/qutebrowser/browser/downloads.py index 854fd1ca5..a2917e4f0 100644 --- a/qutebrowser/browser/downloads.py +++ b/qutebrowser/browser/downloads.py @@ -32,10 +32,11 @@ import enum from PyQt5.QtCore import (pyqtSlot, pyqtSignal, Qt, QObject, QModelIndex, QTimer, QAbstractListModel, QUrl) +from qutebrowser.browser import pdfjs from qutebrowser.commands import cmdexc, cmdutils from qutebrowser.config import config from qutebrowser.utils import (usertypes, standarddir, utils, message, log, - qtutils) + qtutils, objreg) from qutebrowser.qt import sip @@ -224,9 +225,6 @@ class _DownloadTarget: """Abstract base class for different download targets.""" - def __init__(self): - raise NotImplementedError - def suggested_filename(self): """Get the suggested filename for this download target.""" raise NotImplementedError @@ -300,6 +298,17 @@ class OpenFileDownloadTarget(_DownloadTarget): return 'temporary file' +class PDFJSDownloadTarget(_DownloadTarget): + + """Open the download via PDF.js.""" + + def suggested_filename(self): + raise NoFilenameError + + def __str__(self): + return 'temporary PDF.js file' + + class DownloadItemStats(QObject): """Statistics (bytes done, total bytes, time, etc.) about a download. @@ -405,6 +414,8 @@ class AbstractDownloadItem(QObject): arg: The error message as string. remove_requested: Emitted when the removal of this download was requested. + pdfjs_requested: Emitted when PDF.js should be opened with the given + filename. """ data_changed = pyqtSignal() @@ -412,6 +423,7 @@ class AbstractDownloadItem(QObject): error = pyqtSignal(str) cancelled = pyqtSignal() remove_requested = pyqtSignal() + pdfjs_requested = pyqtSignal(str) def __init__(self, parent=None): super().__init__(parent) @@ -730,6 +742,19 @@ class AbstractDownloadItem(QObject): return self.open_file(cmdline) + def _pdfjs_if_successful(self): + """Open the file via PDF.js if downloading was successful.""" + if not self.successful: + log.downloads.debug("{} finished but not successful, not opening!" + .format(self)) + return + + filename = self._get_open_filename() + if filename is None: # pragma: no cover + log.downloads.error("No filename to open the download!") + return + self.pdfjs_requested.emit(filename) + def set_target(self, target): """Set the target for a given download. @@ -741,7 +766,7 @@ class AbstractDownloadItem(QObject): elif isinstance(target, FileDownloadTarget): self._set_filename( target.filename, force_overwrite=target.force_overwrite) - elif isinstance(target, OpenFileDownloadTarget): + elif isinstance(target, (OpenFileDownloadTarget, PDFJSDownloadTarget)): try: fobj = temp_download_manager.get_tmpfile(self.basename) except OSError as exc: @@ -749,8 +774,15 @@ class AbstractDownloadItem(QObject): message.error(msg) self.cancel() return - self.finished.connect( - functools.partial(self._open_if_successful, target.cmdline)) + + if isinstance(target, OpenFileDownloadTarget): + self.finished.connect( + functools.partial(self._open_if_successful, target.cmdline)) + elif isinstance(target, PDFJSDownloadTarget): + self.finished.connect(self._pdfjs_if_successful) + else: + assert False, target + self._set_tempfile(fobj) else: # pragma: no cover raise ValueError("Unsupported download target: {}".format(target)) @@ -797,6 +829,13 @@ class AbstractDownloadManager(QObject): dl.stats.update_speed() self.data_changed.emit(-1) + @pyqtSlot(str) + def _on_pdfjs_requested(self, filename): + """Open PDF.js when a download requests it.""" + tabbed_browser = objreg.get('tabbed-browser', scope='window', + window='last-focused') + tabbed_browser.tabopen(pdfjs.get_main_url(filename)) + def _init_item(self, download, auto_remove, suggested_filename): """Initialize a newly created DownloadItem.""" download.cancelled.connect(download.remove) @@ -813,6 +852,8 @@ class AbstractDownloadManager(QObject): download.data_changed.connect( functools.partial(self._on_data_changed, download)) download.error.connect(self._on_error) + download.pdfjs_requested.connect(self._on_pdfjs_requested) + download.basename = suggested_filename idx = len(self.downloads) download.index = idx + 1 # "Human readable" index diff --git a/qutebrowser/browser/pdfjs.py b/qutebrowser/browser/pdfjs.py index 379b30240..49c65e2ee 100644 --- a/qutebrowser/browser/pdfjs.py +++ b/qutebrowser/browser/pdfjs.py @@ -22,9 +22,10 @@ import os -from PyQt5.QtCore import QUrl +from PyQt5.QtCore import QUrl, QUrlQuery from qutebrowser.utils import utils, javascript, jinja +from qutebrowser.config import config class PDFJSNotFound(Exception): @@ -210,3 +211,17 @@ def is_available(): return False else: return True + + +def should_use_pdfjs(mimetype): + return (mimetype in ['application/pdf', 'application/x-pdf'] and + config.val.content.pdfjs) + + +def get_main_url(filename): + """Get the URL to be opened to view a local PDF.""" + url = QUrl('qute://pdfjs') + query = QUrlQuery() + query.addQueryItem('file', QUrl.fromLocalFile(filename).toString()) + url.setQuery(query) + return url diff --git a/qutebrowser/browser/qutescheme.py b/qutebrowser/browser/qutescheme.py index edf9f02bd..1126a3204 100644 --- a/qutebrowser/browser/qutescheme.py +++ b/qutebrowser/browser/qutescheme.py @@ -523,6 +523,11 @@ def qute_pastebin_version(_url): @add_handler('pdfjs') def qute_pdfjs(url): """Handler for qute://pdfjs. Return the pdf.js viewer.""" + if url.path() == '/': + filepath = QUrlQuery(url).queryItemValue("file") + data = pdfjs.generate_pdfjs_page(QUrl.fromLocalFile(filepath)) + return 'text/html', data + try: data = pdfjs.get_pdfjs_res(url.path()) except pdfjs.PDFJSNotFound as e: @@ -534,5 +539,6 @@ def qute_pdfjs(url): raise NotFoundError("Can't find pdfjs resource '{}'".format(e.path)) else: mimetype, _encoding = mimetypes.guess_type(url.fileName()) - assert mimetype is not None, url + if mimetype is None: + mimetype = 'application/octet-stream' return mimetype, data diff --git a/qutebrowser/browser/webkit/webpage.py b/qutebrowser/browser/webkit/webpage.py index 90e56ec56..c0e3ce57b 100644 --- a/qutebrowser/browser/webkit/webpage.py +++ b/qutebrowser/browser/webkit/webpage.py @@ -30,7 +30,7 @@ from PyQt5.QtPrintSupport import QPrintDialog from PyQt5.QtWebKitWidgets import QWebPage, QWebFrame from qutebrowser.config import config -from qutebrowser.browser import pdfjs, shared +from qutebrowser.browser import pdfjs, shared, downloads from qutebrowser.browser.webkit import http from qutebrowser.browser.webkit.network import networkmanager from qutebrowser.utils import message, usertypes, log, jinja, objreg @@ -275,10 +275,9 @@ class BrowserPage(QWebPage): else: reply.finished.connect(functools.partial( self.display_content, reply, 'image/jpeg')) - elif (mimetype in ['application/pdf', 'application/x-pdf'] and - config.val.content.pdfjs): - # Use pdf.js to display the page - self._show_pdfjs(reply) + elif pdfjs.should_use_pdfjs(mimetype): + download_manager.fetch(reply, + target=downloads.PDFJSDownloadTarget()) else: # Unknown mimetype, so download anyways. download_manager.fetch(reply,