Skip to content

Commit

Permalink
Redesign the Cookies API
Browse files Browse the repository at this point in the history
Addresses [Issue #761](http://code.google.com/p/phantomjs/issues/detail?id=761).

This is a squash of 5 commits.

1. Complete reimplementation of the CookieJar and the Cookie API - Part 1.

The save/load mechanism is inspired by the Qt Demo "browser".
More info: http://doc.qt.nokia.com/4.7-snapshot/demos-browser.html

2. Making the CookieJar a singleton.

We need to have multiple NetworkAccessManager to monitor the network activity of the page,
but we also need to maintain 1 CookieJar: in this way we now have a shared bucket of Cookies.

3. Exposing the new Cookies API to the JS space.

* Updated the completions.
* Ensured backward compatibility of the API.
* It's now possible to "phantom.cookieEnabled = false" to disable cookies completely.
* New methods: addCookie, deleteCookie, clearCookies

4. Provided some internal Doc for the new Cookies API

5. Ensuring the "page.deleteCookie(name)" method works only if a cookie name is given.
  • Loading branch information
detro authored and n1k0 committed Sep 24, 2012
1 parent 5b1cfb3 commit c503040
Show file tree
Hide file tree
Showing 10 changed files with 575 additions and 137 deletions.
406 changes: 316 additions & 90 deletions src/cookiejar.cpp

Large diffs are not rendered by default.

44 changes: 39 additions & 5 deletions src/cookiejar.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
This file is part of the PhantomJS project from Ofi Labs.
Copyright (C) 2011 Ariya Hidayat <ariya.hidayat@gmail.com>
Copyright (C) 2012 Ivan De Marino <ivan.de.marino@gmail.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
Expand Down Expand Up @@ -30,20 +31,53 @@
#ifndef COOKIEJAR_H
#define COOKIEJAR_H

#include <QSettings>
#include <QNetworkCookieJar>
#include <QVariantList>
#include <QVariantMap>

class CookieJar: public QNetworkCookieJar
{
Q_OBJECT

private:
QString m_cookiesFile;
CookieJar(QString cookiesFile, QObject *parent = NULL);

public:
CookieJar(QString cookiesFile);
static CookieJar *instance(QString cookiesFile = QString());
virtual ~CookieJar();

bool setCookiesFromUrl(const QList<QNetworkCookie> & cookieList, const QUrl & url);
bool setCookiesFromUrl(const QList<QNetworkCookie> &cookieList, const QUrl & url);
QList<QNetworkCookie> cookiesForUrl (const QUrl & url) const;
void setCookies(const QVariantList &cookies);
QVariantList cookies() const;

void addCookie(const QNetworkCookie &cookie, const QString &url = QString());
void addCookieFromMap(const QVariantMap &cookie, const QString &url = QString());
void addCookies(const QList<QNetworkCookie> &cookiesList, const QString &url = QString());
void addCookiesFromMap(const QVariantList &cookiesList, const QString &url = QString());

QList<QNetworkCookie> cookies(const QString &url = QString()) const;
QVariantList cookiesToMap(const QString &url = QString()) const;

QNetworkCookie cookie(const QString &name, const QString &url = QString()) const;
QVariantMap cookieToMap(const QString &name, const QString &url = QString()) const;

void deleteCookie(const QString &name, const QString &url = QString());
void deleteCookies(const QString &url = QString());
void clearCookies();

void enable();
void disable();
bool isEnabled() const;

private slots:
void save();
void load();
bool purgeExpiredCookies();
bool purgeSessionCookies();

private:
QSettings *m_cookieStorage;
bool m_enabled;
};

#endif // COOKIEJAR_H
19 changes: 9 additions & 10 deletions src/networkaccessmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include <QNetworkRequest>
#include <QSslSocket>

#include "phantom.h"
#include "config.h"
#include "cookiejar.h"
#include "networkaccessmanager.h"
Expand Down Expand Up @@ -73,7 +74,7 @@ NetworkAccessManager::NetworkAccessManager(QObject *parent, const Config *config
, m_idCounter(0)
, m_networkDiskCache(0)
{
setCookieJar(new CookieJar(config->cookiesFile()));
setCookieJar(CookieJar::instance());

if (config->diskCacheEnabled()) {
m_networkDiskCache = new QNetworkDiskCache(this);
Expand Down Expand Up @@ -107,16 +108,14 @@ QVariantMap NetworkAccessManager::customHeaders() const
return m_customHeaders;
}

void NetworkAccessManager::setCookies(const QVariantList &cookies)
void NetworkAccessManager::setCookieJar(QNetworkCookieJar *cookieJar)
{
CookieJar* cookiejar = static_cast<CookieJar*>(cookieJar());
cookiejar->setCookies(cookies);
}

QVariantList NetworkAccessManager::cookies() const
{
CookieJar* cookiejar = static_cast<CookieJar*>(cookieJar());
return cookiejar->cookies();
QNetworkAccessManager::setCookieJar(cookieJar);
// Remove NetworkAccessManager's ownership of this CookieJar and
// pass it to the PhantomJS Singleton object.
// CookieJar is a SINGLETON, shouldn't be deleted when
// the NetworkAccessManager is deleted but only when we shutdown.
cookieJar->setParent(Phantom::instance());
}

// protected:
Expand Down
4 changes: 2 additions & 2 deletions src/networkaccessmanager.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ class NetworkAccessManager : public QNetworkAccessManager
void setPassword(const QString &password);
void setCustomHeaders(const QVariantMap &headers);
QVariantMap customHeaders() const;
void setCookies(const QVariantList &cookies);
QVariantList cookies() const;

void setCookieJar(QNetworkCookieJar *cookieJar);

protected:
bool m_ignoreSslErrors;
Expand Down
30 changes: 25 additions & 5 deletions src/phantom.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
#include "repl.h"
#include "system.h"
#include "callback.h"
#include "cookiejar.h"

static Phantom *phantomInstance = NULL;

Expand Down Expand Up @@ -98,6 +99,9 @@ void Phantom::init()
return;
}

// Initialize the CookieJar
CookieJar::instance(m_config.cookiesFile());

m_page = new WebPage(this, QUrl::fromLocalFile(m_config.scriptFile()));
m_pages.append(m_page);

Expand Down Expand Up @@ -253,6 +257,25 @@ bool Phantom::printDebugMessages() const
return m_config.printDebugMessages();
}

QVariantMap Phantom::keys() const
{
return m_keyMap;
}

bool Phantom::areCookiesEnabled() const
{
return CookieJar::instance()->isEnabled();
}

void Phantom::setCookiesEnabled(const bool value)
{
if (value) {
CookieJar::instance()->enable();
} else {
CookieJar::instance()->disable();
}
}

// public slots:
QObject *Phantom::createWebPage()
{
Expand Down Expand Up @@ -382,12 +405,9 @@ void Phantom::initCompletions()
addCompletion("outputEncoding");
addCompletion("scriptName");
addCompletion("version");
addCompletion("keys");
addCompletion("cookiesEnabled");
// functions
addCompletion("exit");
addCompletion("injectJs");
}

QVariantMap Phantom::keys() const
{
return m_keyMap;
}
4 changes: 4 additions & 0 deletions src/phantom.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ class Phantom: public REPLCompletable
Q_PROPERTY(QVariantMap version READ version)
Q_PROPERTY(QObject *page READ page)
Q_PROPERTY(QVariantMap keys READ keys)
Q_PROPERTY(bool cookiesEnabled READ areCookiesEnabled WRITE setCookiesEnabled)

private:
// Private constructor: the Phantom class is a singleton
Expand Down Expand Up @@ -97,6 +98,9 @@ class Phantom: public REPLCompletable

QVariantMap keys() const;

bool areCookiesEnabled() const;
void setCookiesEnabled(const bool value);

public slots:
QObject *createWebPage();
QObject *createWebServer();
Expand Down
30 changes: 28 additions & 2 deletions src/webpage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
#include "config.h"
#include "consts.h"
#include "callback.h"
#include "cookiejar.h"

// Ensure we have at least head and body.
#define BLANK_HTML "<html><head></head><body></body></html>"
Expand Down Expand Up @@ -563,12 +564,33 @@ QVariantMap WebPage::customHeaders() const

void WebPage::setCookies(const QVariantList &cookies)
{
m_networkAccessManager->setCookies(cookies);
// Delete all the cookies for this URL
CookieJar::instance()->deleteCookies(this->url());
// Add a new set of cookies foor this URL
CookieJar::instance()->addCookiesFromMap(cookies, this->url());
}

QVariantList WebPage::cookies() const
{
return m_networkAccessManager->cookies();
// Return all the Cookies visible to this Page, as a list of Maps (aka JSON in JS space)
return CookieJar::instance()->cookiesToMap(this->url());
}

void WebPage::addCookie(const QVariantMap &cookie)
{
CookieJar::instance()->addCookieFromMap(cookie, this->url());
}

void WebPage::deleteCookie(const QString &cookieName)
{
if (!cookieName.isEmpty()) {
CookieJar::instance()->deleteCookie(cookieName, this->url());
}
}

void WebPage::clearCookies()
{
CookieJar::instance()->deleteCookie(this->url());
}

void WebPage::openUrl(const QString &address, const QVariant &op, const QVariantMap &settings)
Expand Down Expand Up @@ -1251,6 +1273,7 @@ void WebPage::initCompletions()
addCompletion("frameName");
addCompletion("framesName");
addCompletion("framesCount");
addCompletion("cookies");
// functions
addCompletion("evaluate");
addCompletion("includeJs");
Expand All @@ -1265,6 +1288,9 @@ void WebPage::initCompletions()
addCompletion("switchToFrame");
addCompletion("switchToMainFrame");
addCompletion("switchToParentFrame");
addCompletion("addCookie");
addCompletion("deleteCookie");
addCompletion("clearCookies");
// callbacks
addCompletion("onAlert");
addCompletion("onCallback");
Expand Down
45 changes: 45 additions & 0 deletions src/webpage.h
Original file line number Diff line number Diff line change
Expand Up @@ -329,8 +329,53 @@ public slots:
*/
QString currentFrameName() const;

/**
* Allows to set cookies by this Page, at the current URL.
* This means that loading new URLs, causes the cookies to change dynamically
* as in a normal desktop browser.
*
* Cookies are expected in the format:
* <pre>
* {
* "name" : "cookie name (string)",
* "value" : "cookie value (string)",
* "domain" : "cookie domain (string)",
* "path" : "cookie path (string, optional)",
* "httponly" : "http only cookie (boolean, optional)",
* "secure" : "secure cookie (boolean, optional)",
* "expires" : "expiration date (string, GMT format, optional)"
* }
* </pre>
* @brief setCookies
* @param cookies Expects a QList of QVariantMaps
*/
void setCookies(const QVariantList &cookies);
/**
* Cookies visible by this Page, at the current URL.
*
* @see WebPage::setCookies for details on the format
* @brief cookies
* @return QList of QVariantMap cookies visible to this Page, at the current URL.
*/
QVariantList cookies() const;
/**
* Add a Cookie in QVariantMap format
* @see WebPage::setCookies for details on the format
* @brief addCookie
* @param cookie Cookie in QVariantMap format
*/
void addCookie(const QVariantMap &cookie);
/**
* Delete cookie by name from the ones visible by this Page, at the current URL
* @brief deleteCookie
* @param cookieName Name of the Cookie to delete
*/
void deleteCookie(const QString &cookieName);
/**
* Delete All Cookies visible by this Page, at the current URL
* @brief clearCookies
*/
void clearCookies();

signals:
void initialized();
Expand Down
5 changes: 5 additions & 0 deletions test/phantom-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,9 @@ describe("phantom global object", function() {
it("should have 'keys' property", function() {
expect(phantom.hasOwnProperty('keys')).toBeTruthy();
});

it("should have 'cookiesEnabled' property, and should be 'true' by default", function() {
expect(phantom.hasOwnProperty('cookiesEnabled')).toBeTruthy();
expect(phantom.cookiesEnabled).toBeTruthy();
});
});
Loading

0 comments on commit c503040

Please sign in to comment.