From f3066a7f3dc3c3a69344193b0fb50504c5760086 Mon Sep 17 00:00:00 2001 From: Robert Hogan Date: Mon, 26 Apr 2010 19:46:28 +0100 Subject: QNAM HTTP: Introduce attributes for controlling cookies and auth Introduced QNetworkRequest::CookieLoadControlAttribute, QNetworkRequest::CookieSaveControlAttribute and QNetworkRequest::AuthenticationReuseControlAttribute These are true by default. They only come into play when QtWebKit processes a cross-origin XMLHttpRequest. In such cases QtWebKit sets each of the attributes to false when it creates a QNetworkRequest where the XMLHttpRequest is cross-origin and has withCredentials set to false: var req = new XMLHttpRequest; req.open("GET", "http://host/resource.php", false); req.withCredentials = false; // actually false by default For more information: http://www.w3.org/TR/XMLHttpRequest2/#credentials-flag The QtWebKit counterpart of this patch is tracked at: https://bugs.webkit.org/show_bug.cgi?id=32967 Merge-Request: 592 Reviewed-by: Markus Goetz --- src/network/access/qhttpnetworkconnection.cpp | 11 +++++- src/network/access/qhttpnetworkconnection_p.h | 2 + .../access/qhttpnetworkconnectionchannel.cpp | 7 +++- src/network/access/qhttpnetworkrequest.cpp | 13 ++++++- src/network/access/qhttpnetworkrequest_p.h | 4 ++ src/network/access/qnetworkaccessbackend.cpp | 5 +++ src/network/access/qnetworkaccessbackend_p.h | 1 + src/network/access/qnetworkaccesshttpbackend.cpp | 13 +++++++ src/network/access/qnetworkaccesshttpbackend_p.h | 1 + src/network/access/qnetworkaccessmanager.cpp | 27 ++++++++----- src/network/access/qnetworkreplyimpl.cpp | 8 +++- src/network/access/qnetworkrequest.cpp | 45 ++++++++++++++++++++++ src/network/access/qnetworkrequest.h | 7 ++++ 13 files changed, 128 insertions(+), 16 deletions(-) diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp index 559124f..31c64f0 100644 --- a/src/network/access/qhttpnetworkconnection.cpp +++ b/src/network/access/qhttpnetworkconnection.cpp @@ -343,9 +343,16 @@ bool QHttpNetworkConnectionPrivate::handleAuthenticateChallenge(QAbstractSocket copyCredentials(i, auth, isProxy); QMetaObject::invokeMethod(q, "_q_restartAuthPendingRequests", Qt::QueuedConnection); } + } else if (priv->phase == QAuthenticatorPrivate::Start) { + // If the url's authenticator has a 'user' set we will end up here (phase is only set to 'Done' by + // parseHttpResponse above if 'user' is empty). So if credentials were supplied with the request, + // such as in the case of an XMLHttpRequest, this is our only opportunity to cache them. + emit q->cacheCredentials(reply->request(), auth, q); } - // changing values in QAuthenticator will reset the 'phase' - if (priv->phase == QAuthenticatorPrivate::Done) { + // - Changing values in QAuthenticator will reset the 'phase'. + // - If withCredentials has been set to false (e.g. by QtWebKit for a cross-origin XMLHttpRequest) then + // we need to bail out if authentication is required. + if (priv->phase == QAuthenticatorPrivate::Done || !reply->request().withCredentials()) { // authentication is cancelled, send the current contents to the user. emit channels[i].reply->headerChanged(); emit channels[i].reply->readyRead(); diff --git a/src/network/access/qhttpnetworkconnection_p.h b/src/network/access/qhttpnetworkconnection_p.h index b5bd300..51666d6 100644 --- a/src/network/access/qhttpnetworkconnection_p.h +++ b/src/network/access/qhttpnetworkconnection_p.h @@ -133,6 +133,8 @@ Q_SIGNALS: #endif void authenticationRequired(const QHttpNetworkRequest &request, QAuthenticator *authenticator, const QHttpNetworkConnection *connection = 0); + void cacheCredentials(const QHttpNetworkRequest &request, QAuthenticator *authenticator, + const QHttpNetworkConnection *connection = 0); void error(QNetworkReply::NetworkError errorCode, const QString &detail = QString()); private: diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp index 3b7bc9e..d24eb1f 100644 --- a/src/network/access/qhttpnetworkconnectionchannel.cpp +++ b/src/network/access/qhttpnetworkconnectionchannel.cpp @@ -173,7 +173,7 @@ bool QHttpNetworkConnectionChannel::sendRequest() pendingEncrypt = false; // if the url contains authentication parameters, use the new ones // both channels will use the new authentication parameters - if (!request.url().userInfo().isEmpty()) { + if (!request.url().userInfo().isEmpty() && request.withCredentials()) { QUrl url = request.url(); QAuthenticator &auth = authenticator; if (url.userName() != auth.user() @@ -187,7 +187,10 @@ bool QHttpNetworkConnectionChannel::sendRequest() url.setUserInfo(QString()); request.setUrl(url); } - connection->d_func()->createAuthorization(socket, request); + // Will only be false if QtWebKit is performing a cross-origin XMLHttpRequest + // and withCredentials has not been set to true. + if (request.withCredentials()) + connection->d_func()->createAuthorization(socket, request); #ifndef QT_NO_NETWORKPROXY QByteArray header = QHttpNetworkRequestPrivate::header(request, (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy)); diff --git a/src/network/access/qhttpnetworkrequest.cpp b/src/network/access/qhttpnetworkrequest.cpp index 9eb2399..639025e 100644 --- a/src/network/access/qhttpnetworkrequest.cpp +++ b/src/network/access/qhttpnetworkrequest.cpp @@ -49,7 +49,7 @@ QT_BEGIN_NAMESPACE QHttpNetworkRequestPrivate::QHttpNetworkRequestPrivate(QHttpNetworkRequest::Operation op, QHttpNetworkRequest::Priority pri, const QUrl &newUrl) : QHttpNetworkHeaderPrivate(newUrl), operation(op), priority(pri), uploadByteDevice(0), - autoDecompress(false), pipeliningAllowed(false) + autoDecompress(false), pipeliningAllowed(false), withCredentials(true) { } @@ -62,6 +62,7 @@ QHttpNetworkRequestPrivate::QHttpNetworkRequestPrivate(const QHttpNetworkRequest autoDecompress = other.autoDecompress; pipeliningAllowed = other.pipeliningAllowed; customVerb = other.customVerb; + withCredentials = other.withCredentials; } QHttpNetworkRequestPrivate::~QHttpNetworkRequestPrivate() @@ -274,6 +275,16 @@ void QHttpNetworkRequest::setPipeliningAllowed(bool b) d->pipeliningAllowed = b; } +bool QHttpNetworkRequest::withCredentials() const +{ + return d->withCredentials; +} + +void QHttpNetworkRequest::setWithCredentials(bool b) +{ + d->withCredentials = b; +} + void QHttpNetworkRequest::setUploadByteDevice(QNonContiguousByteDevice *bd) { d->uploadByteDevice = bd; diff --git a/src/network/access/qhttpnetworkrequest_p.h b/src/network/access/qhttpnetworkrequest_p.h index 1b35a84..15cab73 100644 --- a/src/network/access/qhttpnetworkrequest_p.h +++ b/src/network/access/qhttpnetworkrequest_p.h @@ -113,6 +113,9 @@ public: bool isPipeliningAllowed() const; void setPipeliningAllowed(bool b); + bool withCredentials() const; + void setWithCredentials(bool b); + void setUploadByteDevice(QNonContiguousByteDevice *bd); QNonContiguousByteDevice* uploadByteDevice() const; @@ -142,6 +145,7 @@ public: mutable QNonContiguousByteDevice* uploadByteDevice; bool autoDecompress; bool pipeliningAllowed; + bool withCredentials; }; diff --git a/src/network/access/qnetworkaccessbackend.cpp b/src/network/access/qnetworkaccessbackend.cpp index f188bd5..2a02c99 100644 --- a/src/network/access/qnetworkaccessbackend.cpp +++ b/src/network/access/qnetworkaccessbackend.cpp @@ -327,6 +327,11 @@ void QNetworkAccessBackend::authenticationRequired(QAuthenticator *authenticator manager->authenticationRequired(this, authenticator); } +void QNetworkAccessBackend::cacheCredentials(QAuthenticator *authenticator) +{ + manager->addCredentials(this->reply->url, authenticator); +} + void QNetworkAccessBackend::metaDataChanged() { reply->metaDataChanged(); diff --git a/src/network/access/qnetworkaccessbackend_p.h b/src/network/access/qnetworkaccessbackend_p.h index 4ce37a6..4fe6de6 100644 --- a/src/network/access/qnetworkaccessbackend_p.h +++ b/src/network/access/qnetworkaccessbackend_p.h @@ -188,6 +188,7 @@ protected slots: void proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *auth); #endif void authenticationRequired(QAuthenticator *auth); + void cacheCredentials(QAuthenticator *auth); void metaDataChanged(); void redirectionRequested(const QUrl &destination); void sslErrors(const QList &errors); diff --git a/src/network/access/qnetworkaccesshttpbackend.cpp b/src/network/access/qnetworkaccesshttpbackend.cpp index 3154ed6..a6c5c02 100644 --- a/src/network/access/qnetworkaccesshttpbackend.cpp +++ b/src/network/access/qnetworkaccesshttpbackend.cpp @@ -346,6 +346,8 @@ void QNetworkAccessHttpBackend::setupConnection() #endif connect(http, SIGNAL(authenticationRequired(QHttpNetworkRequest,QAuthenticator*)), SLOT(httpAuthenticationRequired(QHttpNetworkRequest,QAuthenticator*))); + connect(http, SIGNAL(cacheCredentials(QHttpNetworkRequest,QAuthenticator*)), + SLOT(httpCacheCredentials(QHttpNetworkRequest,QAuthenticator*))); connect(http, SIGNAL(error(QNetworkReply::NetworkError,QString)), SLOT(httpError(QNetworkReply::NetworkError,QString))); #ifndef QT_NO_OPENSSL @@ -578,6 +580,11 @@ void QNetworkAccessHttpBackend::postRequest() if (request().attribute(QNetworkRequest::HttpPipeliningAllowedAttribute).toBool() == true) httpRequest.setPipeliningAllowed(true); + if (static_cast + (request().attribute(QNetworkRequest::AuthenticationReuseAttribute, + QNetworkRequest::Automatic).toInt()) == QNetworkRequest::Manual) + httpRequest.setWithCredentials(false); + httpReply = http->sendRequest(httpRequest); httpReply->setParent(this); #ifndef QT_NO_OPENSSL @@ -861,6 +868,12 @@ void QNetworkAccessHttpBackend::httpAuthenticationRequired(const QHttpNetworkReq authenticationRequired(auth); } +void QNetworkAccessHttpBackend::httpCacheCredentials(const QHttpNetworkRequest &, + QAuthenticator *auth) +{ + cacheCredentials(auth); +} + void QNetworkAccessHttpBackend::httpError(QNetworkReply::NetworkError errorCode, const QString &errorString) { diff --git a/src/network/access/qnetworkaccesshttpbackend_p.h b/src/network/access/qnetworkaccesshttpbackend_p.h index e5cc0ab..254907f 100644 --- a/src/network/access/qnetworkaccesshttpbackend_p.h +++ b/src/network/access/qnetworkaccesshttpbackend_p.h @@ -107,6 +107,7 @@ private slots: void replyFinished(); void replyHeaderChanged(); void httpAuthenticationRequired(const QHttpNetworkRequest &request, QAuthenticator *auth); + void httpCacheCredentials(const QHttpNetworkRequest &request, QAuthenticator *auth); void httpError(QNetworkReply::NetworkError error, const QString &errorString); bool sendCacheContents(const QNetworkCacheMetaData &metaData); void finished(); // override diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp index feb9d99..1c7661d 100644 --- a/src/network/access/qnetworkaccessmanager.cpp +++ b/src/network/access/qnetworkaccessmanager.cpp @@ -948,10 +948,15 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera // but the data that is outgoing is random-access request.setHeader(QNetworkRequest::ContentLengthHeader, outgoingData->size()); } - if (d->cookieJar) { - QList cookies = d->cookieJar->cookiesForUrl(request.url()); - if (!cookies.isEmpty()) - request.setHeader(QNetworkRequest::CookieHeader, qVariantFromValue(cookies)); + + if (static_cast + (request.attribute(QNetworkRequest::CookieLoadControlAttribute, + QNetworkRequest::Automatic).toInt()) == QNetworkRequest::Automatic) { + if (d->cookieJar) { + QList cookies = d->cookieJar->cookiesForUrl(request.url()); + if (!cookies.isEmpty()) + request.setHeader(QNetworkRequest::CookieHeader, qVariantFromValue(cookies)); + } } // first step: create the reply @@ -967,11 +972,15 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera priv->manager = this; // second step: fetch cached credentials - QNetworkAuthenticationCredential *cred = d->fetchCachedCredentials(url); - if (cred) { - url.setUserName(cred->user); - url.setPassword(cred->password); - priv->urlForLastAuthentication = url; + if (static_cast + (request.attribute(QNetworkRequest::AuthenticationReuseAttribute, + QNetworkRequest::Automatic).toInt()) == QNetworkRequest::Automatic) { + QNetworkAuthenticationCredential *cred = d->fetchCachedCredentials(url); + if (cred) { + url.setUserName(cred->user); + url.setPassword(cred->password); + priv->urlForLastAuthentication = url; + } } // third step: find a backend diff --git a/src/network/access/qnetworkreplyimpl.cpp b/src/network/access/qnetworkreplyimpl.cpp index 128d18f..31ee2a4 100644 --- a/src/network/access/qnetworkreplyimpl.cpp +++ b/src/network/access/qnetworkreplyimpl.cpp @@ -673,8 +673,12 @@ void QNetworkReplyImplPrivate::error(QNetworkReplyImpl::NetworkError code, const void QNetworkReplyImplPrivate::metaDataChanged() { Q_Q(QNetworkReplyImpl); - // do we have cookies? - if (cookedHeaders.contains(QNetworkRequest::SetCookieHeader) && !manager.isNull()) { + // 1. do we have cookies? + // 2. are we allowed to set them? + if (cookedHeaders.contains(QNetworkRequest::SetCookieHeader) && !manager.isNull() + && (static_cast + (request.attribute(QNetworkRequest::CookieSaveControlAttribute, + QNetworkRequest::Automatic).toInt()) == QNetworkRequest::Automatic)) { QList cookies = qvariant_cast >(cookedHeaders.value(QNetworkRequest::SetCookieHeader)); QNetworkCookieJar *jar = manager->cookieJar(); diff --git a/src/network/access/qnetworkrequest.cpp b/src/network/access/qnetworkrequest.cpp index 61c116d..79f169f 100644 --- a/src/network/access/qnetworkrequest.cpp +++ b/src/network/access/qnetworkrequest.cpp @@ -190,6 +190,40 @@ QT_BEGIN_NAMESPACE of other verbs than GET, POST, PUT and DELETE). This verb is set when calling QNetworkAccessManager::sendCustomRequest(). + \value CookieLoadControlAttribute + Requests only, type: QVariant::Int (default: QNetworkRequest::Automatic) + Indicates whether to send 'Cookie' headers in the request. + + This attribute is set to false by QtWebKit when creating a cross-origin + XMLHttpRequest where withCredentials has not been set explicitly to true by the + Javascript that created the request. + + See http://www.w3.org/TR/XMLHttpRequest2/#credentials-flag for more information. + + \value CookieSaveControlAttribute + Requests only, type: QVariant::Int (default: QNetworkRequest::Automatic) + Indicates whether to save 'Cookie' headers received from the server in reply + to the request. + + This attribute is set to false by QtWebKit when creating a cross-origin + XMLHttpRequest where withCredentials has not been set explicitly to true by the + Javascript that created the request. + + See http://www.w3.org/TR/XMLHttpRequest2/#credentials-flag for more information. + + \value AuthenticationReuseControlAttribute + Requests only, type: QVariant::Int (default: QNetworkRequest::Automatic) + Indicates whether to use cached authorization credentials in the request, + if available. If this is set to QNetworkRequest::Manual and the authentication + mechanism is 'Basic' or 'Digest', Qt will not send an an 'Authorization' HTTP + header with any cached credentials it may have for the request's URL. + + This attribute is set to QNetworkRequest::Manual by QtWebKit when creating a cross-origin + XMLHttpRequest where withCredentials has not been set explicitly to true by the + Javascript that created the request. + + See http://www.w3.org/TR/XMLHttpRequest2/#credentials-flag for more information. + \value User Special type. Additional information can be passed in QVariants with types ranging from User to UserMax. The default @@ -222,6 +256,17 @@ QT_BEGIN_NAMESPACE if the item was not cached (i.e., off-line mode) */ +/*! + \enum QNetworkRequest::LoadControl + + Indicates if an aspect of the request's loading mechanism has been + manually overridden, e.g. by QtWebKit. + + \value Automatic default value: indicates default behaviour. + + \value Manual indicates behaviour has been manually overridden. +*/ + class QNetworkRequestPrivate: public QSharedData, public QNetworkHeadersPrivate { public: diff --git a/src/network/access/qnetworkrequest.h b/src/network/access/qnetworkrequest.h index a0ef1a6..d2945c4 100644 --- a/src/network/access/qnetworkrequest.h +++ b/src/network/access/qnetworkrequest.h @@ -79,6 +79,9 @@ public: HttpPipeliningAllowedAttribute, HttpPipeliningWasUsedAttribute, CustomVerbAttribute, + CookieLoadControlAttribute, + AuthenticationReuseAttribute, + CookieSaveControlAttribute, User = 1000, UserMax = 32767 @@ -89,6 +92,10 @@ public: PreferCache, AlwaysCache }; + enum LoadControl { + Automatic = 0, + Manual + }; enum Priority { HighPriority = 1, -- cgit v0.12 From 3326a5811fbd50add0aa370af39f4a69b3a5480e Mon Sep 17 00:00:00 2001 From: Markus Goetz Date: Tue, 4 May 2010 10:34:44 +0200 Subject: QNAM HTTP: Add doc since tags for new enums Related to MR-592 --- src/network/access/qnetworkrequest.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/network/access/qnetworkrequest.cpp b/src/network/access/qnetworkrequest.cpp index 79f169f..911eadc 100644 --- a/src/network/access/qnetworkrequest.cpp +++ b/src/network/access/qnetworkrequest.cpp @@ -200,6 +200,8 @@ QT_BEGIN_NAMESPACE See http://www.w3.org/TR/XMLHttpRequest2/#credentials-flag for more information. + \since 4.7 + \value CookieSaveControlAttribute Requests only, type: QVariant::Int (default: QNetworkRequest::Automatic) Indicates whether to save 'Cookie' headers received from the server in reply @@ -211,6 +213,8 @@ QT_BEGIN_NAMESPACE See http://www.w3.org/TR/XMLHttpRequest2/#credentials-flag for more information. + \since 4.7 + \value AuthenticationReuseControlAttribute Requests only, type: QVariant::Int (default: QNetworkRequest::Automatic) Indicates whether to use cached authorization credentials in the request, @@ -224,6 +228,8 @@ QT_BEGIN_NAMESPACE See http://www.w3.org/TR/XMLHttpRequest2/#credentials-flag for more information. + \since 4.7 + \value User Special type. Additional information can be passed in QVariants with types ranging from User to UserMax. The default @@ -258,6 +264,7 @@ QT_BEGIN_NAMESPACE /*! \enum QNetworkRequest::LoadControl + \since 4.7 Indicates if an aspect of the request's loading mechanism has been manually overridden, e.g. by QtWebKit. -- cgit v0.12 From e9f9887cfb6f3eace17640cb951f847d2e4a8c53 Mon Sep 17 00:00:00 2001 From: Jakob Truelsen Date: Mon, 3 May 2010 21:08:10 +0200 Subject: Propperly escape title and creator in PDF documents The title and creator fields in the PDF information section were not propperly escaped leading to invalid pdf documents when the title contain ')','(','\' (or any of the bazilion utf16 characters where one of the bytes happen to be the same as the ascii code for one of these). Merge-request: 2375 Reviewed-by: Benjamin Poulain --- src/gui/painting/qprintengine_pdf.cpp | 46 ++++++++++++++++++++--------------- src/gui/painting/qprintengine_pdf_p.h | 1 + tests/auto/qprinter/tst_qprinter.cpp | 28 ++++++++++++++++++++- 3 files changed, 54 insertions(+), 21 deletions(-) diff --git a/src/gui/painting/qprintengine_pdf.cpp b/src/gui/painting/qprintengine_pdf.cpp index b8bf15e..0a747e7 100644 --- a/src/gui/painting/qprintengine_pdf.cpp +++ b/src/gui/painting/qprintengine_pdf.cpp @@ -931,29 +931,16 @@ void QPdfEnginePrivate::writeHeader() void QPdfEnginePrivate::writeInfo() { info = addXrefEntry(-1); - - // The 'text string' type in PDF is encoded either as PDFDocEncoding, or - // Unicode UTF-16 with a Unicode byte order mark as the first character - // (0xfeff), with the high-order byte first. - QByteArray array("<<\n/Title (\xfe\xff"); - const ushort *utf16Title = title.utf16(); - for (int i=0; i < title.size(); ++i) { - array.append((*(utf16Title + i)) >> 8); - array.append((*(utf16Title + i)) & 0xff); - } - array.append(")\n/Creator (\xfe\xff"); - const ushort *utf16Creator = creator.utf16(); - for (int i=0; i < creator.size(); ++i) { - array.append((*(utf16Creator + i)) >> 8); - array.append((*(utf16Creator + i)) & 0xff); - } - array.append(")\n/Producer (Qt " QT_VERSION_STR " (C) 2010 Nokia Corporation and/or its subsidiary(-ies))\n"); - write(array); - + xprintf("<<\n/Title "); + printString(title); + xprintf("\n/Creator "); + printString(creator); + xprintf("\n/Producer "); + printString(QString::fromLatin1("Qt " QT_VERSION_STR " (C) 2010 Nokia Corporation and/or its subsidiary(-ies)")); QDateTime now = QDateTime::currentDateTime().toUTC(); QTime t = now.time(); QDate d = now.date(); - xprintf("/CreationDate (D:%d%02d%02d%02d%02d%02d)\n", + xprintf("\n/CreationDate (D:%d%02d%02d%02d%02d%02d)\n", d.year(), d.month(), d.day(), @@ -1230,6 +1217,25 @@ int QPdfEnginePrivate::addXrefEntry(int object, bool printostr) return object; } +void QPdfEnginePrivate::printString(const QString &string) { + // The 'text string' type in PDF is encoded either as PDFDocEncoding, or + // Unicode UTF-16 with a Unicode byte order mark as the first character + // (0xfeff), with the high-order byte first. + QByteArray array("(\xfe\xff"); + const ushort *utf16 = string.utf16(); + + for (int i=0; i < string.size(); ++i) { + char part[2] = {(*(utf16 + i)) >> 8, (*(utf16 + i)) & 0xff}; + for(int j=0; j < 2; ++j) { + if (part[j] == '(' || part[j] == ')' || part[j] == '\\') + array.append('\\'); + array.append(part[j]); + } + } + array.append(")"); + write(array); +} + QT_END_NAMESPACE #endif // QT_NO_PRINTER diff --git a/src/gui/painting/qprintengine_pdf_p.h b/src/gui/painting/qprintengine_pdf_p.h index cb6c59d..e0ca56f 100644 --- a/src/gui/painting/qprintengine_pdf_p.h +++ b/src/gui/painting/qprintengine_pdf_p.h @@ -170,6 +170,7 @@ private: void writePage(); int addXrefEntry(int object, bool printostr = true); + void printString(const QString &string); void xprintf(const char* fmt, ...); inline void write(const QByteArray &data) { stream->writeRawData(data.constData(), data.size()); diff --git a/tests/auto/qprinter/tst_qprinter.cpp b/tests/auto/qprinter/tst_qprinter.cpp index 49bddb2..8b79533 100644 --- a/tests/auto/qprinter/tst_qprinter.cpp +++ b/tests/auto/qprinter/tst_qprinter.cpp @@ -110,6 +110,7 @@ private slots: void testCurrentPage(); void taskQTBUG4497_reusePrinterOnDifferentFiles(); + void testPdfTitle(); private: }; @@ -417,7 +418,7 @@ void tst_QPrinter::testMargins() printer.setFullPage(fullpage); printer.setPageSize((QPrinter::PageSize)pagesize); if (withPainter) - painter = new QPainter(&printer); + painter = new QPainter(&printer); #ifdef QT3_SUPPORT Q3PaintDeviceMetrics metrics(&printer); @@ -1028,5 +1029,30 @@ void tst_QPrinter::testCurrentPage() } +void tst_QPrinter::testPdfTitle() +{ + // Check the document name is represented correctly in produced pdf + { + QPainter painter; + QPrinter printer; + // This string is just the UTF-8 encoding of the string: \()f ø hiragana o + const char title[]={0x5c, 0x28, 0x29, 0x66, 0xc3, 0xb8, 0xe3, 0x81, 0x8a, 0x00}; + printer.setOutputFileName("file.pdf"); + printer.setDocName(QString::fromUtf8(title)); + painter.begin(&printer); + painter.end(); + } + QFile file("file.pdf"); + QVERIFY(file.open(QIODevice::ReadOnly)); + // The we expect the title to appear in the PDF as: + // ASCII('\title (') UTF16(\\\(\)f ø hiragana o) ASCII(')'). + // which has the following binary representation + const char expected[] = { + 0x2f, 0x54, 0x69, 0x74, 0x6c, 0x65, 0x20, 0x28, 0xfe, + 0xff, 0x00, 0x5c, 0x5c, 0x00, 0x5c, 0x28, 0x00, 0x5c, + 0x29, 0x00, 0x66, 0x00, 0xf8, 0x30, 0x4a, 0x29}; + QVERIFY(file.readAll().contains(QByteArray(expected, 26))); +} + QTEST_MAIN(tst_QPrinter) #include "tst_qprinter.moc" -- cgit v0.12 From 923110602c653ae9df9d562e19ef92666a6f8867 Mon Sep 17 00:00:00 2001 From: Carlos Manuel Duclos Vergara Date: Tue, 4 May 2010 11:57:18 +0200 Subject: Suggestions' widget on another space The problem here arises from the fact that when a window is raisen we didn't check the current space. This patch fixes that and adds a simple logic so the window is raisen in the right space. Task-number: QTCREATORBUG-827 Reviewed-by: Richard Moe Gustavsen --- src/gui/kernel/qwidget_mac.mm | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/gui/kernel/qwidget_mac.mm b/src/gui/kernel/qwidget_mac.mm index e29b755..81dd746 100644 --- a/src/gui/kernel/qwidget_mac.mm +++ b/src/gui/kernel/qwidget_mac.mm @@ -3824,9 +3824,28 @@ void QWidgetPrivate::raise_sys() #if QT_MAC_USE_COCOA QMacCocoaAutoReleasePool pool; if (isRealWindow()) { - // Calling orderFront shows the window on Cocoa too. + // With the introduction of spaces it is not as simple as just raising the window. + // First we need to check if we are in the right space. If we are, then we just continue + // as usual. The problem comes when we are not in the active space. There are two main cases: + // 1. Our parent was moved to a new space. In this case we want the window to be raised + // in the same space as its parent. + // 2. We don't have a parent. For this case we will just raise the window and let Cocoa + // switch to the corresponding space. + // NOTICE: There are a lot of corner cases here. We are keeping this simple for now, if + // required we will introduce special handling for some of them. if (!q->testAttribute(Qt::WA_DontShowOnScreen) && q->isVisible()) { - [qt_mac_window_for(q) orderFront:qt_mac_window_for(q)]; + OSWindowRef window = qt_mac_window_for(q); + if(![window isOnActiveSpace]) { + QWidget *parentWidget = q->parentWidget(); + if(parentWidget) { + OSWindowRef parentWindow = qt_mac_window_for(parentWidget); + if(parentWindow && [parentWindow isOnActiveSpace]) { + recreateMacWindow(); + window = qt_mac_window_for(q); + } + } + } + [window orderFront:window]; } if (qt_mac_raise_process) { //we get to be the active process now ProcessSerialNumber psn; -- cgit v0.12 From 3f6558c2e12f507b71e0b99deb0fc76ce36734af Mon Sep 17 00:00:00 2001 From: Martin Smith Date: Tue, 4 May 2010 13:02:34 +0200 Subject: qdoc: Fixed class list problem. Task: QTBUG-10373 Task: QTBUG-10382 --- tools/qdoc3/htmlgenerator.cpp | 48 +++++++++++++++++++++++++++++++------------ tools/qdoc3/htmlgenerator.h | 1 + 2 files changed, 36 insertions(+), 13 deletions(-) diff --git a/tools/qdoc3/htmlgenerator.cpp b/tools/qdoc3/htmlgenerator.cpp index 40145ff..1840a85 100644 --- a/tools/qdoc3/htmlgenerator.cpp +++ b/tools/qdoc3/htmlgenerator.cpp @@ -599,7 +599,7 @@ int HtmlGenerator::generateAtom(const Atom *atom, generateAnnotatedList(relative, marker, nonCompatClasses); } else if (atom->string() == "classes") { - generateCompactList(relative, marker, nonCompatClasses); + generateCompactList(relative, marker, nonCompatClasses, true); } else if (atom->string().contains("classesbymodule")) { QString arg = atom->string().trimmed(); @@ -647,10 +647,10 @@ int HtmlGenerator::generateAtom(const Atom *atom, generateClassHierarchy(relative, marker, nonCompatClasses); } else if (atom->string() == "compatclasses") { - generateCompactList(relative, marker, compatClasses); + generateCompactList(relative, marker, compatClasses, false); } else if (atom->string() == "obsoleteclasses") { - generateCompactList(relative, marker, obsoleteClasses); + generateCompactList(relative, marker, obsoleteClasses, false); } else if (atom->string() == "functionindex") { generateFunctionIndex(relative, marker); @@ -659,10 +659,10 @@ int HtmlGenerator::generateAtom(const Atom *atom, generateLegaleseList(relative, marker); } else if (atom->string() == "mainclasses") { - generateCompactList(relative, marker, mainClasses); + generateCompactList(relative, marker, mainClasses, true); } else if (atom->string() == "services") { - generateCompactList(relative, marker, serviceClasses); + generateCompactList(relative, marker, serviceClasses, false); } else if (atom->string() == "overviews") { generateOverviewList(relative, marker); @@ -802,9 +802,9 @@ int HtmlGenerator::generateAtom(const Atom *atom, << "\">\n"; out() << "

" << protectEnc((*s).name) << "

\n"; if (idx == Class) - generateCompactList(0, marker, ncmap.value(), QString("Q")); + generateCompactList(0, marker, ncmap.value(), false, QString("Q")); else if (idx == QmlClass) - generateCompactList(0, marker, nqcmap.value(), QString("Q")); + generateCompactList(0, marker, nqcmap.value(), false, QString("Q")); else if (idx == MemberFunction) { ParentMaps parentmaps; ParentMaps::iterator pmap; @@ -2314,10 +2314,11 @@ void HtmlGenerator::generateAnnotatedList(const Node *relative, void HtmlGenerator::generateCompactList(const Node *relative, CodeMarker *marker, const NodeMap &classMap, + bool includeAlphabet, QString commonPrefix) { const int NumParagraphs = 37; // '0' to '9', 'A' to 'Z', '_' - const int NumColumns = 3; // number of columns in the result + const int NumColumns = 2; // number of columns in the result if (classMap.isEmpty()) return; @@ -2428,15 +2429,15 @@ void HtmlGenerator::generateCompactList(const Node *relative, for (j = 0; j < NumParagraphs; j++) // j = 0..36 paragraphOffset[j + 1] = paragraphOffset[j] + paragraph[j].count(); - int firstOffset[NumColumns + 1]; // 4 + 1 - int currentOffset[NumColumns]; // 4 - int currentParagraphNo[NumColumns]; // 4 - int currentOffsetInParagraph[NumColumns]; // 4 + int firstOffset[NumColumns + 1]; + int currentOffset[NumColumns]; + int currentParagraphNo[NumColumns]; + int currentOffsetInParagraph[NumColumns]; int numRows = (classMap.count() + NumColumns - 1) / NumColumns; int curParagNo = 0; - for (i = 0; i < NumColumns; i++) { // i = 0..3 + for (i = 0; i < NumColumns; i++) { firstOffset[i] = qMin(i * numRows, classMap.size()); currentOffset[i] = firstOffset[i]; @@ -2452,6 +2453,16 @@ void HtmlGenerator::generateCompactList(const Node *relative, } firstOffset[NumColumns] = classMap.count(); + if (includeAlphabet) { + out() << "

"; + for (int i = 0; i < 26; i++) { + QChar ch('a' + i); + out() << QString("%2 ").arg(ch).arg(ch.toUpper()); + } + out() << "

\n"; + } + + QSet used; out() << "\n"; for (k = 0; k < numRows; k++) { out() << "\n"; @@ -2476,6 +2487,11 @@ void HtmlGenerator::generateCompactList(const Node *relative, out() << "\n"; } out() << "
"; if (currentOffsetInParagraph[i] == 0) { // start a new paragraph + if (includeAlphabet) { + QChar c = paragraphName[currentParagraphNo[i]][0].toLower(); + out() << QString("").arg(c); + used.insert(c.cell()); + } out() << "" << paragraphName[currentParagraphNo[i]] << " "; @@ -2517,6 +2533,12 @@ void HtmlGenerator::generateCompactList(const Node *relative, out() << "
\n"; + char C = 'a'; + while (C <= 'z') { + if (!used.contains(C)) + out() << QString("").arg(C); + ++C; + } } void HtmlGenerator::generateFunctionIndex(const Node *relative, diff --git a/tools/qdoc3/htmlgenerator.h b/tools/qdoc3/htmlgenerator.h index d80cbdb..e060257 100644 --- a/tools/qdoc3/htmlgenerator.h +++ b/tools/qdoc3/htmlgenerator.h @@ -173,6 +173,7 @@ class HtmlGenerator : public PageGenerator void generateCompactList(const Node *relative, CodeMarker *marker, const NodeMap &classMap, + bool includeAlphabet, QString commonPrefix = QString()); void generateFunctionIndex(const Node *relative, CodeMarker *marker); void generateLegaleseList(const Node *relative, CodeMarker *marker); -- cgit v0.12 From 78c96aff83d9e5a181f549311d7e9e83b28f1b57 Mon Sep 17 00:00:00 2001 From: Martin Smith Date: Tue, 4 May 2010 13:41:03 +0200 Subject: qdoc: Fixed some breadcrumbs. --- doc/src/classes.qdoc | 6 +++--- doc/src/modules.qdoc | 46 +++++++++++++++++++++---------------------- tools/qdoc3/htmlgenerator.cpp | 1 + 3 files changed, 27 insertions(+), 26 deletions(-) diff --git a/doc/src/classes.qdoc b/doc/src/classes.qdoc index 1552f56..594c1fe 100644 --- a/doc/src/classes.qdoc +++ b/doc/src/classes.qdoc @@ -59,7 +59,7 @@ /*! \page classes.html - \title All Qt Classes + \title All Classes \ingroup classlists \brief If you know the name of the class you want, find it here. @@ -71,7 +71,7 @@ \generatelist classes - \sa {Qt3 Support Classes}, {All Qt Modules}, {Obsolete Classes} + \sa {Qt3 Support Classes}, {All Modules}, {Obsolete Classes} */ /*! @@ -162,7 +162,7 @@ /*! \page namespaces.html - \title All Qt Namespaces + \title All Namespaces \ingroup classlists \brief A Qt namespace contains enum types, functions, and sometimes classes. diff --git a/doc/src/modules.qdoc b/doc/src/modules.qdoc index 1ab1c00..2c0744b 100644 --- a/doc/src/modules.qdoc +++ b/doc/src/modules.qdoc @@ -41,7 +41,7 @@ /*! \group modules - \title All Qt Modules + \title All Modules \startpage index.html Qt Reference Documentation \nextpage QtCore @@ -96,8 +96,8 @@ /*! \module QtCore \title QtCore Module - \contentspage All Qt Modules - \previouspage All Qt Modules + \contentspage All Modules + \previouspage All Modules \nextpage QtGui \ingroup modules @@ -115,7 +115,7 @@ /*! \module QtGui \title QtGui Module - \contentspage All Qt Modules + \contentspage All Modules \previouspage QtCore \nextpage QtNetwork \ingroup modules @@ -132,7 +132,7 @@ \module QtMultimedia \page qtmultimedia-module.html \title QtMultimedia Module - \contentspage All Qt Modules + \contentspage All Modules \previouspage QtCore \nextpage QtNetwork \ingroup modules @@ -156,7 +156,7 @@ /*! \module QtNetwork \title QtNetwork Module - \contentspage All Qt Modules + \contentspage All Modules \previouspage QtMultimedia \nextpage QtOpenGL \ingroup modules @@ -178,7 +178,7 @@ /*! \module QtOpenGL \title QtOpenGL Module - \contentspage All Qt Modules + \contentspage All Modules \previouspage QtNetwork \nextpage QtOpenVG \ingroup modules @@ -231,7 +231,7 @@ \module QtOpenVG \title QtOpenVG Module \since 4.6 - \contentspage All Qt Modules + \contentspage All Modules \previouspage QtOpenGL \nextpage QtScript \ingroup modules @@ -286,7 +286,7 @@ \module QtScript \title QtScript Module \since 4.3 - \contentspage All Qt Modules + \contentspage All Modules \previouspage QtOpenVG \nextpage QtScriptTools \ingroup modules @@ -346,7 +346,7 @@ \module QtScriptTools \title QtScriptTools Module \since 4.5 - \contentspage All Qt Modules + \contentspage All Modules \previouspage QtScript \nextpage QtSql \ingroup modules @@ -370,7 +370,7 @@ /*! \module QtSql \title QtSql Module - \contentspage All Qt Modules + \contentspage All Modules \previouspage QtScript \nextpage QtSvg \ingroup modules @@ -393,7 +393,7 @@ \module QtSvg \title QtSvg Module \since 4.1 - \contentspage All Qt Modules + \contentspage All Modules \previouspage QtSql \nextpage QtWebKit \ingroup modules @@ -444,7 +444,7 @@ /*! \module QtXml \title QtXml Module - \contentspage All Qt Modules + \contentspage All Modules \previouspage QtSvg \nextpage QtXmlPatterns \ingroup modules @@ -471,7 +471,7 @@ \module QtXmlPatterns \title QtXmlPatterns Module \since 4.4 - \contentspage All Qt Modules + \contentspage All Modules \previouspage QtXml \nextpage Phonon Module \ingroup modules @@ -551,7 +551,7 @@ \page phonon-module.html \module Phonon \title Phonon Module - \contentspage All Qt Modules + \contentspage All Modules \previouspage QtXmlPatterns \nextpage Qt3Support \ingroup modules @@ -621,7 +621,7 @@ /*! \module Qt3Support \title Qt3Support Module - \contentspage All Qt Modules + \contentspage All Modules \previouspage Phonon Module \nextpage QtDesigner \ingroup modules @@ -654,7 +654,7 @@ /*! \module QtDesigner \title QtDesigner Module - \contentspage All Qt Modules + \contentspage All Modules \previouspage Qt3Support \nextpage QtUiTools \ingroup modules @@ -681,7 +681,7 @@ \module QtUiTools \title QtUiTools Module \since 4.1 - \contentspage All Qt Modules + \contentspage All Modules \previouspage QtDesigner \nextpage QtHelp \ingroup modules @@ -717,7 +717,7 @@ /*! \module QtHelp \title QtHelp Module - \contentspage All Qt Modules + \contentspage All Modules \previouspage QtUiTools \nextpage QtTest \ingroup modules @@ -776,7 +776,7 @@ /*! \module QtTest \title QtTest Module - \contentspage All Qt Modules + \contentspage All Modules \previouspage QtHelp \nextpage QAxContainer \ingroup modules @@ -806,7 +806,7 @@ /*! \module QAxContainer \title QAxContainer Module - \contentspage All Qt Modules + \contentspage All Modules \previouspage QtTest \nextpage QAxServer \ingroup modules @@ -858,7 +858,7 @@ /*! \module QAxServer \title QAxServer Module - \contentspage All Qt Modules + \contentspage All Modules \previouspage QAxContainer \nextpage QtDBus module \ingroup modules @@ -910,7 +910,7 @@ /*! \module QtDBus \title QtDBus module - \contentspage All Qt Modules + \contentspage All Modules \previouspage QAxServer \ingroup modules diff --git a/tools/qdoc3/htmlgenerator.cpp b/tools/qdoc3/htmlgenerator.cpp index 1840a85..4e6d656 100644 --- a/tools/qdoc3/htmlgenerator.cpp +++ b/tools/qdoc3/htmlgenerator.cpp @@ -1739,6 +1739,7 @@ void HtmlGenerator::generateBreadCrumbs(const QString& title, } } else if (node->subType() == Node::QmlClass) { + out() << "
  • QML Elements
  • "; out() << "
  • name() << "\">" << title << "
  • "; } -- cgit v0.12 From 5c1fe0fc017e116b2643d2b8278f7fca6fec10a1 Mon Sep 17 00:00:00 2001 From: Jens Bache-Wiig Date: Tue, 4 May 2010 14:04:48 +0200 Subject: Fix missing pressed state for scrollbars with QGtkStyle Pushing this to 4.7 as it is an easy fix and highly noticable in the latest official release of Ubuntu. Task-number: QTBUG-10396 Reviewed-by: thorbjorn --- src/gui/styles/qgtkstyle.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/gui/styles/qgtkstyle.cpp b/src/gui/styles/qgtkstyle.cpp index 9c61023..e3ca8b2 100644 --- a/src/gui/styles/qgtkstyle.cpp +++ b/src/gui/styles/qgtkstyle.cpp @@ -1643,6 +1643,7 @@ void QGtkStyle::drawComplexControl(ComplexControl control, const QStyleOptionCom style = scrollbarWidget->style; gboolean trough_under_steppers = true; gboolean trough_side_details = false; + gboolean activate_slider = false; gboolean stepper_size = 14; gint trough_border = 1; if (!d->gtk_check_version(2, 10, 0)) { @@ -1650,6 +1651,7 @@ void QGtkStyle::drawComplexControl(ComplexControl control, const QStyleOptionCom "trough-border", &trough_border, "trough-side-details", &trough_side_details, "trough-under-steppers", &trough_under_steppers, + "activate-slider", &activate_slider, "stepper-size", &stepper_size, NULL); } if (trough_under_steppers) { @@ -1695,6 +1697,9 @@ void QGtkStyle::drawComplexControl(ComplexControl control, const QStyleOptionCom if (!(option->state & State_Enabled)) state = GTK_STATE_INSENSITIVE; + else if (activate_slider && + option->state & State_Sunken && (scrollBar->activeSubControls & SC_ScrollBarSlider)) + state = GTK_STATE_ACTIVE; else if (option->state & State_MouseOver && (scrollBar->activeSubControls & SC_ScrollBarSlider)) state = GTK_STATE_PRELIGHT; -- cgit v0.12