diff --git a/lib/capybara/driver/webkit/browser.rb b/lib/capybara/driver/webkit/browser.rb
index de603ef..e4b7ea5 100644
--- a/lib/capybara/driver/webkit/browser.rb
+++ b/lib/capybara/driver/webkit/browser.rb
@@ -86,6 +86,20 @@ class Capybara::Driver::Webkit
command "Render", path, width, height
end
+ def set_cookie(cookie)
+ command "SetCookie", cookie
+ end
+
+ def clear_cookies
+ command "ClearCookies"
+ end
+
+ def get_cookies
+ command("GetCookies").lines
+ .map { |line| line.strip }
+ .select { |line| !line.empty? }
+ end
+
private
def start_server
diff --git a/spec/driver_spec.rb b/spec/driver_spec.rb
index 1e3d379..107041f 100644
--- a/spec/driver_spec.rb
+++ b/spec/driver_spec.rb
@@ -884,6 +884,61 @@ describe Capybara::Driver::Webkit do
end
end
+ context "cookie-based app" do
+ before(:all) do
+ @cookie = 'cookie=abc; domain=127.0.0.1; path=/'
+ @app = lambda do |env|
+ request = ::Rack::Request.new(env)
+
+ body = <<-HTML
+
+ #{request.cookies["cookie"] || ""}
+
+ HTML
+ [200,
+ { 'Content-Type' => 'text/html; charset=UTF-8',
+ 'Content-Length' => body.length.to_s,
+ 'Set-Cookie' => @cookie,
+ },
+ [body]]
+ end
+ end
+
+ def echoed_cookie
+ subject.find('id("cookie")').first.text
+ end
+
+ it "remembers the cookie on second visit" do
+ echoed_cookie.should == ""
+ subject.visit "/"
+ echoed_cookie.should == "abc"
+ end
+
+ it "uses a custom cookie" do
+ subject.browser.set_cookie @cookie
+ subject.visit "/"
+ echoed_cookie.should == "abc"
+ end
+
+ it "clears cookies" do
+ subject.browser.clear_cookies
+ subject.visit "/"
+ echoed_cookie.should == ""
+ end
+
+ it "allows enumeration of cookies" do
+ cookies = subject.browser.get_cookies
+
+ cookies.size.should == 1
+
+ cookie = Hash[cookies[0].split(/\s*;\s*/)
+ .map { |x| x.split("=", 2) }]
+ cookie["cookie"].should == "abc"
+ cookie["domain"].should include "127.0.0.1"
+ cookie["path"].should == "/"
+ end
+ end
+
context "with socket debugger" do
let(:socket_debugger_class){ Capybara::Driver::Webkit::SocketDebugger }
let(:browser_with_debugger){
diff --git a/src/ClearCookies.cpp b/src/ClearCookies.cpp
new file mode 100644
index 0000000..048e877
--- /dev/null
+++ b/src/ClearCookies.cpp
@@ -0,0 +1,18 @@
+#include "ClearCookies.h"
+#include "WebPage.h"
+#include "NetworkCookieJar.h"
+#include
+
+ClearCookies::ClearCookies(WebPage *page, QObject *parent)
+ : Command(page, parent)
+{ }
+
+void ClearCookies::start(QStringList &arguments)
+{
+ Q_UNUSED(arguments);
+ NetworkCookieJar *jar = qobject_cast(page()
+ ->networkAccessManager()
+ ->cookieJar());
+ jar->clearCookies();
+ emit finished(new Response(true));
+}
diff --git a/src/ClearCookies.h b/src/ClearCookies.h
new file mode 100644
index 0000000..0a1841a
--- /dev/null
+++ b/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);
+};
diff --git a/src/Connection.cpp b/src/Connection.cpp
index a79cd54..ce4ed7a 100644
--- a/src/Connection.cpp
+++ b/src/Connection.cpp
@@ -16,6 +16,9 @@
#include "Body.h"
#include "Status.h"
#include "Headers.h"
+#include "SetCookie.h"
+#include "ClearCookies.h"
+#include "GetCookies.h"
#include
#include
diff --git a/src/GetCookies.cpp b/src/GetCookies.cpp
new file mode 100644
index 0000000..ecf3097
--- /dev/null
+++ b/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(page()
+ ->networkAccessManager()
+ ->cookieJar());
+ foreach (QNetworkCookie cookie, jar->getAllCookies()) {
+ m_buffer.append(cookie.toRawForm());
+ m_buffer.append("\n");
+ }
+ emit finished(new Response(true, m_buffer));
+}
diff --git a/src/GetCookies.h b/src/GetCookies.h
new file mode 100644
index 0000000..d98f844
--- /dev/null
+++ b/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;
+};
diff --git a/src/NetworkCookieJar.cpp b/src/NetworkCookieJar.cpp
new file mode 100644
index 0000000..4ecd718
--- /dev/null
+++ b/src/NetworkCookieJar.cpp
@@ -0,0 +1,101 @@
+#include "NetworkCookieJar.h"
+#include "QtCore/qdatetime.h"
+
+NetworkCookieJar::NetworkCookieJar(QObject *parent)
+ : QNetworkCookieJar(parent)
+{ }
+
+QList NetworkCookieJar::getAllCookies() const
+{
+ return allCookies();
+}
+
+void NetworkCookieJar::clearCookies()
+{
+ setAllCookies(QList());
+}
+
+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& 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 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 ¤t = 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 ¤t = 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);
+}
diff --git a/src/NetworkCookieJar.h b/src/NetworkCookieJar.h
new file mode 100644
index 0000000..8796532
--- /dev/null
+++ b/src/NetworkCookieJar.h
@@ -0,0 +1,15 @@
+#include
+#include
+
+class NetworkCookieJar : public QNetworkCookieJar {
+
+ Q_OBJECT;
+
+ public:
+
+ NetworkCookieJar(QObject *parent = 0);
+
+ QList getAllCookies() const;
+ void clearCookies();
+ void overwriteCookies(const QList& cookieList);
+};
diff --git a/src/Reset.cpp b/src/Reset.cpp
index e5663ea..c4de016 100644
--- a/src/Reset.cpp
+++ b/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,7 +11,7 @@ void Reset::start(QStringList &arguments) {
page()->triggerAction(QWebPage::Stop);
page()->currentFrame()->setHtml("");
- page()->networkAccessManager()->setCookieJar(new QNetworkCookieJar());
+ page()->networkAccessManager()->setCookieJar(new NetworkCookieJar());
page()->setCustomNetworkAccessManager();
page()->setUserAgent(NULL);
page()->resetResponseHeaders();
diff --git a/src/SetCookie.cpp b/src/SetCookie.cpp
new file mode 100644
index 0000000..3b2fd9d
--- /dev/null
+++ b/src/SetCookie.cpp
@@ -0,0 +1,18 @@
+#include "SetCookie.h"
+#include "WebPage.h"
+#include "NetworkCookieJar.h"
+#include
+
+SetCookie::SetCookie(WebPage *page, QObject *parent)
+ : Command(page, parent)
+{ }
+
+void SetCookie::start(QStringList &arguments)
+{
+ QList cookies = QNetworkCookie::parseCookies(arguments[0].toAscii());
+ NetworkCookieJar *jar = qobject_cast(page()
+ ->networkAccessManager()
+ ->cookieJar());
+ jar->overwriteCookies(cookies);
+ emit finished(new Response(true));
+}
diff --git a/src/SetCookie.h b/src/SetCookie.h
new file mode 100644
index 0000000..8959c79
--- /dev/null
+++ b/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);
+};
diff --git a/src/WebPage.cpp b/src/WebPage.cpp
index 753ed11..53e0b14 100644
--- a/src/WebPage.cpp
+++ b/src/WebPage.cpp
@@ -1,6 +1,7 @@
#include "WebPage.h"
#include "JavascriptInvocation.h"
#include "NetworkAccessManager.h"
+#include "NetworkCookieJar.h"
#include "UnsupportedContentHandler.h"
#include
#include
@@ -23,6 +24,7 @@ WebPage::WebPage(QObject *parent) : QWebPage(parent) {
void WebPage::setCustomNetworkAccessManager() {
NetworkAccessManager *manager = new NetworkAccessManager();
+ manager->setCookieJar(new NetworkCookieJar());
this->setNetworkAccessManager(manager);
connect(manager, SIGNAL(finished(QNetworkReply *)), this, SLOT(replyFinished(QNetworkReply *)));
}
diff --git a/src/find_command.h b/src/find_command.h
index a480c7d..4965719 100644
--- a/src/find_command.h
+++ b/src/find_command.h
@@ -16,4 +16,7 @@ CHECK_COMMAND(Header)
CHECK_COMMAND(Render)
CHECK_COMMAND(Body)
CHECK_COMMAND(Status)
-CHECK_COMMAND(Headers)
\ No newline at end of file
+CHECK_COMMAND(Headers)
+CHECK_COMMAND(SetCookie)
+CHECK_COMMAND(ClearCookies)
+CHECK_COMMAND(GetCookies)
diff --git a/src/webkit_server.pro b/src/webkit_server.pro
index 1e89330..25acabd 100644
--- a/src/webkit_server.pro
+++ b/src/webkit_server.pro
@@ -18,12 +18,16 @@ HEADERS = \
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 \
@@ -43,12 +47,16 @@ SOURCES = \
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