Merge branch 'master' of git://github.com/ariya/phantomjs

1.3
IceArmy 2011-09-09 00:42:47 -07:00
commit 54ce2f72e1
11 changed files with 215 additions and 189 deletions

View File

@ -4,7 +4,10 @@
/* /*
This file is part of the PhantomJS project from Ofi Labs. This file is part of the PhantomJS project from Ofi Labs.
Copyright (C) 2011 Ariya Hidayat <ariya.hidayat@gmail.com>
Copyright (C) 2011 Ivan De Marino <ivan.de.marino@gmail.com> Copyright (C) 2011 Ivan De Marino <ivan.de.marino@gmail.com>
Copyright (C) 2011 James Roe <roejames12@hotmail.com>
Copyright (C) 2011 execjosh, http://execjosh.blogspot.com
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met: modification, are permitted provided that the following conditions are met:
@ -30,10 +33,159 @@
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
function require (name) { function require(name) {
var exports; var exports;
if (name === 'webpage') {
exports = function (opts) {
var page = phantom.createWebPage(),
handlers = {};
function checkType(o, type) {
return typeof o === type;
}
function isObject(o) {
return checkType(o, 'object');
}
function isUndefined(o) {
return checkType(o, 'undefined');
}
function isUndefinedOrNull(o) {
return isUndefined(o) || null === o;
}
function copyInto(target, source) {
if (target === source || isUndefinedOrNull(source)) {
return target;
}
target = target || {};
// Copy into objects only
if (isObject(target)) {
// Make sure source exists
source = source || {};
if (isObject(source)) {
var i, newTarget, newSource;
for (i in source) {
if (source.hasOwnProperty(i)) {
newTarget = target[i];
newSource = source[i];
if (newTarget && isObject(newSource)) {
// Deep copy
newTarget = copyInto(target[i], newSource);
} else {
newTarget = newSource;
}
if (!isUndefined(newTarget)) {
target[i] = newTarget;
}
}
}
} else {
target = source;
}
}
return target;
}
function defineSetter(handlerName, signalName) {
page.__defineSetter__(handlerName, function (f) {
if (handlers && typeof handlers[signalName] === 'function') {
try {
this[signalName].disconnect(handlers[signalName]);
} catch (e) {}
}
handlers[signalName] = f;
this[signalName].connect(handlers[signalName]);
});
}
// deep copy
page.settings = JSON.parse(JSON.stringify(phantom.defaultPageSettings));
defineSetter("onInitialized", "initialized");
defineSetter("onLoadStarted", "loadStarted");
defineSetter("onLoadFinished", "loadFinished");
defineSetter("onResourceRequested", "resourceRequested");
defineSetter("onResourceReceived", "resourceReceived");
defineSetter("onAlert", "javaScriptAlertSent");
defineSetter("onConsoleMessage", "javaScriptConsoleMessageSent");
page.open = function (url, arg1, arg2, arg3, arg4) {
if (arguments.length === 1) {
this.openUrl(url, 'get', this.settings);
return;
}
if (arguments.length === 2 && typeof arg1 === 'function') {
this.onLoadFinished = arg1;
this.openUrl(url, 'get', this.settings);
return;
} else if (arguments.length === 2) {
this.openUrl(url, arg1, this.settings);
return;
} else if (arguments.length === 3 && typeof arg2 === 'function') {
this.onLoadFinished = arg2;
this.openUrl(url, arg1, this.settings);
return;
} else if (arguments.length === 3) {
this.openUrl(url, {
operation: arg1,
data: arg2
}, this.settings);
return;
} else if (arguments.length === 4) {
this.onLoadFinished = arg3;
this.openUrl(url, {
operation: arg1,
data: arg2
}, this.settings);
return;
}
throw "Wrong use of WebPage#open";
};
page.includeJs = function (scriptUrl, onScriptLoaded) {
// Register temporary signal handler for 'alert()'
this.javaScriptAlertSent.connect(function (msgFromAlert) {
if (msgFromAlert === scriptUrl) {
// Resource loaded, time to fire the callback
onScriptLoaded(scriptUrl);
// And disconnect the signal handler
try {
this.javaScriptAlertSent.disconnect(arguments.callee);
} catch (e) {}
}
});
// Append the script tag to the body
this._appendScriptElement(scriptUrl);
};
// Copy options into page
if (opts) {
page = copyInto(page, opts);
}
return page;
};
}
if (name === 'fs') { if (name === 'fs') {
exports = phantom.createFilesystem(); exports = phantom.createFilesystem();
@ -104,9 +256,9 @@ function require (name) {
* @param destination Path of the destination file * @param destination Path of the destination file
*/ */
exports.copy = function (source, destination) { exports.copy = function (source, destination) {
if (!fs._copy(source, destination)) { if (!fs._copy(source, destination)) {
throw "Unable to copy file '" + source + "' at '" + destination + "'"; throw "Unable to copy file '" + source + "' at '" + destination + "'";
} }
}; };
/** Copy a directory tree. /** Copy a directory tree.
@ -116,9 +268,9 @@ function require (name) {
* @param destination Path of the destination directory tree * @param destination Path of the destination directory tree
*/ */
exports.copyTree = function (source, destination) { exports.copyTree = function (source, destination) {
if (!fs._copyTree(source, destination)) { if (!fs._copyTree(source, destination)) {
throw "Unable to copy directory tree '" + source + "' at '" + destination + "'"; throw "Unable to copy directory tree '" + source + "' at '" + destination + "'";
} }
}; };
/** Move a file. /** Move a file.
@ -128,8 +280,8 @@ function require (name) {
* @param destination Path of the destination file * @param destination Path of the destination file
*/ */
exports.move = function (source, destination) { exports.move = function (source, destination) {
fs.copy(source, destination); fs.copy(source, destination);
fs.remove(source); fs.remove(source);
}; };
/** Removes a file. /** Removes a file.
@ -138,9 +290,9 @@ function require (name) {
* @param path Path of the file to remove * @param path Path of the file to remove
*/ */
exports.remove = function (path) { exports.remove = function (path) {
if (!fs._remove(path)) { if (!fs._remove(path)) {
throw "Unable to remove file '" + path + "'"; throw "Unable to remove file '" + path + "'";
} }
}; };
/** Removes a directory. /** Removes a directory.
@ -149,9 +301,9 @@ function require (name) {
* @param path Path of the directory to remove * @param path Path of the directory to remove
*/ */
exports.removeDirectory = function (path) { exports.removeDirectory = function (path) {
if (!fs._removeDirectory(path)) { if (!fs._removeDirectory(path)) {
throw "Unable to remove directory '" + path + "'"; throw "Unable to remove directory '" + path + "'";
} }
}; };
/** Removes a directory tree. /** Removes a directory tree.
@ -160,13 +312,13 @@ function require (name) {
* @param path Path of the directory tree to remove * @param path Path of the directory tree to remove
*/ */
exports.removeTree = function (path) { exports.removeTree = function (path) {
if (!fs._removeTree(path)) { if (!fs._removeTree(path)) {
throw "Unable to remove directory tree '" + path + "'"; throw "Unable to remove directory tree '" + path + "'";
} }
}; };
exports.touch = function (path) { exports.touch = function (path) {
fs.write(path, "", 'a'); fs.write(path, "", 'a');
}; };
} }
@ -178,3 +330,5 @@ function require (name) {
return exports; return exports;
} }
// Legacy way to use WebPage
window.WebPage = require('webpage');

View File

@ -34,11 +34,12 @@
CookieJar::CookieJar(QString cookieFile) CookieJar::CookieJar(QString cookieFile)
: QNetworkCookieJar() : QNetworkCookieJar()
{ {
m_cookieFile = cookieFile; m_cookieFile = cookieFile;
} }
bool CookieJar::setCookiesFromUrl(const QList<QNetworkCookie> & cookieList, const QUrl & url) { bool CookieJar::setCookiesFromUrl(const QList<QNetworkCookie> & cookieList, const QUrl & url)
{
QSettings settings(m_cookieFile, QSettings::IniFormat); QSettings settings(m_cookieFile, QSettings::IniFormat);
settings.beginGroup(url.host()); settings.beginGroup(url.host());
@ -52,7 +53,8 @@ bool CookieJar::setCookiesFromUrl(const QList<QNetworkCookie> & cookieList, cons
return true; return true;
} }
QList<QNetworkCookie> CookieJar::cookiesForUrl(const QUrl & url) const { QList<QNetworkCookie> CookieJar::cookiesForUrl(const QUrl & url) const
{
QSettings settings(m_cookieFile, QSettings::IniFormat); QSettings settings(m_cookieFile, QSettings::IniFormat);
QList<QNetworkCookie> cookieList; QList<QNetworkCookie> cookieList;
@ -66,4 +68,3 @@ QList<QNetworkCookie> CookieJar::cookiesForUrl(const QUrl & url) const {
return cookieList; return cookieList;
} }

View File

@ -48,6 +48,7 @@ Phantom::Phantom(QObject *parent)
, m_terminated(false) , m_terminated(false)
, m_returnValue(0) , m_returnValue(0)
, m_netAccessMan(0) , m_netAccessMan(0)
, m_filesystem(0)
{ {
// second argument: script name // second argument: script name
QStringList args = QApplication::arguments(); QStringList args = QApplication::arguments();
@ -112,11 +113,10 @@ Phantom::Phantom(QObject *parent)
m_page->mainFrame()->addToJavaScriptWindowObject("phantom", this); m_page->mainFrame()->addToJavaScriptWindowObject("phantom", this);
// Load all the required JavaScript 'shims' // Load all the required JavaScript 'shims'
QString jsShims[2] = { QString jsShims[1] = {
":/bootstrap.js", ":/bootstrap.js"
":/webpage-shim.js"
}; };
for (int i = 0, len = 2; i < len; ++i) { for (int i = 0, len = 1; i < len; ++i) {
QFile f(jsShims[i]); QFile f(jsShims[i]);
f.open(QFile::ReadOnly); //< It's OK to assume this succeed. If it doesn't, we have a bigger problem. f.open(QFile::ReadOnly); //< It's OK to assume this succeed. If it doesn't, we have a bigger problem.
m_page->mainFrame()->evaluateJavaScript(QString::fromUtf8(f.readAll())); m_page->mainFrame()->evaluateJavaScript(QString::fromUtf8(f.readAll()));
@ -201,7 +201,10 @@ QObject *Phantom::createWebPage()
QObject *Phantom::createFilesystem() QObject *Phantom::createFilesystem()
{ {
return &m_filesystem; if (!m_filesystem)
m_filesystem = new FileSystem(this);
return m_filesystem;
} }
bool Phantom::injectJs(const QString &jsFilePath) { bool Phantom::injectJs(const QString &jsFilePath) {

View File

@ -87,7 +87,7 @@ private:
QString m_script; QString m_script;
NetworkAccessManager *m_netAccessMan; NetworkAccessManager *m_netAccessMan;
QVariantMap m_defaultPageSettings; QVariantMap m_defaultPageSettings;
FileSystem m_filesystem; FileSystem *m_filesystem;
QList<QPointer<WebPage> > m_pages; QList<QPointer<WebPage> > m_pages;
Config m_config; Config m_config;
}; };

View File

@ -36,7 +36,6 @@ SOURCES += phantom.cpp \
config.cpp config.cpp
OTHER_FILES += usage.txt \ OTHER_FILES += usage.txt \
webpage-shim.js \
bootstrap.js \ bootstrap.js \
configurator.js configurator.js

View File

@ -3,7 +3,6 @@
<file>phantomjs-icon.png</file> <file>phantomjs-icon.png</file>
<file>coffee-script.js</file> <file>coffee-script.js</file>
<file>usage.txt</file> <file>usage.txt</file>
<file>webpage-shim.js</file>
<file>bootstrap.js</file> <file>bootstrap.js</file>
<file>configurator.js</file> <file>configurator.js</file>
</qresource> </qresource>

View File

@ -6,7 +6,7 @@ Options:
--config=/path/to/config Specifies path to a JSON-formatted config file. --config=/path/to/config Specifies path to a JSON-formatted config file.
--load-images=[yes|no] Loads all inlined images (default is 'yes'). --load-images=[yes|no] Loads all inlined images (default is 'yes').
--load-plugins=[yes|no] Loads all plugins (i.e. 'Flash', 'Silverlight', ...) (default is 'no'). --load-plugins=[yes|no] Loads all plugins (i.e. 'Flash', 'Silverlight', ...) (default is 'no').
--proxy=address:port Sets the network proxy (e.g. "--proxy=http://192.168.1.42:8080"). --proxy=address:port Sets the network proxy (e.g. "--proxy=192.168.1.42:8080").
--auth=username:password Sets the authentication username and password (e.g. "--auth=username:password"). --auth=username:password Sets the authentication username and password (e.g. "--auth=username:password").
--disk-cache=[yes|no] Enables disk cache (at desktop services cache storage location, default is 'no'). --disk-cache=[yes|no] Enables disk cache (at desktop services cache storage location, default is 'no').
--max-disk-cache-size=size Limits the size of disk cache (in KB). --max-disk-cache-size=size Limits the size of disk cache (in KB).

View File

@ -33,150 +33,3 @@
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
// This allows creating a new web page using the construct "new WebPage",
// which feels more natural than "phantom.createWebPage()".
window.WebPage = function (opts) {
var page = phantom.createWebPage(),
handlers = {};
function checkType(o, type) {
return typeof o === type;
}
function isObject(o) {
return checkType(o, 'object');
}
function isUndefined(o) {
return checkType(o, 'undefined');
}
function isUndefinedOrNull(o) {
return isUndefined(o) || null === o;
}
function copyInto(target, source) {
if (target === source || isUndefinedOrNull(source)) {
return target;
}
target = target || {};
// Copy into objects only
if (isObject(target)) {
// Make sure source exists
source = source || {};
if (isObject(source)) {
var i, newTarget, newSource;
for (i in source) {
if (source.hasOwnProperty(i)) {
newTarget = target[i];
newSource = source[i];
if (newTarget && isObject(newSource)) {
// Deep copy
newTarget = copyInto(target[i], newSource);
} else {
newTarget = newSource;
}
if (!isUndefined(newTarget)) {
target[i] = newTarget;
}
}
}
} else {
target = source;
}
}
return target;
}
function defineSetter(handlerName, signalName) {
page.__defineSetter__(handlerName, function (f) {
if (handlers && typeof handlers[signalName] === 'function') {
try {
this[signalName].disconnect(handlers[signalName]);
} catch (e) {}
}
handlers[signalName] = f;
this[signalName].connect(handlers[signalName]);
});
}
// deep copy
page.settings = JSON.parse(JSON.stringify(phantom.defaultPageSettings));
defineSetter("onInitialized", "initialized");
defineSetter("onLoadStarted", "loadStarted");
defineSetter("onLoadFinished", "loadFinished");
defineSetter("onResourceRequested", "resourceRequested");
defineSetter("onResourceReceived", "resourceReceived");
defineSetter("onAlert", "javaScriptAlertSent");
defineSetter("onConsoleMessage", "javaScriptConsoleMessageSent");
page.open = function (url, arg1, arg2, arg3, arg4) {
if (arguments.length === 1) {
this.openUrl(url, 'get', this.settings);
return;
}
if (arguments.length === 2 && typeof arg1 === 'function') {
this.onLoadFinished = arg1;
this.openUrl(url, 'get', this.settings);
return;
} else if (arguments.length === 2) {
this.openUrl(url, arg1, this.settings);
return;
} else if (arguments.length === 3 && typeof arg2 === 'function') {
this.onLoadFinished = arg2;
this.openUrl(url, arg1, this.settings);
return;
} else if (arguments.length === 3) {
this.openUrl(url, {
operation: arg1,
data: arg2
}, this.settings);
return;
} else if (arguments.length === 4) {
this.onLoadFinished = arg3;
this.openUrl(url, {
operation: arg1,
data: arg2
}, this.settings);
return;
}
throw "Wrong use of WebPage#open";
};
page.includeJs = function (scriptUrl, onScriptLoaded) {
// Register temporary signal handler for 'alert()'
this.javaScriptAlertSent.connect(function (msgFromAlert) {
if (msgFromAlert === scriptUrl) {
// Resource loaded, time to fire the callback
onScriptLoaded(scriptUrl);
// And disconnect the signal handler
try {
this.javaScriptAlertSent.disconnect(arguments.callee);
} catch (e) {}
}
});
// Append the script tag to the body
this._appendScriptElement(scriptUrl);
};
// Copy options into page
if (opts) {
page = copyInto(page, opts);
}
return page;
};

View File

@ -237,6 +237,7 @@ void WebPage::setScrollPosition(const QVariantMap &size)
int top = size.value("top").toInt(); int top = size.value("top").toInt();
int left = size.value("left").toInt(); int left = size.value("left").toInt();
m_scrollPosition = QPoint(left,top); m_scrollPosition = QPoint(left,top);
m_mainFrame->setScrollPosition(m_scrollPosition);
} }
QVariantMap WebPage::scrollPosition() const QVariantMap WebPage::scrollPosition() const
@ -345,18 +346,15 @@ bool WebPage::render(const QString &fileName)
} }
QImage WebPage::renderImage() QImage WebPage::renderImage()
{ {
QSize viewportSize = m_webPage->viewportSize(); QSize contentsSize = m_mainFrame->contentsSize();
QRect frameRect = QRect(QPoint(0, 0), viewportSize); contentsSize -= QSize(m_scrollPosition.x(), m_scrollPosition.y());
QRect frameRect = QRect(QPoint(0, 0), contentsSize);
if (!m_clipRect.isNull()) if (!m_clipRect.isNull())
frameRect = m_clipRect; frameRect = m_clipRect;
if(!m_scrollPosition.isNull()) QSize viewportSize = m_webPage->viewportSize();
{ m_webPage->setViewportSize(contentsSize);
m_mainFrame->setScrollPosition(m_scrollPosition);
}
// m_webPage->setViewportSize(m_mainFrame->contentsSize());
QImage buffer(frameRect.size(), QImage::Format_ARGB32); QImage buffer(frameRect.size(), QImage::Format_ARGB32);
buffer.fill(qRgba(255, 255, 255, 0)); buffer.fill(qRgba(255, 255, 255, 0));

18
test/module-spec.js Normal file
View File

@ -0,0 +1,18 @@
describe("module loading using require", function() {
it("should work for 'webpage' module", function() {
expect(typeof require('webpage')).toEqual('function');
});
it("should work for 'fs' module", function() {
expect(typeof require('fs')).toEqual('object');
});
it("should throw an error for an unknown module", function() {
var module = 'foobar';
expect(function(){
var foo = require(module);
}).toThrow("Unknown module " + module + " for require()");
});
});

View File

@ -56,6 +56,7 @@ function expectHasPropertyString(o, name) {
// Load specs // Load specs
var fs = require('fs'); var fs = require('fs');
phantom.injectJs("./phantom-spec.js"); phantom.injectJs("./phantom-spec.js");
phantom.injectJs("./module-spec.js");
phantom.injectJs("./webpage-spec.js"); phantom.injectJs("./webpage-spec.js");
phantom.injectJs("./fs-spec-01.js"); //< Filesystem Specs 01 (Basic) phantom.injectJs("./fs-spec-01.js"); //< Filesystem Specs 01 (Basic)
phantom.injectJs("./fs-spec-02.js"); //< Filesystem Specs 02 (Attributes) phantom.injectJs("./fs-spec-02.js"); //< Filesystem Specs 02 (Attributes)