diff --git a/phantomjs.pro b/phantomjs.pro new file mode 100644 index 0000000000..24663df32f --- /dev/null +++ b/phantomjs.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs +CONFIG += ordered +SUBDIRS += src/phantomjs.pro diff --git a/src/phantomjs.cpp b/src/phantomjs.cpp new file mode 100644 index 0000000000..dbef988dd5 --- /dev/null +++ b/src/phantomjs.cpp @@ -0,0 +1,293 @@ +/* + This file is part of the PhantomJS project from Ofi Labs. + + Copyright (C) 2010 Ariya Hidayat + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include + +#if QT_VERSION < QT_VERSION_CHECK(4, 7, 0) +#error Use Qt 4.7 or later version +#endif + +class WebPage: public QWebPage +{ + Q_OBJECT +public: + WebPage(QObject *parent = 0); + +public slots: + bool shouldInterruptJavaScript(); + +protected: + QString userAgentForUrl(const QUrl &url) const; + +private: + QString m_userAgent; + friend class Phantom; +}; + +WebPage::WebPage(QObject *parent) + : QWebPage(parent) +{ + m_userAgent = QWebPage::userAgentForUrl(QUrl()); +} + +bool WebPage::shouldInterruptJavaScript() +{ + QApplication::processEvents(QEventLoop::AllEvents, 42); + return false; +} + +QString WebPage::userAgentForUrl(const QUrl &url) const +{ + Q_UNUSED(url); + return m_userAgent; +} + +class Phantom: public QObject +{ + Q_OBJECT + Q_PROPERTY(QStringList arguments READ arguments) + Q_PROPERTY(QString content READ content WRITE setContent) + Q_PROPERTY(bool inspectorEnabled READ isInspectorEnabled WRITE setInspectorEnabled) + Q_PROPERTY(bool inspectorVisible READ isInspectorVisible WRITE setInspectorVisible) + Q_PROPERTY(QString loadStatus READ loadStatus) + Q_PROPERTY(QString storage READ storage WRITE setStorage) + Q_PROPERTY(QString userAgent READ userAgent WRITE setUserAgent) + +public: + Phantom(QObject *parent = 0); + + QStringList arguments() const; + + QString content() const; + void setContent(const QString &content); + + void execute(const QString &fileName); + int returnValue() const; + + QString loadStatus() const; + + bool isInspectorEnabled() const; + void setInspectorEnabled(bool enable); + + bool isInspectorVisible() const; + void setInspectorVisible(bool visible); + + void setStorage(const QString &value); + QString storage() const; + + void setUserAgent(const QString &ua); + QString userAgent() const; + +public slots: + void exit(int code = 0); + void log(const QString &msg); + void open(const QString &address); + void sleep(int ms); + +private slots: + void inject(); + void finish(bool); + +private: + QStringList m_arguments; + QWebInspector m_inspector; + QString m_loadStatus; + WebPage m_page; + int m_returnValue; + QString m_script; + QString m_storage; +}; + +Phantom::Phantom(QObject *parent) + : QObject(parent) + , m_returnValue(0) +{ + QPalette palette = m_page.palette(); + palette.setBrush(QPalette::Base, Qt::transparent); + m_page.setPalette(palette); + + // first argument: program name (phantomjs) + // second argument: script name + m_arguments = QApplication::arguments(); + m_arguments.removeFirst(); + m_arguments.removeFirst(); + + m_inspector.setPage(&m_page); + + connect(m_page.mainFrame(), SIGNAL(javaScriptWindowObjectCleared()), SLOT(inject())); + connect(&m_page, SIGNAL(loadFinished(bool)), this, SLOT(finish(bool))); + + m_page.settings()->setAttribute(QWebSettings::OfflineStorageDatabaseEnabled, true); + m_page.settings()->setAttribute(QWebSettings::LocalStorageEnabled, true); + m_page.settings()->setLocalStoragePath(QDesktopServices::storageLocation(QDesktopServices::DataLocation)); + m_page.settings()->setOfflineStoragePath(QDesktopServices::storageLocation(QDesktopServices::DataLocation)); +} + +QStringList Phantom::arguments() const +{ + return m_arguments; +} + +QString Phantom::content() const +{ + return m_page.mainFrame()->toHtml(); +} + +void Phantom::setContent(const QString &content) +{ + m_page.mainFrame()->setHtml(content); +} + +void Phantom::execute(const QString &fileName) +{ + QFile file; + file.setFileName(fileName); + if (!file.open(QFile::ReadOnly)) { + std::cerr << "Can't open " << qPrintable(fileName) << std::endl << std::endl; + QApplication::instance()->exit(1); + return; + } + m_script = file.readAll(); + file.close(); + + m_page.mainFrame()->evaluateJavaScript(m_script); +} + +void Phantom::exit(int code) +{ + m_returnValue = code; + QTimer::singleShot(0, qApp, SLOT(quit())); +} + +void Phantom::finish(bool success) +{ + m_loadStatus = success ? "success" : "fail"; + m_page.mainFrame()->evaluateJavaScript(m_script); +} + +void Phantom::inject() +{ + m_page.mainFrame()->addToJavaScriptWindowObject("phantom", this); +} + +bool Phantom::isInspectorEnabled() const +{ + return m_page.settings()->testAttribute(QWebSettings::DeveloperExtrasEnabled); +} + +bool Phantom::isInspectorVisible() const +{ + return m_inspector.isVisible(); +} + +QString Phantom::loadStatus() const +{ + return m_loadStatus; +} + +void Phantom::log(const QString &msg) +{ + std::cout << qPrintable(msg) << std::endl; +} + +void Phantom::open(const QString &address) +{ + m_page.triggerAction(QWebPage::Stop); + m_loadStatus = "loading"; + m_page.mainFrame()->setUrl(address); +} + +void Phantom::setInspectorEnabled(bool enable) +{ + m_page.settings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, enable); +} + +void Phantom::setInspectorVisible(bool visible) +{ + m_inspector.setVisible(visible); +} + +int Phantom::returnValue() const +{ + return m_returnValue; +} + +void Phantom::sleep(int ms) +{ + QTime startTime = QTime::currentTime(); + while (true) { + QApplication::processEvents(QEventLoop::AllEvents, 25); + if (startTime.msecsTo(QTime::currentTime()) > ms) + break; + } +} + +void Phantom::setStorage(const QString &value) +{ + m_storage = value; +} + +QString Phantom::storage() const +{ + return m_storage; +} + +void Phantom::setUserAgent(const QString &ua) +{ + m_page.m_userAgent = ua; +} + +QString Phantom::userAgent() const +{ + return m_page.m_userAgent; +} + +#include "phantomjs.moc" + +int main(int argc, char** argv) +{ + if (argc < 2) { + std::cerr << "phantomjs script.js" << std::endl << std::endl; + return 1; + } + + QApplication app(argc, argv); + + app.setApplicationName("PhantomJS"); + app.setOrganizationName("Ofi Labs"); + app.setOrganizationDomain("www.ofilabs.com"); + app.setApplicationVersion("1.0"); + + Phantom phantom; + phantom.execute(QString::fromLocal8Bit(argv[1])); + app.exec(); + return phantom.returnValue(); +} diff --git a/src/phantomjs.pro b/src/phantomjs.pro new file mode 100644 index 0000000000..bf4eaf74fd --- /dev/null +++ b/src/phantomjs.pro @@ -0,0 +1,5 @@ +TEMPLATE = app +TARGET = phantomjs +DESTDIR = ../bin +SOURCES = phantomjs.cpp +QT += network webkit