Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge in a lot of upstream changes.

  • Loading branch information...
commit fdd178c47dc933c18cf8b444d30641232997fa90 1 parent 3e2fee9
@tristandunn authored
View
18 src/ClearCookies.cpp
@@ -0,0 +1,18 @@
+#include "ClearCookies.h"
+#include "WebPage.h"
+#include "NetworkCookieJar.h"
+#include <QNetworkCookie>
+
+ClearCookies::ClearCookies(WebPage *page, QObject *parent)
+ : Command(page, parent)
+{ }
+
+void ClearCookies::start(QStringList &arguments)
+{
+ Q_UNUSED(arguments);
+ NetworkCookieJar *jar = qobject_cast<NetworkCookieJar*>(page()
+ ->networkAccessManager()
+ ->cookieJar());
+ jar->clearCookies();
+ emit finished(new Response(true));
+}
View
11 src/ClearCookies.h
@@ -0,0 +1,11 @@
+#include "Command.h"
+
+class WebPage;
+
+class ClearCookies : public Command {
+ Q_OBJECT;
+
+ public:
+ ClearCookies(WebPage *page, QObject *parent = 0);
+ virtual void start(QStringList &arguments);
+};
View
6 src/Connection.cpp
@@ -1,5 +1,6 @@
#include "Connection.h"
#include "WebPage.h"
+#include "UnsupportedContentHandler.h"
#include "Visit.h"
#include "Find.h"
#include "Command.h"
@@ -15,6 +16,9 @@
#include "Body.h"
#include "Status.h"
#include "Headers.h"
+#include "SetCookie.h"
+#include "ClearCookies.h"
+#include "GetCookies.h"
#include <QTcpSocket>
#include <iostream>
@@ -28,7 +32,7 @@ Connection::Connection(QTcpSocket *socket, WebPage *page, QObject *parent) :
m_pageSuccess = true;
m_commandWaiting = false;
connect(m_socket, SIGNAL(readyRead()), this, SLOT(checkNext()));
- connect(m_page, SIGNAL(loadFinished(bool)), this, SLOT(pendingLoadFinished(bool)));
+ connect(m_page, SIGNAL(pageFinished(bool)), this, SLOT(pendingLoadFinished(bool)));
}
void Connection::checkNext() {
View
22 src/GetCookies.cpp
@@ -0,0 +1,22 @@
+#include "GetCookies.h"
+#include "WebPage.h"
+#include "NetworkCookieJar.h"
+
+GetCookies::GetCookies(WebPage *page, QObject *parent)
+ : Command(page, parent)
+{
+ m_buffer = "";
+}
+
+void GetCookies::start(QStringList &arguments)
+{
+ Q_UNUSED(arguments);
+ NetworkCookieJar *jar = qobject_cast<NetworkCookieJar*>(page()
+ ->networkAccessManager()
+ ->cookieJar());
+ foreach (QNetworkCookie cookie, jar->getAllCookies()) {
+ m_buffer.append(cookie.toRawForm());
+ m_buffer.append("\n");
+ }
+ emit finished(new Response(true, m_buffer));
+}
View
14 src/GetCookies.h
@@ -0,0 +1,14 @@
+#include "Command.h"
+
+class WebPage;
+
+class GetCookies : public Command {
+ Q_OBJECT;
+
+ public:
+ GetCookies(WebPage *page, QObject *parent = 0);
+ virtual void start(QStringList &arguments);
+
+ private:
+ QString m_buffer;
+};
View
7 src/NetworkAccessManager.cpp
@@ -6,14 +6,17 @@
NetworkAccessManager::NetworkAccessManager(QObject *parent):QNetworkAccessManager(parent) {
}
-QNetworkReply* NetworkAccessManager::createRequest(QNetworkAccessManager::Operation oparation, const QNetworkRequest &request, QIODevice * outgoingData = 0) {
+QNetworkReply* NetworkAccessManager::createRequest(QNetworkAccessManager::Operation operation, const QNetworkRequest &request, QIODevice * outgoingData = 0) {
QNetworkRequest new_request(request);
+ if (operation != QNetworkAccessManager::PostOperation && operation != QNetworkAccessManager::PutOperation) {
+ new_request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant());
+ }
QHashIterator<QString, QString> item(m_headers);
while (item.hasNext()) {
item.next();
new_request.setRawHeader(item.key().toAscii(), item.value().toAscii());
}
- return QNetworkAccessManager::createRequest(oparation, new_request, outgoingData);
+ return QNetworkAccessManager::createRequest(operation, new_request, outgoingData);
};
void NetworkAccessManager::addHeader(QString key, QString value) {
View
101 src/NetworkCookieJar.cpp
@@ -0,0 +1,101 @@
+#include "NetworkCookieJar.h"
+#include "QtCore/qdatetime.h"
+
+NetworkCookieJar::NetworkCookieJar(QObject *parent)
+ : QNetworkCookieJar(parent)
+{ }
+
+QList<QNetworkCookie> NetworkCookieJar::getAllCookies() const
+{
+ return allCookies();
+}
+
+void NetworkCookieJar::clearCookies()
+{
+ setAllCookies(QList<QNetworkCookie>());
+}
+
+static inline bool isParentDomain(QString domain, QString reference)
+{
+ if (!reference.startsWith(QLatin1Char('.')))
+ return domain == reference;
+
+ return domain.endsWith(reference) || domain == reference.mid(1);
+}
+
+void NetworkCookieJar::overwriteCookies(const QList<QNetworkCookie>& cookieList)
+{
+ /* this function is basically a copy-and-paste of the original
+ QNetworkCookieJar::setCookiesFromUrl with the domain and
+ path validations removed */
+
+ QString defaultPath(QLatin1Char('/'));
+ QDateTime now = QDateTime::currentDateTime();
+ QList<QNetworkCookie> newCookies = allCookies();
+
+ foreach (QNetworkCookie cookie, cookieList) {
+ bool isDeletion = (!cookie.isSessionCookie() &&
+ cookie.expirationDate() < now);
+
+ // validate the cookie & set the defaults if unset
+ if (cookie.path().isEmpty())
+ cookie.setPath(defaultPath);
+
+ // don't do path checking. See http://bugreports.qt.nokia.com/browse/QTBUG-5815
+ // else if (!isParentPath(pathAndFileName, cookie.path())) {
+ // continue; // not accepted
+ // }
+
+ if (cookie.domain().isEmpty()) {
+ continue;
+ } else {
+ // Ensure the domain starts with a dot if its field was not empty
+ // in the HTTP header. There are some servers that forget the
+ // leading dot and this is actually forbidden according to RFC 2109,
+ // but all browsers accept it anyway so we do that as well.
+ if (!cookie.domain().startsWith(QLatin1Char('.')))
+ cookie.setDomain(QLatin1Char('.') + cookie.domain());
+
+ QString domain = cookie.domain();
+
+ // the check for effective TLDs makes the "embedded dot" rule from RFC 2109 section 4.3.2
+ // redundant; the "leading dot" rule has been relaxed anyway, see above
+ // we remove the leading dot for this check
+ /*
+ if (QNetworkCookieJarPrivate::isEffectiveTLD(domain.remove(0, 1)))
+ continue; // not accepted
+ */
+ }
+
+ for (int i = 0; i < newCookies.size(); ++i) {
+ // does this cookie already exist?
+ const QNetworkCookie &current = newCookies.at(i);
+ if (cookie.name() == current.name() &&
+ cookie.domain() == current.domain() &&
+ cookie.path() == current.path()) {
+ // found a match
+ newCookies.removeAt(i);
+ break;
+ }
+ }
+
+ // did not find a match
+ if (!isDeletion) {
+ int countForDomain = 0;
+ for (int i = newCookies.size() - 1; i >= 0; --i) {
+ // Start from the end and delete the oldest cookies to keep a maximum count of 50.
+ const QNetworkCookie &current = newCookies.at(i);
+ if (isParentDomain(cookie.domain(), current.domain())
+ || isParentDomain(current.domain(), cookie.domain())) {
+ if (countForDomain >= 49)
+ newCookies.removeAt(i);
+ else
+ ++countForDomain;
+ }
+ }
+
+ newCookies += cookie;
+ }
+ }
+ setAllCookies(newCookies);
+}
View
15 src/NetworkCookieJar.h
@@ -0,0 +1,15 @@
+#include <QtNetwork/QNetworkCookieJar>
+#include <QtNetwork/QNetworkCookie>
+
+class NetworkCookieJar : public QNetworkCookieJar {
+
+ Q_OBJECT;
+
+ public:
+
+ NetworkCookieJar(QObject *parent = 0);
+
+ QList<QNetworkCookie> getAllCookies() const;
+ void clearCookies();
+ void overwriteCookies(const QList<QNetworkCookie>& cookieList);
+};
View
6 src/Reset.cpp
@@ -1,6 +1,7 @@
#include "Reset.h"
#include "WebPage.h"
#include "NetworkAccessManager.h"
+#include "NetworkCookieJar.h"
Reset::Reset(WebPage *page, QObject *parent) : Command(page, parent) {
}
@@ -10,9 +11,10 @@ void Reset::start(QStringList &arguments) {
page()->triggerAction(QWebPage::Stop);
page()->currentFrame()->setHtml("<html><body></body></html>");
- page()->networkAccessManager()->setCookieJar(new QNetworkCookieJar());
- page()->setNetworkAccessManager(new NetworkAccessManager());
+ page()->networkAccessManager()->setCookieJar(new NetworkCookieJar());
+ page()->setCustomNetworkAccessManager();
page()->setUserAgent(NULL);
+ page()->resetResponseHeaders();
emit finished(new Response(true));
}
View
18 src/SetCookie.cpp
@@ -0,0 +1,18 @@
+#include "SetCookie.h"
+#include "WebPage.h"
+#include "NetworkCookieJar.h"
+#include <QNetworkCookie>
+
+SetCookie::SetCookie(WebPage *page, QObject *parent)
+ : Command(page, parent)
+{ }
+
+void SetCookie::start(QStringList &arguments)
+{
+ QList<QNetworkCookie> cookies = QNetworkCookie::parseCookies(arguments[0].toAscii());
+ NetworkCookieJar *jar = qobject_cast<NetworkCookieJar*>(page()
+ ->networkAccessManager()
+ ->cookieJar());
+ jar->overwriteCookies(cookies);
+ emit finished(new Response(true));
+}
View
11 src/SetCookie.h
@@ -0,0 +1,11 @@
+#include "Command.h"
+
+class WebPage;
+
+class SetCookie : public Command {
+ Q_OBJECT;
+
+ public:
+ SetCookie(WebPage *page, QObject *parent = 0);
+ virtual void start(QStringList &arguments);
+};
View
31 src/UnsupportedContentHandler.cpp
@@ -0,0 +1,31 @@
+#include "UnsupportedContentHandler.h"
+#include "WebPage.h"
+#include <QNetworkReply>
+
+UnsupportedContentHandler::UnsupportedContentHandler(WebPage *page, QNetworkReply *reply, QObject *parent) : QObject(parent) {
+ m_page = page;
+ m_reply = reply;
+ connect(m_reply, SIGNAL(finished()), this, SLOT(handleUnsupportedContent()));
+ disconnect(m_page, SIGNAL(loadFinished(bool)), m_page, SLOT(loadFinished(bool)));
+}
+
+void UnsupportedContentHandler::handleUnsupportedContent() {
+ QVariant contentMimeType = m_reply->header(QNetworkRequest::ContentTypeHeader);
+ if(contentMimeType.isNull()) {
+ this->finish(false);
+ } else {
+ this->loadUnsupportedContent();
+ this->finish(true);
+ }
+ this->deleteLater();
+}
+
+void UnsupportedContentHandler::loadUnsupportedContent() {
+ QByteArray text = m_reply->readAll();
+ m_page->mainFrame()->setContent(text, QString("text/plain"), m_reply->url());
+}
+
+void UnsupportedContentHandler::finish(bool success) {
+ connect(m_page, SIGNAL(loadFinished(bool)), m_page, SLOT(loadFinished(bool)));
+ m_page->loadFinished(success);
+}
View
18 src/UnsupportedContentHandler.h
@@ -0,0 +1,18 @@
+#include <QObject>
+class WebPage;
+class QNetworkReply;
+class UnsupportedContentHandler : public QObject {
+ Q_OBJECT
+
+ public:
+ UnsupportedContentHandler(WebPage *page, QNetworkReply *reply, QObject *parent = 0);
+
+ public slots:
+ void handleUnsupportedContent();
+
+ private:
+ WebPage *m_page;
+ QNetworkReply *m_reply;
+ void loadUnsupportedContent();
+ void finish(bool success);
+};
View
9 src/Visit.cpp
@@ -3,16 +3,12 @@
#include "WebPage.h"
Visit::Visit(WebPage *page, QObject *parent) : Command(page, parent) {
- connect(page, SIGNAL(loadFinished(bool)), this, SLOT(loadFinished(bool)));
+ connect(page, SIGNAL(pageFinished(bool)), this, SLOT(loadFinished(bool)));
}
void Visit::start(QStringList &arguments) {
QUrl requestedUrl = QUrl(arguments[0]);
- page()->currentFrame()->setUrl(QUrl(requestedUrl));
- if(requestedUrl.hasFragment()) {
- // workaround for https://bugs.webkit.org/show_bug.cgi?id=32723
- page()->currentFrame()->setUrl(QUrl(requestedUrl));
- }
+ page()->currentFrame()->load(QUrl(requestedUrl));
}
void Visit::loadFinished(bool success) {
@@ -20,5 +16,6 @@ void Visit::loadFinished(bool success) {
if (!success)
message = page()->failureString();
+ disconnect(page(), SIGNAL(pageFinished(bool)), this, SLOT(loadFinished(bool)));
emit finished(new Response(success, message));
}
View
50 src/WebPage.cpp
@@ -1,23 +1,32 @@
#include "WebPage.h"
#include "JavascriptInvocation.h"
#include "NetworkAccessManager.h"
+#include "NetworkCookieJar.h"
+#include "UnsupportedContentHandler.h"
#include <QResource>
#include <iostream>
WebPage::WebPage(QObject *parent) : QWebPage(parent) {
+ setForwardUnsupportedContent(true);
loadJavascript();
setUserStylesheet();
m_loading = false;
-
- NetworkAccessManager *manager = new NetworkAccessManager();
- this->setNetworkAccessManager(manager);
- connect(manager, SIGNAL(finished(QNetworkReply *)), this, SLOT(replyFinished(QNetworkReply *)));
+ this->setCustomNetworkAccessManager();
connect(this, SIGNAL(loadStarted()), this, SLOT(loadStarted()));
connect(this, SIGNAL(loadFinished(bool)), this, SLOT(loadFinished(bool)));
connect(this, SIGNAL(frameCreated(QWebFrame *)),
this, SLOT(frameCreated(QWebFrame *)));
+ connect(this, SIGNAL(unsupportedContent(QNetworkReply*)),
+ this, SLOT(handleUnsupportedContent(QNetworkReply*)));
+}
+
+void WebPage::setCustomNetworkAccessManager() {
+ NetworkAccessManager *manager = new NetworkAccessManager();
+ manager->setCookieJar(new NetworkCookieJar());
+ this->setNetworkAccessManager(manager);
+ connect(manager, SIGNAL(finished(QNetworkReply *)), this, SLOT(replyFinished(QNetworkReply *)));
}
void WebPage::loadJavascript() {
@@ -108,8 +117,8 @@ void WebPage::loadStarted() {
}
void WebPage::loadFinished(bool success) {
- Q_UNUSED(success);
m_loading = false;
+ emit pageFinished(success);
}
bool WebPage::isLoading() const {
@@ -156,7 +165,6 @@ QString WebPage::chooseFile(QWebFrame *parentFrame, const QString &suggestedFile
bool WebPage::extension(Extension extension, const ExtensionOption *option, ExtensionReturn *output) {
Q_UNUSED(option);
-
if (extension == ChooseMultipleFilesExtension) {
QStringList names = QStringList() << getLastAttachedFileName();
static_cast<ChooseMultipleFilesExtensionReturn*>(output)->fileNames = names;
@@ -170,22 +178,34 @@ QString WebPage::getLastAttachedFileName() {
}
void WebPage::replyFinished(QNetworkReply *reply) {
- QStringList headers;
- lastStatus = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
- QList<QByteArray> list = reply->rawHeaderList();
+ if (reply->url() == this->currentFrame()->url()) {
+ QStringList headers;
+ m_lastStatus = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
+ QList<QByteArray> list = reply->rawHeaderList();
- int length = list.size();
- for(int i = 0; i < length; i++) {
- headers << list.at(i)+": "+reply->rawHeader(list.at(i));
- }
+ int length = list.size();
+ for(int i = 0; i < length; i++) {
+ headers << list.at(i)+": "+reply->rawHeader(list.at(i));
+ }
- m_pageHeaders = headers.join("\n");
+ m_pageHeaders = headers.join("\n");
+ }
}
int WebPage::getLastStatus() {
- return lastStatus;
+ return m_lastStatus;
+}
+
+void WebPage::resetResponseHeaders() {
+ m_lastStatus = 0;
+ m_pageHeaders = QString();
}
QString WebPage::pageHeaders() {
return m_pageHeaders;
}
+
+void WebPage::handleUnsupportedContent(QNetworkReply *reply) {
+ UnsupportedContentHandler *handler = new UnsupportedContentHandler(this, reply);
+ Q_UNUSED(handler);
+}
View
8 src/WebPage.h
@@ -11,6 +11,8 @@ class WebPage : public QWebPage {
QString userAgentForUrl(const QUrl &url ) const;
void setUserAgent(QString userAgent);
int getLastStatus();
+ void resetResponseHeaders();
+ void setCustomNetworkAccessManager();
bool render(const QString &fileName);
virtual bool extension (Extension extension, const ExtensionOption *option=0, ExtensionReturn *output=0);
@@ -23,6 +25,10 @@ class WebPage : public QWebPage {
QString pageHeaders();
void frameCreated(QWebFrame *);
void replyFinished(QNetworkReply *reply);
+ void handleUnsupportedContent(QNetworkReply *reply);
+
+ signals:
+ void pageFinished(bool);
protected:
virtual void javaScriptConsoleMessage(const QString &message, int lineNumber, const QString &sourceID);
@@ -38,7 +44,7 @@ class WebPage : public QWebPage {
QString getLastAttachedFileName();
void loadJavascript();
void setUserStylesheet();
- int lastStatus;
+ int m_lastStatus;
QString m_pageHeaders;
};
View
13 src/capybara.js
@@ -27,6 +27,10 @@ Capybara = {
return results.join(",");
},
+ isAttached: function(index) {
+ return document.evaluate("ancestor-or-self::html", this.nodes[index], null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue != null;
+ },
+
text: function (index) {
var node = this.nodes[index];
var type = (node.type || node.tagName).toLowerCase();
@@ -105,7 +109,14 @@ Capybara = {
if (type == "text" || type == "textarea" || type == "password") {
this.trigger(index, "focus");
node.value = "";
- var length = this.attribute(index, "maxlength") || value.length;
+ var maxLength = this.attribute(index, "maxlength"),
+ length;
+ if (maxLength && value.length > maxLength) {
+ length = maxLength;
+ } else {
+ length = value.length;
+ }
+
for(var strindex = 0; strindex < length; strindex++) {
node.value += value[strindex];
this.trigger(index, "keydown");
View
5 src/find_command.h
@@ -16,4 +16,7 @@ CHECK_COMMAND(Header)
CHECK_COMMAND(Render)
CHECK_COMMAND(Body)
CHECK_COMMAND(Status)
-CHECK_COMMAND(Headers)
+CHECK_COMMAND(Headers)
+CHECK_COMMAND(SetCookie)
+CHECK_COMMAND(ClearCookies)
+CHECK_COMMAND(GetCookies)
View
10 src/main.cpp
@@ -1,8 +1,18 @@
#include "Server.h"
#include <QtGui>
#include <iostream>
+#ifdef Q_OS_UNIX
+ #include <unistd.h>
+#endif
int main(int argc, char **argv) {
+#ifdef Q_OS_UNIX
+ if (setpgid(0, 0) < 0) {
+ std::cerr << "Unable to set new process group." << std::endl;
+ return 1;
+ }
+#endif
+
QApplication app(argc, argv);
app.setApplicationName("capybara-webkit");
app.setOrganizationName("thoughtbot, inc");
View
59 src/webkit_server.pro
@@ -1,8 +1,63 @@
TEMPLATE = app
TARGET = webkit_server
DESTDIR = .
-HEADERS = WebPage.h Server.h Connection.h Command.h Visit.h Find.h Reset.h Node.h JavascriptInvocation.h Url.h Source.h Evaluate.h Execute.h FrameFocus.h Response.h NetworkAccessManager.h Header.h Render.h body.h Status.h Headers.h
-SOURCES = main.cpp WebPage.cpp Server.cpp Connection.cpp Command.cpp Visit.cpp Find.cpp Reset.cpp Node.cpp JavascriptInvocation.cpp Url.cpp Source.cpp Evaluate.cpp Execute.cpp FrameFocus.cpp Response.cpp NetworkAccessManager.cpp Header.cpp Render.cpp body.cpp Status.cpp Headers.cpp
+HEADERS = \
+ WebPage.h \
+ Server.h \
+ Connection.h \
+ Command.h \
+ Visit.h \
+ Find.h \
+ Reset.h \
+ Node.h \
+ JavascriptInvocation.h \
+ Url.h \
+ Source.h \
+ Evaluate.h \
+ Execute.h \
+ FrameFocus.h \
+ Response.h \
+ NetworkAccessManager.h \
+ NetworkCookieJar.h \
+ Header.h \
+ Render.h \
+ body.h \
+ Status.h \
+ Headers.h \
+ UnsupportedContentHandler.h \
+ SetCookie.h \
+ ClearCookies.h \
+ GetCookies.h \
+
+SOURCES = \
+ main.cpp \
+ WebPage.cpp \
+ Server.cpp \
+ Connection.cpp \
+ Command.cpp \
+ Visit.cpp \
+ Find.cpp \
+ Reset.cpp \
+ Node.cpp \
+ JavascriptInvocation.cpp \
+ Url.cpp \
+ Source.cpp \
+ Evaluate.cpp \
+ Execute.cpp \
+ FrameFocus.cpp \
+ Response.cpp \
+ NetworkAccessManager.cpp \
+ NetworkCookieJar.cpp \
+ Header.cpp \
+ Render.cpp \
+ body.cpp \
+ Status.cpp \
+ Headers.cpp \
+ UnsupportedContentHandler.cpp \
+ SetCookie.cpp \
+ ClearCookies.cpp \
+ GetCookies.cpp \
+
RESOURCES = webkit_server.qrc
QT += network webkit
CONFIG += console
Please sign in to comment.
Something went wrong with that request. Please try again.