mirror of https://github.com/vitalif/phantomjs
commit
a5885abcbf
|
@ -0,0 +1,25 @@
|
||||||
|
// Use 'page.loadJsFile()' to load the script itself in the Page context
|
||||||
|
|
||||||
|
if ( typeof(phantom) !== "undefined" ) {
|
||||||
|
var page = new WebPage();
|
||||||
|
|
||||||
|
// Route "console.log()" calls from within the Page context to the main Phantom context (i.e. current "this")
|
||||||
|
page.onConsoleMessage = function(msg) {
|
||||||
|
console.log(msg);
|
||||||
|
};
|
||||||
|
|
||||||
|
page.onAlert = function(msg) {
|
||||||
|
console.log(msg);
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log("* Script running in the Phantom context.");
|
||||||
|
console.log("* Script will 'inject' itself in a page...");
|
||||||
|
page.open("about:blank", function(status) {
|
||||||
|
if ( status === "success" ) {
|
||||||
|
console.log(page.injectJs("injectme.js") ? "... done injecting itself!" : "... fail! Check the $PWD?!");
|
||||||
|
}
|
||||||
|
phantom.exit();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
alert("* Script running in the Page context.");
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
// Read the Phantom webpage '#intro' element text using jQuery and "includeJs"
|
||||||
|
|
||||||
|
var page = new WebPage();
|
||||||
|
|
||||||
|
page.onConsoleMessage = function(msg) {
|
||||||
|
console.log(msg);
|
||||||
|
};
|
||||||
|
|
||||||
|
page.open("http://www.phantomjs.org", function(status) {
|
||||||
|
if ( status === "success" ) {
|
||||||
|
page.includeJs("http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js", function() {
|
||||||
|
page.evaluate(function() {
|
||||||
|
console.log("$(\"#intro\").text() -> " + $("#intro").text());
|
||||||
|
});
|
||||||
|
phantom.exit();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
|
@ -7,36 +7,36 @@ window.WebPage = function() {
|
||||||
page.settings = JSON.parse(JSON.stringify(phantom.defaultPageSettings));
|
page.settings = JSON.parse(JSON.stringify(phantom.defaultPageSettings));
|
||||||
|
|
||||||
// private, don't touch this
|
// private, don't touch this
|
||||||
page.handlers = {};
|
page._handlers = {};
|
||||||
|
|
||||||
page.__defineSetter__("onLoadStarted", function(f) {
|
page.__defineSetter__("onLoadStarted", function(f) {
|
||||||
if (this.handlers && typeof this.handlers.loadStarted === 'function') {
|
if (this._handlers && typeof this._handlers.loadStarted === 'function') {
|
||||||
try {
|
try {
|
||||||
this.loadStarted.disconnect(this.handlers.loadStarted);
|
this.loadStarted.disconnect(this._handlers.loadStarted);
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
this.handlers.loadStarted = f;
|
this._handlers.loadStarted = f;
|
||||||
this.loadStarted.connect(this.handlers.loadStarted);
|
this.loadStarted.connect(this._handlers.loadStarted);
|
||||||
});
|
});
|
||||||
|
|
||||||
page.__defineSetter__("onLoadFinished", function(f) {
|
page.__defineSetter__("onLoadFinished", function(f) {
|
||||||
if (this.handlers && typeof this.handlers.loadFinished === 'function') {
|
if (this._handlers && typeof this._handlers.loadFinished === 'function') {
|
||||||
try {
|
try {
|
||||||
this.loadFinished.disconnect(this.handlers.loadFinished);
|
this.loadFinished.disconnect(this._handlers.loadFinished);
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
this.handlers.loadFinished = f;
|
this._handlers.loadFinished = f;
|
||||||
this.loadFinished.connect(this.handlers.loadFinished);
|
this.loadFinished.connect(this._handlers.loadFinished);
|
||||||
});
|
});
|
||||||
|
|
||||||
page.__defineSetter__("onResourceRequested", function(f) {
|
page.__defineSetter__("onResourceRequested", function(f) {
|
||||||
if (this.handlers && typeof this.handlers.resourceRequested === 'function') {
|
if (this._handlers && typeof this._handlers.resourceRequested === 'function') {
|
||||||
try {
|
try {
|
||||||
this.resourceRequested.disconnect(this.handlers.resourceRequested);
|
this.resourceRequested.disconnect(this._handlers.resourceRequested);
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
this.handlers.resourceRequested = f;
|
this._handlers.resourceRequested = f;
|
||||||
this.resourceRequested.connect(this.handlers.resourceRequested);
|
this.resourceRequested.connect(this._handlers.resourceRequested);
|
||||||
});
|
});
|
||||||
|
|
||||||
page.__defineSetter__("onResourceReceived", function(f) {
|
page.__defineSetter__("onResourceReceived", function(f) {
|
||||||
|
@ -83,5 +83,22 @@ window.WebPage = function() {
|
||||||
throw "Wrong use of WebPage#open";
|
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(this);
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Append the script tag to the body
|
||||||
|
this._appendScriptElement(scriptUrl);
|
||||||
|
};
|
||||||
|
|
||||||
return page;
|
return page;
|
||||||
}
|
}
|
||||||
|
|
12
src/consts.h
12
src/consts.h
|
@ -35,5 +35,17 @@
|
||||||
#define PHANTOMJS_VERSION_MINOR 2
|
#define PHANTOMJS_VERSION_MINOR 2
|
||||||
#define PHANTOMJS_VERSION_PATCH 0
|
#define PHANTOMJS_VERSION_PATCH 0
|
||||||
#define PHANTOMJS_VERSION_STRING "1.2.0"
|
#define PHANTOMJS_VERSION_STRING "1.2.0"
|
||||||
|
#define COFFEE_SCRIPT_EXTENSION ".coffee"
|
||||||
|
|
||||||
|
#define JS_ELEMENT_CLICK "(function (el) { " \
|
||||||
|
"var ev = document.createEvent('MouseEvents');" \
|
||||||
|
"ev.initEvent(\"click\", true, true);" \
|
||||||
|
"el.dispatchEvent(ev);" \
|
||||||
|
"})(this);"
|
||||||
|
|
||||||
|
#define JS_APPEND_SCRIPT_ELEMENT "var el = document.createElement('script');" \
|
||||||
|
"el.onload = function() { alert('%1'); };" \
|
||||||
|
"el.src = '%1';" \
|
||||||
|
"document.body.appendChild(el);"
|
||||||
|
|
||||||
#endif // CONSTS_H
|
#endif // CONSTS_H
|
||||||
|
|
|
@ -52,8 +52,7 @@ CSConverter::CSConverter(QObject *parent)
|
||||||
QString CSConverter::convert(const QString &script)
|
QString CSConverter::convert(const QString &script)
|
||||||
{
|
{
|
||||||
setProperty("source", script);
|
setProperty("source", script);
|
||||||
QWebFrame *frame = m_webPage.mainFrame();
|
QVariant result = m_webPage.mainFrame()->evaluateJavaScript("this.CoffeeScript.compile(converter.source)");
|
||||||
QVariant result = frame->evaluateJavaScript("this.CoffeeScript.compile(converter.source)");
|
|
||||||
if (result.type() == QVariant::String)
|
if (result.type() == QVariant::String)
|
||||||
return result.toString();
|
return result.toString();
|
||||||
return QString();
|
return QString();
|
||||||
|
|
|
@ -58,6 +58,8 @@ int main(int argc, char** argv)
|
||||||
Phantom phantom;
|
Phantom phantom;
|
||||||
if (phantom.execute()) {
|
if (phantom.execute()) {
|
||||||
app.exec();
|
app.exec();
|
||||||
|
} else {
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
return phantom.returnValue();
|
return phantom.returnValue();
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,8 @@
|
||||||
|
|
||||||
#include <QtGui>
|
#include <QtGui>
|
||||||
#include <QtWebKit>
|
#include <QtWebKit>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QFileInfo>
|
||||||
|
|
||||||
#include <gifwriter.h>
|
#include <gifwriter.h>
|
||||||
#include "consts.h"
|
#include "consts.h"
|
||||||
|
@ -184,31 +186,9 @@ QVariantMap Phantom::defaultPageSettings() const
|
||||||
|
|
||||||
bool Phantom::execute()
|
bool Phantom::execute()
|
||||||
{
|
{
|
||||||
if (m_scriptFile.isEmpty())
|
return !m_scriptFile.isEmpty() && //< script filename provided
|
||||||
return false;
|
Utils::injectJsInFrame(m_scriptFile, QDir::currentPath(), m_page->mainFrame()) && //< script injected
|
||||||
|
!m_terminated; //< not terminated
|
||||||
QFile file;
|
|
||||||
file.setFileName(m_scriptFile);
|
|
||||||
if (!file.open(QFile::ReadOnly)) {
|
|
||||||
std::cerr << "Can't open '" << qPrintable(m_scriptFile) << "'" << std::endl << std::endl;
|
|
||||||
exit(1);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
m_script = QString::fromUtf8(file.readAll());
|
|
||||||
file.close();
|
|
||||||
|
|
||||||
if (m_scriptFile.endsWith(".coffee")) {
|
|
||||||
if (!m_converter)
|
|
||||||
m_converter = new CSConverter(this);
|
|
||||||
m_script = m_converter->convert(m_script);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_script.startsWith("#!")) {
|
|
||||||
m_script.prepend("//");
|
|
||||||
}
|
|
||||||
|
|
||||||
m_page->mainFrame()->evaluateJavaScript(m_script);
|
|
||||||
return !m_terminated;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int Phantom::returnValue() const
|
int Phantom::returnValue() const
|
||||||
|
@ -230,6 +210,7 @@ QObject *Phantom::createWebPage()
|
||||||
WebPage *page = new WebPage(this);
|
WebPage *page = new WebPage(this);
|
||||||
page->applySettings(m_defaultPageSettings);
|
page->applySettings(m_defaultPageSettings);
|
||||||
page->setNetworkAccessManager(m_netAccessMan);
|
page->setNetworkAccessManager(m_netAccessMan);
|
||||||
|
page->setScriptLookupDir(QFileInfo(m_scriptFile).dir().absolutePath());
|
||||||
return page;
|
return page;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,6 +221,10 @@ void Phantom::exit(int code)
|
||||||
QApplication::instance()->exit(code);
|
QApplication::instance()->exit(code);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Phantom::injectJs(const QString &jsFilePath) {
|
||||||
|
return Utils::injectJsInFrame(jsFilePath, QDir::currentPath(), m_page->mainFrame());
|
||||||
|
}
|
||||||
|
|
||||||
void Phantom::printConsoleMessage(const QString &msg)
|
void Phantom::printConsoleMessage(const QString &msg)
|
||||||
{
|
{
|
||||||
std::cout << qPrintable(msg) << std::endl;
|
std::cout << qPrintable(msg) << std::endl;
|
||||||
|
|
|
@ -58,6 +58,7 @@ public:
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
QObject *createWebPage();
|
QObject *createWebPage();
|
||||||
|
bool injectJs(const QString &jsFilePath);
|
||||||
void exit(int code = 0);
|
void exit(int code = 0);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
|
|
@ -31,7 +31,9 @@
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
|
#include <QDir>
|
||||||
|
|
||||||
|
#include "consts.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
// public:
|
// public:
|
||||||
|
@ -67,6 +69,50 @@ void Utils::messageHandler(QtMsgType type, const char *msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString Utils::coffee2js(const QString &script)
|
||||||
|
{
|
||||||
|
// We need only one instance of the CSConverter to survive for the whole life of PhantomJS
|
||||||
|
static CSConverter *coffeeScriptConverter = NULL;
|
||||||
|
if ( !coffeeScriptConverter ) {
|
||||||
|
coffeeScriptConverter = new CSConverter();
|
||||||
|
}
|
||||||
|
|
||||||
|
return coffeeScriptConverter->convert(script);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Utils::injectJsInFrame(const QString &jsFilePath, const QString &scriptLookupDir, QWebFrame *targetFrame)
|
||||||
|
{
|
||||||
|
// Don't do anything if an empty string is passed
|
||||||
|
if (!jsFilePath.isEmpty()) {
|
||||||
|
QFile jsFile;
|
||||||
|
|
||||||
|
// Is file in the PWD?
|
||||||
|
jsFile.setFileName(QDir::fromNativeSeparators(jsFilePath)); //< Normalise User-provided path
|
||||||
|
if (!jsFile.exists()) {
|
||||||
|
// File is not in the PWD. Is it in the lookup directory?
|
||||||
|
jsFile.setFileName( scriptLookupDir + '/' + QDir::fromNativeSeparators(jsFilePath) );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( jsFile.open(QFile::ReadOnly) ) {
|
||||||
|
QString scriptBody = QString::fromUtf8(jsFile.readAll());
|
||||||
|
// Remove CLI script heading
|
||||||
|
if (scriptBody.startsWith("#!")) {
|
||||||
|
scriptBody.prepend("//");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute JS code in the context of the document
|
||||||
|
targetFrame->evaluateJavaScript(jsFile.fileName().endsWith(COFFEE_SCRIPT_EXTENSION) ?
|
||||||
|
Utils::coffee2js(scriptBody) : //< convert from Coffee Script
|
||||||
|
scriptBody);
|
||||||
|
jsFile.close();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
std::cerr << "Can't open '" << qPrintable(jsFilePath) << "'" << std::endl << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// private:
|
// private:
|
||||||
Utils::Utils()
|
Utils::Utils()
|
||||||
{
|
{
|
||||||
|
|
|
@ -31,6 +31,9 @@
|
||||||
#define UTILS_H
|
#define UTILS_H
|
||||||
|
|
||||||
#include <QtGlobal>
|
#include <QtGlobal>
|
||||||
|
#include <QWebFrame>
|
||||||
|
|
||||||
|
#include "csconverter.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Aggregate common utility functions.
|
* Aggregate common utility functions.
|
||||||
|
@ -42,6 +45,8 @@ class Utils
|
||||||
public:
|
public:
|
||||||
static void showUsage();
|
static void showUsage();
|
||||||
static void messageHandler(QtMsgType type, const char *msg);
|
static void messageHandler(QtMsgType type, const char *msg);
|
||||||
|
static QString coffee2js(const QString &script);
|
||||||
|
static bool injectJsInFrame(const QString &jsFilePath, const QString &scriptLookupDir, QWebFrame *targetFrame);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Utils(); //< This class shouldn't be instantiated
|
Utils(); //< This class shouldn't be instantiated
|
||||||
|
|
|
@ -43,12 +43,16 @@
|
||||||
#include <QWebFrame>
|
#include <QWebFrame>
|
||||||
#include <QWebPage>
|
#include <QWebPage>
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
|
#if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
|
||||||
#include <QWebElement>
|
#include <QWebElement>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <gifwriter.h>
|
#include <gifwriter.h>
|
||||||
|
|
||||||
|
#include "consts.h"
|
||||||
|
|
||||||
class CustomPage: public QWebPage
|
class CustomPage: public QWebPage
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -160,6 +164,17 @@ void WebPage::setContent(const QString &content)
|
||||||
m_mainFrame->setHtml(content);
|
m_mainFrame->setHtml(content);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QString WebPage::scriptLookupDir() const
|
||||||
|
{
|
||||||
|
return m_scriptLookupDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebPage::setScriptLookupDir(const QString &dirPath)
|
||||||
|
{
|
||||||
|
m_scriptLookupDir = dirPath;
|
||||||
|
}
|
||||||
|
|
||||||
void WebPage::applySettings(const QVariantMap &def)
|
void WebPage::applySettings(const QVariantMap &def)
|
||||||
{
|
{
|
||||||
QWebSettings *opt = m_webPage->settings();
|
QWebSettings *opt = m_webPage->settings();
|
||||||
|
@ -459,12 +474,6 @@ bool WebPage::renderPdf(const QString &fileName)
|
||||||
|
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
|
#if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
|
||||||
|
|
||||||
#define ELEMENT_CLICK "(function (el) { " \
|
|
||||||
"var ev = document.createEvent('MouseEvents');" \
|
|
||||||
"ev.initEvent('click', true, true);" \
|
|
||||||
"el.dispatchEvent(ev);" \
|
|
||||||
"})(this)"
|
|
||||||
|
|
||||||
void WebPage::uploadFile(const QString &selector, const QString &fileName)
|
void WebPage::uploadFile(const QString &selector, const QString &fileName)
|
||||||
{
|
{
|
||||||
QWebElement el = m_mainFrame->findFirstElement(selector);
|
QWebElement el = m_mainFrame->findFirstElement(selector);
|
||||||
|
@ -472,6 +481,15 @@ void WebPage::uploadFile(const QString &selector, const QString &fileName)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_webPage->m_uploadFile = fileName;
|
m_webPage->m_uploadFile = fileName;
|
||||||
el.evaluateJavaScript(ELEMENT_CLICK);
|
el.evaluateJavaScript(JS_ELEMENT_CLICK);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
bool WebPage::injectJs(const QString &jsFilePath) {
|
||||||
|
return Utils::injectJsInFrame(jsFilePath, m_scriptLookupDir, m_mainFrame);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebPage::_appendScriptElement(const QString &scriptUrl) {
|
||||||
|
m_mainFrame->evaluateJavaScript( QString(JS_APPEND_SCRIPT_ELEMENT).arg(scriptUrl) );
|
||||||
|
}
|
||||||
|
|
|
@ -42,6 +42,7 @@ class WebPage: public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_PROPERTY(QString content READ content WRITE setContent)
|
Q_PROPERTY(QString content READ content WRITE setContent)
|
||||||
|
Q_PROPERTY(QString scriptLookupDir READ scriptLookupDir WRITE setScriptLookupDir)
|
||||||
Q_PROPERTY(QVariantMap viewportSize READ viewportSize WRITE setViewportSize)
|
Q_PROPERTY(QVariantMap viewportSize READ viewportSize WRITE setViewportSize)
|
||||||
Q_PROPERTY(QVariantMap paperSize READ paperSize WRITE setPaperSize)
|
Q_PROPERTY(QVariantMap paperSize READ paperSize WRITE setPaperSize)
|
||||||
Q_PROPERTY(QVariantMap clipRect READ clipRect WRITE setClipRect)
|
Q_PROPERTY(QVariantMap clipRect READ clipRect WRITE setClipRect)
|
||||||
|
@ -55,6 +56,9 @@ public:
|
||||||
QString content() const;
|
QString content() const;
|
||||||
void setContent(const QString &content);
|
void setContent(const QString &content);
|
||||||
|
|
||||||
|
QString scriptLookupDir() const;
|
||||||
|
void setScriptLookupDir(const QString &dirPath);
|
||||||
|
|
||||||
void setViewportSize(const QVariantMap &size);
|
void setViewportSize(const QVariantMap &size);
|
||||||
QVariantMap viewportSize() const;
|
QVariantMap viewportSize() const;
|
||||||
|
|
||||||
|
@ -64,11 +68,12 @@ public:
|
||||||
void setPaperSize(const QVariantMap &size);
|
void setPaperSize(const QVariantMap &size);
|
||||||
QVariantMap paperSize() const;
|
QVariantMap paperSize() const;
|
||||||
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void openUrl(const QString &address, const QVariant &op, const QVariantMap &settings);
|
void openUrl(const QString &address, const QVariant &op, const QVariantMap &settings);
|
||||||
QVariant evaluate(const QString &code);
|
QVariant evaluate(const QString &code);
|
||||||
bool render(const QString &fileName);
|
bool render(const QString &fileName);
|
||||||
|
bool injectJs(const QString &jsFilePath);
|
||||||
|
void _appendScriptElement(const QString &scriptUrl);
|
||||||
|
|
||||||
// moc does not understand QT_VERSION_CHECK and hence the encoded hex
|
// moc does not understand QT_VERSION_CHECK and hence the encoded hex
|
||||||
#if QT_VERSION >= 0x040600
|
#if QT_VERSION >= 0x040600
|
||||||
|
@ -91,6 +96,7 @@ private:
|
||||||
QWebFrame *m_mainFrame;
|
QWebFrame *m_mainFrame;
|
||||||
QRect m_clipRect;
|
QRect m_clipRect;
|
||||||
QVariantMap m_paperSize; // For PDF output via render()
|
QVariantMap m_paperSize; // For PDF output via render()
|
||||||
|
QString m_scriptLookupDir;
|
||||||
|
|
||||||
QImage renderImage();
|
QImage renderImage();
|
||||||
bool renderPdf(const QString &fileName);
|
bool renderPdf(const QString &fileName);
|
||||||
|
|
Loading…
Reference in New Issue