PDA

View Full Version : QtWebkit problems with custom QNetworkAccessManager+QNetworkReply



colin207
21st April 2010, 09:31
Hi

I have run into a mysterious issue with QNetworkAccessManager and/or QtWebkit. Using qt 4.6.2 with vs2008.

My app uses webkit to provide a user interface so the user can navigate through and display app-specific data structure held within the app's memory.

To do this the app has a very similar structure to the 'fancybrowser' sample included with Qt, but to which I added a custom QNetworkAccessManager which detects url's that use a private scheme (I used "myp:") and passes them to my own QNetworkReply derivation which knows how to supply the app specific html. All other urls are passed to the QNetworkAccessManager base class for default handling. This is exactly the same code structure as described in a recent Qt Quarterly article describing these techniques.

This appears to work however there is a problem. I have found that whenever the webview is displaying html obtained from an app-private url e.g myp:/fred, any image within the returned html whose source url is not http based just does not get displayed. Why is this?

Here's a more specific description of the problem.

Lets say I create a trivial bit of html which just displays three different images using <img> tags. The first image is stored locally and referenced with a file: based url. The second image is built into the app's binary as a Qt resource and is referenced with a qrc: url. The final image is taken from the net and so has a http: url. I store this html snippet in a local file C:\test.html:


<html><body>
FROM file:///c:/Dalmatian.jpg:<br><img src='file:///c:/Dalmatian.jpg'><br>
FROM qrc:/5453039.jpg:<br><img src='qrc:/5453039.jpg'><br>
FROM http://www.google.co.uk/intl/en_uk/images/logo.gif:<br>
<img src='http://www.google.co.uk/intl/en_uk/images/logo.gif'>
</body></html>

Using my modded fancybrowser I can display this by entering "file:///c:/test.html" in the url box and all is fine, with the 3 images displaying as expected. Here's a screenshot (like my classy images!):

http://www.tenset.co.uk/qtcentre/img2a.jpg

BUT if I arrange for the precise same html snippet to be returned by my own QNetworkReply class, in response to entering an app-private url starting "myp:", what I find is that the only image that displays is the one with the http: url; the images with file: and qrc: urls just dont appear. Here's the screenshot after I put 'myp:' in the url bar. Note images 1+2 missing.

http://www.tenset.co.uk/qtcentre/img1a.jpg

Why is this? What is going on? Is there a workaround/fix? Why is it that the provider of the html seems to affect how QtWebkit displays it?

Thanks.

Here is the relevant code. Note I can reproduce this problem by taking a copy of the qt fancybrowser sample, adding in the code below. Oh and I bind the jpg image referred to (using the qrc: url) into the app by adding a Q_INIT_RESOURCE line to main(). But that is all. No other changes.

First the QNetworkAccessManager derivation which is virtually identical to the example code from a recent Qt Quarterly.


// header
#include <QNetworkAccessManager>
class MyNetworkAccessManager : public QNetworkAccessManager
{
Q_OBJECT

public:
MyNetworkAccessManager(QNetworkAccessManager *oldManager, QObject *parent = 0);

protected:
QNetworkReply* createRequest(QNetworkAccessManager::Operation op,
const QNetworkRequest &req, QIODevice *device);
};

// code
MyNetworkAccessManager::MyNetworkAccessManager(
QNetworkAccessManager *oldManager, QObject *parent /*= 0*/)
: QNetworkAccessManager(parent)
{
setCache(oldManager->cache());
setCookieJar(oldManager->cookieJar());
setProxy(oldManager->proxy());
setProxyFactory(oldManager->proxyFactory());
}

QNetworkReply* MyNetworkAccessManager::createRequest(
QNetworkAccessManager::Operation op, const QNetworkRequest &req,
QIODevice *device)
{
if ((op == QNetworkAccessManager::GetOperation ||
op == QNetworkAccessManager::HeadOperation) && req.url().scheme() == "myp")
return new MyNetworkReply(this, req, op);
// defer to default:
return QNetworkAccessManager::createRequest(op, req, device);
}


The code to install this network manager is placed in the main window class immediately after creation of the QWebView:


.
.
view = new QWebView(this);
// MYCODE BEGINS
QNetworkAccessManager *oldManager = view->page()->networkAccessManager();
MyNetworkAccessManager *newManager = new MyNetworkAccessManager(oldManager, this);
view->page()->setNetworkAccessManager(newManager);
/ MYCODE ENDS
.
.


The QNetworkReply class. Here I have cut it down to its barest minimum, all this does is setup a reply containing a fixed snippet of html above. It just sticks the html in a QByteArray and set up readData/bytesAvailable etc to access this.


class MyNetworkReply : public QNetworkReply
{
Q_OBJECT

public:
MyNetworkReply(QObject *parent, const QNetworkRequest &req,
const QNetworkAccessManager::Operation op)
: QNetworkReply(parent)
{
setRequest(req);
setUrl(req.url());
setOperation(op);
QNetworkReply::open(QIODevice::ReadOnly | QIODevice::Unbuffered);

QString string = "<html><body>FROM MyNetworkReply:<br>\
FROM file:///c:/Dalmatian.jpg:<br><img src='file:///c:/Dalmatian.jpg'><br>\
FROM qrc:/5453039.jpg:<br><img src='qrc:/5453039.jpg'><br>\
FROM http://www.google.co.uk/intl/en_uk/images/logo.gif:<br>\
<img src='http://www.google.co.uk/intl/en_uk/images/logo.gif'>\
</body></html>\n";

m_content = string.toUtf8();
m_lOffset = 0;

setHeader(QNetworkRequest::ContentTypeHeader,
QVariant("text/html; charset=UTF-8"));
setHeader(QNetworkRequest::ContentLengthHeader,
QVariant(m_content.size()));

QMetaObject::invokeMethod(this, "metaDataChanged",
Qt::QueuedConnection);
QMetaObject::invokeMethod(this, "downloadProgress",
Qt::QueuedConnection, Q_ARG(qint64, m_content.size()), Q_ARG(qint64, m_content.size()));
QMetaObject::invokeMethod(this, "readyRead",
Qt::QueuedConnection);
QMetaObject::invokeMethod(this, "finished",
Qt::QueuedConnection);
}
void abort() { QNetworkReply::close(); }
qint64 bytesAvailable() const { return m_content.size() - m_lOffset; }
bool isSequential() const { return true; }

protected:
qint64 readData(char* pData, qint64 lMaxSize)
{
if (m_lOffset >= m_content.size())
return -1;

qint64 lCount = qMin(lMaxSize, m_content.size() - m_lOffset);
memcpy(pData, m_content.constData() + m_lOffset, lCount);
m_lOffset += lCount;
return lCount;
}

private slots:

private:
QByteArray m_content;
qint64 m_lOffset;
};

Thành Viên Mới
17th January 2011, 08:31
are you running on Windows ?, i also error like you, but it on Linux embedded

nowrep
18th January 2012, 15:01
QWebSecurityOrigin::addLocalScheme("myp");
For more info, see QWebSecurityOrigin class in documentation.