diff options
-rw-r--r-- | src/core/url_request_custom_job.cpp | 62 | ||||
-rw-r--r-- | src/core/url_request_custom_job.h | 4 | ||||
-rw-r--r-- | src/core/url_request_custom_job_delegate.cpp | 11 | ||||
-rw-r--r-- | src/core/url_request_custom_job_delegate.h | 3 | ||||
-rw-r--r-- | src/core/url_request_custom_job_proxy.cpp | 7 | ||||
-rw-r--r-- | src/core/url_request_custom_job_proxy.h | 6 | ||||
-rw-r--r-- | tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp | 85 |
7 files changed, 173 insertions, 5 deletions
diff --git a/src/core/url_request_custom_job.cpp b/src/core/url_request_custom_job.cpp index 47c9b3b4c..b49135f79 100644 --- a/src/core/url_request_custom_job.cpp +++ b/src/core/url_request_custom_job.cpp @@ -56,6 +56,9 @@ URLRequestCustomJob::URLRequestCustomJob(URLRequest *request, , m_proxy(new URLRequestCustomJobProxy(this, scheme, adapter)) , m_device(nullptr) , m_error(0) + , m_pendingReadSize(0) + , m_pendingReadPos(0) + , m_pendingReadBuffer(nullptr) { } @@ -83,6 +86,12 @@ void URLRequestCustomJob::Kill() DCHECK_CURRENTLY_ON(content::BrowserThread::IO); if (m_device && m_device->isOpen()) m_device->close(); + if (m_pendingReadBuffer) { + m_pendingReadBuffer->Release(); + m_pendingReadBuffer = nullptr; + m_pendingReadSize = 0; + m_pendingReadPos = 0; + } m_device = nullptr; content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, base::Bind(&URLRequestCustomJobProxy::release, @@ -127,13 +136,64 @@ int URLRequestCustomJob::ReadRawData(IOBuffer *buf, int bufSize) if (m_error) return m_error; qint64 rv = m_device ? m_device->read(buf->data(), bufSize) : -1; - if (rv >= 0) { + if (rv > 0) { return static_cast<int>(rv); + } else if (rv == 0) { + // Returning zero is interpreted as EOF by Chromium, so only + // return zero if we are the end of the file. + if (m_device->atEnd()) + return 0; + // Otherwise return IO_PENDING and call ReadRawDataComplete when we have data + // for them. + buf->AddRef(); + m_pendingReadPos = 0; + m_pendingReadSize = bufSize; + m_pendingReadBuffer = buf; + return ERR_IO_PENDING; } else { // QIODevice::read might have called fail on us. if (m_error) return m_error; + if (m_device && m_device->atEnd()) + return 0; return ERR_FAILED; } } + +void URLRequestCustomJob::notifyReadyRead() +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + if (!m_device) + return; + if (!m_pendingReadSize) + return; + Q_ASSERT(m_pendingReadBuffer); + if (!m_pendingReadBuffer) + return; + + qint64 rv = m_device->read(m_pendingReadBuffer->data() + m_pendingReadPos, m_pendingReadSize - m_pendingReadPos); + if (rv == 0) + return; + if (rv < 0) { + if (m_error) + rv = m_error; + else if (m_device->atEnd()) + rv = 0; + else + rv = ERR_FAILED; + } else { + m_pendingReadPos += rv; + if (m_pendingReadPos < m_pendingReadSize && !m_device->atEnd()) + return; + rv = m_pendingReadPos; + } + // killJob may be called from ReadRawDataComplete + net::IOBuffer *buf = m_pendingReadBuffer; + m_pendingReadBuffer = nullptr; + m_pendingReadSize = 0; + m_pendingReadPos = 0; + ReadRawDataComplete(rv); + buf->Release(); +} + } // namespace diff --git a/src/core/url_request_custom_job.h b/src/core/url_request_custom_job.h index 68a834d48..021cf3204 100644 --- a/src/core/url_request_custom_job.h +++ b/src/core/url_request_custom_job.h @@ -70,12 +70,16 @@ protected: virtual ~URLRequestCustomJob(); private: + void notifyReadyRead(); scoped_refptr<URLRequestCustomJobProxy> m_proxy; std::string m_mimeType; std::string m_charset; GURL m_redirect; QIODevice *m_device; int m_error; + int m_pendingReadSize; + int m_pendingReadPos; + net::IOBuffer *m_pendingReadBuffer; friend class URLRequestCustomJobProxy; diff --git a/src/core/url_request_custom_job_delegate.cpp b/src/core/url_request_custom_job_delegate.cpp index 14de9a812..6b82cebb5 100644 --- a/src/core/url_request_custom_job_delegate.cpp +++ b/src/core/url_request_custom_job_delegate.cpp @@ -73,16 +73,23 @@ QByteArray URLRequestCustomJobDelegate::method() const void URLRequestCustomJobDelegate::reply(const QByteArray &contentType, QIODevice *device) { + if (device) + QObject::connect(device, &QIODevice::readyRead, this, &URLRequestCustomJobDelegate::slotReadyRead); content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, base::Bind(&URLRequestCustomJobProxy::reply, m_proxy,contentType.toStdString(),device)); } +void URLRequestCustomJobDelegate::slotReadyRead() +{ + content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, + base::Bind(&URLRequestCustomJobProxy::readyRead, m_proxy)); +} + void URLRequestCustomJobDelegate::abort() { content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, - base::Bind(&URLRequestCustomJobProxy::abort, - m_proxy)); + base::Bind(&URLRequestCustomJobProxy::abort, m_proxy)); } void URLRequestCustomJobDelegate::redirect(const QUrl &url) diff --git a/src/core/url_request_custom_job_delegate.h b/src/core/url_request_custom_job_delegate.h index eb99f3576..3f5e6d591 100644 --- a/src/core/url_request_custom_job_delegate.h +++ b/src/core/url_request_custom_job_delegate.h @@ -74,6 +74,9 @@ public: void abort(); void fail(Error); +private Q_SLOTS: + void slotReadyRead(); + private: URLRequestCustomJobDelegate(URLRequestCustomJobProxy *proxy, const QUrl &url, diff --git a/src/core/url_request_custom_job_proxy.cpp b/src/core/url_request_custom_job_proxy.cpp index d53602c85..832d3d11e 100644 --- a/src/core/url_request_custom_job_proxy.cpp +++ b/src/core/url_request_custom_job_proxy.cpp @@ -144,6 +144,13 @@ void URLRequestCustomJobProxy::fail(int error) // else we fail on the next read, or the read that might already be in progress } +void URLRequestCustomJobProxy::readyRead() +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + if (m_job) + m_job->notifyReadyRead(); +} + void URLRequestCustomJobProxy::initialize(GURL url, std::string method) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); diff --git a/src/core/url_request_custom_job_proxy.h b/src/core/url_request_custom_job_proxy.h index df7171f5e..3ea30a4e1 100644 --- a/src/core/url_request_custom_job_proxy.h +++ b/src/core/url_request_custom_job_proxy.h @@ -63,6 +63,7 @@ public: QWeakPointer<const BrowserContextAdapter> adapter); ~URLRequestCustomJobProxy(); + // Called from URLRequestCustomJobDelegate via post: //void setReplyCharset(const std::string &); void reply(std::string mimeType, QIODevice *device); void redirect(GURL url); @@ -70,12 +71,13 @@ public: void fail(int error); void release(); void initialize(GURL url, std::string method); + void readyRead(); - //IO thread owned + // IO thread owned: URLRequestCustomJob *m_job; bool m_started; - //UI thread owned + // UI thread owned: std::string m_scheme; URLRequestCustomJobDelegate *m_delegate; QWeakPointer<const BrowserContextAdapter> m_adapter; diff --git a/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp b/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp index 093bc2e43..400105152 100644 --- a/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp +++ b/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp @@ -49,6 +49,7 @@ private Q_SLOTS: void urlSchemeHandlers(); void urlSchemeHandlerFailRequest(); void urlSchemeHandlerFailOnRead(); + void urlSchemeHandlerStreaming(); void customUserAgent(); void httpAcceptLanguage(); void downloadItem(); @@ -178,6 +179,74 @@ public: } }; +class StreamingIODevice : public QIODevice { + Q_OBJECT +public: + StreamingIODevice(QObject *parent) : QIODevice(parent), m_bytesRead(0), m_bytesAvailable(0) + { + setOpenMode(QIODevice::ReadOnly); + m_timer.start(100, this); + } + bool isSequential() const override { return true; } + qint64 bytesAvailable() const override + { return m_bytesAvailable; } + bool atEnd() const override + { + return (m_data.size() >= 1000 && m_bytesRead >= 1000); + } +protected: + void timerEvent(QTimerEvent *) override + { + QMutexLocker lock(&m_mutex); + m_bytesAvailable += 200; + m_data.append(200, 'c'); + emit readyRead(); + if (m_data.size() >= 1000) { + m_timer.stop(); + emit readChannelFinished(); + } + } + + qint64 readData(char *data, qint64 maxlen) override + { + QMutexLocker lock(&m_mutex); + qint64 len = qMin(qint64(m_bytesAvailable), maxlen); + if (len) { + memcpy(data, m_data.constData() + m_bytesRead, len); + m_bytesAvailable -= len; + m_bytesRead += len; + } else if (m_data.size() > 0) + return -1; + + return len; + } + qint64 writeData(const char *, qint64) override + { + return 0; + } + +private: + QMutex m_mutex; + QByteArray m_data; + QBasicTimer m_timer; + int m_bytesRead; + int m_bytesAvailable; +}; + +class StreamingUrlSchemeHandler : public QWebEngineUrlSchemeHandler +{ +public: + StreamingUrlSchemeHandler(QObject *parent = nullptr) + : QWebEngineUrlSchemeHandler(parent) + { + } + + void requestStarted(QWebEngineUrlRequestJob *job) + { + job->reply("text/plain;charset=utf-8", new StreamingIODevice(job)); + } +}; + static bool loadSync(QWebEngineView *view, const QUrl &url, int timeout = 5000) { // Ripped off QTRY_VERIFY. @@ -310,6 +379,22 @@ void tst_QWebEngineProfile::urlSchemeHandlerFailOnRead() QCOMPARE(toPlainTextSync(view.page()), QString()); } +void tst_QWebEngineProfile::urlSchemeHandlerStreaming() +{ + StreamingUrlSchemeHandler handler; + QWebEngineProfile profile; + profile.installUrlSchemeHandler("stream", &handler); + QWebEngineView view; + QSignalSpy loadFinishedSpy(&view, SIGNAL(loadFinished(bool))); + view.setPage(new QWebEnginePage(&profile, &view)); + view.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false); + view.load(QUrl(QStringLiteral("stream://whatever"))); + QVERIFY(loadFinishedSpy.wait()); + QByteArray result; + result.append(1000, 'c'); + QCOMPARE(toPlainTextSync(view.page()), QString::fromLatin1(result)); +} + void tst_QWebEngineProfile::customUserAgent() { QString defaultUserAgent = QWebEngineProfile::defaultProfile()->httpUserAgent(); |